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