@cosyte/synth 0.1.4 → 0.1.6
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 +24 -0
- package/README.md +14 -10
- package/dist/astm/index.cjs +164 -102
- package/dist/astm/index.cjs.map +1 -1
- package/dist/astm/index.d.cts +7 -0
- package/dist/astm/index.d.ts +7 -0
- package/dist/astm/index.mjs +163 -102
- package/dist/astm/index.mjs.map +1 -1
- package/dist/ccda/index.cjs +100 -28
- package/dist/ccda/index.cjs.map +1 -1
- package/dist/ccda/index.d.cts +6 -0
- package/dist/ccda/index.d.ts +6 -0
- package/dist/ccda/index.mjs +87 -16
- package/dist/ccda/index.mjs.map +1 -1
- package/dist/deid/index.cjs +305 -165
- package/dist/deid/index.cjs.map +1 -1
- package/dist/deid/index.mjs +292 -153
- package/dist/deid/index.mjs.map +1 -1
- package/dist/fhir/index.cjs +179 -76
- package/dist/fhir/index.cjs.map +1 -1
- package/dist/fhir/index.d.cts +6 -0
- package/dist/fhir/index.d.ts +6 -0
- package/dist/fhir/index.mjs +178 -76
- package/dist/fhir/index.mjs.map +1 -1
- package/dist/hl7/index.cjs +86 -12
- package/dist/hl7/index.cjs.map +1 -1
- package/dist/hl7/index.d.cts +6 -0
- package/dist/hl7/index.d.ts +6 -0
- package/dist/hl7/index.mjs +85 -12
- package/dist/hl7/index.mjs.map +1 -1
- package/dist/index.cjs +3 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.mjs +3 -5
- package/dist/index.mjs.map +1 -1
- package/dist/ncpdp/index.cjs +131 -36
- package/dist/ncpdp/index.cjs.map +1 -1
- package/dist/ncpdp/index.d.cts +7 -0
- package/dist/ncpdp/index.d.ts +7 -0
- package/dist/ncpdp/index.mjs +130 -36
- package/dist/ncpdp/index.mjs.map +1 -1
- package/dist/x12/index.cjs +147 -50
- package/dist/x12/index.cjs.map +1 -1
- package/dist/x12/index.d.cts +6 -0
- package/dist/x12/index.d.ts +6 -0
- package/dist/x12/index.mjs +146 -50
- package/dist/x12/index.mjs.map +1 -1
- package/package.json +20 -15
package/dist/deid/index.cjs
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var x12$1 = require('@cosyte/deid/x12');
|
|
6
|
-
var ncpdp = require('@cosyte/deid/ncpdp');
|
|
7
|
-
var ccda$1 = require('@cosyte/deid/ccda');
|
|
8
|
-
var fhir = require('@cosyte/fhir');
|
|
9
|
-
var x12 = require('@cosyte/x12');
|
|
10
|
-
var ccda = require('@cosyte/ccda');
|
|
11
|
-
var telecom = require('@cosyte/ncpdp/telecom');
|
|
12
|
-
var hl7$1 = require('@cosyte/hl7');
|
|
13
|
-
var deid = require('@cosyte/deid');
|
|
3
|
+
var module$1 = require('module');
|
|
4
|
+
var url = require('url');
|
|
14
5
|
|
|
15
|
-
|
|
6
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
16
13
|
|
|
17
14
|
// src/rng/splitmix32.ts
|
|
18
15
|
function splitmix32(seed) {
|
|
@@ -44,7 +41,32 @@ function sfc32Next(s) {
|
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
// src/codes.ts
|
|
47
|
-
var SYNTH_FATAL_CODES = {
|
|
44
|
+
var SYNTH_FATAL_CODES = Object.freeze({
|
|
45
|
+
/**
|
|
46
|
+
* A format was requested that this build cannot generate through a real parser builder/serializer.
|
|
47
|
+
* Fatal, never a hand-written byte fallback.
|
|
48
|
+
*
|
|
49
|
+
* **No code path in this build raises it.** All six formats generate, so it is reserved for a
|
|
50
|
+
* future format that does not, and is kept because removing a published code is a breaking change.
|
|
51
|
+
* An unsupported *kind* within a format that does generate is `SYNTH_UNSUPPORTED_KIND`.
|
|
52
|
+
*/
|
|
53
|
+
SYNTH_UNSUPPORTED_FORMAT: "SYNTH_UNSUPPORTED_FORMAT",
|
|
54
|
+
/**
|
|
55
|
+
* A vendor quirk was requested that the target format's profile system does not support. Fatal,
|
|
56
|
+
* never a silent no-op and never a fabricated quirk.
|
|
57
|
+
*/
|
|
58
|
+
SYNTH_UNSUPPORTED_QUIRK: "SYNTH_UNSUPPORTED_QUIRK",
|
|
59
|
+
/**
|
|
60
|
+
* A quirk transform found no structural anchor to mutate, so the fixture would not carry the
|
|
61
|
+
* deviation it is labelled with. Fatal: a golden file that lies about its parser verdict is worse
|
|
62
|
+
* than no golden file.
|
|
63
|
+
*/
|
|
64
|
+
SYNTH_QUIRK_ANCHOR_ABSENT: "SYNTH_QUIRK_ANCHOR_ABSENT",
|
|
65
|
+
/**
|
|
66
|
+
* A bare parse of a freshly-generated quirk artifact did not produce exactly the declared intended
|
|
67
|
+
* warning code(s). Fatal, never emit a mislabeled fixture.
|
|
68
|
+
*/
|
|
69
|
+
SYNTH_INTENDED_WARNING_MISMATCH: "SYNTH_INTENDED_WARNING_MISMATCH",
|
|
48
70
|
/** A concept's code-system URI has no OID mapping in the C-CDA example-code table. Fatal. */
|
|
49
71
|
SYNTH_UNMAPPED_CODE_SYSTEM: "SYNTH_UNMAPPED_CODE_SYSTEM",
|
|
50
72
|
/** A money value could not be read as an X12 decimal. Fatal: a generator never rounds to a float. */
|
|
@@ -53,13 +75,25 @@ var SYNTH_FATAL_CODES = {
|
|
|
53
75
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
54
76
|
/** A value was drawn from an empty pool. Fatal, never a fabricated substitute. */
|
|
55
77
|
SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
|
|
78
|
+
/** A `defineSynthProfile` spec was not usable (a missing or blank `name`). Fatal. */
|
|
79
|
+
SYNTH_INVALID_PROFILE: "SYNTH_INVALID_PROFILE",
|
|
56
80
|
/**
|
|
57
81
|
* A caller-supplied selector (a message kind, a document type, a corpus mix entry, a claim
|
|
58
82
|
* variant, a Bundle type, a resource profile) is not in the closed set that governs it. Fatal:
|
|
59
83
|
* see `resolveKind`: a selector union is erased at run time, and a selector that falls through
|
|
60
84
|
* either mislabels the fixture or hands the value to a peer builder that quotes it back.
|
|
61
85
|
*/
|
|
62
|
-
SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND"
|
|
86
|
+
SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND",
|
|
87
|
+
/**
|
|
88
|
+
* A requested profile IS published by the adopted implementation guide, and this build does not
|
|
89
|
+
* generate it. Deliberately distinct from `SYNTH_UNSUPPORTED_KIND`, which says the name is not in
|
|
90
|
+
* the adopted set at all: "the guide does not publish this" and "we do not generate this yet" are
|
|
91
|
+
* different answers, and a caller building to a regulatory profile set has to be able to tell them
|
|
92
|
+
* apart without matching message text. Fatal, and raised **before** anything is generated: a
|
|
93
|
+
* mislabelled artifact is worse than no artifact.
|
|
94
|
+
*/
|
|
95
|
+
SYNTH_PROFILE_NOT_GENERATED: "SYNTH_PROFILE_NOT_GENERATED"
|
|
96
|
+
});
|
|
63
97
|
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
64
98
|
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.",
|
|
65
99
|
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).",
|
|
@@ -415,6 +449,40 @@ var safe = Object.freeze({
|
|
|
415
449
|
npi,
|
|
416
450
|
dea
|
|
417
451
|
});
|
|
452
|
+
function defaultHost() {
|
|
453
|
+
if (typeof undefined !== "function") return { require: __require, resolve: void 0 };
|
|
454
|
+
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) };
|
|
455
|
+
}
|
|
456
|
+
function isRequireEsmRefusal(error) {
|
|
457
|
+
return error?.code === "ERR_REQUIRE_ESM";
|
|
458
|
+
}
|
|
459
|
+
function loadPeer(specifier, host) {
|
|
460
|
+
if (host.resolve === void 0) return host.require(specifier);
|
|
461
|
+
const entry = url.fileURLToPath(host.resolve(specifier));
|
|
462
|
+
try {
|
|
463
|
+
return host.require(entry);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
if (!isRequireEsmRefusal(error)) throw error;
|
|
466
|
+
return host.require(specifier);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function lazyPeer(specifier, host) {
|
|
470
|
+
let loaded;
|
|
471
|
+
return () => loaded ??= loadPeer(specifier, defaultHost());
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// src/peers.ts
|
|
475
|
+
var hl7Peer = lazyPeer("@cosyte/hl7");
|
|
476
|
+
var fhirPeer = lazyPeer("@cosyte/fhir");
|
|
477
|
+
var ccdaPeer = lazyPeer("@cosyte/ccda");
|
|
478
|
+
var x12Peer = lazyPeer("@cosyte/x12");
|
|
479
|
+
var ncpdpTelecomPeer = lazyPeer("@cosyte/ncpdp/telecom");
|
|
480
|
+
var deidPeer = lazyPeer("@cosyte/deid");
|
|
481
|
+
var deidHl7Peer = lazyPeer("@cosyte/deid/hl7");
|
|
482
|
+
var deidFhirPeer = lazyPeer("@cosyte/deid/fhir");
|
|
483
|
+
var deidX12Peer = lazyPeer("@cosyte/deid/x12");
|
|
484
|
+
var deidNcpdpPeer = lazyPeer("@cosyte/deid/ncpdp");
|
|
485
|
+
var deidCcdaPeer = lazyPeer("@cosyte/deid/ccda");
|
|
418
486
|
|
|
419
487
|
// src/hl7/common.ts
|
|
420
488
|
function seededTimestamp(rng) {
|
|
@@ -427,7 +495,7 @@ function seededTimestamp(rng) {
|
|
|
427
495
|
function mshScaffold(rng, type) {
|
|
428
496
|
const timestamp = seededTimestamp(rng);
|
|
429
497
|
const controlId = `SYNTH${rng.digits(10)}`;
|
|
430
|
-
const message =
|
|
498
|
+
const message = hl7Peer().buildMessage({
|
|
431
499
|
type,
|
|
432
500
|
sendingApp: "COSYTE-SYNTH",
|
|
433
501
|
sendingFacility: "SYNTH-FAC",
|
|
@@ -550,6 +618,7 @@ function obxSegment(rng, setId) {
|
|
|
550
618
|
const obs = rng.pick(EXAMPLE_LAB_OBSERVATIONS);
|
|
551
619
|
const value = `${String(rng.int(1, 300))}.${String(rng.int(0, 9))}`;
|
|
552
620
|
const observedAt = seededTimestamp(rng);
|
|
621
|
+
const units = obs.units ?? "";
|
|
553
622
|
return [
|
|
554
623
|
String(setId),
|
|
555
624
|
// OBX-1 set id
|
|
@@ -561,7 +630,7 @@ function obxSegment(rng, setId) {
|
|
|
561
630
|
// OBX-4 observation sub-id
|
|
562
631
|
value,
|
|
563
632
|
// OBX-5 observation value
|
|
564
|
-
|
|
633
|
+
units,
|
|
565
634
|
// OBX-6 units
|
|
566
635
|
"",
|
|
567
636
|
// OBX-7 reference range
|
|
@@ -877,36 +946,36 @@ function prop(name2, value) {
|
|
|
877
946
|
return { name: name2, value };
|
|
878
947
|
}
|
|
879
948
|
function str(value) {
|
|
880
|
-
return
|
|
949
|
+
return fhirPeer().primitive(value);
|
|
881
950
|
}
|
|
882
951
|
function dec(raw) {
|
|
883
|
-
return
|
|
952
|
+
return fhirPeer().primitive(fhirPeer().decimal(raw));
|
|
884
953
|
}
|
|
885
954
|
function bool(value) {
|
|
886
|
-
return
|
|
955
|
+
return fhirPeer().primitive(value);
|
|
887
956
|
}
|
|
888
957
|
function coding(concept) {
|
|
889
|
-
return
|
|
958
|
+
return fhirPeer().complex([
|
|
890
959
|
prop("system", str(concept.system)),
|
|
891
960
|
prop("code", str(concept.code)),
|
|
892
961
|
prop("display", str(concept.display))
|
|
893
962
|
]);
|
|
894
963
|
}
|
|
895
964
|
function codeableConcept(concept) {
|
|
896
|
-
return
|
|
965
|
+
return fhirPeer().complex([prop("coding", fhirPeer().list([coding(concept)]))]);
|
|
897
966
|
}
|
|
898
967
|
function reference(ref) {
|
|
899
|
-
return
|
|
968
|
+
return fhirPeer().complex([prop("reference", str(ref))]);
|
|
900
969
|
}
|
|
901
970
|
function narrative(text) {
|
|
902
971
|
const escaped = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
903
|
-
return
|
|
972
|
+
return fhirPeer().complex([
|
|
904
973
|
prop("status", str("generated")),
|
|
905
974
|
prop("div", str(`<div xmlns="http://www.w3.org/1999/xhtml"><p>${escaped}</p></div>`))
|
|
906
975
|
]);
|
|
907
976
|
}
|
|
908
977
|
function meta(profiles) {
|
|
909
|
-
return
|
|
978
|
+
return fhirPeer().complex([prop("profile", fhirPeer().list(profiles.map((p) => str(p))))]);
|
|
910
979
|
}
|
|
911
980
|
function toFhirDate(ymd) {
|
|
912
981
|
return `${ymd.slice(0, 4)}-${ymd.slice(4, 6)}-${ymd.slice(6, 8)}`;
|
|
@@ -923,13 +992,13 @@ function fhirPatientIdentity(rng) {
|
|
|
923
992
|
return { id, person, mrn, birthDate, gender, address: address2, phone: phone2, email: email2 };
|
|
924
993
|
}
|
|
925
994
|
function mrnIdentifier(mrn) {
|
|
926
|
-
return
|
|
995
|
+
return fhirPeer().complex([
|
|
927
996
|
prop(
|
|
928
997
|
"type",
|
|
929
|
-
|
|
998
|
+
fhirPeer().complex([
|
|
930
999
|
prop(
|
|
931
1000
|
"coding",
|
|
932
|
-
|
|
1001
|
+
fhirPeer().list([
|
|
933
1002
|
coding({
|
|
934
1003
|
system: SYSTEM.IDENTIFIER_TYPE,
|
|
935
1004
|
code: "MR",
|
|
@@ -1214,13 +1283,16 @@ var EXAMPLE_ENCOUNTER_CLASSES = Object.freeze([
|
|
|
1214
1283
|
// src/fhir/patient.ts
|
|
1215
1284
|
var PATIENT_PROFILES = Object.freeze(["base", "us-core"]);
|
|
1216
1285
|
function ombExtension(url, category) {
|
|
1217
|
-
return
|
|
1286
|
+
return fhirPeer().complex([
|
|
1218
1287
|
prop("url", str(url)),
|
|
1219
1288
|
prop(
|
|
1220
1289
|
"extension",
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1290
|
+
fhirPeer().list([
|
|
1291
|
+
fhirPeer().complex([
|
|
1292
|
+
prop("url", str("ombCategory")),
|
|
1293
|
+
prop("valueCoding", coding(category))
|
|
1294
|
+
]),
|
|
1295
|
+
fhirPeer().complex([prop("url", str("text")), prop("valueString", str(category.display))])
|
|
1224
1296
|
])
|
|
1225
1297
|
)
|
|
1226
1298
|
]);
|
|
@@ -1251,24 +1323,27 @@ function generatePatient(options = {}) {
|
|
|
1251
1323
|
props.push(
|
|
1252
1324
|
prop(
|
|
1253
1325
|
"extension",
|
|
1254
|
-
|
|
1326
|
+
fhirPeer().list([
|
|
1255
1327
|
ombExtension(US_CORE_RACE_EXTENSION, race),
|
|
1256
1328
|
ombExtension(US_CORE_ETHNICITY_EXTENSION, ethnicity),
|
|
1257
|
-
|
|
1329
|
+
fhirPeer().complex([
|
|
1330
|
+
prop("url", str(US_CORE_BIRTHSEX_EXTENSION)),
|
|
1331
|
+
prop("valueCode", str(birthsex))
|
|
1332
|
+
])
|
|
1258
1333
|
])
|
|
1259
1334
|
)
|
|
1260
1335
|
);
|
|
1261
1336
|
}
|
|
1262
|
-
props.push(prop("identifier",
|
|
1337
|
+
props.push(prop("identifier", fhirPeer().list([mrnIdentifier(id.mrn)])));
|
|
1263
1338
|
props.push(prop("active", bool(true)));
|
|
1264
1339
|
props.push(
|
|
1265
1340
|
prop(
|
|
1266
1341
|
"name",
|
|
1267
|
-
|
|
1268
|
-
|
|
1342
|
+
fhirPeer().list([
|
|
1343
|
+
fhirPeer().complex([
|
|
1269
1344
|
prop("use", str("official")),
|
|
1270
1345
|
prop("family", str(id.person.family)),
|
|
1271
|
-
prop("given",
|
|
1346
|
+
prop("given", fhirPeer().list([str(id.person.given)]))
|
|
1272
1347
|
])
|
|
1273
1348
|
])
|
|
1274
1349
|
)
|
|
@@ -1276,13 +1351,13 @@ function generatePatient(options = {}) {
|
|
|
1276
1351
|
props.push(
|
|
1277
1352
|
prop(
|
|
1278
1353
|
"telecom",
|
|
1279
|
-
|
|
1280
|
-
|
|
1354
|
+
fhirPeer().list([
|
|
1355
|
+
fhirPeer().complex([
|
|
1281
1356
|
prop("system", str("phone")),
|
|
1282
1357
|
prop("value", str(id.phone)),
|
|
1283
1358
|
prop("use", str("home"))
|
|
1284
1359
|
]),
|
|
1285
|
-
|
|
1360
|
+
fhirPeer().complex([prop("system", str("email")), prop("value", str(id.email))])
|
|
1286
1361
|
])
|
|
1287
1362
|
)
|
|
1288
1363
|
);
|
|
@@ -1291,10 +1366,10 @@ function generatePatient(options = {}) {
|
|
|
1291
1366
|
props.push(
|
|
1292
1367
|
prop(
|
|
1293
1368
|
"address",
|
|
1294
|
-
|
|
1295
|
-
|
|
1369
|
+
fhirPeer().list([
|
|
1370
|
+
fhirPeer().complex([
|
|
1296
1371
|
prop("use", str("home")),
|
|
1297
|
-
prop("line",
|
|
1372
|
+
prop("line", fhirPeer().list([str(id.address.street)])),
|
|
1298
1373
|
prop("city", str(id.address.city)),
|
|
1299
1374
|
prop("state", str(id.address.state)),
|
|
1300
1375
|
prop("postalCode", str(id.address.zip))
|
|
@@ -1302,8 +1377,10 @@ function generatePatient(options = {}) {
|
|
|
1302
1377
|
])
|
|
1303
1378
|
)
|
|
1304
1379
|
);
|
|
1305
|
-
return
|
|
1380
|
+
return fhirPeer().complex(props);
|
|
1306
1381
|
}
|
|
1382
|
+
|
|
1383
|
+
// src/fhir/condition.ts
|
|
1307
1384
|
function generateCondition(options = {}) {
|
|
1308
1385
|
const { seed = 0, subject = "Patient/syn-patient-1", usCore = true } = options;
|
|
1309
1386
|
const rng = createRng(seed);
|
|
@@ -1333,7 +1410,7 @@ function generateCondition(options = {}) {
|
|
|
1333
1410
|
props.push(
|
|
1334
1411
|
prop(
|
|
1335
1412
|
"category",
|
|
1336
|
-
|
|
1413
|
+
fhirPeer().list([
|
|
1337
1414
|
codeableConcept({
|
|
1338
1415
|
system: SYSTEM.CONDITION_CATEGORY,
|
|
1339
1416
|
code: "problem-list-item",
|
|
@@ -1344,15 +1421,17 @@ function generateCondition(options = {}) {
|
|
|
1344
1421
|
);
|
|
1345
1422
|
props.push(prop("code", codeableConcept(code)));
|
|
1346
1423
|
props.push(prop("subject", reference(subject)));
|
|
1347
|
-
return
|
|
1424
|
+
return fhirPeer().complex(props);
|
|
1348
1425
|
}
|
|
1426
|
+
|
|
1427
|
+
// src/fhir/observation.ts
|
|
1349
1428
|
function drawValue(rng, concept) {
|
|
1350
1429
|
const span = concept.high - concept.low;
|
|
1351
1430
|
const raw = concept.low + rng.float() * span;
|
|
1352
1431
|
return raw.toFixed(concept.decimals);
|
|
1353
1432
|
}
|
|
1354
1433
|
function valueQuantity(rng, concept) {
|
|
1355
|
-
return
|
|
1434
|
+
return fhirPeer().complex([
|
|
1356
1435
|
prop("value", dec(drawValue(rng, concept))),
|
|
1357
1436
|
prop("unit", str(concept.unit)),
|
|
1358
1437
|
prop("system", str(SYSTEM.UCUM)),
|
|
@@ -1374,7 +1453,7 @@ function buildObservation(seed, subject, usCore, profileUrl, category, pool, eff
|
|
|
1374
1453
|
props.push(
|
|
1375
1454
|
prop(
|
|
1376
1455
|
"category",
|
|
1377
|
-
|
|
1456
|
+
fhirPeer().list([
|
|
1378
1457
|
codeableConcept({
|
|
1379
1458
|
system: SYSTEM.OBSERVATION_CATEGORY,
|
|
1380
1459
|
code: category.code,
|
|
@@ -1390,7 +1469,7 @@ function buildObservation(seed, subject, usCore, profileUrl, category, pool, eff
|
|
|
1390
1469
|
const d = String(rng.int(1, 28)).padStart(2, "0");
|
|
1391
1470
|
if (effective) props.push(prop("effectiveDateTime", str(`${String(y)}-${m}-${d}`)));
|
|
1392
1471
|
props.push(prop("valueQuantity", valueQuantity(rng, concept)));
|
|
1393
|
-
return
|
|
1472
|
+
return fhirPeer().complex(props);
|
|
1394
1473
|
}
|
|
1395
1474
|
function generateObservationLab(options = {}) {
|
|
1396
1475
|
const { seed = 0, subject = "Patient/syn-patient-1", usCore = true } = options;
|
|
@@ -1416,6 +1495,8 @@ function generateVitalSign(options = {}) {
|
|
|
1416
1495
|
true
|
|
1417
1496
|
);
|
|
1418
1497
|
}
|
|
1498
|
+
|
|
1499
|
+
// src/fhir/medication-request.ts
|
|
1419
1500
|
function generateMedicationRequest(options = {}) {
|
|
1420
1501
|
const {
|
|
1421
1502
|
seed = 0,
|
|
@@ -1440,8 +1521,10 @@ function generateMedicationRequest(options = {}) {
|
|
|
1440
1521
|
props.push(prop("subject", reference(subject)));
|
|
1441
1522
|
props.push(prop("authoredOn", str(`${String(y)}-${m}-${d}`)));
|
|
1442
1523
|
props.push(prop("requester", reference(requester)));
|
|
1443
|
-
return
|
|
1524
|
+
return fhirPeer().complex(props);
|
|
1444
1525
|
}
|
|
1526
|
+
|
|
1527
|
+
// src/fhir/encounter.ts
|
|
1445
1528
|
function generateEncounter(options = {}) {
|
|
1446
1529
|
const { seed = 0, subject = "Patient/syn-patient-1", usCore = true } = options;
|
|
1447
1530
|
const rng = createRng(seed);
|
|
@@ -1458,8 +1541,8 @@ function generateEncounter(options = {}) {
|
|
|
1458
1541
|
props.push(
|
|
1459
1542
|
prop(
|
|
1460
1543
|
"identifier",
|
|
1461
|
-
|
|
1462
|
-
|
|
1544
|
+
fhirPeer().list([
|
|
1545
|
+
fhirPeer().complex([
|
|
1463
1546
|
prop("system", str(`urn:oid:${id.assigningAuthorityOid}`)),
|
|
1464
1547
|
prop("value", str(id.value))
|
|
1465
1548
|
])
|
|
@@ -1468,11 +1551,13 @@ function generateEncounter(options = {}) {
|
|
|
1468
1551
|
);
|
|
1469
1552
|
props.push(prop("status", str("finished")));
|
|
1470
1553
|
props.push(prop("class", coding(cls)));
|
|
1471
|
-
props.push(prop("type",
|
|
1554
|
+
props.push(prop("type", fhirPeer().list([codeableConcept(type)])));
|
|
1472
1555
|
props.push(prop("subject", reference(subject)));
|
|
1473
|
-
props.push(prop("period",
|
|
1474
|
-
return
|
|
1556
|
+
props.push(prop("period", fhirPeer().complex([prop("start", str(`${start}T09:00:00Z`))])));
|
|
1557
|
+
return fhirPeer().complex(props);
|
|
1475
1558
|
}
|
|
1559
|
+
|
|
1560
|
+
// src/fhir/immunization.ts
|
|
1476
1561
|
function generateImmunization(options = {}) {
|
|
1477
1562
|
const { seed = 0, patient = "Patient/syn-patient-1", usCore = true } = options;
|
|
1478
1563
|
const rng = createRng(seed);
|
|
@@ -1489,8 +1574,10 @@ function generateImmunization(options = {}) {
|
|
|
1489
1574
|
props.push(prop("patient", reference(patient)));
|
|
1490
1575
|
props.push(prop("occurrenceDateTime", str(occurrence)));
|
|
1491
1576
|
props.push(prop("primarySource", bool(true)));
|
|
1492
|
-
return
|
|
1577
|
+
return fhirPeer().complex(props);
|
|
1493
1578
|
}
|
|
1579
|
+
|
|
1580
|
+
// src/fhir/allergy-intolerance.ts
|
|
1494
1581
|
function generateAllergyIntolerance(options = {}) {
|
|
1495
1582
|
const { seed = 0, patient = "Patient/syn-patient-1", usCore = true } = options;
|
|
1496
1583
|
const rng = createRng(seed);
|
|
@@ -1523,11 +1610,17 @@ function generateAllergyIntolerance(options = {}) {
|
|
|
1523
1610
|
props.push(
|
|
1524
1611
|
prop(
|
|
1525
1612
|
"reaction",
|
|
1526
|
-
|
|
1613
|
+
fhirPeer().list([
|
|
1614
|
+
fhirPeer().complex([
|
|
1615
|
+
prop("manifestation", fhirPeer().list([codeableConcept(manifestation)]))
|
|
1616
|
+
])
|
|
1617
|
+
])
|
|
1527
1618
|
)
|
|
1528
1619
|
);
|
|
1529
|
-
return
|
|
1620
|
+
return fhirPeer().complex(props);
|
|
1530
1621
|
}
|
|
1622
|
+
|
|
1623
|
+
// src/fhir/procedure.ts
|
|
1531
1624
|
function generateProcedure(options = {}) {
|
|
1532
1625
|
const { seed = 0, subject = "Patient/syn-patient-1", usCore = true } = options;
|
|
1533
1626
|
const rng = createRng(seed);
|
|
@@ -1543,8 +1636,10 @@ function generateProcedure(options = {}) {
|
|
|
1543
1636
|
props.push(prop("code", codeableConcept(code)));
|
|
1544
1637
|
props.push(prop("subject", reference(subject)));
|
|
1545
1638
|
props.push(prop("performedDateTime", str(`${performed}T10:00:00Z`)));
|
|
1546
|
-
return
|
|
1639
|
+
return fhirPeer().complex(props);
|
|
1547
1640
|
}
|
|
1641
|
+
|
|
1642
|
+
// src/fhir/diagnostic-report.ts
|
|
1548
1643
|
function generateDiagnosticReport(options = {}) {
|
|
1549
1644
|
const { seed = 0, subject = "Patient/syn-patient-1", results = [], usCore = true } = options;
|
|
1550
1645
|
const rng = createRng(seed);
|
|
@@ -1560,7 +1655,7 @@ function generateDiagnosticReport(options = {}) {
|
|
|
1560
1655
|
props.push(
|
|
1561
1656
|
prop(
|
|
1562
1657
|
"category",
|
|
1563
|
-
|
|
1658
|
+
fhirPeer().list([
|
|
1564
1659
|
codeableConcept({
|
|
1565
1660
|
system: SYSTEM.DIAGNOSTIC_SERVICE_SECTION,
|
|
1566
1661
|
code: "LAB",
|
|
@@ -1574,15 +1669,17 @@ function generateDiagnosticReport(options = {}) {
|
|
|
1574
1669
|
props.push(prop("effectiveDateTime", str(`${day}T08:30:00Z`)));
|
|
1575
1670
|
props.push(prop("issued", str(`${day}T09:00:00.000Z`)));
|
|
1576
1671
|
if (results.length > 0) {
|
|
1577
|
-
props.push(prop("result",
|
|
1672
|
+
props.push(prop("result", fhirPeer().list(results.map((r) => reference(r)))));
|
|
1578
1673
|
}
|
|
1579
|
-
return
|
|
1674
|
+
return fhirPeer().complex(props);
|
|
1580
1675
|
}
|
|
1676
|
+
|
|
1677
|
+
// src/fhir/composition.ts
|
|
1581
1678
|
function section(sec) {
|
|
1582
|
-
return
|
|
1679
|
+
return fhirPeer().complex([
|
|
1583
1680
|
prop("title", str(sec.title)),
|
|
1584
1681
|
prop("code", codeableConcept({ system: SYSTEM.LOINC, code: sec.code, display: sec.title })),
|
|
1585
|
-
prop("entry",
|
|
1682
|
+
prop("entry", fhirPeer().list(sec.entries.map((e) => reference(e))))
|
|
1586
1683
|
]);
|
|
1587
1684
|
}
|
|
1588
1685
|
function buildComposition(rng, input) {
|
|
@@ -1601,12 +1698,12 @@ function buildComposition(rng, input) {
|
|
|
1601
1698
|
),
|
|
1602
1699
|
prop("subject", reference(input.subject)),
|
|
1603
1700
|
prop("date", str(`${input.date}T09:00:00Z`)),
|
|
1604
|
-
prop("author",
|
|
1701
|
+
prop("author", fhirPeer().list([reference(input.organization)])),
|
|
1605
1702
|
prop("title", str("Synthetic Continuity of Care Document")),
|
|
1606
1703
|
prop("custodian", reference(input.organization)),
|
|
1607
|
-
prop("section",
|
|
1704
|
+
prop("section", fhirPeer().list(input.sections.map((s) => section(s))))
|
|
1608
1705
|
];
|
|
1609
|
-
return
|
|
1706
|
+
return fhirPeer().complex(props);
|
|
1610
1707
|
}
|
|
1611
1708
|
|
|
1612
1709
|
// src/fhir/bundle.ts
|
|
@@ -1621,14 +1718,14 @@ function resourceTypeOf(resource) {
|
|
|
1621
1718
|
}
|
|
1622
1719
|
function buildOrganization(rng) {
|
|
1623
1720
|
const id = safe.identifier(rng, "AN");
|
|
1624
|
-
return
|
|
1721
|
+
return fhirPeer().complex([
|
|
1625
1722
|
prop("resourceType", str("Organization")),
|
|
1626
1723
|
prop("id", str(`syn-org-${rng.digits(8)}`)),
|
|
1627
1724
|
prop("text", narrative("Synthetic health organization.")),
|
|
1628
1725
|
prop(
|
|
1629
1726
|
"identifier",
|
|
1630
|
-
|
|
1631
|
-
|
|
1727
|
+
fhirPeer().list([
|
|
1728
|
+
fhirPeer().complex([
|
|
1632
1729
|
prop("system", str(`urn:oid:${id.assigningAuthorityOid}`)),
|
|
1633
1730
|
prop("value", str(id.value))
|
|
1634
1731
|
])
|
|
@@ -1741,11 +1838,14 @@ function generateBundle(options = {}) {
|
|
|
1741
1838
|
entryProps.push(
|
|
1742
1839
|
prop(
|
|
1743
1840
|
"request",
|
|
1744
|
-
|
|
1841
|
+
fhirPeer().complex([
|
|
1842
|
+
prop("method", str("POST")),
|
|
1843
|
+
prop("url", str(resourceTypeOf(member.resource)))
|
|
1844
|
+
])
|
|
1745
1845
|
)
|
|
1746
1846
|
);
|
|
1747
1847
|
}
|
|
1748
|
-
return
|
|
1848
|
+
return fhirPeer().complex(entryProps);
|
|
1749
1849
|
});
|
|
1750
1850
|
const props = [
|
|
1751
1851
|
prop("resourceType", str("Bundle")),
|
|
@@ -1756,7 +1856,7 @@ function generateBundle(options = {}) {
|
|
|
1756
1856
|
props.push(
|
|
1757
1857
|
prop(
|
|
1758
1858
|
"identifier",
|
|
1759
|
-
|
|
1859
|
+
fhirPeer().complex([
|
|
1760
1860
|
prop("system", str(`urn:oid:${id.assigningAuthorityOid}`)),
|
|
1761
1861
|
prop("value", str(`urn:uuid:${safe.uuid(rng)}`))
|
|
1762
1862
|
])
|
|
@@ -1765,11 +1865,13 @@ function generateBundle(options = {}) {
|
|
|
1765
1865
|
}
|
|
1766
1866
|
props.push(prop("type", str(type)));
|
|
1767
1867
|
if (type === "document") props.push(prop("timestamp", str(`${spine.date}T09:00:00.000Z`)));
|
|
1768
|
-
props.push(prop("entry",
|
|
1769
|
-
return
|
|
1868
|
+
props.push(prop("entry", fhirPeer().list(entries)));
|
|
1869
|
+
return fhirPeer().complex(props);
|
|
1770
1870
|
}
|
|
1871
|
+
|
|
1872
|
+
// src/x12/money.ts
|
|
1771
1873
|
function dec2(value) {
|
|
1772
|
-
const d =
|
|
1874
|
+
const d = x12Peer().X12Decimal.fromString(value);
|
|
1773
1875
|
if (d === void 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INVALID_DECIMAL);
|
|
1774
1876
|
return d;
|
|
1775
1877
|
}
|
|
@@ -1838,39 +1940,49 @@ function x12EnvelopeTiming(rng) {
|
|
|
1838
1940
|
}
|
|
1839
1941
|
|
|
1840
1942
|
// src/x12/example-codes.ts
|
|
1841
|
-
var PROFESSIONAL_PROCEDURES = Object.freeze(
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1943
|
+
var PROFESSIONAL_PROCEDURES = Object.freeze(
|
|
1944
|
+
[
|
|
1945
|
+
{ code: "99213", label: "office/outpatient visit, established patient" },
|
|
1946
|
+
{ code: "99214", label: "office/outpatient visit, established patient, moderate" },
|
|
1947
|
+
{ code: "99203", label: "office/outpatient visit, new patient" },
|
|
1948
|
+
{ code: "85025", label: "complete blood count with differential" },
|
|
1949
|
+
{ code: "80053", label: "comprehensive metabolic panel" },
|
|
1950
|
+
{ code: "93000", label: "electrocardiogram, routine" }
|
|
1951
|
+
].map((entry) => Object.freeze(entry))
|
|
1952
|
+
);
|
|
1849
1953
|
var PROCEDURE_MODIFIERS = Object.freeze(["25", "59", "76", "GT"]);
|
|
1850
1954
|
var PLACES_OF_SERVICE = Object.freeze(["11", "22", "21", "12", "49"]);
|
|
1851
|
-
var DIAGNOSES = Object.freeze(
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
var
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
])
|
|
1955
|
+
var DIAGNOSES = Object.freeze(
|
|
1956
|
+
[
|
|
1957
|
+
{ code: "J20.9", label: "acute bronchitis, unspecified" },
|
|
1958
|
+
{ code: "E11.9", label: "type 2 diabetes mellitus without complications" },
|
|
1959
|
+
{ code: "I10", label: "essential (primary) hypertension" },
|
|
1960
|
+
{ code: "M54.5", label: "low back pain" },
|
|
1961
|
+
{ code: "R07.9", label: "chest pain, unspecified" },
|
|
1962
|
+
{ code: "Z00.00", label: "general adult medical exam without abnormal findings" }
|
|
1963
|
+
].map((entry) => Object.freeze(entry))
|
|
1964
|
+
);
|
|
1965
|
+
var REVENUE_CODES = Object.freeze(
|
|
1966
|
+
[
|
|
1967
|
+
{ code: "0120", label: "room & board, semi-private" },
|
|
1968
|
+
{ code: "0300", label: "laboratory, general" },
|
|
1969
|
+
{ code: "0450", label: "emergency room, general" },
|
|
1970
|
+
{ code: "0636", label: "drugs requiring detailed coding" }
|
|
1971
|
+
].map((entry) => Object.freeze(entry))
|
|
1972
|
+
);
|
|
1973
|
+
var INSTITUTIONAL_PROCEDURES = Object.freeze(
|
|
1974
|
+
[
|
|
1975
|
+
{ code: "99221", label: "initial hospital care, per day" },
|
|
1976
|
+
{ code: "99231", label: "subsequent hospital care, per day" }
|
|
1977
|
+
].map((entry) => Object.freeze(entry))
|
|
1978
|
+
);
|
|
1979
|
+
var DENTAL_PROCEDURES = Object.freeze(
|
|
1980
|
+
[
|
|
1981
|
+
{ code: "D2391", label: "resin-based composite, one surface, posterior" },
|
|
1982
|
+
{ code: "D1110", label: "prophylaxis, adult" },
|
|
1983
|
+
{ code: "D0120", label: "periodic oral evaluation" }
|
|
1984
|
+
].map((entry) => Object.freeze(entry))
|
|
1985
|
+
);
|
|
1874
1986
|
var TOOTH_CODES = Object.freeze(["14", "3", "19", "30"]);
|
|
1875
1987
|
var TOOTH_SURFACES = Object.freeze(["M", "O", "D", "B", "L"]);
|
|
1876
1988
|
var SERVICE_TYPE_CODES = Object.freeze(["30", "1", "35", "88", "98"]);
|
|
@@ -2064,11 +2176,11 @@ function generate837(variant, options) {
|
|
|
2064
2176
|
const spec = specOf(rng, resolved);
|
|
2065
2177
|
switch (resolved) {
|
|
2066
2178
|
case "P":
|
|
2067
|
-
return
|
|
2179
|
+
return x12Peer().build837P(spec);
|
|
2068
2180
|
case "I":
|
|
2069
|
-
return
|
|
2181
|
+
return x12Peer().build837I(spec);
|
|
2070
2182
|
case "D":
|
|
2071
|
-
return
|
|
2183
|
+
return x12Peer().build837D(spec);
|
|
2072
2184
|
}
|
|
2073
2185
|
}
|
|
2074
2186
|
function generate837P(options) {
|
|
@@ -2080,6 +2192,8 @@ function generate837I(options) {
|
|
|
2080
2192
|
function generate837D(options) {
|
|
2081
2193
|
return generate837("D", options);
|
|
2082
2194
|
}
|
|
2195
|
+
|
|
2196
|
+
// src/x12/remit-835.ts
|
|
2083
2197
|
function generate835(options) {
|
|
2084
2198
|
const rng = createRng(options.seed);
|
|
2085
2199
|
const partners = x12TradingPartners(rng);
|
|
@@ -2180,8 +2294,10 @@ function generate835(options) {
|
|
|
2180
2294
|
},
|
|
2181
2295
|
claims: [claim]
|
|
2182
2296
|
};
|
|
2183
|
-
return
|
|
2297
|
+
return x12Peer().build835(spec);
|
|
2184
2298
|
}
|
|
2299
|
+
|
|
2300
|
+
// src/x12/eligibility-271.ts
|
|
2185
2301
|
function generate271(options) {
|
|
2186
2302
|
const rng = createRng(options.seed);
|
|
2187
2303
|
const partners = x12TradingPartners(rng);
|
|
@@ -2261,7 +2377,7 @@ function generate271(options) {
|
|
|
2261
2377
|
}
|
|
2262
2378
|
]
|
|
2263
2379
|
};
|
|
2264
|
-
return
|
|
2380
|
+
return x12Peer().build271(spec);
|
|
2265
2381
|
}
|
|
2266
2382
|
|
|
2267
2383
|
// src/ncpdp/identity.ts
|
|
@@ -2302,19 +2418,23 @@ function ncpdpCardholder(rng) {
|
|
|
2302
2418
|
}
|
|
2303
2419
|
|
|
2304
2420
|
// src/ncpdp/example-codes.ts
|
|
2305
|
-
var EXAMPLE_DRUGS = Object.freeze(
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2421
|
+
var EXAMPLE_DRUGS = Object.freeze(
|
|
2422
|
+
[
|
|
2423
|
+
{ description: "Amoxicillin 500 MG Oral Capsule", ndc: "00000010101", form: "EA" },
|
|
2424
|
+
{ description: "Lisinopril 10 MG Oral Tablet", ndc: "00000020202", form: "EA" },
|
|
2425
|
+
{ description: "Atorvastatin 20 MG Oral Tablet", ndc: "00000030303", form: "EA" },
|
|
2426
|
+
{ description: "Metformin 500 MG Oral Tablet", ndc: "00000040404", form: "EA" },
|
|
2427
|
+
{ description: "Amlodipine 5 MG Oral Tablet", ndc: "00000050505", form: "EA" },
|
|
2428
|
+
{ description: "Omeprazole 20 MG Delayed Release Capsule", ndc: "00000060606", form: "EA" },
|
|
2429
|
+
{ description: "Levothyroxine 50 MCG Oral Tablet", ndc: "00000070707", form: "EA" },
|
|
2430
|
+
{ description: "Azithromycin 250 MG Oral Tablet", ndc: "00000080808", form: "EA" },
|
|
2431
|
+
{ description: "Sertraline 50 MG Oral Tablet", ndc: "00000090909", form: "EA" },
|
|
2432
|
+
{ description: "Albuterol 90 MCG Metered Dose Inhaler", ndc: "00000101010", form: "EA" }
|
|
2433
|
+
].map((entry) => Object.freeze(entry))
|
|
2434
|
+
);
|
|
2317
2435
|
var DAW_CODES = Object.freeze(["0", "1", "2"]);
|
|
2436
|
+
|
|
2437
|
+
// src/ncpdp/telecom.ts
|
|
2318
2438
|
var SYNTHETIC_BIN = "999999";
|
|
2319
2439
|
function digitsOnly(value) {
|
|
2320
2440
|
return value.replace(/\D/g, "");
|
|
@@ -2414,7 +2534,9 @@ function generateTelecom(code, options) {
|
|
|
2414
2534
|
prescriberSegment(rng),
|
|
2415
2535
|
claimSegment(rng, false)
|
|
2416
2536
|
];
|
|
2417
|
-
return
|
|
2537
|
+
return ncpdpTelecomPeer().serializeTelecom(
|
|
2538
|
+
ncpdpTelecomPeer().buildTelecomRequest({ header: head, segments })
|
|
2539
|
+
);
|
|
2418
2540
|
}
|
|
2419
2541
|
function generateB1(options) {
|
|
2420
2542
|
return generateTelecom("B1", options);
|
|
@@ -2425,11 +2547,23 @@ function generateB2(options) {
|
|
|
2425
2547
|
function generateB3(options) {
|
|
2426
2548
|
return generateTelecom("B3", options);
|
|
2427
2549
|
}
|
|
2550
|
+
|
|
2551
|
+
// src/ccda/example-codes.ts
|
|
2552
|
+
var LOINC_OID = "2.16.840.1.113883.6.1";
|
|
2553
|
+
var SNOMED_CT_OID = "2.16.840.1.113883.6.96";
|
|
2554
|
+
var RXNORM_OID = "2.16.840.1.113883.6.88";
|
|
2555
|
+
var CVX_OID = "2.16.840.1.113883.12.292";
|
|
2556
|
+
var NCI_ROUTE_OID = "2.16.840.1.113883.3.26.1.1";
|
|
2557
|
+
var LOINC = LOINC_OID;
|
|
2558
|
+
var SNOMED_CT = SNOMED_CT_OID;
|
|
2559
|
+
var RXNORM = RXNORM_OID;
|
|
2560
|
+
var CVX = CVX_OID;
|
|
2561
|
+
var NCI_ROUTE = NCI_ROUTE_OID;
|
|
2428
2562
|
var URI_TO_OID = Object.freeze({
|
|
2429
|
-
"http://loinc.org":
|
|
2430
|
-
"http://snomed.info/sct":
|
|
2431
|
-
"http://www.nlm.nih.gov/research/umls/rxnorm":
|
|
2432
|
-
"http://hl7.org/fhir/sid/cvx":
|
|
2563
|
+
"http://loinc.org": LOINC,
|
|
2564
|
+
"http://snomed.info/sct": SNOMED_CT,
|
|
2565
|
+
"http://www.nlm.nih.gov/research/umls/rxnorm": RXNORM,
|
|
2566
|
+
"http://hl7.org/fhir/sid/cvx": CVX
|
|
2433
2567
|
});
|
|
2434
2568
|
function toBuildCode(concept) {
|
|
2435
2569
|
const codeSystem = URI_TO_OID[concept.system];
|
|
@@ -2453,24 +2587,24 @@ var VITAL_SIGNS = EXAMPLE_VITAL_SIGNS;
|
|
|
2453
2587
|
var VACCINES = EXAMPLE_VACCINES2;
|
|
2454
2588
|
var PROCEDURES = EXAMPLE_PROCEDURES;
|
|
2455
2589
|
var SMOKING_STATUSES = Object.freeze([
|
|
2456
|
-
Object.freeze({ code: "266919005", codeSystem:
|
|
2457
|
-
Object.freeze({ code: "8517006", codeSystem:
|
|
2590
|
+
Object.freeze({ code: "266919005", codeSystem: SNOMED_CT, displayName: "Never smoker" }),
|
|
2591
|
+
Object.freeze({ code: "8517006", codeSystem: SNOMED_CT, displayName: "Former smoker" }),
|
|
2458
2592
|
Object.freeze({
|
|
2459
2593
|
code: "449868002",
|
|
2460
|
-
codeSystem:
|
|
2594
|
+
codeSystem: SNOMED_CT,
|
|
2461
2595
|
displayName: "Current every day smoker"
|
|
2462
2596
|
}),
|
|
2463
2597
|
Object.freeze({
|
|
2464
2598
|
code: "428041000124106",
|
|
2465
|
-
codeSystem:
|
|
2599
|
+
codeSystem: SNOMED_CT,
|
|
2466
2600
|
displayName: "Current some day smoker"
|
|
2467
2601
|
})
|
|
2468
2602
|
]);
|
|
2469
2603
|
var ROUTES = Object.freeze([
|
|
2470
|
-
Object.freeze({ code: "C38288", codeSystem:
|
|
2471
|
-
Object.freeze({ code: "C28161", codeSystem:
|
|
2472
|
-
Object.freeze({ code: "C38276", codeSystem:
|
|
2473
|
-
Object.freeze({ code: "C38299", codeSystem:
|
|
2604
|
+
Object.freeze({ code: "C38288", codeSystem: NCI_ROUTE, displayName: "Oral" }),
|
|
2605
|
+
Object.freeze({ code: "C28161", codeSystem: NCI_ROUTE, displayName: "Intramuscular" }),
|
|
2606
|
+
Object.freeze({ code: "C38276", codeSystem: NCI_ROUTE, displayName: "Intravenous" }),
|
|
2607
|
+
Object.freeze({ code: "C38299", codeSystem: NCI_ROUTE, displayName: "Subcutaneous" })
|
|
2474
2608
|
]);
|
|
2475
2609
|
|
|
2476
2610
|
// src/ccda/identity.ts
|
|
@@ -2578,16 +2712,18 @@ function generateCcda(options = {}) {
|
|
|
2578
2712
|
const { seed = 0 } = options;
|
|
2579
2713
|
const documentType = resolveKind(CCDA_DOCUMENT_TYPES, options.documentType ?? "ccd");
|
|
2580
2714
|
const rng = createRng(seed);
|
|
2581
|
-
return
|
|
2715
|
+
return ccdaPeer().buildCcda(buildInit(rng, documentType));
|
|
2582
2716
|
}
|
|
2717
|
+
|
|
2718
|
+
// src/deid/loop.ts
|
|
2583
2719
|
var DEID_LOOP_POLICY_NAME = "synth-deid-loop-removal";
|
|
2584
2720
|
function deidLoopPolicy() {
|
|
2585
|
-
return
|
|
2721
|
+
return deidPeer().defineDeidPolicy({
|
|
2586
2722
|
name: DEID_LOOP_POLICY_NAME,
|
|
2587
2723
|
transforms: {
|
|
2588
|
-
[
|
|
2589
|
-
[
|
|
2590
|
-
[
|
|
2724
|
+
[deidPeer().SAFE_HARBOR_CATEGORIES.MRN]: "redact",
|
|
2725
|
+
[deidPeer().SAFE_HARBOR_CATEGORIES.HEALTH_PLAN_BENEFICIARY]: "redact",
|
|
2726
|
+
[deidPeer().SAFE_HARBOR_CATEGORIES.ACCOUNT]: "redact"
|
|
2591
2727
|
}
|
|
2592
2728
|
});
|
|
2593
2729
|
}
|
|
@@ -2664,7 +2800,8 @@ function recordTargetSentinels(xml) {
|
|
|
2664
2800
|
seen.add(trimmed);
|
|
2665
2801
|
out.push({ token: trimmed, locus, category: "names" });
|
|
2666
2802
|
};
|
|
2667
|
-
for (const m of scope.matchAll(/<given>([^<]+)<\/given>/g))
|
|
2803
|
+
for (const m of scope.matchAll(/<given>([^<]+)<\/given>/g))
|
|
2804
|
+
add(m[1] ?? "", "recordTarget/given");
|
|
2668
2805
|
for (const m of scope.matchAll(/<family>([^<]+)<\/family>/g))
|
|
2669
2806
|
add(m[1] ?? "", "recordTarget/family");
|
|
2670
2807
|
for (const m of scope.matchAll(/<id\b[^>]*\bextension="([^"]+)"/g))
|
|
@@ -2723,7 +2860,8 @@ var DEID_LOOP_SKIPPED = Object.freeze([
|
|
|
2723
2860
|
reason: "@cosyte/synth does not generate DICOM, so there is nothing to pair."
|
|
2724
2861
|
})
|
|
2725
2862
|
]);
|
|
2726
|
-
var
|
|
2863
|
+
var policy;
|
|
2864
|
+
var loopPolicy = () => policy ??= deidLoopPolicy();
|
|
2727
2865
|
function residue(loci) {
|
|
2728
2866
|
return loci.map((l) => l.value).join("\n");
|
|
2729
2867
|
}
|
|
@@ -2748,8 +2886,8 @@ function hl7DeidLoop(options = {}) {
|
|
|
2748
2886
|
const kind = options.kind ?? "ORU^R01";
|
|
2749
2887
|
const message = generateHl7(kind, seed);
|
|
2750
2888
|
const original = message.toString();
|
|
2751
|
-
const planted = identifierSentinels(
|
|
2752
|
-
const deidDoc =
|
|
2889
|
+
const planted = identifierSentinels(deidHl7Peer().extractHl7Loci(message).loci, original);
|
|
2890
|
+
const deidDoc = deidHl7Peer().deidentifyHl7(message, { policy: loopPolicy() }).document;
|
|
2753
2891
|
const deidentified = deidDoc.toString();
|
|
2754
2892
|
return assembleVerdict({
|
|
2755
2893
|
format: "hl7",
|
|
@@ -2758,17 +2896,17 @@ function hl7DeidLoop(options = {}) {
|
|
|
2758
2896
|
planted,
|
|
2759
2897
|
original,
|
|
2760
2898
|
deidentified,
|
|
2761
|
-
phiResidue: residue(
|
|
2899
|
+
phiResidue: residue(deidHl7Peer().extractHl7Loci(deidDoc).loci),
|
|
2762
2900
|
clinicalCodes: HL7_CLINICAL
|
|
2763
2901
|
});
|
|
2764
2902
|
}
|
|
2765
2903
|
function fhirDeidLoop(options = {}) {
|
|
2766
2904
|
const seed = options.seed ?? 0;
|
|
2767
2905
|
const bundle = generateBundle({ seed });
|
|
2768
|
-
const original =
|
|
2769
|
-
const planted = identifierSentinels(
|
|
2770
|
-
const deidDoc =
|
|
2771
|
-
const deidentified =
|
|
2906
|
+
const original = fhirPeer().serializeResource(bundle);
|
|
2907
|
+
const planted = identifierSentinels(deidFhirPeer().extractFhirLoci(bundle).loci, original);
|
|
2908
|
+
const deidDoc = deidFhirPeer().deidentifyFhir(bundle, { policy: loopPolicy() }).document;
|
|
2909
|
+
const deidentified = fhirPeer().serializeResource(deidDoc);
|
|
2772
2910
|
return assembleVerdict({
|
|
2773
2911
|
format: "fhir",
|
|
2774
2912
|
artifact: "Bundle",
|
|
@@ -2776,7 +2914,7 @@ function fhirDeidLoop(options = {}) {
|
|
|
2776
2914
|
planted,
|
|
2777
2915
|
original,
|
|
2778
2916
|
deidentified,
|
|
2779
|
-
phiResidue: residue(
|
|
2917
|
+
phiResidue: residue(deidFhirPeer().extractFhirLoci(deidDoc).loci),
|
|
2780
2918
|
clinicalCodes: FHIR_CLINICAL
|
|
2781
2919
|
});
|
|
2782
2920
|
}
|
|
@@ -2798,9 +2936,9 @@ function x12DeidLoop(options = {}) {
|
|
|
2798
2936
|
const seed = options.seed ?? 0;
|
|
2799
2937
|
const variant = options.variant ?? "837P";
|
|
2800
2938
|
const interchange = generateX12(variant, seed);
|
|
2801
|
-
const original =
|
|
2802
|
-
const planted = identifierSentinels(
|
|
2803
|
-
const deidentified =
|
|
2939
|
+
const original = x12Peer().serializeX12(interchange);
|
|
2940
|
+
const planted = identifierSentinels(deidX12Peer().extractX12Loci(interchange).loci, original);
|
|
2941
|
+
const deidentified = deidX12Peer().deidentifyX12(interchange, { policy: loopPolicy() }).x12;
|
|
2804
2942
|
return assembleVerdict({
|
|
2805
2943
|
format: "x12",
|
|
2806
2944
|
artifact: variant,
|
|
@@ -2808,7 +2946,7 @@ function x12DeidLoop(options = {}) {
|
|
|
2808
2946
|
planted,
|
|
2809
2947
|
original,
|
|
2810
2948
|
deidentified,
|
|
2811
|
-
phiResidue: residue(
|
|
2949
|
+
phiResidue: residue(deidX12Peer().extractX12Loci(x12Peer().parseX12(deidentified)).loci),
|
|
2812
2950
|
clinicalCodes: X12_CLINICAL
|
|
2813
2951
|
});
|
|
2814
2952
|
}
|
|
@@ -2826,9 +2964,9 @@ function ncpdpTelecomDeidLoop(options = {}) {
|
|
|
2826
2964
|
const seed = options.seed ?? 0;
|
|
2827
2965
|
const transaction = options.transaction ?? "B1";
|
|
2828
2966
|
const original = generateTelecomWire(transaction, seed);
|
|
2829
|
-
const tx =
|
|
2830
|
-
const planted = identifierSentinels(
|
|
2831
|
-
const deidentified =
|
|
2967
|
+
const tx = ncpdpTelecomPeer().parseTelecom(original);
|
|
2968
|
+
const planted = identifierSentinels(deidNcpdpPeer().extractTelecomLoci(tx).loci, original);
|
|
2969
|
+
const deidentified = deidNcpdpPeer().deidentifyTelecom(tx, { policy: loopPolicy() }).telecom;
|
|
2832
2970
|
return assembleVerdict({
|
|
2833
2971
|
format: "ncpdp-telecom",
|
|
2834
2972
|
artifact: transaction,
|
|
@@ -2836,7 +2974,9 @@ function ncpdpTelecomDeidLoop(options = {}) {
|
|
|
2836
2974
|
planted,
|
|
2837
2975
|
original,
|
|
2838
2976
|
deidentified,
|
|
2839
|
-
phiResidue: residue(
|
|
2977
|
+
phiResidue: residue(
|
|
2978
|
+
deidNcpdpPeer().extractTelecomLoci(ncpdpTelecomPeer().parseTelecom(deidentified)).loci
|
|
2979
|
+
),
|
|
2840
2980
|
clinicalCodes: NCPDP_CLINICAL
|
|
2841
2981
|
});
|
|
2842
2982
|
}
|
|
@@ -2844,9 +2984,9 @@ function ccdaDeidLoop(options = {}) {
|
|
|
2844
2984
|
const seed = options.seed ?? 0;
|
|
2845
2985
|
const documentType = options.documentType ?? "ccd";
|
|
2846
2986
|
const doc = generateCcda({ seed, documentType: documentType === "ccd" ? "ccd" : "referralNote" });
|
|
2847
|
-
const original =
|
|
2987
|
+
const original = ccdaPeer().serializeCcda(doc);
|
|
2848
2988
|
const planted = recordTargetSentinels(original);
|
|
2849
|
-
const deidentified =
|
|
2989
|
+
const deidentified = deidCcdaPeer().deidentifyCcda(doc, { policy: loopPolicy() }).document.toString();
|
|
2850
2990
|
return assembleVerdict({
|
|
2851
2991
|
format: "ccda",
|
|
2852
2992
|
artifact: documentType === "ccd" ? "CCD" : "ReferralNote",
|
|
@@ -2861,9 +3001,9 @@ function ccdaDeidLoop(options = {}) {
|
|
|
2861
3001
|
function summarizeDeidCoverage(results) {
|
|
2862
3002
|
const byFormatMap = /* @__PURE__ */ new Map();
|
|
2863
3003
|
for (const r of results) {
|
|
2864
|
-
const
|
|
2865
|
-
|
|
2866
|
-
byFormatMap.set(r.format,
|
|
3004
|
+
const list = byFormatMap.get(r.format) ?? [];
|
|
3005
|
+
list.push(r);
|
|
3006
|
+
byFormatMap.set(r.format, list);
|
|
2867
3007
|
}
|
|
2868
3008
|
const byFormat = [];
|
|
2869
3009
|
for (const [format, runs] of byFormatMap) {
|