@cosyte/fhir 0.0.10 → 0.1.0
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/README.md +173 -6
- package/dist/index.cjs +2844 -1262
- package/dist/index.d.cts +946 -83
- package/dist/index.d.ts +946 -83
- package/dist/index.mjs +2842 -1263
- package/package.json +55 -28
package/dist/index.mjs
CHANGED
|
@@ -218,6 +218,12 @@ function markNonObjectMeta(node, source) {
|
|
|
218
218
|
function markDroppedText(node) {
|
|
219
219
|
return { ...node, droppedText: true };
|
|
220
220
|
}
|
|
221
|
+
function isForeignRoot(node) {
|
|
222
|
+
return node.kind === "complex" && node.foreignRoot === true;
|
|
223
|
+
}
|
|
224
|
+
function markForeignRoot(node) {
|
|
225
|
+
return { ...node, foreignRoot: true };
|
|
226
|
+
}
|
|
221
227
|
function primitive(value, meta = {}) {
|
|
222
228
|
const node = { kind: "primitive", value };
|
|
223
229
|
if (meta.id !== void 0) node.id = meta.id;
|
|
@@ -1235,1139 +1241,1798 @@ function typesOf(resource) {
|
|
|
1235
1241
|
return getAllProperties(resource, "resourceType").flatMap((node) => [...primitiveStrings(node)]);
|
|
1236
1242
|
}
|
|
1237
1243
|
|
|
1238
|
-
// src/
|
|
1239
|
-
var
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
"
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
"
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
"
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1244
|
+
// src/validate/issues.ts
|
|
1245
|
+
var ISSUE_SEVERITIES = {
|
|
1246
|
+
FATAL: "fatal",
|
|
1247
|
+
ERROR: "error",
|
|
1248
|
+
WARNING: "warning",
|
|
1249
|
+
INFORMATION: "information"
|
|
1250
|
+
};
|
|
1251
|
+
var ISSUE_TYPES = {
|
|
1252
|
+
/** Structural issue, an element that is not allowed here, or a cardinality-max violation. */
|
|
1253
|
+
STRUCTURE: "structure",
|
|
1254
|
+
/** A required element (min cardinality ≥ 1) is missing. */
|
|
1255
|
+
REQUIRED: "required",
|
|
1256
|
+
/** An element value is invalid against its datatype value-domain (a primitive-regex failure). */
|
|
1257
|
+
VALUE: "value",
|
|
1258
|
+
/** A code is not a member of a required-strength value set binding. */
|
|
1259
|
+
CODE_INVALID: "code-invalid",
|
|
1260
|
+
/** A content-validation rule (a resource `constraint` / invariant) failed. */
|
|
1261
|
+
INVARIANT: "invariant",
|
|
1262
|
+
/** The content uses a modifier the processor does not support and cannot safely ignore. */
|
|
1263
|
+
NOT_SUPPORTED: "not-supported",
|
|
1264
|
+
/** Informational only, carries no defect (e.g. "this resource type has no schema yet"). */
|
|
1265
|
+
INFORMATIONAL: "informational",
|
|
1266
|
+
/** A business rule / profile-level assertion failed (e.g. a declared profile version is unknown). */
|
|
1267
|
+
BUSINESS_RULE: "business-rule",
|
|
1268
|
+
/** A referenced resource could not be found within the resolution closure (a Bundle reference). */
|
|
1269
|
+
NOT_FOUND: "not-found"
|
|
1270
|
+
};
|
|
1271
|
+
var VALIDATION_CODES = {
|
|
1272
|
+
/** Layer 1, an element the resource's structure does not define at this location. */
|
|
1273
|
+
UNKNOWN_ELEMENT: "UNKNOWN_ELEMENT",
|
|
1274
|
+
/** Layer 1, the resource carries no `resourceType`, so it cannot be structurally validated. */
|
|
1275
|
+
RESOURCE_TYPE_UNKNOWN: "RESOURCE_TYPE_UNKNOWN",
|
|
1276
|
+
/** Layer 1, no schema is available for this resource type; structural layers were skipped. */
|
|
1277
|
+
RESOURCE_NOT_MODELED: "RESOURCE_NOT_MODELED",
|
|
1278
|
+
/** Layer 1, an element's node shape (primitive / complex) is not what its datatype expects. */
|
|
1279
|
+
TYPE_MISMATCH: "TYPE_MISMATCH",
|
|
1280
|
+
/** Layer 1, more than one variant of a `choice[x]` element is present. */
|
|
1281
|
+
CHOICE_AMBIGUOUS: "CHOICE_AMBIGUOUS",
|
|
1282
|
+
/** Layer 2, a required element (min ≥ 1) is absent. */
|
|
1283
|
+
CARDINALITY_MIN: "CARDINALITY_MIN",
|
|
1284
|
+
/** Layer 2, an element appears more times than its maximum cardinality allows. */
|
|
1285
|
+
CARDINALITY_MAX: "CARDINALITY_MAX",
|
|
1286
|
+
/** Layer 3, a primitive value does not match its datatype's lexical form. */
|
|
1287
|
+
PRIMITIVE_INVALID: "PRIMITIVE_INVALID",
|
|
1288
|
+
/** Layer 3, a `code` value is outside a required-strength enumerated binding. */
|
|
1289
|
+
CODE_INVALID: "CODE_INVALID",
|
|
1290
|
+
/**
|
|
1291
|
+
* Safety, an element carries a `modifierExtension` this library does not understand.
|
|
1292
|
+
* FHIR's `?!` rule forbids ignoring an unknown modifier, so this **fails closed** (an `error`): the
|
|
1293
|
+
* element cannot be safely processed. See {@link ./safety.js}.
|
|
1294
|
+
*/
|
|
1295
|
+
UNHANDLED_MODIFIER_EXTENSION: "UNHANDLED_MODIFIER_EXTENSION",
|
|
1296
|
+
/**
|
|
1297
|
+
* Safety, the document wrote a property name more than once, so an element holds several values
|
|
1298
|
+
* and nothing says which the sender meant. FHIR JSON requires unique property names (json.html §2.6.2:
|
|
1299
|
+
* "Property names SHALL be unique") and expresses repetition with an array, so this is a violated
|
|
1300
|
+
* `SHALL` and an `error`. The reader keeps every value (see {@link ../model/node.js} `duplicates`),
|
|
1301
|
+
* so this reports an ambiguity, never a loss.
|
|
1302
|
+
*/
|
|
1303
|
+
DUPLICATE_PROPERTY: "DUPLICATE_PROPERTY",
|
|
1304
|
+
/**
|
|
1305
|
+
* Safety, a single-valued (`0..1`) safety element, or `resourceType`, arrived wrapped in a JSON
|
|
1306
|
+
* array. FHIR JSON writes a single-valued element as a name/value pair and uses an array only for a
|
|
1307
|
+
* repeating element (json.html §2.6.2.2), so this is a non-conformant encoding and an `error`. It is the
|
|
1308
|
+
* shape a **generic XML-to-JSON converter** produces for every element, which is how a C-CDA or v2
|
|
1309
|
+
* feed commonly reaches a FHIR surface, and left unreported it reaches the same harm as a repeated
|
|
1310
|
+
* property name: a single-value read finds no code in the array, so a retraction or a negation the
|
|
1311
|
+
* sender wrote goes unreported and the record reads live. Nothing is lost, the wrapper is preserved
|
|
1312
|
+
* and the safety layer reads through it; this reports that the encoding was ambiguous.
|
|
1313
|
+
*/
|
|
1314
|
+
ARRAY_WRAPPED_SCALAR: "ARRAY_WRAPPED_SCALAR",
|
|
1315
|
+
/**
|
|
1316
|
+
* Safety, an `Observation.value[x]` choice element arrived wrapped in a JSON array. The same
|
|
1317
|
+
* non-conformant encoding {@link VALIDATION_CODES.ARRAY_WRAPPED_SCALAR} reports (json.html
|
|
1318
|
+
* §2.6.2.2 reserves the array for a repeating element), at the one position whose cardinality is
|
|
1319
|
+
* decided by a choice rather than by a per-resource element table, so it needs a code of its own
|
|
1320
|
+
* rather than a wider window on that one. An `error`.
|
|
1321
|
+
*
|
|
1322
|
+
* **Why the value readout needs its own channel.** `readObservationValue` fails safe on this
|
|
1323
|
+
* shape, it reports the variant that is present and no `quantity`, so no wrong number is handed
|
|
1324
|
+
* out, but "the variant is present and the magnitude is absent" reads identically to an
|
|
1325
|
+
* `Observation` whose `valueQuantity` carried no `value`. A dose the sender wrote is then
|
|
1326
|
+
* indistinguishable from one it did not, and this is the record that the position held content
|
|
1327
|
+
* nothing here would read.
|
|
1328
|
+
*
|
|
1329
|
+
* **The window is `Observation.value[x]` and `Observation.component.value[x]`, at every
|
|
1330
|
+
* Observation resource root the model holds**, which is the window the value readout itself reads
|
|
1331
|
+
* (`../quantity/value.js`). Both are `0..1` in R4 (observation.html), so this needs no
|
|
1332
|
+
* per-resource cardinality table and cannot fire on a conformant document. It is deliberately not
|
|
1333
|
+
* every R4 `0..1` element: that is the per-resource model this library does not have.
|
|
1334
|
+
*
|
|
1335
|
+
* Value-free, the location of the choice variant, never the magnitude, unit or code inside the
|
|
1336
|
+
* wrapper.
|
|
1337
|
+
*/
|
|
1338
|
+
ARRAY_WRAPPED_CHOICE: "ARRAY_WRAPPED_CHOICE",
|
|
1339
|
+
/**
|
|
1340
|
+
* Safety, the document wrote a JSON array **inside another array**. FHIR JSON uses an array for a
|
|
1341
|
+
* repeating element and for nothing else (json.html §2.6.2.2), so a list of lists has no meaning at
|
|
1342
|
+
* any position and this is a non-conformant encoding wherever it appears, which is why it needs no
|
|
1343
|
+
* cardinality rule and cannot fire on a conformant document. Reported at every position the model
|
|
1344
|
+
* has a node for; a `_`-sibling the reader discards whole is the stated exception, and draws a
|
|
1345
|
+
* reader warning instead: `UNKNOWN_PROPERTY` for an unrecognised member of a `_`-sibling object,
|
|
1346
|
+
* `MISPLACED_PRIMITIVE_EXTENSION` for a sibling on an object or on a non-primitive array, not one
|
|
1347
|
+
* code for all three. An `error`, and one of the two on this list
|
|
1348
|
+
* where the reader could **not model** what the sender wrote (the other is
|
|
1349
|
+
* {@link VALIDATION_CODES.DROPPED_ELEMENT_TEXT}): the codec does not model an inner array, so this
|
|
1350
|
+
* reports a loss of *structure* rather than an ambiguity, though the array's JSON text is kept and
|
|
1351
|
+
* readable (see {@link ../model/node.js} `nestedArrayContent`). Left unreported it is among the worst of the
|
|
1352
|
+
* set, because the model then looks exactly like an element that was legitimately absent, and a
|
|
1353
|
+
* refuted allergy, a resolved condition, or an entire resource inside a Bundle entry reads back as
|
|
1354
|
+
* a clean document. Value-free, the position the inner array occupied, never its contents.
|
|
1355
|
+
*/
|
|
1356
|
+
NESTED_ARRAY: "NESTED_ARRAY",
|
|
1357
|
+
/**
|
|
1358
|
+
* Safety, an XML document wrote **character data directly on a FHIR element**. FHIR XML carries a
|
|
1359
|
+
* primitive's value in the `value` attribute (xml.html §2.6.1), so text written as element content
|
|
1360
|
+
* has no slot on the model and the reader drops it: `<status>entered-in-error</status>` yields a
|
|
1361
|
+
* `status` with no value. An `error`, and the only code on this list where the content is neither
|
|
1362
|
+
* modeled **nor kept**: unlike {@link VALIDATION_CODES.NESTED_ARRAY}, which preserves the array's
|
|
1363
|
+
* JSON text, the character data is discarded outright, because reading it back would be a
|
|
1364
|
+
* tolerance for a non-conformant encoding rather than a report of one. Left unreported it reaches the same harm as
|
|
1365
|
+
* {@link VALIDATION_CODES.NESTED_ARRAY} by the other wire format, because the model is again
|
|
1366
|
+
* indistinguishable from an element that was legitimately absent: a retraction, a `refuted`
|
|
1367
|
+
* verification status, or a dose *number* beside a surviving unit and UCUM code all read back as a
|
|
1368
|
+
* clean document. Value-free, the position the text occupied, never its contents.
|
|
1369
|
+
*/
|
|
1370
|
+
DROPPED_ELEMENT_TEXT: "DROPPED_ELEMENT_TEXT",
|
|
1371
|
+
/**
|
|
1372
|
+
* Safety, an element carries a DataAbsentReason extension **and a value of its own**, so the
|
|
1373
|
+
* document asserts both that the element holds that value and that it holds none. An `error`
|
|
1374
|
+
* (`structure`): the two cannot both be true, nothing here ranks them, and a consumer that
|
|
1375
|
+
* happened to read one of the two would report the record as though the other had not been
|
|
1376
|
+
* written. Both survive on the model and on the safety readout; this reports that they disagree.
|
|
1377
|
+
* Value-free, the location of the element, never the value and never the reason.
|
|
1378
|
+
*
|
|
1379
|
+
* Cannot fire on a conformant document: an element the sender has data for is written with the
|
|
1380
|
+
* data and no marker. It is **not** the `Observation.dataAbsentReason` ELEMENT beside a `value[x]`,
|
|
1381
|
+
* which is the `obs-6` invariant and reports as {@link VALIDATION_CODES.INVARIANT_VIOLATED}; the
|
|
1382
|
+
* element is not the extension, and the two never report about one another's shape.
|
|
1383
|
+
*/
|
|
1384
|
+
ABSENCE_MARKER_CONFLICT: "ABSENCE_MARKER_CONFLICT",
|
|
1385
|
+
/**
|
|
1386
|
+
* Safety, an element carries a DataAbsentReason extension whose reason this library could not
|
|
1387
|
+
* read: no `valueCode`, one holding no readable string, an empty one, one written twice, or a code
|
|
1388
|
+
* outside the closed fifteen-concept value set the extension's `value[x]` binds to at **required**
|
|
1389
|
+
* strength. An `error` (`code-invalid`), on the same footing as any other required-binding miss:
|
|
1390
|
+
* the set is closed and published, so membership is decided from the set itself with no
|
|
1391
|
+
* terminology service involved and no value-set expansion.
|
|
1392
|
+
*
|
|
1393
|
+
* **Nothing is coerced.** The code is not trimmed, case-folded or substituted, and the element is
|
|
1394
|
+
* not read as `unknown` and not read as populated: doing either would author a reason the sender
|
|
1395
|
+
* did not spell, or erase a declaration the sender did make. The element stays value-absent and
|
|
1396
|
+
* this is the record that a declaration was made and could not be honoured. Value-free, the
|
|
1397
|
+
* location of the element, never the code that failed to match.
|
|
1398
|
+
*/
|
|
1399
|
+
ABSENCE_MARKER_UNREADABLE: "ABSENCE_MARKER_UNREADABLE",
|
|
1400
|
+
/**
|
|
1401
|
+
* Safety, the resource is marked `entered-in-error` and is therefore **retracted, not
|
|
1402
|
+
* data**. Surfaced as `information` (it is not itself a defect) so a consumer cannot miss it.
|
|
1403
|
+
*/
|
|
1404
|
+
RETRACTED_RESOURCE: "RETRACTED_RESOURCE",
|
|
1405
|
+
/**
|
|
1406
|
+
* Invariant, a resource constraint failed. Three sources raise it, each with or without the
|
|
1407
|
+
* others: the safety layer's named invariants (`ait-1`/`ait-2`, `con-3`/`con-4`/`con-5`,
|
|
1408
|
+
* `obs-6`/`obs-7`); the base-constraint layer, which evaluates the other error-severity
|
|
1409
|
+
* constraints R4 declares on the eight modeled types and their `DomainResource` / `Element` /
|
|
1410
|
+
* `Extension` base (`pat-1`, `obs-3`, `con-1`, `con-2`, `imm-1`, `dom-2` to `dom-5`, `ele-1`,
|
|
1411
|
+
* `ext-1`) with no profile supplied; and a supplied profile's own `constraint`s. The specific
|
|
1412
|
+
* constraint key travels in {@link ValidationIssue.constraint}, and the severity mirrors the
|
|
1413
|
+
* constraint's own (`error`, except a `warning` constraint such as the best-practice `con-3`).
|
|
1414
|
+
*/
|
|
1415
|
+
INVARIANT_VIOLATED: "INVARIANT_VIOLATED",
|
|
1416
|
+
/**
|
|
1417
|
+
* Invariant, a `constraint`'s FHIRPath `expression`, from a supplied profile or from the R4 base
|
|
1418
|
+
* constraints the base-constraint layer evaluates, is **outside the bounded
|
|
1419
|
+
* engine's subset** and could not be evaluated. Always `information` (`informational`):
|
|
1420
|
+
* the constraint is reported **unchecked, never assumed to pass** (fail-safe), the
|
|
1421
|
+
* library does not claim conformance to an invariant it could not test. The constraint `key` travels
|
|
1422
|
+
* in {@link ValidationIssue.constraint}. Value-free (the location + key, never an instance value).
|
|
1423
|
+
* A profile's verbatim copy of an R4 base constraint that the base-constraint layer decided at the
|
|
1424
|
+
* same occurrence is not reported this way, because that constraint was evaluated: R4's `dom-3`
|
|
1425
|
+
* on a resource with nothing contained draws nothing, with or without a profile.
|
|
1426
|
+
*/
|
|
1427
|
+
INVARIANT_UNCHECKED: "INVARIANT_UNCHECKED",
|
|
1428
|
+
/**
|
|
1429
|
+
* Quantity/UCUM, a `Quantity` claims the UCUM `system` but its `code` is absent or not a
|
|
1430
|
+
* shape-valid UCUM expression, so the unit cannot be trusted for machine use. A `warning`
|
|
1431
|
+
* (`value`): the value is **preserved verbatim and never converted**, the library does not bundle
|
|
1432
|
+
* UCUM content, so it cannot assert the code *is* a real unit, only that it is present and well-shaped.
|
|
1433
|
+
*/
|
|
1434
|
+
UCUM_UNIT_UNRECOGNIZED: "UCUM_UNIT_UNRECOGNIZED",
|
|
1435
|
+
/**
|
|
1436
|
+
* Quantity/UCUM, a vital-signs Observation's measured value carries a unit the FHIR
|
|
1437
|
+
* vital-signs profile forbids for that LOINC code (wrong UCUM `code`, or a non-UCUM `system`). An
|
|
1438
|
+
* `error` (`code-invalid`): the vital-signs profile *requires* the unit, so a nonconformant one is a
|
|
1439
|
+
* profile violation, compared on the UCUM `code` (case- and bracket-sensitive), never the `unit` string.
|
|
1440
|
+
*/
|
|
1441
|
+
VITAL_SIGN_UNIT_NONCONFORMANT: "VITAL_SIGN_UNIT_NONCONFORMANT",
|
|
1442
|
+
/**
|
|
1443
|
+
* Quantity/UCUM, an Observation whose profile expects a numeric `Quantity` value carries
|
|
1444
|
+
* a different `value[x]` variant instead (e.g. `valueString`). A `warning` (`value`): the value is
|
|
1445
|
+
* preserved and surfaced by its real type, a caller must not read it as a number.
|
|
1446
|
+
*/
|
|
1447
|
+
VALUE_TYPE_UNEXPECTED: "VALUE_TYPE_UNEXPECTED",
|
|
1448
|
+
/**
|
|
1449
|
+
* Terminology, a bound coding's `system` URI is not in the frozen known-systems registry
|
|
1450
|
+
* (and not one the binding's value set draws from). Always `information` (`code-invalid`): an
|
|
1451
|
+
* unknown system may be a legitimate local/proprietary one, so it is never a defect, it only means
|
|
1452
|
+
* the library cannot validate codes drawn from it. Content-free, so it can never flip validity.
|
|
1453
|
+
*/
|
|
1454
|
+
CODE_SYSTEM_UNKNOWN: "CODE_SYSTEM_UNKNOWN",
|
|
1455
|
+
/**
|
|
1456
|
+
* Terminology, a bound coding uses a **known** code `system` that is not one the
|
|
1457
|
+
* binding's value set draws from (e.g. an ICD-10-CM code where the binding expects RxNorm + SNOMED).
|
|
1458
|
+
* This is the content-free "wrong system for this binding" check, decided from the `system` alone,
|
|
1459
|
+
* with no value-set content. Severity follows the binding strength (`required` → `error`;
|
|
1460
|
+
* `extensible`/`preferred` → `warning`, since a different system may be a legitimate extension;
|
|
1461
|
+
* `example` → none). Compared on the `system` URI, never a code value.
|
|
1462
|
+
*/
|
|
1463
|
+
CODE_SYSTEM_UNEXPECTED: "CODE_SYSTEM_UNEXPECTED",
|
|
1464
|
+
/**
|
|
1465
|
+
* Terminology, a configured terminology service reported that a bound coding's
|
|
1466
|
+
* `(system, code)` is **not a member** of the binding's value set. Severity follows the binding
|
|
1467
|
+
* strength (`required`/`extensible` → `error`; `preferred` → `warning`; `example` → `information`,
|
|
1468
|
+
* never an error). Emitted **only** when a service definitively answers `not-in`; with no service,
|
|
1469
|
+
* or an `unknown` answer, the library degrades to the content-free system checks and never
|
|
1470
|
+
* false-errors (fail-safe). Value-free, the coding location, never the code itself.
|
|
1471
|
+
*
|
|
1472
|
+
* The **only** finding that carries a {@link ValidationIssue.codeSystemVersion} record, because it
|
|
1473
|
+
* is the only one a terminology service produces: the release the service declared its answer was
|
|
1474
|
+
* made against, or the explicit mark that it declared none. Severity, emission and the fail-safe
|
|
1475
|
+
* degrade are all independent of it.
|
|
1476
|
+
*/
|
|
1477
|
+
CODE_NOT_IN_VALUESET: "CODE_NOT_IN_VALUESET",
|
|
1478
|
+
/**
|
|
1479
|
+
* Profile, an instance element is present under a **closed** slicing whose discriminators
|
|
1480
|
+
* matched none of the profile's defined slices. A `structure` `error`: `closed` slicing forbids
|
|
1481
|
+
* content outside the named slices. (Under `open` slicing an unmatched element is allowed and draws
|
|
1482
|
+
* nothing; under `openAtEnd` it is allowed only in the trailing position, this library flags a
|
|
1483
|
+
* closed-slicing miss and leaves the ordering nuance unenforced.)
|
|
1484
|
+
*/
|
|
1485
|
+
PROFILE_SLICE_UNMATCHED: "PROFILE_SLICE_UNMATCHED",
|
|
1486
|
+
/**
|
|
1487
|
+
* Profile, a slicing whose discriminator this library cannot evaluate (a `profile`
|
|
1488
|
+
* discriminator, which needs recursive profile resolution, or the R5-only `position`). Emitted as
|
|
1489
|
+
* `information` so slice membership is reported **unchecked, never silently passed** (the
|
|
1490
|
+
* fail-safe): the library does not guess a slice assignment it cannot justify.
|
|
1491
|
+
*/
|
|
1492
|
+
PROFILE_SLICE_UNCHECKED: "PROFILE_SLICE_UNCHECKED",
|
|
1493
|
+
/**
|
|
1494
|
+
* Profile, an element the profile marks **must-support** is absent from the instance.
|
|
1495
|
+
* **Always `information`, never an error** (the fail-safe, and the single most important
|
|
1496
|
+
* must-support rule): must-support is a *system obligation* on the sender to be able to populate the
|
|
1497
|
+
* element and on the receiver to tolerate its absence, it is **not** an instance-presence
|
|
1498
|
+
* requirement. A strict client that errors on an absent must-support element is the classic bug this
|
|
1499
|
+
* code exists to avoid.
|
|
1500
|
+
*/
|
|
1501
|
+
MUST_SUPPORT_ABSENT: "MUST_SUPPORT_ABSENT",
|
|
1502
|
+
/**
|
|
1503
|
+
* Profile, the instance's `meta.profile` declares a profile at a version the supplied
|
|
1504
|
+
* profile set does not carry (`canonical|version` with a different version, or an unresolvable
|
|
1505
|
+
* canonical). A `warning` (`business-rule`): an unknown profile
|
|
1506
|
+
* version is flagged rather than silently best-effort-validating against a different one.
|
|
1507
|
+
*/
|
|
1508
|
+
PROFILE_VERSION_MISMATCH: "PROFILE_VERSION_MISMATCH",
|
|
1509
|
+
/**
|
|
1510
|
+
* Profile, an element carries a value that is not **exactly** the profile's `fixed[x]`.
|
|
1511
|
+
* A `value` `error`: `fixed[x]` is an equality constraint (the element SHALL match the fixed value
|
|
1512
|
+
* exactly, including every nested property). Compared structurally and precision-exactly (decimals
|
|
1513
|
+
* via {@link ../model/decimal.js}), never by echoing the value.
|
|
1514
|
+
*/
|
|
1515
|
+
PROFILE_FIXED_MISMATCH: "PROFILE_FIXED_MISMATCH",
|
|
1516
|
+
/**
|
|
1517
|
+
* Profile, an element does not match the profile's `pattern[x]`. A `value` `error`:
|
|
1518
|
+
* `pattern[x]` is a **subset** constraint (the element SHALL contain *at least* the pattern's
|
|
1519
|
+
* properties and values, but may carry more), the weaker sibling of `fixed[x]`. Value-free.
|
|
1520
|
+
*/
|
|
1521
|
+
PROFILE_PATTERN_MISMATCH: "PROFILE_PATTERN_MISMATCH",
|
|
1522
|
+
/**
|
|
1523
|
+
* Bundle, a `Reference` inside a Bundle entry (or a `#fragment` inside a resource's
|
|
1524
|
+
* `contained`) that could not be resolved within the resolution closure: a fragment whose target
|
|
1525
|
+
* contained resource is absent, or a relative `Type/id` reference naming no entry in the Bundle. A
|
|
1526
|
+
* `warning` (`not-found`) and **never fatal**, the target may legitimately live outside the
|
|
1527
|
+
* supplied closure (a partial Bundle, an external server), so the reference is **preserved**, only
|
|
1528
|
+
* flagged. An absolute/logical reference that is simply external to the Bundle draws **no** finding.
|
|
1529
|
+
* Value-free, the FHIRPath location of the reference, never the reference string itself.
|
|
1530
|
+
*/
|
|
1531
|
+
REFERENCE_UNRESOLVED: "REFERENCE_UNRESOLVED",
|
|
1532
|
+
/**
|
|
1533
|
+
* Bundle, the `#fragment` references among a resource's `contained` resources form a
|
|
1534
|
+
* **cycle** (a → b → a, or a self-reference). An `error` (`structure`): a containment cycle is
|
|
1535
|
+
* malformed and, to a naive transitive resolver, a denial-of-service (an unbounded loop / stack
|
|
1536
|
+
* blow-up). The bounded, iterative cycle guard detects it and reports it here rather than looping,
|
|
1537
|
+
* DoS-safe by construction. Value-free, the location of the `contained` element, never a value.
|
|
1538
|
+
*/
|
|
1539
|
+
CONTAINED_CYCLE: "CONTAINED_CYCLE",
|
|
1540
|
+
/**
|
|
1541
|
+
* Bundle, a Bundle entry's `fullUrl` is a RESTful URL (relative `Type/id` or an absolute
|
|
1542
|
+
* URL ending in `Type/id`) whose id disagrees with the entry `resource.id`. An `error`
|
|
1543
|
+
* (`business-rule`): FHIR requires a RESTful `fullUrl` to be consistent with the resource it wraps,
|
|
1544
|
+
* and a disagreement can cause a reference to resolve to the wrong resource. A `urn:uuid:` (logical)
|
|
1545
|
+
* `fullUrl` places no constraint on `resource.id`, so it never triggers this. Value-free, the
|
|
1546
|
+
* location of the `fullUrl`, never either id.
|
|
1547
|
+
*/
|
|
1548
|
+
FULLURL_ID_MISMATCH: "FULLURL_ID_MISMATCH"
|
|
1549
|
+
};
|
|
1550
|
+
var CODE_SYSTEM_VERSION_RECORD_SYSTEM = "https://cosyte.com/fhir/CodeSystem/code-system-version-record";
|
|
1551
|
+
var CODE_SYSTEM_VERSION_RECORD_CODES = {
|
|
1552
|
+
/** The service declared a release; the string it declared travels beside this marker. */
|
|
1553
|
+
DECLARED: "declared",
|
|
1554
|
+
/**
|
|
1555
|
+
* The service was consulted, answered definitively, and declared **no** release. It is an
|
|
1556
|
+
* applicable, unanswered question, **not** "current" and not "not applicable": the marker is
|
|
1557
|
+
* emitted precisely so the silence cannot be read as currency.
|
|
1558
|
+
*/
|
|
1559
|
+
UNDECLARED: "undeclared"
|
|
1560
|
+
};
|
|
1561
|
+
var ISSUE_TYPE_OF = {
|
|
1562
|
+
UNKNOWN_ELEMENT: ISSUE_TYPES.STRUCTURE,
|
|
1563
|
+
RESOURCE_TYPE_UNKNOWN: ISSUE_TYPES.STRUCTURE,
|
|
1564
|
+
RESOURCE_NOT_MODELED: ISSUE_TYPES.INFORMATIONAL,
|
|
1565
|
+
TYPE_MISMATCH: ISSUE_TYPES.STRUCTURE,
|
|
1566
|
+
CHOICE_AMBIGUOUS: ISSUE_TYPES.STRUCTURE,
|
|
1567
|
+
CARDINALITY_MIN: ISSUE_TYPES.REQUIRED,
|
|
1568
|
+
CARDINALITY_MAX: ISSUE_TYPES.STRUCTURE,
|
|
1569
|
+
PRIMITIVE_INVALID: ISSUE_TYPES.VALUE,
|
|
1570
|
+
CODE_INVALID: ISSUE_TYPES.CODE_INVALID,
|
|
1571
|
+
UNHANDLED_MODIFIER_EXTENSION: ISSUE_TYPES.NOT_SUPPORTED,
|
|
1572
|
+
DUPLICATE_PROPERTY: ISSUE_TYPES.STRUCTURE,
|
|
1573
|
+
ARRAY_WRAPPED_SCALAR: ISSUE_TYPES.STRUCTURE,
|
|
1574
|
+
ARRAY_WRAPPED_CHOICE: ISSUE_TYPES.STRUCTURE,
|
|
1575
|
+
NESTED_ARRAY: ISSUE_TYPES.STRUCTURE,
|
|
1576
|
+
DROPPED_ELEMENT_TEXT: ISSUE_TYPES.STRUCTURE,
|
|
1577
|
+
ABSENCE_MARKER_CONFLICT: ISSUE_TYPES.STRUCTURE,
|
|
1578
|
+
ABSENCE_MARKER_UNREADABLE: ISSUE_TYPES.CODE_INVALID,
|
|
1579
|
+
RETRACTED_RESOURCE: ISSUE_TYPES.INFORMATIONAL,
|
|
1580
|
+
INVARIANT_VIOLATED: ISSUE_TYPES.INVARIANT,
|
|
1581
|
+
INVARIANT_UNCHECKED: ISSUE_TYPES.INFORMATIONAL,
|
|
1582
|
+
UCUM_UNIT_UNRECOGNIZED: ISSUE_TYPES.VALUE,
|
|
1583
|
+
VITAL_SIGN_UNIT_NONCONFORMANT: ISSUE_TYPES.CODE_INVALID,
|
|
1584
|
+
VALUE_TYPE_UNEXPECTED: ISSUE_TYPES.VALUE,
|
|
1585
|
+
CODE_SYSTEM_UNKNOWN: ISSUE_TYPES.CODE_INVALID,
|
|
1586
|
+
CODE_SYSTEM_UNEXPECTED: ISSUE_TYPES.CODE_INVALID,
|
|
1587
|
+
CODE_NOT_IN_VALUESET: ISSUE_TYPES.CODE_INVALID,
|
|
1588
|
+
PROFILE_SLICE_UNMATCHED: ISSUE_TYPES.STRUCTURE,
|
|
1589
|
+
PROFILE_SLICE_UNCHECKED: ISSUE_TYPES.INFORMATIONAL,
|
|
1590
|
+
MUST_SUPPORT_ABSENT: ISSUE_TYPES.INFORMATIONAL,
|
|
1591
|
+
PROFILE_VERSION_MISMATCH: ISSUE_TYPES.BUSINESS_RULE,
|
|
1592
|
+
PROFILE_FIXED_MISMATCH: ISSUE_TYPES.VALUE,
|
|
1593
|
+
PROFILE_PATTERN_MISMATCH: ISSUE_TYPES.VALUE,
|
|
1594
|
+
REFERENCE_UNRESOLVED: ISSUE_TYPES.NOT_FOUND,
|
|
1595
|
+
CONTAINED_CYCLE: ISSUE_TYPES.STRUCTURE,
|
|
1596
|
+
FULLURL_ID_MISMATCH: ISSUE_TYPES.BUSINESS_RULE
|
|
1597
|
+
};
|
|
1598
|
+
var DIAGNOSTIC_OF = {
|
|
1599
|
+
UNKNOWN_ELEMENT: "Element is not defined at this location in the resource structure.",
|
|
1600
|
+
RESOURCE_TYPE_UNKNOWN: "Resource is missing a resourceType and cannot be structurally validated.",
|
|
1601
|
+
RESOURCE_NOT_MODELED: "No structural schema is available for this resource type; structural validation was skipped.",
|
|
1602
|
+
TYPE_MISMATCH: "Element value is not of the shape its datatype requires.",
|
|
1603
|
+
CHOICE_AMBIGUOUS: "More than one variant of a choice element is present.",
|
|
1604
|
+
CARDINALITY_MIN: "Required element is missing.",
|
|
1605
|
+
CARDINALITY_MAX: "Element appears more times than its maximum cardinality allows.",
|
|
1606
|
+
PRIMITIVE_INVALID: "Primitive value does not match the required lexical form for its datatype.",
|
|
1607
|
+
CODE_INVALID: "Code is not in the required value set for this element.",
|
|
1608
|
+
UNHANDLED_MODIFIER_EXTENSION: "Element carries a modifierExtension this processor does not understand; it cannot be safely processed and is rejected (fail-closed).",
|
|
1609
|
+
DUPLICATE_PROPERTY: "Property name appears more than once on this object; FHIR requires unique property names and uses an array for a repeating element, so the element's value is ambiguous.",
|
|
1610
|
+
ARRAY_WRAPPED_SCALAR: "Single-valued element is wrapped in an array; FHIR JSON writes a 0..1 element as a name/value pair and uses an array only for a repeating element, so the element's encoding is ambiguous.",
|
|
1611
|
+
ARRAY_WRAPPED_CHOICE: "Observation value[x] choice is wrapped in an array; FHIR JSON writes a 0..1 choice as a name/value pair and uses an array only for a repeating element, so the value's encoding is ambiguous and no magnitude, unit or code was read from inside the wrapper.",
|
|
1612
|
+
NESTED_ARRAY: "A JSON array appears inside another array; FHIR JSON uses an array only for a repeating element, so this shape has no meaning and its contents were not read. Content the sender wrote is missing from the model at this position.",
|
|
1613
|
+
DROPPED_ELEMENT_TEXT: "Character data was written directly on this element; FHIR XML carries a primitive's value in the value attribute, so an element has no slot for text and it was not read. Content the sender wrote is missing from the model at this position.",
|
|
1614
|
+
ABSENCE_MARKER_CONFLICT: "Element carries a data-absent-reason extension and a value of its own; the document asserts both that the element holds that value and that it holds none, and neither is preferred here. Both are preserved.",
|
|
1615
|
+
ABSENCE_MARKER_UNREADABLE: "Element carries a data-absent-reason extension whose reason could not be read: it is absent, empty, written more than once, or outside the value set the extension requires. The reason is not guessed and the element is not read as populated.",
|
|
1616
|
+
RETRACTED_RESOURCE: "Resource is marked entered-in-error; it is retracted and must not be treated as active data.",
|
|
1617
|
+
INVARIANT_VIOLATED: "A resource invariant (content-validation constraint) was violated.",
|
|
1618
|
+
INVARIANT_UNCHECKED: "A resource invariant could not be evaluated by the bounded FHIRPath engine; it is reported unchecked rather than assumed to pass.",
|
|
1619
|
+
UCUM_UNIT_UNRECOGNIZED: "Quantity declares the UCUM system but its unit code is absent or not a well-formed UCUM expression; the unit is preserved verbatim and never converted.",
|
|
1620
|
+
VITAL_SIGN_UNIT_NONCONFORMANT: "Vital-signs measurement carries a unit the vital-signs profile does not allow for this code.",
|
|
1621
|
+
VALUE_TYPE_UNEXPECTED: "Observation value is present but not the expected type for this profile.",
|
|
1622
|
+
CODE_SYSTEM_UNKNOWN: "Coding uses a code system that is not in the known-systems registry; its codes cannot be validated (an unrecognized system is not itself an error).",
|
|
1623
|
+
CODE_SYSTEM_UNEXPECTED: "Coding uses a code system that the bound value set does not draw from.",
|
|
1624
|
+
CODE_NOT_IN_VALUESET: "Coding is not a member of the value set required by this element's binding.",
|
|
1625
|
+
PROFILE_SLICE_UNMATCHED: "Element is present under closed slicing but matches none of the profile's defined slices.",
|
|
1626
|
+
PROFILE_SLICE_UNCHECKED: "Slice membership could not be evaluated (an unsupported discriminator); it is reported unchecked rather than assumed to pass.",
|
|
1627
|
+
MUST_SUPPORT_ABSENT: "Profile marks this element must-support; it is absent. Must-support is a system obligation, not an instance-presence requirement, this is informational, never an error.",
|
|
1628
|
+
PROFILE_VERSION_MISMATCH: "Instance declares a profile at a version the supplied profile set does not carry; it was not validated against that exact version.",
|
|
1629
|
+
PROFILE_FIXED_MISMATCH: "Element value does not exactly match the value the profile fixes for it.",
|
|
1630
|
+
PROFILE_PATTERN_MISMATCH: "Element does not match the pattern the profile requires for it.",
|
|
1631
|
+
REFERENCE_UNRESOLVED: "Reference could not be resolved within the supplied closure; it is preserved and flagged, never dropped (the target may live outside this Bundle).",
|
|
1632
|
+
CONTAINED_CYCLE: "Contained resources reference each other in a cycle; the containment graph is malformed and was reported by the bounded cycle guard rather than followed into an unbounded loop.",
|
|
1633
|
+
FULLURL_ID_MISMATCH: "Bundle entry fullUrl is a RESTful URL whose id disagrees with the wrapped resource's id."
|
|
1634
|
+
};
|
|
1635
|
+
function diagnosticFor(code) {
|
|
1636
|
+
return DIAGNOSTIC_OF[code];
|
|
1637
|
+
}
|
|
1638
|
+
function validationIssue(code, severity, expression, constraint, codeSystemVersion) {
|
|
1639
|
+
const issue = { code, severity, type: ISSUE_TYPE_OF[code], expression };
|
|
1640
|
+
const withConstraint = constraint === void 0 ? issue : { ...issue, constraint };
|
|
1641
|
+
return codeSystemVersion === void 0 ? withConstraint : { ...withConstraint, codeSystemVersion };
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
// src/quantity/ucum.ts
|
|
1645
|
+
var UCUM_SYSTEM = "http://unitsofmeasure.org";
|
|
1646
|
+
var LOINC_SYSTEM = "http://loinc.org";
|
|
1647
|
+
var OBSERVATION_CATEGORY_SYSTEM = "http://terminology.hl7.org/CodeSystem/observation-category";
|
|
1648
|
+
var VITAL_SIGNS_CATEGORY = "vital-signs";
|
|
1649
|
+
var VITAL_SIGNS_PROFILE = "http://hl7.org/fhir/StructureDefinition/vitalsigns";
|
|
1650
|
+
var VITAL_SIGN_UNITS = /* @__PURE__ */ new Map([
|
|
1651
|
+
["29463-7", ["g", "kg", "[lb_av]"]],
|
|
1652
|
+
// Body weight
|
|
1653
|
+
["8302-2", ["cm", "[in_i]"]],
|
|
1654
|
+
// Body height
|
|
1655
|
+
["8306-3", ["cm", "[in_i]"]],
|
|
1656
|
+
// Body height (lying) / body length
|
|
1657
|
+
["8287-5", ["cm", "[in_i]"]],
|
|
1658
|
+
// Head circumference (Occipital-frontal, by tape)
|
|
1659
|
+
["9843-4", ["cm", "[in_i]"]],
|
|
1660
|
+
// Head circumference
|
|
1661
|
+
["8310-5", ["Cel", "[degF]"]],
|
|
1662
|
+
// Body temperature
|
|
1663
|
+
["8867-4", ["/min"]],
|
|
1664
|
+
// Heart rate
|
|
1665
|
+
["9279-1", ["/min"]],
|
|
1666
|
+
// Respiratory rate
|
|
1667
|
+
["8480-6", ["mm[Hg]"]],
|
|
1668
|
+
// Systolic blood pressure
|
|
1669
|
+
["8462-4", ["mm[Hg]"]],
|
|
1670
|
+
// Diastolic blood pressure
|
|
1671
|
+
["2708-6", ["%"]],
|
|
1672
|
+
// Oxygen saturation in Arterial blood
|
|
1673
|
+
["59408-5", ["%"]],
|
|
1674
|
+
// Oxygen saturation (SpO2) by pulse oximetry
|
|
1675
|
+
["39156-5", ["kg/m2"]]
|
|
1676
|
+
// Body mass index
|
|
1677
|
+
]);
|
|
1678
|
+
function requiredVitalSignUnits(loincCode) {
|
|
1679
|
+
return VITAL_SIGN_UNITS.get(loincCode);
|
|
1680
|
+
}
|
|
1681
|
+
function decimalValue(node) {
|
|
1682
|
+
if (node === void 0 || !isPrimitive(node)) return void 0;
|
|
1683
|
+
const { value } = node;
|
|
1684
|
+
if (typeof value === "object") return value;
|
|
1685
|
+
return typeof value === "string" ? decimalFromLexical(value) : void 0;
|
|
1686
|
+
}
|
|
1687
|
+
function readQuantity(node) {
|
|
1688
|
+
if (node === void 0 || node.kind !== "complex") return void 0;
|
|
1689
|
+
const q = node;
|
|
1690
|
+
return {
|
|
1691
|
+
value: decimalValue(getProperty(q, "value")),
|
|
1692
|
+
comparator: primitiveString(getProperty(q, "comparator")),
|
|
1693
|
+
unit: primitiveString(getProperty(q, "unit")),
|
|
1694
|
+
system: primitiveString(getProperty(q, "system")),
|
|
1695
|
+
code: primitiveString(getProperty(q, "code"))
|
|
1506
1696
|
};
|
|
1507
|
-
walkComplex(resource, path, out, true);
|
|
1508
|
-
return { ...out, modifierElements: dedupeModifierElements(out.modifierElements) };
|
|
1509
1697
|
}
|
|
1510
|
-
function
|
|
1511
|
-
if (
|
|
1512
|
-
|
|
1698
|
+
function validateUcumShape(code) {
|
|
1699
|
+
if (code.length === 0) return "invalid";
|
|
1700
|
+
const skeleton = code.replace(/\{[^{}]*\}/g, "");
|
|
1701
|
+
if (skeleton.includes("{") || skeleton.includes("}")) return "invalid";
|
|
1702
|
+
if (/\s/.test(skeleton)) return "invalid";
|
|
1703
|
+
const pairs = { "]": "[", ")": "(" };
|
|
1704
|
+
const stack = [];
|
|
1705
|
+
for (const ch of skeleton) {
|
|
1706
|
+
if (ch === "[" || ch === "(") stack.push(ch);
|
|
1707
|
+
else if (ch === "]" || ch === ")") {
|
|
1708
|
+
if (stack.pop() !== pairs[ch]) return "invalid";
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
return stack.length === 0 ? "ok" : "invalid";
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// src/quantity/value.ts
|
|
1715
|
+
var OBSERVATION_VALUE_TYPES = [
|
|
1716
|
+
"Quantity",
|
|
1717
|
+
"CodeableConcept",
|
|
1718
|
+
"String",
|
|
1719
|
+
"Boolean",
|
|
1720
|
+
"Integer",
|
|
1721
|
+
"Range",
|
|
1722
|
+
"Ratio",
|
|
1723
|
+
"SampledData",
|
|
1724
|
+
"Time",
|
|
1725
|
+
"DateTime",
|
|
1726
|
+
"Period"
|
|
1727
|
+
];
|
|
1728
|
+
function presentValues(node) {
|
|
1729
|
+
const byName = new Map(node.properties.map((p) => [p.name, p.value]));
|
|
1730
|
+
const out = [];
|
|
1731
|
+
for (const type of OBSERVATION_VALUE_TYPES) {
|
|
1732
|
+
const value = byName.get(`value${type}`);
|
|
1733
|
+
if (value !== void 0) out.push({ type, node: value });
|
|
1734
|
+
}
|
|
1735
|
+
return out;
|
|
1736
|
+
}
|
|
1737
|
+
function readObservationValue(node) {
|
|
1738
|
+
const [first, ...rest] = presentValues(node);
|
|
1739
|
+
if (first === void 0) return void 0;
|
|
1740
|
+
const wrapped = isList(first.node);
|
|
1741
|
+
return {
|
|
1742
|
+
type: first.type,
|
|
1743
|
+
property: `value${first.type}`,
|
|
1744
|
+
node: first.node,
|
|
1745
|
+
// Never from inside a wrapper, and the guard is written here rather than left to
|
|
1746
|
+
// {@link readQuantity} returning `undefined` for a list. Picking the member would author a
|
|
1747
|
+
// magnitude at a position the sender spelled ambiguously, which is the one thing Postel's Law
|
|
1748
|
+
// does not license: tolerate the form, never invent the content.
|
|
1749
|
+
quantity: first.type === "Quantity" && !wrapped ? readQuantity(first.node) : void 0,
|
|
1750
|
+
ambiguous: rest.map((r) => r.type),
|
|
1751
|
+
encodingIssue: wrapped ? VALIDATION_CODES.ARRAY_WRAPPED_CHOICE : void 0
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
var OBSERVATION_VALUE_PROPERTIES = new Set(
|
|
1755
|
+
OBSERVATION_VALUE_TYPES.map((type) => `value${type}`)
|
|
1756
|
+
);
|
|
1757
|
+
function collectChoiceWrappers(element, path, keep, out) {
|
|
1758
|
+
for (const property of [...element.properties, ...element.duplicates ?? []]) {
|
|
1759
|
+
if (!OBSERVATION_VALUE_PROPERTIES.has(property.name)) continue;
|
|
1760
|
+
if (!isList(property.value)) continue;
|
|
1761
|
+
if (!keep(property.value.items)) continue;
|
|
1762
|
+
const at = childPath(path, property.name);
|
|
1763
|
+
if (!out.includes(at)) out.push(at);
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
function walkForObservationRoots(node, path, keep, out) {
|
|
1767
|
+
if (isList(node)) {
|
|
1768
|
+
node.items.forEach(
|
|
1769
|
+
(item, index) => walkForObservationRoots(item, `${path}[${String(index)}]`, keep, out)
|
|
1770
|
+
);
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
if (isPrimitive(node)) return;
|
|
1774
|
+
if (typesOf(node).includes("Observation")) {
|
|
1775
|
+
collectChoiceWrappers(node, path, keep, out);
|
|
1776
|
+
for (const member of getAllProperties(node, "component")) {
|
|
1777
|
+
const items = isList(member) ? member.items : [member];
|
|
1778
|
+
items.forEach((item, index) => {
|
|
1779
|
+
if (!isComplex(item)) return;
|
|
1780
|
+
const at = isList(member) ? `${path}.component[${String(index)}]` : `${path}.component`;
|
|
1781
|
+
collectChoiceWrappers(item, at, keep, out);
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
for (const property of [...node.properties, ...node.duplicates ?? []]) {
|
|
1786
|
+
walkForObservationRoots(property.value, childPath(path, property.name), keep, out);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
var ANY_ARITY = () => true;
|
|
1790
|
+
function arrayWrappedValueChoices(resource, path) {
|
|
1791
|
+
const out = [];
|
|
1792
|
+
walkForObservationRoots(resource, path, ANY_ARITY, out);
|
|
1793
|
+
return out;
|
|
1794
|
+
}
|
|
1795
|
+
function unspellableXmlValueChoices(resource, path) {
|
|
1796
|
+
const out = [];
|
|
1797
|
+
walkForObservationRoots(resource, path, (items) => items.length < 2, out);
|
|
1798
|
+
return out;
|
|
1799
|
+
}
|
|
1800
|
+
function readReferenceRanges(observation) {
|
|
1801
|
+
const node = getProperty(observation, "referenceRange");
|
|
1802
|
+
if (node === void 0) return [];
|
|
1803
|
+
const items = isList(node) ? node.items : [node];
|
|
1804
|
+
const ranges = [];
|
|
1805
|
+
for (const item of items) {
|
|
1806
|
+
if (item.kind !== "complex") continue;
|
|
1807
|
+
ranges.push({
|
|
1808
|
+
low: readQuantity(getProperty(item, "low")),
|
|
1809
|
+
high: readQuantity(getProperty(item, "high")),
|
|
1810
|
+
type: codingsOf(getProperty(item, "type")),
|
|
1811
|
+
text: primitiveString(getProperty(item, "text"))
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
return ranges;
|
|
1815
|
+
}
|
|
1816
|
+
function readInterpretations(observation) {
|
|
1817
|
+
return codingsOf(getProperty(observation, "interpretation"));
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// src/safety/modifier-elements.ts
|
|
1821
|
+
var PATIENT = "Patient";
|
|
1822
|
+
var PRACTITIONER = "Practitioner";
|
|
1823
|
+
var IMMUNIZATION = "Immunization";
|
|
1824
|
+
var MEDICATION_REQUEST = "MedicationRequest";
|
|
1825
|
+
var IDENTIFIER = "identifier";
|
|
1826
|
+
var DECEASED = "deceased";
|
|
1827
|
+
var INTENT = "intent";
|
|
1828
|
+
var INTENT_CODES = /* @__PURE__ */ new Set([
|
|
1829
|
+
"proposal",
|
|
1830
|
+
"plan",
|
|
1831
|
+
"order",
|
|
1832
|
+
"original-order",
|
|
1833
|
+
"reflex-order",
|
|
1834
|
+
"filler-order",
|
|
1835
|
+
"instance-order",
|
|
1836
|
+
"option"
|
|
1837
|
+
]);
|
|
1838
|
+
var USE = "use";
|
|
1839
|
+
var USE_ROOT_TYPES = /* @__PURE__ */ new Set([
|
|
1840
|
+
"AllergyIntolerance",
|
|
1841
|
+
"Condition",
|
|
1842
|
+
"DiagnosticReport",
|
|
1843
|
+
"Immunization",
|
|
1844
|
+
"MedicationRequest",
|
|
1845
|
+
"MedicationStatement",
|
|
1846
|
+
"Observation",
|
|
1847
|
+
PATIENT
|
|
1848
|
+
]);
|
|
1849
|
+
var IDENTIFIER_USE = /* @__PURE__ */ new Set([
|
|
1850
|
+
"usual",
|
|
1851
|
+
"official",
|
|
1852
|
+
"temp",
|
|
1853
|
+
"secondary",
|
|
1854
|
+
"old"
|
|
1855
|
+
]);
|
|
1856
|
+
var NAME_USE = /* @__PURE__ */ new Set([
|
|
1857
|
+
"usual",
|
|
1858
|
+
"official",
|
|
1859
|
+
"temp",
|
|
1860
|
+
"nickname",
|
|
1861
|
+
"anonymous",
|
|
1862
|
+
"old",
|
|
1863
|
+
"maiden"
|
|
1864
|
+
]);
|
|
1865
|
+
var ADDRESS_USE = /* @__PURE__ */ new Set([
|
|
1866
|
+
"home",
|
|
1867
|
+
"work",
|
|
1868
|
+
"temp",
|
|
1869
|
+
"old",
|
|
1870
|
+
"billing"
|
|
1871
|
+
]);
|
|
1872
|
+
var CONTACT_POINT_USE = /* @__PURE__ */ new Set([
|
|
1873
|
+
"home",
|
|
1874
|
+
"work",
|
|
1875
|
+
"temp",
|
|
1876
|
+
"old",
|
|
1877
|
+
"mobile"
|
|
1878
|
+
]);
|
|
1879
|
+
var IDENTIFIER_MEMBERS = /* @__PURE__ */ new Set(["identifier", "groupIdentifier"]);
|
|
1880
|
+
var PATIENT_MEMBERS = /* @__PURE__ */ new Map([
|
|
1881
|
+
["name", NAME_USE],
|
|
1882
|
+
["telecom", CONTACT_POINT_USE],
|
|
1883
|
+
["address", ADDRESS_USE]
|
|
1884
|
+
]);
|
|
1885
|
+
var CONTACT = "contact";
|
|
1886
|
+
var UNGATED_ELEMENTS = ["implicitRules", "comparator"];
|
|
1887
|
+
var MODIFIER_ELEMENT_ROOT_TYPES = /* @__PURE__ */ new Set([
|
|
1888
|
+
...SAFETY_RESOURCE_TYPES,
|
|
1889
|
+
PATIENT,
|
|
1890
|
+
PRACTITIONER,
|
|
1891
|
+
"Bundle"
|
|
1892
|
+
]);
|
|
1893
|
+
function carries(node, name) {
|
|
1894
|
+
return getAllProperties(node, name).length > 0 || getAllProperties(node, `_${name}`).length > 0;
|
|
1895
|
+
}
|
|
1896
|
+
function eachEntry(value, path, visit2) {
|
|
1897
|
+
if (isList(value)) {
|
|
1898
|
+
value.items.forEach((item, index) => {
|
|
1899
|
+
if (isComplex(item)) visit2(item, `${path}[${String(index)}]`);
|
|
1900
|
+
});
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
if (isComplex(value)) visit2(value, path);
|
|
1513
1904
|
}
|
|
1514
|
-
function
|
|
1905
|
+
function collectModifierElements(node, path, out) {
|
|
1906
|
+
for (const element of UNGATED_ELEMENTS) {
|
|
1907
|
+
if (carries(node, element)) out.push({ element, location: childPath(path, element) });
|
|
1908
|
+
}
|
|
1515
1909
|
const types = typesOf(node);
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
if (property.name !== "resourceType" && !(clinical && isSafetyType)) continue;
|
|
1520
|
-
const at = childPath(path, property.name);
|
|
1521
|
-
if (isList(property.value)) report(at, unspellableInXml(property.name, property.value));
|
|
1522
|
-
if (clinical && isSafetyType && SAFETY_CODEABLE_ELEMENTS.has(property.name)) {
|
|
1523
|
-
checkCodingWrapping(property.value, at, report);
|
|
1910
|
+
if (types.includes(PATIENT)) {
|
|
1911
|
+
if (carries(node, "active")) {
|
|
1912
|
+
out.push({ element: "active", location: childPath(path, "active") });
|
|
1524
1913
|
}
|
|
1914
|
+
for (const member of deceasedMembers(node)) {
|
|
1915
|
+
out.push({ element: "deceased", location: childPath(path, member) });
|
|
1916
|
+
}
|
|
1917
|
+
if (carries(node, "link")) out.push({ element: "link", location: childPath(path, "link") });
|
|
1525
1918
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
const
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
if (!isComplex(coding)) return;
|
|
1536
|
-
const at = isList(member) ? `${base}.coding[${String(index)}]` : `${base}.coding`;
|
|
1537
|
-
for (const name of CODING_SCALAR_ELEMENTS) {
|
|
1538
|
-
const members = getAllProperties(coding, name);
|
|
1539
|
-
if (members.some((scalar) => isList(scalar))) {
|
|
1540
|
-
report(
|
|
1541
|
-
`${at}.${name}`,
|
|
1542
|
-
members.some((scalar) => unspellableInXml(name, scalar))
|
|
1543
|
-
);
|
|
1544
|
-
}
|
|
1919
|
+
if (types.includes(IMMUNIZATION) && carries(node, "isSubpotent")) {
|
|
1920
|
+
out.push({ element: "isSubpotent", location: childPath(path, "isSubpotent") });
|
|
1921
|
+
}
|
|
1922
|
+
if (types.includes(PRACTITIONER)) {
|
|
1923
|
+
const at = childPath(path, IDENTIFIER);
|
|
1924
|
+
for (const written of getAllProperties(node, IDENTIFIER)) {
|
|
1925
|
+
eachEntry(written, at, (entry, entryPath) => {
|
|
1926
|
+
if (carries(entry, "use")) {
|
|
1927
|
+
out.push({ element: "use", location: childPath(entryPath, "use") });
|
|
1545
1928
|
}
|
|
1546
1929
|
});
|
|
1547
1930
|
}
|
|
1548
|
-
}
|
|
1931
|
+
}
|
|
1549
1932
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
const
|
|
1553
|
-
|
|
1554
|
-
const
|
|
1555
|
-
if (
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
if (written.some(hasUnreadableBoolean)) out.unreadableBoolean.push(at);
|
|
1933
|
+
function dedupeModifierElements(reports) {
|
|
1934
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1935
|
+
const out = [];
|
|
1936
|
+
for (const report of reports) {
|
|
1937
|
+
const key = `${report.element}\0${report.location}`;
|
|
1938
|
+
if (seen.has(key)) continue;
|
|
1939
|
+
seen.add(key);
|
|
1940
|
+
out.push(report);
|
|
1559
1941
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1942
|
+
return out;
|
|
1943
|
+
}
|
|
1944
|
+
function deceasedMembers(node) {
|
|
1945
|
+
const members = /* @__PURE__ */ new Set();
|
|
1946
|
+
for (const property of [...node.properties, ...node.duplicates ?? []]) {
|
|
1947
|
+
const name = property.name.startsWith("_") ? property.name.slice(1) : property.name;
|
|
1948
|
+
if (name.startsWith(DECEASED)) members.add(name);
|
|
1949
|
+
}
|
|
1950
|
+
return [...members];
|
|
1951
|
+
}
|
|
1952
|
+
function collectIntent(node, path, intents, unreadable) {
|
|
1953
|
+
if (!typesOf(node).includes(MEDICATION_REQUEST)) return;
|
|
1954
|
+
const written = getAllProperties(node, INTENT);
|
|
1955
|
+
if (written.length === 0 && getAllProperties(node, `_${INTENT}`).length === 0) return;
|
|
1956
|
+
const at = childPath(path, INTENT);
|
|
1957
|
+
const only = written.length === 1 ? written[0] : void 0;
|
|
1958
|
+
const value = only !== void 0 && isPrimitive(only) ? only.value : void 0;
|
|
1959
|
+
if (typeof value === "string" && isIntentCode(value)) {
|
|
1960
|
+
intents.push({ code: value, location: at });
|
|
1961
|
+
} else {
|
|
1962
|
+
unreadable.push(at);
|
|
1563
1963
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
);
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1964
|
+
}
|
|
1965
|
+
function isIntentCode(value) {
|
|
1966
|
+
return INTENT_CODES.has(value);
|
|
1967
|
+
}
|
|
1968
|
+
function collectDatatypeUses(node, path, out) {
|
|
1969
|
+
const types = typesOf(node);
|
|
1970
|
+
if (types.includes(PRACTITIONER)) return;
|
|
1971
|
+
if (!types.some((type) => USE_ROOT_TYPES.has(type))) return;
|
|
1972
|
+
readUsesUnder(node, path, types.includes(PATIENT) ? "patient-root" : "other", out);
|
|
1973
|
+
}
|
|
1974
|
+
function readUsesUnder(node, path, place, out) {
|
|
1975
|
+
for (const property of [...node.properties, ...node.duplicates ?? []]) {
|
|
1976
|
+
const at = childPath(path, property.name);
|
|
1977
|
+
const codes = coveredCodes(property.name, place);
|
|
1978
|
+
if (codes !== void 0) {
|
|
1979
|
+
eachEntry(property.value, at, (entry, entryPath) => {
|
|
1980
|
+
readUse(entry, entryPath, codes, out);
|
|
1981
|
+
});
|
|
1581
1982
|
}
|
|
1983
|
+
const next = place === "patient-root" && property.name === CONTACT ? "patient-contact" : "other";
|
|
1984
|
+
descendForUses(property.value, at, next, out);
|
|
1582
1985
|
}
|
|
1583
1986
|
}
|
|
1584
|
-
function
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1987
|
+
function descendForUses(value, path, place, out) {
|
|
1988
|
+
if (isList(value)) {
|
|
1989
|
+
value.items.forEach((item, index) => {
|
|
1990
|
+
descendForUses(item, `${path}[${String(index)}]`, place, out);
|
|
1991
|
+
});
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
if (isPrimitive(value)) {
|
|
1995
|
+
(value.extension ?? []).forEach((extension, index) => {
|
|
1996
|
+
descendForUses(extension, `${path}.extension[${String(index)}]`, "other", out);
|
|
1997
|
+
});
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
if (getAllProperties(value, "resourceType").length > 0) return;
|
|
2001
|
+
readUsesUnder(value, path, place, out);
|
|
2002
|
+
}
|
|
2003
|
+
function coveredCodes(name, place) {
|
|
2004
|
+
if (IDENTIFIER_MEMBERS.has(name)) return IDENTIFIER_USE;
|
|
2005
|
+
if (place === "other") return void 0;
|
|
2006
|
+
return PATIENT_MEMBERS.get(name);
|
|
2007
|
+
}
|
|
2008
|
+
function readUse(entry, at, codes, out) {
|
|
2009
|
+
const written = getAllProperties(entry, USE);
|
|
2010
|
+
if (written.length === 0 && getAllProperties(entry, `_${USE}`).length === 0) return;
|
|
2011
|
+
const only = written.length === 1 ? written[0] : void 0;
|
|
2012
|
+
const value = only !== void 0 && isPrimitive(only) ? only.value : void 0;
|
|
2013
|
+
const code = typeof value === "string" && isUseCode(value, codes) ? value : void 0;
|
|
2014
|
+
out.push({ location: childPath(at, USE), code });
|
|
2015
|
+
}
|
|
2016
|
+
function isUseCode(value, codes) {
|
|
2017
|
+
return codes.has(value);
|
|
2018
|
+
}
|
|
2019
|
+
function resolveDatatypeUses(readings) {
|
|
2020
|
+
const byLocation = /* @__PURE__ */ new Map();
|
|
2021
|
+
for (const reading of readings) {
|
|
2022
|
+
const codes = byLocation.get(reading.location) ?? /* @__PURE__ */ new Set();
|
|
2023
|
+
codes.add(reading.code);
|
|
2024
|
+
byLocation.set(reading.location, codes);
|
|
2025
|
+
}
|
|
2026
|
+
const uses = [];
|
|
2027
|
+
const unreadable = [];
|
|
2028
|
+
for (const [location, codes] of byLocation) {
|
|
2029
|
+
const [only] = [...codes];
|
|
2030
|
+
if (codes.size === 1 && only !== void 0) uses.push({ code: only, location });
|
|
2031
|
+
else unreadable.push(location);
|
|
2032
|
+
}
|
|
2033
|
+
return { uses, unreadable };
|
|
2034
|
+
}
|
|
2035
|
+
function modifierLocationRebaser(prefix, types) {
|
|
2036
|
+
const declared = types[0];
|
|
2037
|
+
if (declared === void 0) return (location) => location;
|
|
2038
|
+
const root = MODIFIER_ELEMENT_ROOT_TYPES.has(declared) ? rootPath(declared) : WITHHELD;
|
|
2039
|
+
if (root === prefix) return (location) => location;
|
|
2040
|
+
return (location) => location.startsWith(prefix) ? `${root}${location.slice(prefix.length)}` : location;
|
|
2041
|
+
}
|
|
2042
|
+
function rebaseModifierElements(reports, prefix, types) {
|
|
2043
|
+
const rebase = modifierLocationRebaser(prefix, types);
|
|
2044
|
+
return reports.map((report) => ({ element: report.element, location: rebase(report.location) }));
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
// src/safety/absence.ts
|
|
2048
|
+
var DATA_ABSENT_REASON_URL = "http://hl7.org/fhir/StructureDefinition/data-absent-reason";
|
|
2049
|
+
var ABSENCE_CODES = [
|
|
2050
|
+
"unknown",
|
|
2051
|
+
"asked-unknown",
|
|
2052
|
+
"temp-unknown",
|
|
2053
|
+
"not-asked",
|
|
2054
|
+
"asked-declined",
|
|
2055
|
+
"masked",
|
|
2056
|
+
"not-applicable",
|
|
2057
|
+
"unsupported",
|
|
2058
|
+
"as-text",
|
|
2059
|
+
"error",
|
|
2060
|
+
"not-a-number",
|
|
2061
|
+
"negative-infinity",
|
|
2062
|
+
"positive-infinity",
|
|
2063
|
+
"not-performed",
|
|
2064
|
+
"not-permitted"
|
|
2065
|
+
];
|
|
2066
|
+
function isAbsenceCode(value) {
|
|
2067
|
+
return ABSENCE_CODES.includes(value);
|
|
2068
|
+
}
|
|
2069
|
+
var STRUCTURAL_MEMBERS = /* @__PURE__ */ new Set([
|
|
2070
|
+
"id",
|
|
2071
|
+
"url",
|
|
2072
|
+
"extension",
|
|
2073
|
+
"modifierExtension",
|
|
2074
|
+
"resourceType"
|
|
2075
|
+
]);
|
|
2076
|
+
function extensionsOf(node) {
|
|
2077
|
+
if (isPrimitive(node)) return node.extension ?? [];
|
|
2078
|
+
if (!isComplex(node)) return [];
|
|
2079
|
+
return getAllProperties(node, "extension").flatMap(
|
|
2080
|
+
(written) => isList(written) ? written.items : [written]
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
function isAbsenceMarker(extension) {
|
|
2084
|
+
if (!isComplex(extension)) return false;
|
|
2085
|
+
return getAllProperties(extension, "url").some(
|
|
2086
|
+
(written) => primitiveString(written) === DATA_ABSENT_REASON_URL
|
|
2087
|
+
);
|
|
2088
|
+
}
|
|
2089
|
+
function reasonOf(marker) {
|
|
2090
|
+
const written = getAllProperties(marker, "valueCode");
|
|
2091
|
+
if (written.length !== 1) return void 0;
|
|
2092
|
+
const spelled = primitiveString(written[0]);
|
|
2093
|
+
if (spelled === void 0 || !isAbsenceCode(spelled)) return void 0;
|
|
2094
|
+
return spelled;
|
|
2095
|
+
}
|
|
2096
|
+
function carriesValue(node) {
|
|
2097
|
+
if (isPrimitive(node)) return node.value !== void 0;
|
|
2098
|
+
if (!isComplex(node)) return false;
|
|
2099
|
+
return [...node.properties, ...node.duplicates ?? []].some(
|
|
2100
|
+
(property) => !STRUCTURAL_MEMBERS.has(property.name)
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
function pushOnce(out, location) {
|
|
2104
|
+
if (!out.includes(location)) out.push(location);
|
|
2105
|
+
}
|
|
2106
|
+
function visit(node, path, out) {
|
|
2107
|
+
const markers = extensionsOf(node).filter(isAbsenceMarker);
|
|
2108
|
+
if (markers.length === 0) return;
|
|
2109
|
+
if (carriesValue(node)) pushOnce(out.conflicting, path);
|
|
2110
|
+
for (const marker of markers) {
|
|
2111
|
+
const code = reasonOf(marker);
|
|
2112
|
+
if (code === void 0) {
|
|
2113
|
+
pushOnce(out.unreadable, path);
|
|
2114
|
+
continue;
|
|
1591
2115
|
}
|
|
1592
|
-
if (
|
|
1593
|
-
|
|
1594
|
-
out.unspellableInXml.push(location);
|
|
2116
|
+
if (!out.markers.some((seen) => seen.code === code && seen.location === path)) {
|
|
2117
|
+
out.markers.push({ code, location: path });
|
|
1595
2118
|
}
|
|
1596
|
-
}
|
|
1597
|
-
checkArrayWrapping(node, path, report);
|
|
1598
|
-
checkNegations(node, path, report, out);
|
|
2119
|
+
}
|
|
1599
2120
|
}
|
|
1600
|
-
function
|
|
1601
|
-
|
|
1602
|
-
|
|
2121
|
+
function walk(node, path, out) {
|
|
2122
|
+
visit(node, path, out);
|
|
2123
|
+
if (isList(node)) {
|
|
2124
|
+
node.items.forEach((item, index) => {
|
|
2125
|
+
walk(item, `${path}[${String(index)}]`, out);
|
|
2126
|
+
});
|
|
2127
|
+
return;
|
|
1603
2128
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
2129
|
+
if (isPrimitive(node)) {
|
|
2130
|
+
(node.extension ?? []).forEach((extension, index) => {
|
|
2131
|
+
walk(extension, `${path}.extension[${String(index)}]`, out);
|
|
2132
|
+
});
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
for (const property of node.properties) walk(property.value, childPath(path, property.name), out);
|
|
1607
2136
|
for (const property of node.duplicates ?? []) {
|
|
1608
|
-
|
|
1609
|
-
reported.add(property.name);
|
|
1610
|
-
out.shadowed.push(childPath(path, property.name));
|
|
1611
|
-
}
|
|
1612
|
-
visitProperty(property, path, out);
|
|
2137
|
+
walk(property.value, childPath(path, property.name), out);
|
|
1613
2138
|
}
|
|
1614
2139
|
}
|
|
1615
|
-
function
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
2140
|
+
function collectAbsence(resource, path) {
|
|
2141
|
+
const out = { markers: [], unreadable: [], conflicting: [] };
|
|
2142
|
+
walk(resource, path, out);
|
|
2143
|
+
return out;
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
// src/safety/status.ts
|
|
2147
|
+
function readDoNotPerform(resource) {
|
|
2148
|
+
const values = getAllProperties(resource, "doNotPerform");
|
|
2149
|
+
if (values.some((node) => primitiveBooleans(node).includes(true))) return true;
|
|
2150
|
+
return primitiveBooleans(values[0])[0];
|
|
2151
|
+
}
|
|
2152
|
+
function clinicalSystemFor(rt) {
|
|
2153
|
+
if (rt === "AllergyIntolerance") return ALLERGY_CLINICAL_SYSTEM;
|
|
2154
|
+
if (rt === "Condition") return CONDITION_CLINICAL_SYSTEM;
|
|
2155
|
+
return void 0;
|
|
1620
2156
|
}
|
|
1621
|
-
function
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
if (url === void 0 || !KNOWN_MODIFIER_EXTENSION_URLS.has(url)) {
|
|
1626
|
-
out.push(
|
|
1627
|
-
isList(value) ? `${path}.modifierExtension[${String(index)}]` : `${path}.modifierExtension`
|
|
1628
|
-
);
|
|
1629
|
-
}
|
|
1630
|
-
});
|
|
2157
|
+
function verificationSystemFor(rt) {
|
|
2158
|
+
if (rt === "AllergyIntolerance") return ALLERGY_VERIFICATION_SYSTEM;
|
|
2159
|
+
if (rt === "Condition") return CONDITION_VERIFICATION_SYSTEM;
|
|
2160
|
+
return void 0;
|
|
1631
2161
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
2162
|
+
var NEGATION_ORDER = [
|
|
2163
|
+
"entered-in-error",
|
|
2164
|
+
"refuted",
|
|
2165
|
+
"no-known-allergy",
|
|
2166
|
+
"do-not-perform",
|
|
2167
|
+
"not-taken",
|
|
2168
|
+
"not-done"
|
|
2169
|
+
];
|
|
2170
|
+
function unhandledModifierExtensions(resource, path) {
|
|
2171
|
+
return walkSafety(resource, path).modifiers;
|
|
1636
2172
|
}
|
|
1637
|
-
function
|
|
2173
|
+
function modifierElements(resource) {
|
|
1638
2174
|
const types = typesOf(resource);
|
|
1639
|
-
const rt = types[0];
|
|
1640
|
-
const isType = (type) => types.includes(type);
|
|
1641
|
-
const status = primitiveStrings(getProperty(resource, "status"))[0];
|
|
1642
|
-
const clinicalStatus = safetyCodeOf(
|
|
1643
|
-
getProperty(resource, "clinicalStatus"),
|
|
1644
|
-
clinicalSystemFor(rt)
|
|
1645
|
-
);
|
|
1646
|
-
const verificationStatus = safetyCodeOf(
|
|
1647
|
-
getProperty(resource, "verificationStatus"),
|
|
1648
|
-
verificationSystemFor(rt)
|
|
1649
|
-
);
|
|
1650
2175
|
const prefix = safetyPrefix(types);
|
|
1651
|
-
|
|
1652
|
-
const doNotPerform = readDoNotPerform(resource);
|
|
1653
|
-
const retracted = isRetracted(resource);
|
|
1654
|
-
const noKnownAllergy = isType("AllergyIntolerance") && getAllProperties(resource, "code").some(
|
|
1655
|
-
(node) => safetyHasCoding(node, SNOMED_SCT, NO_KNOWN_ALLERGY)
|
|
1656
|
-
);
|
|
1657
|
-
const found = new Set(walk2.negations);
|
|
1658
|
-
if (noKnownAllergy) found.add("no-known-allergy");
|
|
1659
|
-
const negations = NEGATION_ORDER.filter((kind) => found.has(kind));
|
|
1660
|
-
const { modifiers, shadowed, arrayWrapped, unreadableBoolean, nearMissCode, unreadableCode } = walk2;
|
|
1661
|
-
const modifierElementReports = rebaseModifierElements(walk2.modifierElements, prefix, types);
|
|
1662
|
-
const nested = nestedArrays(resource, prefix);
|
|
1663
|
-
const dropped = droppedText(resource, prefix);
|
|
1664
|
-
const absence = collectAbsence(resource, prefix);
|
|
1665
|
-
return {
|
|
1666
|
-
resourceType: rt === void 0 ? void 0 : rootPath(rt),
|
|
1667
|
-
status,
|
|
1668
|
-
clinicalStatus,
|
|
1669
|
-
verificationStatus,
|
|
1670
|
-
doNotPerform,
|
|
1671
|
-
retracted,
|
|
1672
|
-
noKnownAllergy,
|
|
1673
|
-
negations,
|
|
1674
|
-
unhandledModifierExtensions: modifiers,
|
|
1675
|
-
modifierElements: modifierElementReports,
|
|
1676
|
-
shadowedProperties: shadowed,
|
|
1677
|
-
arrayWrappedScalars: arrayWrapped,
|
|
1678
|
-
nestedArrays: nested,
|
|
1679
|
-
droppedText: dropped,
|
|
1680
|
-
unreadableBooleans: unreadableBoolean,
|
|
1681
|
-
nearMissNegationCodes: nearMissCode,
|
|
1682
|
-
unreadableNegationCodes: unreadableCode,
|
|
1683
|
-
absenceMarkers: absence.markers,
|
|
1684
|
-
unreadableAbsenceMarkers: absence.unreadable,
|
|
1685
|
-
conflictingAbsenceMarkers: absence.conflicting,
|
|
1686
|
-
safeToSummarize: modifiers.length === 0 && modifierElementReports.length === 0 && shadowed.length === 0 && arrayWrapped.length === 0 && nested.length === 0 && dropped.length === 0 && unreadableBoolean.length === 0 && nearMissCode.length === 0 && unreadableCode.length === 0 && absence.unreadable.length === 0 && absence.conflicting.length === 0
|
|
1687
|
-
};
|
|
1688
|
-
}
|
|
1689
|
-
var FhirSafetyError = class extends Error {
|
|
1690
|
-
/** FHIRPath locations that forced the refusal (value-free). */
|
|
1691
|
-
locations;
|
|
1692
|
-
/**
|
|
1693
|
-
* @param locations - The FHIRPath locations that forced the refusal (value-free).
|
|
1694
|
-
*/
|
|
1695
|
-
constructor(locations) {
|
|
1696
|
-
super(
|
|
1697
|
-
`Resource cannot be safely summarized: an unhandled modifierExtension, a modifier element, a repeated property name, an array-wrapped single-valued element, an array inside an array, dropped XML element text, a boolean value this library cannot read, a code that spells a negation bar its case or its surrounding whitespace, content where a code belongs, a declared absence whose reason this library cannot read, or a declared absence beside a value leaves an element this library must not flatten (${String(locations.length)} location(s)).`
|
|
1698
|
-
);
|
|
1699
|
-
this.name = "FhirSafetyError";
|
|
1700
|
-
this.locations = locations;
|
|
1701
|
-
}
|
|
1702
|
-
};
|
|
1703
|
-
function assertSafeToSummarize(resource) {
|
|
1704
|
-
const readout = "unhandledModifierExtensions" in resource ? resource : readSafety(resource);
|
|
1705
|
-
const locations = [
|
|
1706
|
-
...readout.unhandledModifierExtensions,
|
|
1707
|
-
...readout.modifierElements.map((report) => report.location),
|
|
1708
|
-
...readout.shadowedProperties,
|
|
1709
|
-
...readout.arrayWrappedScalars,
|
|
1710
|
-
...readout.nestedArrays,
|
|
1711
|
-
...readout.droppedText,
|
|
1712
|
-
...readout.unreadableBooleans,
|
|
1713
|
-
...readout.nearMissNegationCodes,
|
|
1714
|
-
...readout.unreadableNegationCodes,
|
|
1715
|
-
// The two absence channels that withdraw the affirmation. `absenceMarkers` is deliberately
|
|
1716
|
-
// absent from this list, exactly as it is absent from `safeToSummarize`'s conjunction: a
|
|
1717
|
-
// declaration the caller can read is a disclosure, not a loss.
|
|
1718
|
-
...readout.unreadableAbsenceMarkers,
|
|
1719
|
-
...readout.conflictingAbsenceMarkers
|
|
1720
|
-
];
|
|
1721
|
-
if (locations.length > 0) throw new FhirSafetyError(locations);
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
// src/codec/serialize-guard.ts
|
|
1725
|
-
var SERIALIZE_ERROR_CODES = {
|
|
1726
|
-
/**
|
|
1727
|
-
* The model carries character data the XML reader dropped, at one or more locations. There is no
|
|
1728
|
-
* conformant encoding of it in either wire format, and emitting the element as unfilled would
|
|
1729
|
-
* launder the `DROPPED_ELEMENT_TEXT` finding across a round trip.
|
|
1730
|
-
*/
|
|
1731
|
-
DROPPED_ELEMENT_TEXT: "DROPPED_ELEMENT_TEXT",
|
|
1732
|
-
/**
|
|
1733
|
-
* The model carries a name that cannot occupy the `Name` slot of an XML start tag, so writing it
|
|
1734
|
-
* would emit markup that does not re-read as the element the model holds. **XML only**: JSON
|
|
1735
|
-
* escapes a member name, so this refusal never reaches it and that route stays open. **Narrowed
|
|
1736
|
-
* 2026-08-07 from "encodes every one of these correctly", which was false and shipped**: a model
|
|
1737
|
-
* refused here can carry one of `serializeResource`'s own declared exceptions and emit that.
|
|
1738
|
-
*
|
|
1739
|
-
* See {@link breaksTag} for the exact predicate and for what is deliberately NOT refused.
|
|
1740
|
-
*/
|
|
1741
|
-
UNSERIALIZABLE_ELEMENT_NAME: "UNSERIALIZABLE_ELEMENT_NAME",
|
|
1742
|
-
/**
|
|
1743
|
-
* A `div` property carries a string the XML writer would emit as raw markup, and that string does
|
|
1744
|
-
* not contribute exactly one element named `div` to the document. **XML only**: `serializeResource`
|
|
1745
|
-
* carries the string as a string, so this refusal never reaches it and that route stays open.
|
|
1746
|
-
*
|
|
1747
|
-
* **That is a statement about the `div` string, not about the whole model.** `serializeResource`
|
|
1748
|
-
* has its own declared non-spec-clean exceptions, so a model refused here can still route through
|
|
1749
|
-
* it and emit one of those: `{"text":{"div":""},"name":[[{"family":"X"}]]}` is refused here and
|
|
1750
|
-
* `serializeResource` emits the array inside an array unchanged.
|
|
1751
|
-
*
|
|
1752
|
-
* See `emitsOneDivElement` in `../xml/write.js` for the exact predicate and what it does not cover.
|
|
1753
|
-
*/
|
|
1754
|
-
UNSERIALIZABLE_DIV_MARKUP: "UNSERIALIZABLE_DIV_MARKUP",
|
|
1755
|
-
/**
|
|
1756
|
-
* The model carries, at one or more locations, a shape the JSON reader marked because FHIR JSON
|
|
1757
|
-
* gives that position no meaning: an array inside an array, a scalar or `null` where FHIR JSON has
|
|
1758
|
-
* an object (a complex element's own position or a primitive's `_`-sibling), or a `null` in a
|
|
1759
|
-
* primitive's value channel that padded nothing. **XML only**: this refusal does not reach
|
|
1760
|
-
* `serializeResource`, which writes these back from the text the reader preserved at every position
|
|
1761
|
-
* that writer walks. **It does not walk a member a repeated property name shadowed, and this
|
|
1762
|
-
* refusal does reach one** -- `serializeResource` refuses that model on
|
|
1763
|
-
* {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_SHADOWED_PROPERTY} rather than emitting it, so it is
|
|
1764
|
-
* still not a route the shape survives. The XML writer keeps reporting THIS code on such a model,
|
|
1765
|
-
* because this one is raised first. See {@link assertXmlSerializable}.
|
|
1766
|
-
*
|
|
1767
|
-
* XML has no array-of-arrays, no `_`-sibling and no `null`, so the XML writer has nowhere to put
|
|
1768
|
-
* any of it and emits the element the reader was left holding: an empty one, or none. That output
|
|
1769
|
-
* re-reads with an empty issue list, so the finding the reader raised is gone after one trip.
|
|
1770
|
-
*
|
|
1771
|
-
* See {@link assertXmlSerializable} for the exact set of markers and for what it does NOT cover.
|
|
1772
|
-
*/
|
|
1773
|
-
UNSERIALIZABLE_JSON_ONLY_SHAPE: "UNSERIALIZABLE_JSON_ONLY_SHAPE",
|
|
1774
|
-
/**
|
|
1775
|
-
* The model carries, at a location this library already reports as an array-wrapped `0..1` safety
|
|
1776
|
-
* element, a wrapper FHIR XML has no repetition to spell back: one holding fewer than two items,
|
|
1777
|
-
* or any wrapper at all on `resourceType`. **XML only**: `serializeResource` writes the list back
|
|
1778
|
-
* and the re-read reports the same location, so that route stays open.
|
|
1779
|
-
*
|
|
1780
|
-
* XML spells a repeat by repeating the element and has no other mark for one, so a wrapper of
|
|
1781
|
-
* fewer than two items emits at most one element and re-reads as an ordinary single-valued
|
|
1782
|
-
* element. The encoding complaint the reader raised is then gone, and with it an `error`-severity
|
|
1783
|
-
* `ARRAY_WRAPPED_SCALAR` and a `safeToSummarize: false`.
|
|
1784
|
-
*
|
|
1785
|
-
* See {@link assertXmlArrayWrapper} for the exact predicate and for what it deliberately leaves.
|
|
1786
|
-
*/
|
|
1787
|
-
UNSERIALIZABLE_ARRAY_WRAPPER: "UNSERIALIZABLE_ARRAY_WRAPPER",
|
|
1788
|
-
/**
|
|
1789
|
-
* The model carries, at one or more object elements, a member a repeated property name shadowed
|
|
1790
|
-
* ({@link ../model/node.js} `duplicates`). **Both writers**, unlike the four refusals above it:
|
|
1791
|
-
* each walks `properties` only, so each wrote one member per name and the second value left the
|
|
1792
|
-
* document with no diagnostic on it.
|
|
1793
|
-
*
|
|
1794
|
-
* See {@link assertNoShadowedProperty} for the window, the two routes weighed, and what it leaves.
|
|
1795
|
-
*/
|
|
1796
|
-
UNSERIALIZABLE_SHADOWED_PROPERTY: "UNSERIALIZABLE_SHADOWED_PROPERTY",
|
|
1797
|
-
/**
|
|
1798
|
-
* The model carries, at one or more elements, a first `resourceType` that is not a string. FHIR
|
|
1799
|
-
* XML has no `resourceType` element at all -- the type IS the tag -- so the XML writer skipped the
|
|
1800
|
-
* property, and at the root, with no string to name the tag, it wrote `Resource` instead: the
|
|
1801
|
-
* member deleted and the element named a type nobody wrote. **XML only**: `serializeResource` emits a
|
|
1802
|
-
* non-string `resourceType` through its ordinary path, so this refusal never reaches it and that
|
|
1803
|
-
* route stays open.
|
|
1804
|
-
*
|
|
1805
|
-
* See {@link assertXmlResourceType} for the window and for what it deliberately leaves.
|
|
1806
|
-
*/
|
|
1807
|
-
UNSERIALIZABLE_RESOURCE_TYPE: "UNSERIALIZABLE_RESOURCE_TYPE"
|
|
1808
|
-
};
|
|
1809
|
-
var FhirSerializeError = class extends Error {
|
|
1810
|
-
/** Which refusal this is. */
|
|
1811
|
-
code;
|
|
1812
|
-
/** The bounded FHIRPath locations the refusal is about, in walk order. Never document content. */
|
|
1813
|
-
locations;
|
|
1814
|
-
/**
|
|
1815
|
-
* @param message - A value-free description of the refusal.
|
|
1816
|
-
* @param code - Which refusal this is.
|
|
1817
|
-
* @param locations - The bounded FHIRPath locations it is about.
|
|
1818
|
-
*/
|
|
1819
|
-
constructor(message, code, locations) {
|
|
1820
|
-
super(message);
|
|
1821
|
-
this.name = "FhirSerializeError";
|
|
1822
|
-
this.code = code;
|
|
1823
|
-
this.locations = locations;
|
|
1824
|
-
}
|
|
1825
|
-
};
|
|
1826
|
-
function assertSerializable(node) {
|
|
1827
|
-
const locations = droppedText(node, rootPath(typeOf(node) ?? "Resource"));
|
|
1828
|
-
if (locations.length === 0) return;
|
|
1829
|
-
throw new FhirSerializeError(
|
|
1830
|
-
`cannot serialize: the reader dropped character data at ${String(locations.length)} location(s), which this model cannot encode`,
|
|
1831
|
-
SERIALIZE_ERROR_CODES.DROPPED_ELEMENT_TEXT,
|
|
1832
|
-
locations
|
|
1833
|
-
);
|
|
1834
|
-
}
|
|
1835
|
-
function carriesJsonOnlyShape(node) {
|
|
1836
|
-
if (node.kind === "list") return false;
|
|
1837
|
-
if (node.nestedArray === true || node.nestedArraySource !== void 0) return true;
|
|
1838
|
-
if (node.kind === "complex") return node.nonObjectSource !== void 0;
|
|
1839
|
-
return node.undefinedNull === true || node.nonObjectMetaSource !== void 0 || node.nestedArrayMetaSource !== void 0;
|
|
2176
|
+
return rebaseModifierElements(walkSafety(resource, prefix).modifierElements, prefix, types);
|
|
1840
2177
|
}
|
|
1841
|
-
function
|
|
1842
|
-
const
|
|
1843
|
-
|
|
1844
|
-
throw new FhirSerializeError(
|
|
1845
|
-
// Says only what this refusal does NOT reach, which is the wording the two refusals beside it
|
|
1846
|
-
// were narrowed to. It deliberately does not promise that `serializeResource` writes the shape
|
|
1847
|
-
// back: it does for every position that writer walks, and a marked node inside a member a
|
|
1848
|
-
// repeated property name shadowed is dropped by it as well.
|
|
1849
|
-
`cannot serialize to XML: ${String(locations.length)} location(s) carry a shape only FHIR JSON can spell, which XML has no channel for; this refusal does not reach serializeResource`,
|
|
1850
|
-
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_JSON_ONLY_SHAPE,
|
|
1851
|
-
locations
|
|
1852
|
-
);
|
|
2178
|
+
function safetyPrefix(types) {
|
|
2179
|
+
const rt = types[0];
|
|
2180
|
+
return rt === void 0 ? "$this" : rootPath(rt);
|
|
1853
2181
|
}
|
|
1854
|
-
function
|
|
1855
|
-
|
|
1856
|
-
if (locations.length === 0) return;
|
|
1857
|
-
throw new FhirSerializeError(
|
|
1858
|
-
// Says what this refusal does not reach, in the wording the three refusals beside it were
|
|
1859
|
-
// narrowed to. `serializeResource` does write these wrappers back -- pinned by the JSON half of
|
|
1860
|
-
// the same test -- with the shadowed-member limit stated in the docblock rather than here.
|
|
1861
|
-
`cannot serialize to XML: ${String(locations.length)} location(s) carry an array wrapper around a 0..1 element that XML has no repeated element to spell back; this refusal does not reach serializeResource`,
|
|
1862
|
-
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_ARRAY_WRAPPER,
|
|
1863
|
-
locations
|
|
1864
|
-
);
|
|
2182
|
+
function shadowedProperties(resource, path) {
|
|
2183
|
+
return walkSafety(resource, path).shadowed;
|
|
1865
2184
|
}
|
|
1866
|
-
function
|
|
1867
|
-
|
|
1868
|
-
if (locations.length === 0) return;
|
|
1869
|
-
throw new FhirSerializeError(
|
|
1870
|
-
// Names both writers, because both drop it: there is no route here that keeps the member, and a
|
|
1871
|
-
// message pointing at one would point an operator at a silent drop. That is the wording the four
|
|
1872
|
-
// refusals above were narrowed to, at the one refusal where the answer is "neither".
|
|
1873
|
-
`cannot serialize: ${String(locations.length)} location(s) carry a member a repeated property name shadowed, which neither serializeResource nor serializeResourceXml can write`,
|
|
1874
|
-
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_SHADOWED_PROPERTY,
|
|
1875
|
-
locations
|
|
1876
|
-
);
|
|
2185
|
+
function arrayWrappedScalars(resource, path) {
|
|
2186
|
+
return walkSafety(resource, path).arrayWrapped;
|
|
1877
2187
|
}
|
|
1878
|
-
function
|
|
1879
|
-
|
|
1880
|
-
const written = node.properties.find((property) => property.name === "resourceType");
|
|
1881
|
-
if (written === void 0) return false;
|
|
1882
|
-
return !(isPrimitive(written.value) && typeof written.value.value === "string");
|
|
2188
|
+
function unspellableXmlWrappers(resource, path) {
|
|
2189
|
+
return walkSafety(resource, path).unspellableInXml;
|
|
1883
2190
|
}
|
|
1884
|
-
function
|
|
1885
|
-
|
|
1886
|
-
node,
|
|
1887
|
-
rootPath(typeOf(node) ?? "Resource"),
|
|
1888
|
-
lacksTaggableResourceType
|
|
1889
|
-
);
|
|
1890
|
-
if (elements.length === 0) return;
|
|
1891
|
-
const locations = elements.map((element) => childPath(element, "resourceType"));
|
|
1892
|
-
throw new FhirSerializeError(
|
|
1893
|
-
// Says what this refusal does not reach, in the wording the refusals beside it were narrowed to.
|
|
1894
|
-
// `serializeResource` emits a non-string `resourceType` through its ordinary path -- pinned by
|
|
1895
|
-
// the JSON half of the same test -- which is a statement about this shape, not about the rest of
|
|
1896
|
-
// the model: that writer's own declared exceptions still apply to everything else it carries.
|
|
1897
|
-
// It does NOT say "while naming the element Resource": that substitution happens at the root,
|
|
1898
|
-
// and this refusal reports depths where the tag comes from the property name instead.
|
|
1899
|
-
`cannot serialize to XML: ${String(locations.length)} location(s) carry a resourceType with no string to name a tag with, which this writer would delete; this refusal does not reach serializeResource`,
|
|
1900
|
-
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_RESOURCE_TYPE,
|
|
1901
|
-
locations
|
|
1902
|
-
);
|
|
2191
|
+
function nestedArrays(resource, path) {
|
|
2192
|
+
return collectMarked(resource, path, isNestedArray);
|
|
1903
2193
|
}
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
function breaksTag(name) {
|
|
1907
|
-
return name === "" || TAG_BREAKING.test(name) || TAG_OPENER_STEALING.has(name.slice(0, 1));
|
|
2194
|
+
function droppedText(resource, path) {
|
|
2195
|
+
return collectMarked(resource, path, isDroppedText);
|
|
1908
2196
|
}
|
|
1909
|
-
function
|
|
1910
|
-
|
|
1911
|
-
`cannot serialize to XML: ${String(locations.length)} location(s) carry a name that cannot be written as an XML tag without changing which elements the document holds; serializeResource escapes a member name, so this refusal never reaches it`,
|
|
1912
|
-
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_ELEMENT_NAME,
|
|
1913
|
-
locations
|
|
1914
|
-
);
|
|
2197
|
+
function unreadableBooleans(resource, path) {
|
|
2198
|
+
return walkSafety(resource, path).unreadableBoolean;
|
|
1915
2199
|
}
|
|
1916
|
-
function
|
|
1917
|
-
|
|
1918
|
-
`cannot serialize to XML: ${String(locations.length)} div location(s) carry markup that would not be written as the one div element the model names; serializeResource carries the string as a string`,
|
|
1919
|
-
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_DIV_MARKUP,
|
|
1920
|
-
locations
|
|
1921
|
-
);
|
|
2200
|
+
function nearMissNegationCodes(resource, path) {
|
|
2201
|
+
return walkSafety(resource, path).nearMissCode;
|
|
1922
2202
|
}
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
function emitScalar(value) {
|
|
1926
|
-
if (value === void 0) return "null";
|
|
1927
|
-
if (typeof value === "string") return JSON.stringify(value);
|
|
1928
|
-
if (typeof value === "boolean") return value ? "true" : "false";
|
|
1929
|
-
return value.raw;
|
|
2203
|
+
function unreadableNegationCodes(resource, path) {
|
|
2204
|
+
return walkSafety(resource, path).unreadableCode;
|
|
1930
2205
|
}
|
|
1931
|
-
function
|
|
1932
|
-
return
|
|
2206
|
+
function absenceMarkers(resource, path) {
|
|
2207
|
+
return [...collectAbsence(resource, path).markers];
|
|
1933
2208
|
}
|
|
1934
|
-
function
|
|
1935
|
-
return
|
|
2209
|
+
function unreadableAbsenceMarkers(resource, path) {
|
|
2210
|
+
return [...collectAbsence(resource, path).unreadable];
|
|
1936
2211
|
}
|
|
1937
|
-
function
|
|
1938
|
-
return
|
|
2212
|
+
function conflictingAbsenceMarkers(resource, path) {
|
|
2213
|
+
return [...collectAbsence(resource, path).conflicting];
|
|
1939
2214
|
}
|
|
1940
|
-
function
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
if (node.id !== void 0) parts.push(`"id":${JSON.stringify(node.id)}`);
|
|
1945
|
-
if (node.extension !== void 0 && node.extension.length > 0) {
|
|
1946
|
-
parts.push(`"extension":[${node.extension.map(emitComplex).join(",")}]`);
|
|
1947
|
-
}
|
|
1948
|
-
return `{${parts.join(",")}}`;
|
|
2215
|
+
function collectMarked(resource, path, marked) {
|
|
2216
|
+
const out = [];
|
|
2217
|
+
collectNested(resource, path, out, marked);
|
|
2218
|
+
return [...new Set(out)];
|
|
1949
2219
|
}
|
|
1950
|
-
function
|
|
1951
|
-
|
|
1952
|
-
if (
|
|
1953
|
-
|
|
2220
|
+
function collectNested(node, path, out, marked) {
|
|
2221
|
+
if (marked(node)) out.push(path);
|
|
2222
|
+
if (isList(node)) {
|
|
2223
|
+
node.items.forEach(
|
|
2224
|
+
(item, index) => collectNested(item, `${path}[${String(index)}]`, out, marked)
|
|
2225
|
+
);
|
|
2226
|
+
return;
|
|
1954
2227
|
}
|
|
1955
|
-
if (
|
|
1956
|
-
|
|
2228
|
+
if (isPrimitive(node)) {
|
|
2229
|
+
(node.extension ?? []).forEach(
|
|
2230
|
+
(ext, index) => collectNested(ext, `${path}.extension[${String(index)}]`, out, marked)
|
|
2231
|
+
);
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
for (const property of node.properties)
|
|
2235
|
+
collectNested(property.value, childPath(path, property.name), out, marked);
|
|
2236
|
+
for (const property of node.duplicates ?? []) {
|
|
2237
|
+
collectNested(property.value, childPath(path, property.name), out, marked);
|
|
1957
2238
|
}
|
|
1958
|
-
return entries;
|
|
1959
2239
|
}
|
|
1960
|
-
function
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
2240
|
+
function walkSafety(resource, path) {
|
|
2241
|
+
const out = {
|
|
2242
|
+
modifiers: [],
|
|
2243
|
+
modifierElements: [],
|
|
2244
|
+
intents: [],
|
|
2245
|
+
unreadableIntent: [],
|
|
2246
|
+
useReadings: [],
|
|
2247
|
+
datatypeUses: [],
|
|
2248
|
+
unreadableDatatypeUse: [],
|
|
2249
|
+
shadowed: [],
|
|
2250
|
+
arrayWrapped: [],
|
|
2251
|
+
unspellableInXml: [],
|
|
2252
|
+
negations: /* @__PURE__ */ new Set(),
|
|
2253
|
+
unreadableBoolean: [],
|
|
2254
|
+
nearMissCode: [],
|
|
2255
|
+
unreadableCode: []
|
|
2256
|
+
};
|
|
2257
|
+
walkComplex(resource, path, out, true);
|
|
2258
|
+
const uses = resolveDatatypeUses(out.useReadings);
|
|
2259
|
+
const intentKeys = /* @__PURE__ */ new Set();
|
|
2260
|
+
return {
|
|
2261
|
+
...out,
|
|
2262
|
+
modifierElements: dedupeModifierElements(out.modifierElements),
|
|
2263
|
+
intents: out.intents.filter((read) => {
|
|
2264
|
+
const key = `${read.code} ${read.location}`;
|
|
2265
|
+
if (intentKeys.has(key)) return false;
|
|
2266
|
+
intentKeys.add(key);
|
|
2267
|
+
return true;
|
|
2268
|
+
}),
|
|
2269
|
+
unreadableIntent: [...new Set(out.unreadableIntent)],
|
|
2270
|
+
datatypeUses: uses.uses,
|
|
2271
|
+
unreadableDatatypeUse: uses.unreadable
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
function unspellableInXml(name, value) {
|
|
2275
|
+
if (!isList(value)) return false;
|
|
2276
|
+
return name === "resourceType" || value.items.length < 2;
|
|
2277
|
+
}
|
|
2278
|
+
function checkArrayWrapping(node, path, report) {
|
|
2279
|
+
const types = typesOf(node);
|
|
2280
|
+
const isSafetyType = types.some((type) => SAFETY_RESOURCE_TYPES.has(type));
|
|
2281
|
+
for (const property of [...node.properties, ...node.duplicates ?? []]) {
|
|
2282
|
+
const clinical = SAFETY_SCALAR_ELEMENTS.has(property.name);
|
|
2283
|
+
if (property.name !== "resourceType" && !(clinical && isSafetyType)) continue;
|
|
2284
|
+
const at = childPath(path, property.name);
|
|
2285
|
+
if (isList(property.value)) report(at, unspellableInXml(property.name, property.value));
|
|
2286
|
+
if (clinical && isSafetyType && SAFETY_CODEABLE_ELEMENTS.has(property.name)) {
|
|
2287
|
+
checkCodingWrapping(property.value, at, report);
|
|
2288
|
+
}
|
|
1965
2289
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
const
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
const
|
|
1972
|
-
|
|
2290
|
+
}
|
|
2291
|
+
function checkCodingWrapping(value, path, report) {
|
|
2292
|
+
const concepts = isList(value) ? value.items : [value];
|
|
2293
|
+
concepts.forEach((concept, conceptIndex) => {
|
|
2294
|
+
if (!isComplex(concept)) return;
|
|
2295
|
+
const base = isList(value) ? `${path}[${String(conceptIndex)}]` : path;
|
|
2296
|
+
for (const member of getAllProperties(concept, "coding")) {
|
|
2297
|
+
const codings = isList(member) ? member.items : [member];
|
|
2298
|
+
codings.forEach((coding, index) => {
|
|
2299
|
+
if (!isComplex(coding)) return;
|
|
2300
|
+
const at = isList(member) ? `${base}.coding[${String(index)}]` : `${base}.coding`;
|
|
2301
|
+
for (const name of CODING_SCALAR_ELEMENTS) {
|
|
2302
|
+
const members = getAllProperties(coding, name);
|
|
2303
|
+
if (members.some((scalar) => isList(scalar))) {
|
|
2304
|
+
report(
|
|
2305
|
+
`${at}.${name}`,
|
|
2306
|
+
members.some((scalar) => unspellableInXml(name, scalar))
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
}
|
|
2314
|
+
var CODING_SCALAR_ELEMENTS = ["system", "code"];
|
|
2315
|
+
function checkNegations(node, path, report, out) {
|
|
2316
|
+
const written = getAllProperties(node, "doNotPerform");
|
|
2317
|
+
if (written.length > 0) {
|
|
2318
|
+
const at = childPath(path, "doNotPerform");
|
|
2319
|
+
if (written.some((value) => primitiveBooleans(value).includes(true))) {
|
|
2320
|
+
out.negations.add("do-not-perform");
|
|
2321
|
+
}
|
|
2322
|
+
if (written.some(hasUnreadableBoolean)) out.unreadableBoolean.push(at);
|
|
2323
|
+
}
|
|
2324
|
+
if (isRetracted(node)) out.negations.add(ENTERED_IN_ERROR);
|
|
2325
|
+
if (getAllProperties(node, "verificationStatus").some((v) => safetyHasCodeAnySystem(v, REFUTED))) {
|
|
2326
|
+
out.negations.add(REFUTED);
|
|
2327
|
+
}
|
|
2328
|
+
if (statusSpells(node, NOT_TAKEN)) out.negations.add(NOT_TAKEN);
|
|
2329
|
+
if (statusSpells(node, NOT_DONE)) out.negations.add(NOT_DONE);
|
|
2330
|
+
for (const read of NEGATION_CODE_READS) {
|
|
2331
|
+
const written2 = getAllProperties(node, read.element);
|
|
2332
|
+
if (written2.length === 0) continue;
|
|
2333
|
+
if (read.codings === true) {
|
|
2334
|
+
const at = childPath(path, read.element);
|
|
2335
|
+
for (const value of written2) checkCodingWrapping(value, at, report);
|
|
2336
|
+
}
|
|
2337
|
+
const spelled = written2.flatMap((value) => [...read.values(value)]);
|
|
2338
|
+
const near = read.codes.some(
|
|
2339
|
+
(code) => !spelled.includes(code) && spelled.some((value) => isNearMissCode(value, code))
|
|
2340
|
+
);
|
|
2341
|
+
if (near) out.nearMissCode.push(childPath(path, read.element));
|
|
2342
|
+
const unread = read.unread;
|
|
2343
|
+
if (unread !== void 0 && written2.some((value) => unread(value))) {
|
|
2344
|
+
out.unreadableCode.push(childPath(path, read.element));
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
function checkResourceRoot(node, path, out) {
|
|
2349
|
+
const reported = /* @__PURE__ */ new Set();
|
|
2350
|
+
const unspellable = /* @__PURE__ */ new Set();
|
|
2351
|
+
const report = (location, unwritable) => {
|
|
2352
|
+
if (!reported.has(location)) {
|
|
2353
|
+
reported.add(location);
|
|
2354
|
+
out.arrayWrapped.push(location);
|
|
2355
|
+
}
|
|
2356
|
+
if (unwritable && !unspellable.has(location)) {
|
|
2357
|
+
unspellable.add(location);
|
|
2358
|
+
out.unspellableInXml.push(location);
|
|
2359
|
+
}
|
|
2360
|
+
};
|
|
2361
|
+
checkArrayWrapping(node, path, report);
|
|
2362
|
+
checkNegations(node, path, report, out);
|
|
2363
|
+
}
|
|
2364
|
+
function walkComplex(node, path, out, isRoot = false) {
|
|
2365
|
+
if (isRoot || getAllProperties(node, "resourceType").length > 0) {
|
|
2366
|
+
checkResourceRoot(node, path, out);
|
|
1973
2367
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2368
|
+
collectModifierElements(node, path, out.modifierElements);
|
|
2369
|
+
collectIntent(node, path, out.intents, out.unreadableIntent);
|
|
2370
|
+
collectDatatypeUses(node, path, out.useReadings);
|
|
2371
|
+
for (const property of node.properties) visitProperty(property, path, out);
|
|
2372
|
+
const reported = /* @__PURE__ */ new Set();
|
|
2373
|
+
for (const property of node.duplicates ?? []) {
|
|
2374
|
+
if (!reported.has(property.name)) {
|
|
2375
|
+
reported.add(property.name);
|
|
2376
|
+
out.shadowed.push(childPath(path, property.name));
|
|
2377
|
+
}
|
|
2378
|
+
visitProperty(property, path, out);
|
|
1977
2379
|
}
|
|
1978
|
-
return entries;
|
|
1979
2380
|
}
|
|
1980
|
-
function
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
return emitComplex(node);
|
|
1984
|
-
case "list":
|
|
1985
|
-
return `[${node.items.map(emitNode).join(",")}]`;
|
|
1986
|
-
case "primitive":
|
|
1987
|
-
return emitPrimitiveValue(node);
|
|
2381
|
+
function visitProperty(property, path, out) {
|
|
2382
|
+
if (property.name === "modifierExtension") {
|
|
2383
|
+
checkModifierExtension(property.value, path, out.modifiers);
|
|
1988
2384
|
}
|
|
2385
|
+
descend(property.value, childPath(path, property.name), out);
|
|
1989
2386
|
}
|
|
1990
|
-
function
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
if (property.name === "resourceType" && hoisted) {
|
|
1999
|
-
if (isPrimitive(property.value) && hasMeta(property.value)) {
|
|
2000
|
-
parts.push(`"_resourceType":${emitMeta(property.value)}`);
|
|
2001
|
-
}
|
|
2002
|
-
continue;
|
|
2003
|
-
}
|
|
2004
|
-
const value = property.value;
|
|
2005
|
-
switch (value.kind) {
|
|
2006
|
-
case "primitive":
|
|
2007
|
-
parts.push(...emitPrimitiveProperty(property.name, value));
|
|
2008
|
-
break;
|
|
2009
|
-
case "list":
|
|
2010
|
-
parts.push(...emitListProperty(property.name, value));
|
|
2011
|
-
break;
|
|
2012
|
-
case "complex":
|
|
2013
|
-
parts.push(`${JSON.stringify(property.name)}:${emitComplex(value)}`);
|
|
2014
|
-
break;
|
|
2387
|
+
function checkModifierExtension(value, path, out) {
|
|
2388
|
+
const items = isList(value) ? value.items : [value];
|
|
2389
|
+
items.forEach((ext, index) => {
|
|
2390
|
+
const url = isComplex(ext) ? primitiveString(getProperty(ext, "url")) : void 0;
|
|
2391
|
+
if (url === void 0 || !KNOWN_MODIFIER_EXTENSION_URLS.has(url)) {
|
|
2392
|
+
out.push(
|
|
2393
|
+
isList(value) ? `${path}.modifierExtension[${String(index)}]` : `${path}.modifierExtension`
|
|
2394
|
+
);
|
|
2015
2395
|
}
|
|
2016
|
-
}
|
|
2017
|
-
return `{${parts.join(",")}}`;
|
|
2396
|
+
});
|
|
2018
2397
|
}
|
|
2019
|
-
function
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2398
|
+
function descend(node, path, out) {
|
|
2399
|
+
if (isComplex(node)) walkComplex(node, path, out);
|
|
2400
|
+
else if (isList(node))
|
|
2401
|
+
node.items.forEach((item, index) => descend(item, `${path}[${String(index)}]`, out));
|
|
2023
2402
|
}
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
*/
|
|
2095
|
-
ARRAY_WRAPPED_SCALAR: "ARRAY_WRAPPED_SCALAR",
|
|
2096
|
-
/**
|
|
2097
|
-
* Safety, the document wrote a JSON array **inside another array**. FHIR JSON uses an array for a
|
|
2098
|
-
* repeating element and for nothing else (json.html §2.6.2.2), so a list of lists has no meaning at
|
|
2099
|
-
* any position and this is a non-conformant encoding wherever it appears, which is why it needs no
|
|
2100
|
-
* cardinality rule and cannot fire on a conformant document. Reported at every position the model
|
|
2101
|
-
* has a node for; a `_`-sibling the reader discards whole is the stated exception, and draws a
|
|
2102
|
-
* reader warning instead: `UNKNOWN_PROPERTY` for an unrecognised member of a `_`-sibling object,
|
|
2103
|
-
* `MISPLACED_PRIMITIVE_EXTENSION` for a sibling on an object or on a non-primitive array, not one
|
|
2104
|
-
* code for all three. An `error`, and one of the two on this list
|
|
2105
|
-
* where the reader could **not model** what the sender wrote (the other is
|
|
2106
|
-
* {@link VALIDATION_CODES.DROPPED_ELEMENT_TEXT}): the codec does not model an inner array, so this
|
|
2107
|
-
* reports a loss of *structure* rather than an ambiguity, though the array's JSON text is kept and
|
|
2108
|
-
* readable (see {@link ../model/node.js} `nestedArrayContent`). Left unreported it is among the worst of the
|
|
2109
|
-
* set, because the model then looks exactly like an element that was legitimately absent, and a
|
|
2110
|
-
* refuted allergy, a resolved condition, or an entire resource inside a Bundle entry reads back as
|
|
2111
|
-
* a clean document. Value-free, the position the inner array occupied, never its contents.
|
|
2112
|
-
*/
|
|
2113
|
-
NESTED_ARRAY: "NESTED_ARRAY",
|
|
2114
|
-
/**
|
|
2115
|
-
* Safety, an XML document wrote **character data directly on a FHIR element**. FHIR XML carries a
|
|
2116
|
-
* primitive's value in the `value` attribute (xml.html §2.6.1), so text written as element content
|
|
2117
|
-
* has no slot on the model and the reader drops it: `<status>entered-in-error</status>` yields a
|
|
2118
|
-
* `status` with no value. An `error`, and the only code on this list where the content is neither
|
|
2119
|
-
* modeled **nor kept**: unlike {@link VALIDATION_CODES.NESTED_ARRAY}, which preserves the array's
|
|
2120
|
-
* JSON text, the character data is discarded outright, because reading it back would be a
|
|
2121
|
-
* tolerance for a non-conformant encoding rather than a report of one. Left unreported it reaches the same harm as
|
|
2122
|
-
* {@link VALIDATION_CODES.NESTED_ARRAY} by the other wire format, because the model is again
|
|
2123
|
-
* indistinguishable from an element that was legitimately absent: a retraction, a `refuted`
|
|
2124
|
-
* verification status, or a dose *number* beside a surviving unit and UCUM code all read back as a
|
|
2125
|
-
* clean document. Value-free, the position the text occupied, never its contents.
|
|
2126
|
-
*/
|
|
2127
|
-
DROPPED_ELEMENT_TEXT: "DROPPED_ELEMENT_TEXT",
|
|
2128
|
-
/**
|
|
2129
|
-
* Safety, an element carries a DataAbsentReason extension **and a value of its own**, so the
|
|
2130
|
-
* document asserts both that the element holds that value and that it holds none. An `error`
|
|
2131
|
-
* (`structure`): the two cannot both be true, nothing here ranks them, and a consumer that
|
|
2132
|
-
* happened to read one of the two would report the record as though the other had not been
|
|
2133
|
-
* written. Both survive on the model and on the safety readout; this reports that they disagree.
|
|
2134
|
-
* Value-free, the location of the element, never the value and never the reason.
|
|
2135
|
-
*
|
|
2136
|
-
* Cannot fire on a conformant document: an element the sender has data for is written with the
|
|
2137
|
-
* data and no marker. It is **not** the `Observation.dataAbsentReason` ELEMENT beside a `value[x]`,
|
|
2138
|
-
* which is the `obs-6` invariant and reports as {@link VALIDATION_CODES.INVARIANT_VIOLATED}; the
|
|
2139
|
-
* element is not the extension, and the two never report about one another's shape.
|
|
2140
|
-
*/
|
|
2141
|
-
ABSENCE_MARKER_CONFLICT: "ABSENCE_MARKER_CONFLICT",
|
|
2142
|
-
/**
|
|
2143
|
-
* Safety, an element carries a DataAbsentReason extension whose reason this library could not
|
|
2144
|
-
* read: no `valueCode`, one holding no readable string, an empty one, one written twice, or a code
|
|
2145
|
-
* outside the closed fifteen-concept value set the extension's `value[x]` binds to at **required**
|
|
2146
|
-
* strength. An `error` (`code-invalid`), on the same footing as any other required-binding miss:
|
|
2147
|
-
* the set is closed and published, so membership is decided from the set itself with no
|
|
2148
|
-
* terminology service involved and no value-set expansion.
|
|
2149
|
-
*
|
|
2150
|
-
* **Nothing is coerced.** The code is not trimmed, case-folded or substituted, and the element is
|
|
2151
|
-
* not read as `unknown` and not read as populated: doing either would author a reason the sender
|
|
2152
|
-
* did not spell, or erase a declaration the sender did make. The element stays value-absent and
|
|
2153
|
-
* this is the record that a declaration was made and could not be honoured. Value-free, the
|
|
2154
|
-
* location of the element, never the code that failed to match.
|
|
2155
|
-
*/
|
|
2156
|
-
ABSENCE_MARKER_UNREADABLE: "ABSENCE_MARKER_UNREADABLE",
|
|
2157
|
-
/**
|
|
2158
|
-
* Safety, the resource is marked `entered-in-error` and is therefore **retracted, not
|
|
2159
|
-
* data**. Surfaced as `information` (it is not itself a defect) so a consumer cannot miss it.
|
|
2160
|
-
*/
|
|
2161
|
-
RETRACTED_RESOURCE: "RETRACTED_RESOURCE",
|
|
2162
|
-
/**
|
|
2163
|
-
* Safety, a named resource invariant failed (`ait-1`/`ait-2`, `con-3`/`con-4`/`con-5`,
|
|
2164
|
-
* `obs-6`/`obs-7`). The specific constraint key travels in {@link ValidationIssue.constraint}, and
|
|
2165
|
-
* the severity mirrors the constraint's own (`error`, except the best-practice `con-3` → `warning`).
|
|
2166
|
-
*/
|
|
2167
|
-
INVARIANT_VIOLATED: "INVARIANT_VIOLATED",
|
|
2168
|
-
/**
|
|
2169
|
-
* Invariant, a profile `constraint`'s FHIRPath `expression` is **outside the bounded
|
|
2170
|
-
* engine's subset** and could not be evaluated. Always `information` (`informational`):
|
|
2171
|
-
* the constraint is reported **unchecked, never assumed to pass** (fail-safe), the
|
|
2172
|
-
* library does not claim conformance to an invariant it could not test. The constraint `key` travels
|
|
2173
|
-
* in {@link ValidationIssue.constraint}. Value-free (the location + key, never an instance value).
|
|
2174
|
-
*/
|
|
2175
|
-
INVARIANT_UNCHECKED: "INVARIANT_UNCHECKED",
|
|
2176
|
-
/**
|
|
2177
|
-
* Quantity/UCUM, a `Quantity` claims the UCUM `system` but its `code` is absent or not a
|
|
2178
|
-
* shape-valid UCUM expression, so the unit cannot be trusted for machine use. A `warning`
|
|
2179
|
-
* (`value`): the value is **preserved verbatim and never converted**, the library does not bundle
|
|
2180
|
-
* UCUM content, so it cannot assert the code *is* a real unit, only that it is present and well-shaped.
|
|
2181
|
-
*/
|
|
2182
|
-
UCUM_UNIT_UNRECOGNIZED: "UCUM_UNIT_UNRECOGNIZED",
|
|
2183
|
-
/**
|
|
2184
|
-
* Quantity/UCUM, a vital-signs Observation's measured value carries a unit the FHIR
|
|
2185
|
-
* vital-signs profile forbids for that LOINC code (wrong UCUM `code`, or a non-UCUM `system`). An
|
|
2186
|
-
* `error` (`code-invalid`): the vital-signs profile *requires* the unit, so a nonconformant one is a
|
|
2187
|
-
* profile violation, compared on the UCUM `code` (case- and bracket-sensitive), never the `unit` string.
|
|
2188
|
-
*/
|
|
2189
|
-
VITAL_SIGN_UNIT_NONCONFORMANT: "VITAL_SIGN_UNIT_NONCONFORMANT",
|
|
2190
|
-
/**
|
|
2191
|
-
* Quantity/UCUM, an Observation whose profile expects a numeric `Quantity` value carries
|
|
2192
|
-
* a different `value[x]` variant instead (e.g. `valueString`). A `warning` (`value`): the value is
|
|
2193
|
-
* preserved and surfaced by its real type, a caller must not read it as a number.
|
|
2194
|
-
*/
|
|
2195
|
-
VALUE_TYPE_UNEXPECTED: "VALUE_TYPE_UNEXPECTED",
|
|
2403
|
+
function readSafety(resource) {
|
|
2404
|
+
const types = typesOf(resource);
|
|
2405
|
+
const rt = types[0];
|
|
2406
|
+
const isType = (type) => types.includes(type);
|
|
2407
|
+
const status = primitiveStrings(getProperty(resource, "status"))[0];
|
|
2408
|
+
const clinicalStatus = safetyCodeOf(
|
|
2409
|
+
getProperty(resource, "clinicalStatus"),
|
|
2410
|
+
clinicalSystemFor(rt)
|
|
2411
|
+
);
|
|
2412
|
+
const verificationStatus = safetyCodeOf(
|
|
2413
|
+
getProperty(resource, "verificationStatus"),
|
|
2414
|
+
verificationSystemFor(rt)
|
|
2415
|
+
);
|
|
2416
|
+
const prefix = safetyPrefix(types);
|
|
2417
|
+
const walk2 = walkSafety(resource, prefix);
|
|
2418
|
+
const doNotPerform = readDoNotPerform(resource);
|
|
2419
|
+
const retracted = isRetracted(resource);
|
|
2420
|
+
const noKnownAllergy = isType("AllergyIntolerance") && getAllProperties(resource, "code").some(
|
|
2421
|
+
(node) => safetyHasCoding(node, SNOMED_SCT, NO_KNOWN_ALLERGY)
|
|
2422
|
+
);
|
|
2423
|
+
const found = new Set(walk2.negations);
|
|
2424
|
+
if (noKnownAllergy) found.add("no-known-allergy");
|
|
2425
|
+
const negations = NEGATION_ORDER.filter((kind) => found.has(kind));
|
|
2426
|
+
const { modifiers, shadowed, arrayWrapped, unreadableBoolean, nearMissCode, unreadableCode } = walk2;
|
|
2427
|
+
const modifierElementReports = rebaseModifierElements(walk2.modifierElements, prefix, types);
|
|
2428
|
+
const rebase = modifierLocationRebaser(prefix, types);
|
|
2429
|
+
const intents = walk2.intents.map((read) => ({
|
|
2430
|
+
code: read.code,
|
|
2431
|
+
location: rebase(read.location)
|
|
2432
|
+
}));
|
|
2433
|
+
const unreadableIntents = walk2.unreadableIntent.map(rebase);
|
|
2434
|
+
const datatypeUses = walk2.datatypeUses.map((read) => ({
|
|
2435
|
+
code: read.code,
|
|
2436
|
+
location: rebase(read.location)
|
|
2437
|
+
}));
|
|
2438
|
+
const unreadableDatatypeUses = walk2.unreadableDatatypeUse.map(rebase);
|
|
2439
|
+
const nested = nestedArrays(resource, prefix);
|
|
2440
|
+
const dropped = droppedText(resource, prefix);
|
|
2441
|
+
const absence = collectAbsence(resource, prefix);
|
|
2442
|
+
return {
|
|
2443
|
+
resourceType: rt === void 0 ? void 0 : rootPath(rt),
|
|
2444
|
+
status,
|
|
2445
|
+
clinicalStatus,
|
|
2446
|
+
verificationStatus,
|
|
2447
|
+
doNotPerform,
|
|
2448
|
+
retracted,
|
|
2449
|
+
noKnownAllergy,
|
|
2450
|
+
negations,
|
|
2451
|
+
unhandledModifierExtensions: modifiers,
|
|
2452
|
+
modifierElements: modifierElementReports,
|
|
2453
|
+
intents,
|
|
2454
|
+
datatypeUses,
|
|
2455
|
+
shadowedProperties: shadowed,
|
|
2456
|
+
arrayWrappedScalars: arrayWrapped,
|
|
2457
|
+
nestedArrays: nested,
|
|
2458
|
+
droppedText: dropped,
|
|
2459
|
+
unreadableBooleans: unreadableBoolean,
|
|
2460
|
+
nearMissNegationCodes: nearMissCode,
|
|
2461
|
+
unreadableNegationCodes: unreadableCode,
|
|
2462
|
+
unreadableIntents,
|
|
2463
|
+
unreadableDatatypeUses,
|
|
2464
|
+
absenceMarkers: absence.markers,
|
|
2465
|
+
unreadableAbsenceMarkers: absence.unreadable,
|
|
2466
|
+
conflictingAbsenceMarkers: absence.conflicting,
|
|
2467
|
+
safeToSummarize: modifiers.length === 0 && modifierElementReports.length === 0 && shadowed.length === 0 && arrayWrapped.length === 0 && nested.length === 0 && dropped.length === 0 && unreadableBoolean.length === 0 && nearMissCode.length === 0 && unreadableCode.length === 0 && unreadableIntents.length === 0 && unreadableDatatypeUses.length === 0 && absence.unreadable.length === 0 && absence.conflicting.length === 0
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2470
|
+
var FhirSafetyError = class extends Error {
|
|
2471
|
+
/** FHIRPath locations that forced the refusal (value-free). */
|
|
2472
|
+
locations;
|
|
2196
2473
|
/**
|
|
2197
|
-
*
|
|
2198
|
-
* (and not one the binding's value set draws from). Always `information` (`code-invalid`): an
|
|
2199
|
-
* unknown system may be a legitimate local/proprietary one, so it is never a defect, it only means
|
|
2200
|
-
* the library cannot validate codes drawn from it. Content-free, so it can never flip validity.
|
|
2474
|
+
* @param locations - The FHIRPath locations that forced the refusal (value-free).
|
|
2201
2475
|
*/
|
|
2202
|
-
|
|
2476
|
+
constructor(locations) {
|
|
2477
|
+
super(
|
|
2478
|
+
`Resource cannot be safely summarized: an unhandled modifierExtension, a modifier element, a repeated property name, an array-wrapped single-valued element, an array inside an array, dropped XML element text, a boolean value this library cannot read, a code that spells a negation bar its case or its surrounding whitespace, content where a code belongs, a use on an identifier, name, address or contact point this library cannot read, a declared absence whose reason this library cannot read, or a declared absence beside a value leaves an element this library must not flatten (${String(locations.length)} location(s)).`
|
|
2479
|
+
);
|
|
2480
|
+
this.name = "FhirSafetyError";
|
|
2481
|
+
this.locations = locations;
|
|
2482
|
+
}
|
|
2483
|
+
};
|
|
2484
|
+
function assertSafeToSummarize(resource) {
|
|
2485
|
+
const readout = "unhandledModifierExtensions" in resource ? resource : readSafety(resource);
|
|
2486
|
+
const locations = [
|
|
2487
|
+
...readout.unhandledModifierExtensions,
|
|
2488
|
+
...readout.modifierElements.map((report) => report.location),
|
|
2489
|
+
...readout.shadowedProperties,
|
|
2490
|
+
...readout.arrayWrappedScalars,
|
|
2491
|
+
...readout.nestedArrays,
|
|
2492
|
+
...readout.droppedText,
|
|
2493
|
+
...readout.unreadableBooleans,
|
|
2494
|
+
...readout.nearMissNegationCodes,
|
|
2495
|
+
...readout.unreadableNegationCodes,
|
|
2496
|
+
// `intents` is deliberately absent, exactly as it is absent from `safeToSummarize`'s
|
|
2497
|
+
// conjunction: a readable intent is surfaced for the caller, and only an unreadable one refuses.
|
|
2498
|
+
...readout.unreadableIntents,
|
|
2499
|
+
// `datatypeUses` is absent for the same reason `intents` is: a readable `use` is surfaced for
|
|
2500
|
+
// the caller, and only one this library cannot read refuses.
|
|
2501
|
+
...readout.unreadableDatatypeUses,
|
|
2502
|
+
// The two absence channels that withdraw the affirmation. `absenceMarkers` is deliberately
|
|
2503
|
+
// absent from this list, exactly as it is absent from `safeToSummarize`'s conjunction: a
|
|
2504
|
+
// declaration the caller can read is a disclosure, not a loss.
|
|
2505
|
+
...readout.unreadableAbsenceMarkers,
|
|
2506
|
+
...readout.conflictingAbsenceMarkers
|
|
2507
|
+
];
|
|
2508
|
+
if (locations.length > 0) throw new FhirSafetyError(locations);
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
// src/codec/serialize-guard.ts
|
|
2512
|
+
var SERIALIZE_ERROR_CODES = {
|
|
2203
2513
|
/**
|
|
2204
|
-
*
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
* with no value-set content. Severity follows the binding strength (`required` → `error`;
|
|
2208
|
-
* `extensible`/`preferred` → `warning`, since a different system may be a legitimate extension;
|
|
2209
|
-
* `example` → none). Compared on the `system` URI, never a code value.
|
|
2514
|
+
* The model carries character data the XML reader dropped, at one or more locations. There is no
|
|
2515
|
+
* conformant encoding of it in either wire format, and emitting the element as unfilled would
|
|
2516
|
+
* launder the `DROPPED_ELEMENT_TEXT` finding across a round trip.
|
|
2210
2517
|
*/
|
|
2211
|
-
|
|
2518
|
+
DROPPED_ELEMENT_TEXT: "DROPPED_ELEMENT_TEXT",
|
|
2212
2519
|
/**
|
|
2213
|
-
*
|
|
2214
|
-
*
|
|
2215
|
-
*
|
|
2216
|
-
*
|
|
2217
|
-
*
|
|
2218
|
-
*
|
|
2520
|
+
* The model carries a name that cannot occupy the `Name` slot of an XML start tag, so writing it
|
|
2521
|
+
* would emit markup that does not re-read as the element the model holds. **XML only**: JSON
|
|
2522
|
+
* escapes a member name, so this refusal never reaches it and that route stays open. **Narrowed
|
|
2523
|
+
* 2026-08-07 from "encodes every one of these correctly", which was false and shipped**: a model
|
|
2524
|
+
* refused here can carry one of `serializeResource`'s own declared exceptions and emit that.
|
|
2525
|
+
*
|
|
2526
|
+
* See {@link breaksTag} for the exact predicate and for what is deliberately NOT refused.
|
|
2219
2527
|
*/
|
|
2220
|
-
|
|
2528
|
+
UNSERIALIZABLE_ELEMENT_NAME: "UNSERIALIZABLE_ELEMENT_NAME",
|
|
2221
2529
|
/**
|
|
2222
|
-
*
|
|
2223
|
-
*
|
|
2224
|
-
*
|
|
2225
|
-
*
|
|
2226
|
-
*
|
|
2530
|
+
* A `div` property carries a string the XML writer would emit as raw markup, and that string does
|
|
2531
|
+
* not contribute exactly one element named `div` to the document. **XML only**: `serializeResource`
|
|
2532
|
+
* carries the string as a string, so this refusal never reaches it and that route stays open.
|
|
2533
|
+
*
|
|
2534
|
+
* **That is a statement about the `div` string, not about the whole model.** `serializeResource`
|
|
2535
|
+
* has its own declared non-spec-clean exceptions, so a model refused here can still route through
|
|
2536
|
+
* it and emit one of those: `{"text":{"div":""},"name":[[{"family":"X"}]]}` is refused here and
|
|
2537
|
+
* `serializeResource` emits the array inside an array unchanged.
|
|
2538
|
+
*
|
|
2539
|
+
* See `emitsOneDivElement` in `../xml/write.js` for the exact predicate and what it does not cover.
|
|
2227
2540
|
*/
|
|
2228
|
-
|
|
2541
|
+
UNSERIALIZABLE_DIV_MARKUP: "UNSERIALIZABLE_DIV_MARKUP",
|
|
2229
2542
|
/**
|
|
2230
|
-
*
|
|
2231
|
-
*
|
|
2232
|
-
*
|
|
2233
|
-
*
|
|
2543
|
+
* The model carries, at one or more locations, a shape the JSON reader marked because FHIR JSON
|
|
2544
|
+
* gives that position no meaning: an array inside an array, a scalar or `null` where FHIR JSON has
|
|
2545
|
+
* an object (a complex element's own position or a primitive's `_`-sibling), or a `null` in a
|
|
2546
|
+
* primitive's value channel that padded nothing. **XML only**: this refusal does not reach
|
|
2547
|
+
* `serializeResource`, which writes these back from the text the reader preserved at every position
|
|
2548
|
+
* that writer walks. **It does not walk a member a repeated property name shadowed, and this
|
|
2549
|
+
* refusal does reach one** -- `serializeResource` refuses that model on
|
|
2550
|
+
* {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_SHADOWED_PROPERTY} rather than emitting it, so it is
|
|
2551
|
+
* still not a route the shape survives. The XML writer keeps reporting THIS code on such a model,
|
|
2552
|
+
* because this one is raised first. See {@link assertXmlSerializable}.
|
|
2553
|
+
*
|
|
2554
|
+
* XML has no array-of-arrays, no `_`-sibling and no `null`, so the XML writer has nowhere to put
|
|
2555
|
+
* any of it and emits the element the reader was left holding: an empty one, or none. That output
|
|
2556
|
+
* re-reads with an empty issue list, so the finding the reader raised is gone after one trip.
|
|
2557
|
+
*
|
|
2558
|
+
* See {@link assertXmlSerializable} for the exact set of markers and for what it does NOT cover.
|
|
2234
2559
|
*/
|
|
2235
|
-
|
|
2560
|
+
UNSERIALIZABLE_JSON_ONLY_SHAPE: "UNSERIALIZABLE_JSON_ONLY_SHAPE",
|
|
2236
2561
|
/**
|
|
2237
|
-
*
|
|
2238
|
-
*
|
|
2239
|
-
*
|
|
2240
|
-
*
|
|
2241
|
-
*
|
|
2242
|
-
*
|
|
2562
|
+
* The model carries, at a location this library already reports as an array-wrapped `0..1` safety
|
|
2563
|
+
* element, a wrapper FHIR XML has no repetition to spell back: one holding fewer than two items,
|
|
2564
|
+
* or any wrapper at all on `resourceType`. **XML only**: `serializeResource` writes the list back
|
|
2565
|
+
* and the re-read reports the same location, so that route stays open.
|
|
2566
|
+
*
|
|
2567
|
+
* XML spells a repeat by repeating the element and has no other mark for one, so a wrapper of
|
|
2568
|
+
* fewer than two items emits at most one element and re-reads as an ordinary single-valued
|
|
2569
|
+
* element. The encoding complaint the reader raised is then gone, and with it an `error`-severity
|
|
2570
|
+
* `ARRAY_WRAPPED_SCALAR` and a `safeToSummarize: false`.
|
|
2571
|
+
*
|
|
2572
|
+
* See {@link assertXmlArrayWrapper} for the exact predicate and for what it deliberately leaves.
|
|
2243
2573
|
*/
|
|
2244
|
-
|
|
2574
|
+
UNSERIALIZABLE_ARRAY_WRAPPER: "UNSERIALIZABLE_ARRAY_WRAPPER",
|
|
2245
2575
|
/**
|
|
2246
|
-
*
|
|
2247
|
-
*
|
|
2248
|
-
*
|
|
2249
|
-
*
|
|
2576
|
+
* The model carries, at a location this library already reports as an array-wrapped
|
|
2577
|
+
* `Observation.value[x]` choice, a wrapper FHIR XML has no repetition to spell back: one holding
|
|
2578
|
+
* fewer than two items. **XML only**: `serializeResource` writes the list back and the re-read
|
|
2579
|
+
* reports the same location, so that route stays open.
|
|
2580
|
+
*
|
|
2581
|
+
* The same harm as {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_ARRAY_WRAPPER}, at the position
|
|
2582
|
+
* where the number is a DOSE: `{"resourceType":"Observation","status":"final","valueQuantity":
|
|
2583
|
+
* [{"value":5,"system":"http://unitsofmeasure.org","code":"mg"}]}` reads with the encoding
|
|
2584
|
+
* reported and no magnitude handed out, and used to come back from XML as an unambiguous 5 mg
|
|
2585
|
+
* nothing had complained about.
|
|
2586
|
+
*
|
|
2587
|
+
* See {@link assertXmlValueChoiceWrapper} for the window and for what it deliberately leaves.
|
|
2250
2588
|
*/
|
|
2251
|
-
|
|
2589
|
+
UNSERIALIZABLE_CHOICE_WRAPPER: "UNSERIALIZABLE_CHOICE_WRAPPER",
|
|
2252
2590
|
/**
|
|
2253
|
-
*
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
*
|
|
2591
|
+
* The model carries, at one or more object elements, a member a repeated property name shadowed
|
|
2592
|
+
* ({@link ../model/node.js} `duplicates`). **Both writers**, unlike the four refusals above it:
|
|
2593
|
+
* each walks `properties` only, so each wrote one member per name and the second value left the
|
|
2594
|
+
* document with no diagnostic on it.
|
|
2595
|
+
*
|
|
2596
|
+
* See {@link assertNoShadowedProperty} for the window, the two routes weighed, and what it leaves.
|
|
2257
2597
|
*/
|
|
2258
|
-
|
|
2598
|
+
UNSERIALIZABLE_SHADOWED_PROPERTY: "UNSERIALIZABLE_SHADOWED_PROPERTY",
|
|
2259
2599
|
/**
|
|
2260
|
-
*
|
|
2261
|
-
* `
|
|
2262
|
-
*
|
|
2600
|
+
* The model carries, at one or more elements, a first `resourceType` that is not a string. FHIR
|
|
2601
|
+
* XML has no `resourceType` element at all -- the type IS the tag -- so the XML writer skipped the
|
|
2602
|
+
* property, and at the root, with no string to name the tag, it wrote `Resource` instead: the
|
|
2603
|
+
* member deleted and the element named a type nobody wrote. **XML only**: `serializeResource` emits a
|
|
2604
|
+
* non-string `resourceType` through its ordinary path, so this refusal never reaches it and that
|
|
2605
|
+
* route stays open.
|
|
2606
|
+
*
|
|
2607
|
+
* See {@link assertXmlResourceType} for the window and for what it deliberately leaves.
|
|
2263
2608
|
*/
|
|
2264
|
-
|
|
2609
|
+
UNSERIALIZABLE_RESOURCE_TYPE: "UNSERIALIZABLE_RESOURCE_TYPE",
|
|
2265
2610
|
/**
|
|
2266
|
-
*
|
|
2267
|
-
*
|
|
2268
|
-
*
|
|
2269
|
-
*
|
|
2270
|
-
*
|
|
2271
|
-
*
|
|
2272
|
-
*
|
|
2611
|
+
* The model carries, at one or more elements, a root the XML reader read out of a vocabulary that
|
|
2612
|
+
* resolved to something other than FHIR's. **XML only**: `serializeResource` spells a member name
|
|
2613
|
+
* and never a namespace, so it emits the model exactly as it did before this refusal existed and
|
|
2614
|
+
* that route stays open -- which is a statement about that writer's output, **not** a claim that
|
|
2615
|
+
* the JSON channel keeps the flag. It does not, and that half is declared open.
|
|
2616
|
+
*
|
|
2617
|
+
* FHIR XML puts the resource in the FHIR namespace, and the writer has no vendor binding to write
|
|
2618
|
+
* instead, so it emitted a document in the FHIR namespace with no trace of the one the sender
|
|
2619
|
+
* wrote. That output re-reads with an **empty** issue list, so the one warning that said the
|
|
2620
|
+
* document came from another vocabulary is gone after a single trip and the vendor document is
|
|
2621
|
+
* indistinguishable from FHIR.
|
|
2622
|
+
*
|
|
2623
|
+
* See {@link assertXmlForeignRoot} for the window, the two routes weighed, and what it costs.
|
|
2273
2624
|
*/
|
|
2274
|
-
|
|
2625
|
+
UNSERIALIZABLE_FOREIGN_ROOT: "UNSERIALIZABLE_FOREIGN_ROOT",
|
|
2275
2626
|
/**
|
|
2276
|
-
*
|
|
2277
|
-
*
|
|
2278
|
-
*
|
|
2279
|
-
*
|
|
2280
|
-
*
|
|
2627
|
+
* The model holds, at one or more tag positions, a name carrying a colon. XML reads the colon as
|
|
2628
|
+
* a namespace prefix, and the model carries no namespace binding for the writer to declare one
|
|
2629
|
+
* with, so the document would not be namespace-well-formed. **XML only**: `serializeResource`
|
|
2630
|
+
* spells a member name as a JSON string, so this refusal never reaches it and that route stays
|
|
2631
|
+
* open.
|
|
2632
|
+
*
|
|
2633
|
+
* Written anyway, the output named a prefix nothing declared, which a conformant XML parser
|
|
2634
|
+
* rejects, and a prefix rebound between siblings (`<p:x xmlns:p="urn:a"/>` beside
|
|
2635
|
+
* `<p:x xmlns:p="urn:b"/>`) lost its `MIXED_XML_SPELLING` report across one write and one re-read.
|
|
2636
|
+
* **Not {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_ELEMENT_NAME}**, whose line is whether this
|
|
2637
|
+
* library's own round trip survives a name: a colon-bearing name survives that round trip, so the
|
|
2638
|
+
* two are different questions and keep different codes. A name that trips both keeps that one.
|
|
2639
|
+
*
|
|
2640
|
+
* See {@link carriesUndeclarablePrefix} for the exact predicate, its one exemption, and what it
|
|
2641
|
+
* leaves.
|
|
2281
2642
|
*/
|
|
2282
|
-
|
|
2643
|
+
UNSERIALIZABLE_PREFIXED_NAME: "UNSERIALIZABLE_PREFIXED_NAME",
|
|
2283
2644
|
/**
|
|
2284
|
-
*
|
|
2285
|
-
*
|
|
2286
|
-
*
|
|
2287
|
-
*
|
|
2288
|
-
*
|
|
2289
|
-
*
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2645
|
+
* The model holds, at one or more `div` locations, a string the XML writer would splice in as raw
|
|
2646
|
+
* markup that spells the one `div` element the property names, and whose markup names a namespace
|
|
2647
|
+
* prefix, on an element or on an attribute, that no declaration inside the string binds. **XML
|
|
2648
|
+
* only**: `serializeResource` carries the string as a string, so this refusal never reaches it and
|
|
2649
|
+
* that route stays open.
|
|
2650
|
+
*
|
|
2651
|
+
* Written anyway, `<v:div>x</v:div>` put a prefix nothing declared into the output, which a
|
|
2652
|
+
* conformant XML parser rejects, and this library's own re-read of that output turned the narrative
|
|
2653
|
+
* into a property named `v:div`: the narrative gone after one write and one read, with no
|
|
2654
|
+
* diagnostic at either end. **Not {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_PREFIXED_NAME}**,
|
|
2655
|
+
* whose published meaning is a name at a tag position, and **not
|
|
2656
|
+
* {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_DIV_MARKUP}**, whose question is which elements the
|
|
2657
|
+
* string contributes: a string that fails that question keeps that code whatever prefix it names.
|
|
2658
|
+
*
|
|
2659
|
+
* See `bindsEveryPrefix` in `../xml/write.js` for the exact predicate and what it leaves.
|
|
2660
|
+
*
|
|
2661
|
+
* @example
|
|
2662
|
+
* ```ts
|
|
2663
|
+
* import {
|
|
2664
|
+
* FhirSerializeError,
|
|
2665
|
+
* SERIALIZE_ERROR_CODES,
|
|
2666
|
+
* parseResource,
|
|
2667
|
+
* serializeResourceXml,
|
|
2668
|
+
* } from "@cosyte/fhir";
|
|
2669
|
+
* const { resource } = parseResource(
|
|
2670
|
+
* '{"resourceType":"Patient","text":{"status":"generated","div":"<v:div>x</v:div>"}}',
|
|
2671
|
+
* );
|
|
2672
|
+
* try {
|
|
2673
|
+
* serializeResourceXml(resource);
|
|
2674
|
+
* } catch (err) {
|
|
2675
|
+
* if (err instanceof FhirSerializeError && err.code === SERIALIZE_ERROR_CODES.UNSERIALIZABLE_DIV_PREFIX) {
|
|
2676
|
+
* err.locations; // ["Patient.text.div"]
|
|
2677
|
+
* }
|
|
2678
|
+
* }
|
|
2679
|
+
* ```
|
|
2680
|
+
*/
|
|
2681
|
+
UNSERIALIZABLE_DIV_PREFIX: "UNSERIALIZABLE_DIV_PREFIX",
|
|
2682
|
+
/**
|
|
2683
|
+
* The model holds, at one or more tag positions, a name that is not an XML 1.0 `Name` (production
|
|
2684
|
+
* [5]): `a&b`, `1abc`, `-x`, a name carrying U+0000 or an unpaired surrogate. A conforming XML
|
|
2685
|
+
* processor must reject such a document as a fatal error. **XML only**: `serializeResource` spells
|
|
2686
|
+
* a member name as a JSON string, so this refusal never reaches it and that route stays open.
|
|
2687
|
+
*
|
|
2688
|
+
* Written anyway, `<a&b value="v"/>` was a document this library's own reader read back unchanged
|
|
2689
|
+
* and a third-party parser rejected, which is why the tag-breaking refusal, whose line is this
|
|
2690
|
+
* library's own round trip, never reached it. **Not
|
|
2691
|
+
* {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_ELEMENT_NAME}** and **not
|
|
2692
|
+
* {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_PREFIXED_NAME}**: a name that breaks the tag, or
|
|
2693
|
+
* carries a colon, keeps the code it drew before this one existed, whether or not it is a `Name`.
|
|
2694
|
+
* A name carrying a character outside `Char` is not a `Name`, so it draws this code rather than
|
|
2695
|
+
* {@link SERIALIZE_ERROR_CODES.UNSERIALIZABLE_XML_CHARACTER}.
|
|
2696
|
+
*
|
|
2697
|
+
* See {@link isXmlName} for the exact predicate and what it leaves.
|
|
2698
|
+
*
|
|
2699
|
+
* @example
|
|
2700
|
+
* ```ts
|
|
2701
|
+
* import {
|
|
2702
|
+
* FhirSerializeError,
|
|
2703
|
+
* SERIALIZE_ERROR_CODES,
|
|
2704
|
+
* parseResource,
|
|
2705
|
+
* serializeResourceXml,
|
|
2706
|
+
* } from "@cosyte/fhir";
|
|
2707
|
+
* const { resource } = parseResource('{"resourceType":"Patient","1abc":"v"}');
|
|
2708
|
+
* try {
|
|
2709
|
+
* serializeResourceXml(resource);
|
|
2710
|
+
* } catch (err) {
|
|
2711
|
+
* if (err instanceof FhirSerializeError && err.code === SERIALIZE_ERROR_CODES.UNSERIALIZABLE_XML_NAME) {
|
|
2712
|
+
* err.locations; // ["Patient.<withheld>"]
|
|
2713
|
+
* }
|
|
2714
|
+
* }
|
|
2715
|
+
* ```
|
|
2716
|
+
*/
|
|
2717
|
+
UNSERIALIZABLE_XML_NAME: "UNSERIALIZABLE_XML_NAME",
|
|
2718
|
+
/**
|
|
2719
|
+
* The model holds, at one or more locations, a value the XML writer would emit as an attribute
|
|
2720
|
+
* value (a primitive's `value`, an `id` written as an attribute, an `Extension.url`), or a `div`
|
|
2721
|
+
* string it would splice in, carrying a code point outside XML 1.0 `Char` (production [2]): U+0000
|
|
2722
|
+
* to U+0008, U+000B, U+000C, U+000E to U+001F, an unpaired surrogate, U+FFFE or U+FFFF. In a `div`
|
|
2723
|
+
* string that includes a numeric character reference denoting one (`�`), which the Legal
|
|
2724
|
+
* Character constraint makes as fatal as the raw character; each reference is judged on its own, so
|
|
2725
|
+
* `��` is two unpaired surrogates, not one character. **XML only**: `serializeResource`
|
|
2726
|
+
* writes these values as JSON strings, so this refusal never reaches it and that route stays open.
|
|
2727
|
+
*
|
|
2728
|
+
* Written anyway, a U+0000 went into the output raw and a conforming processor rejected the
|
|
2729
|
+
* document. **Refused, never repaired**: a reference to a non-`Char` is itself a fatal error, and
|
|
2730
|
+
* replacing or dropping the character would change a value the sender wrote, so the model is left
|
|
2731
|
+
* exactly as it was and no string is returned. Raised after every other refusal, the name code
|
|
2732
|
+
* included.
|
|
2733
|
+
*
|
|
2734
|
+
* See {@link carriesNonXmlCharacter} for the exact predicate.
|
|
2735
|
+
*
|
|
2736
|
+
* @example
|
|
2737
|
+
* ```ts
|
|
2738
|
+
* import {
|
|
2739
|
+
* FhirSerializeError,
|
|
2740
|
+
* SERIALIZE_ERROR_CODES,
|
|
2741
|
+
* parseResource,
|
|
2742
|
+
* serializeResourceXml,
|
|
2743
|
+
* } from "@cosyte/fhir";
|
|
2744
|
+
* const { resource } = parseResource('{"resourceType":"Patient","gender":"a\\u0000"}');
|
|
2745
|
+
* try {
|
|
2746
|
+
* serializeResourceXml(resource);
|
|
2747
|
+
* } catch (err) {
|
|
2748
|
+
* if (err instanceof FhirSerializeError && err.code === SERIALIZE_ERROR_CODES.UNSERIALIZABLE_XML_CHARACTER) {
|
|
2749
|
+
* err.locations; // ["Patient.gender"]
|
|
2750
|
+
* }
|
|
2751
|
+
* }
|
|
2752
|
+
* ```
|
|
2753
|
+
*/
|
|
2754
|
+
UNSERIALIZABLE_XML_CHARACTER: "UNSERIALIZABLE_XML_CHARACTER"
|
|
2328
2755
|
};
|
|
2329
|
-
var
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
ABSENCE_MARKER_UNREADABLE: "Element carries a data-absent-reason extension whose reason could not be read: it is absent, empty, written more than once, or outside the value set the extension requires. The reason is not guessed and the element is not read as populated.",
|
|
2346
|
-
RETRACTED_RESOURCE: "Resource is marked entered-in-error; it is retracted and must not be treated as active data.",
|
|
2347
|
-
INVARIANT_VIOLATED: "A resource invariant (content-validation constraint) was violated.",
|
|
2348
|
-
INVARIANT_UNCHECKED: "A resource invariant could not be evaluated by the bounded FHIRPath engine; it is reported unchecked rather than assumed to pass.",
|
|
2349
|
-
UCUM_UNIT_UNRECOGNIZED: "Quantity declares the UCUM system but its unit code is absent or not a well-formed UCUM expression; the unit is preserved verbatim and never converted.",
|
|
2350
|
-
VITAL_SIGN_UNIT_NONCONFORMANT: "Vital-signs measurement carries a unit the vital-signs profile does not allow for this code.",
|
|
2351
|
-
VALUE_TYPE_UNEXPECTED: "Observation value is present but not the expected type for this profile.",
|
|
2352
|
-
CODE_SYSTEM_UNKNOWN: "Coding uses a code system that is not in the known-systems registry; its codes cannot be validated (an unrecognized system is not itself an error).",
|
|
2353
|
-
CODE_SYSTEM_UNEXPECTED: "Coding uses a code system that the bound value set does not draw from.",
|
|
2354
|
-
CODE_NOT_IN_VALUESET: "Coding is not a member of the value set required by this element's binding.",
|
|
2355
|
-
PROFILE_SLICE_UNMATCHED: "Element is present under closed slicing but matches none of the profile's defined slices.",
|
|
2356
|
-
PROFILE_SLICE_UNCHECKED: "Slice membership could not be evaluated (an unsupported discriminator); it is reported unchecked rather than assumed to pass.",
|
|
2357
|
-
MUST_SUPPORT_ABSENT: "Profile marks this element must-support; it is absent. Must-support is a system obligation, not an instance-presence requirement, this is informational, never an error.",
|
|
2358
|
-
PROFILE_VERSION_MISMATCH: "Instance declares a profile at a version the supplied profile set does not carry; it was not validated against that exact version.",
|
|
2359
|
-
PROFILE_FIXED_MISMATCH: "Element value does not exactly match the value the profile fixes for it.",
|
|
2360
|
-
PROFILE_PATTERN_MISMATCH: "Element does not match the pattern the profile requires for it.",
|
|
2361
|
-
REFERENCE_UNRESOLVED: "Reference could not be resolved within the supplied closure; it is preserved and flagged, never dropped (the target may live outside this Bundle).",
|
|
2362
|
-
CONTAINED_CYCLE: "Contained resources reference each other in a cycle; the containment graph is malformed and was reported by the bounded cycle guard rather than followed into an unbounded loop.",
|
|
2363
|
-
FULLURL_ID_MISMATCH: "Bundle entry fullUrl is a RESTful URL whose id disagrees with the wrapped resource's id."
|
|
2756
|
+
var FhirSerializeError = class extends Error {
|
|
2757
|
+
/** Which refusal this is. */
|
|
2758
|
+
code;
|
|
2759
|
+
/** The bounded FHIRPath locations the refusal is about, in walk order. Never document content. */
|
|
2760
|
+
locations;
|
|
2761
|
+
/**
|
|
2762
|
+
* @param message - A value-free description of the refusal.
|
|
2763
|
+
* @param code - Which refusal this is.
|
|
2764
|
+
* @param locations - The bounded FHIRPath locations it is about.
|
|
2765
|
+
*/
|
|
2766
|
+
constructor(message, code, locations) {
|
|
2767
|
+
super(message);
|
|
2768
|
+
this.name = "FhirSerializeError";
|
|
2769
|
+
this.code = code;
|
|
2770
|
+
this.locations = locations;
|
|
2771
|
+
}
|
|
2364
2772
|
};
|
|
2365
|
-
function
|
|
2366
|
-
|
|
2773
|
+
function assertSerializable(node) {
|
|
2774
|
+
const locations = droppedText(node, rootPath(typeOf(node) ?? "Resource"));
|
|
2775
|
+
if (locations.length === 0) return;
|
|
2776
|
+
throw new FhirSerializeError(
|
|
2777
|
+
`cannot serialize: the reader dropped character data at ${String(locations.length)} location(s), which this model cannot encode`,
|
|
2778
|
+
SERIALIZE_ERROR_CODES.DROPPED_ELEMENT_TEXT,
|
|
2779
|
+
locations
|
|
2780
|
+
);
|
|
2781
|
+
}
|
|
2782
|
+
function carriesJsonOnlyShape(node) {
|
|
2783
|
+
if (node.kind === "list") return false;
|
|
2784
|
+
if (node.nestedArray === true || node.nestedArraySource !== void 0) return true;
|
|
2785
|
+
if (node.kind === "complex") return node.nonObjectSource !== void 0;
|
|
2786
|
+
return node.undefinedNull === true || node.nonObjectMetaSource !== void 0 || node.nestedArrayMetaSource !== void 0;
|
|
2787
|
+
}
|
|
2788
|
+
function assertXmlSerializable(node) {
|
|
2789
|
+
const locations = collectMarked(node, rootPath(typeOf(node) ?? "Resource"), carriesJsonOnlyShape);
|
|
2790
|
+
if (locations.length === 0) return;
|
|
2791
|
+
throw new FhirSerializeError(
|
|
2792
|
+
// Says only what this refusal does NOT reach, which is the wording the two refusals beside it
|
|
2793
|
+
// were narrowed to. It deliberately does not promise that `serializeResource` writes the shape
|
|
2794
|
+
// back: it does for every position that writer walks, and a marked node inside a member a
|
|
2795
|
+
// repeated property name shadowed is dropped by it as well.
|
|
2796
|
+
`cannot serialize to XML: ${String(locations.length)} location(s) carry a shape only FHIR JSON can spell, which XML has no channel for; this refusal does not reach serializeResource`,
|
|
2797
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_JSON_ONLY_SHAPE,
|
|
2798
|
+
locations
|
|
2799
|
+
);
|
|
2800
|
+
}
|
|
2801
|
+
function assertXmlArrayWrapper(node) {
|
|
2802
|
+
const locations = unspellableXmlWrappers(node, rootPath(typeOf(node) ?? "Resource"));
|
|
2803
|
+
if (locations.length === 0) return;
|
|
2804
|
+
throw new FhirSerializeError(
|
|
2805
|
+
// Says what this refusal does not reach, in the wording the three refusals beside it were
|
|
2806
|
+
// narrowed to. `serializeResource` does write these wrappers back -- pinned by the JSON half of
|
|
2807
|
+
// the same test -- with the shadowed-member limit stated in the docblock rather than here.
|
|
2808
|
+
`cannot serialize to XML: ${String(locations.length)} location(s) carry an array wrapper around a 0..1 element that XML has no repeated element to spell back; this refusal does not reach serializeResource`,
|
|
2809
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_ARRAY_WRAPPER,
|
|
2810
|
+
locations
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
function assertXmlValueChoiceWrapper(node) {
|
|
2814
|
+
const locations = unspellableXmlValueChoices(node, rootPath(typeOf(node) ?? "Resource"));
|
|
2815
|
+
if (locations.length === 0) return;
|
|
2816
|
+
throw new FhirSerializeError(
|
|
2817
|
+
// Says what this refusal does not reach, in the wording the refusals beside it were narrowed to.
|
|
2818
|
+
// `serializeResource` writes the wrapper back and the re-read reports the same location, pinned
|
|
2819
|
+
// by the JSON half of the same test.
|
|
2820
|
+
`cannot serialize to XML: ${String(locations.length)} location(s) carry an array wrapper around a 0..1 value[x] choice that XML has no repeated element to spell back; this refusal does not reach serializeResource`,
|
|
2821
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_CHOICE_WRAPPER,
|
|
2822
|
+
locations
|
|
2823
|
+
);
|
|
2824
|
+
}
|
|
2825
|
+
function assertNoShadowedProperty(node) {
|
|
2826
|
+
const locations = shadowedProperties(node, rootPath(typeOf(node) ?? "Resource"));
|
|
2827
|
+
if (locations.length === 0) return;
|
|
2828
|
+
throw new FhirSerializeError(
|
|
2829
|
+
// Names both writers, because both drop it: there is no route here that keeps the member, and a
|
|
2830
|
+
// message pointing at one would point an operator at a silent drop. That is the wording the four
|
|
2831
|
+
// refusals above were narrowed to, at the one refusal where the answer is "neither".
|
|
2832
|
+
`cannot serialize: ${String(locations.length)} location(s) carry a member a repeated property name shadowed, which neither serializeResource nor serializeResourceXml can write`,
|
|
2833
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_SHADOWED_PROPERTY,
|
|
2834
|
+
locations
|
|
2835
|
+
);
|
|
2836
|
+
}
|
|
2837
|
+
function lacksTaggableResourceType(node) {
|
|
2838
|
+
if (node.kind !== "complex") return false;
|
|
2839
|
+
const written = node.properties.find((property) => property.name === "resourceType");
|
|
2840
|
+
if (written === void 0) return false;
|
|
2841
|
+
return !(isPrimitive(written.value) && typeof written.value.value === "string");
|
|
2842
|
+
}
|
|
2843
|
+
function assertXmlResourceType(node) {
|
|
2844
|
+
const elements = collectMarked(
|
|
2845
|
+
node,
|
|
2846
|
+
rootPath(typeOf(node) ?? "Resource"),
|
|
2847
|
+
lacksTaggableResourceType
|
|
2848
|
+
);
|
|
2849
|
+
if (elements.length === 0) return;
|
|
2850
|
+
const locations = elements.map((element) => childPath(element, "resourceType"));
|
|
2851
|
+
throw new FhirSerializeError(
|
|
2852
|
+
// Says what this refusal does not reach, in the wording the refusals beside it were narrowed to.
|
|
2853
|
+
// `serializeResource` emits a non-string `resourceType` through its ordinary path -- pinned by
|
|
2854
|
+
// the JSON half of the same test -- which is a statement about this shape, not about the rest of
|
|
2855
|
+
// the model: that writer's own declared exceptions still apply to everything else it carries.
|
|
2856
|
+
// It does NOT say "while naming the element Resource": that substitution happens at the root,
|
|
2857
|
+
// and this refusal reports depths where the tag comes from the property name instead.
|
|
2858
|
+
`cannot serialize to XML: ${String(locations.length)} location(s) carry a resourceType with no string to name a tag with, which this writer would delete; this refusal does not reach serializeResource`,
|
|
2859
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_RESOURCE_TYPE,
|
|
2860
|
+
locations
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
function assertXmlForeignRoot(node) {
|
|
2864
|
+
const locations = collectMarked(node, rootPath(typeOf(node) ?? "Resource"), isForeignRoot);
|
|
2865
|
+
if (locations.length === 0) return;
|
|
2866
|
+
throw new FhirSerializeError(
|
|
2867
|
+
// Says what this refusal does not reach, in the wording the refusals beside it were narrowed to.
|
|
2868
|
+
// It names no namespace, no tag and no value: the marker it fires on carries none of them, and
|
|
2869
|
+
// the vendor URI is the one piece of document content this whole residual is about.
|
|
2870
|
+
`cannot serialize to XML: ${String(locations.length)} location(s) hold a resource whose root named another vocabulary, which this writer would emit in the FHIR namespace; this refusal does not reach serializeResource`,
|
|
2871
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_FOREIGN_ROOT,
|
|
2872
|
+
locations
|
|
2873
|
+
);
|
|
2874
|
+
}
|
|
2875
|
+
var TAG_BREAKING = /[ \t\n\r/>=<]/u;
|
|
2876
|
+
var TAG_OPENER_STEALING = /* @__PURE__ */ new Set(["!", "?"]);
|
|
2877
|
+
function breaksTag(name) {
|
|
2878
|
+
return name === "" || TAG_BREAKING.test(name) || TAG_OPENER_STEALING.has(name.slice(0, 1));
|
|
2879
|
+
}
|
|
2880
|
+
function refuseUnserializableNames(locations) {
|
|
2881
|
+
throw new FhirSerializeError(
|
|
2882
|
+
`cannot serialize to XML: ${String(locations.length)} location(s) carry a name that cannot be written as an XML tag without changing which elements the document holds; serializeResource escapes a member name, so this refusal never reaches it`,
|
|
2883
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_ELEMENT_NAME,
|
|
2884
|
+
locations
|
|
2885
|
+
);
|
|
2886
|
+
}
|
|
2887
|
+
function refuseUnserializableDivMarkup(locations) {
|
|
2888
|
+
throw new FhirSerializeError(
|
|
2889
|
+
`cannot serialize to XML: ${String(locations.length)} div location(s) carry markup that would not be written as the one div element the model names; serializeResource carries the string as a string`,
|
|
2890
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_DIV_MARKUP,
|
|
2891
|
+
locations
|
|
2892
|
+
);
|
|
2893
|
+
}
|
|
2894
|
+
var XML_PREFIX = "xml:";
|
|
2895
|
+
function carriesUndeclarablePrefix(name) {
|
|
2896
|
+
if (!name.includes(":")) return false;
|
|
2897
|
+
const local = name.slice(XML_PREFIX.length);
|
|
2898
|
+
return !(name.startsWith(XML_PREFIX) && local !== "" && !local.includes(":"));
|
|
2899
|
+
}
|
|
2900
|
+
function refuseUndeclarablePrefixes(locations) {
|
|
2901
|
+
throw new FhirSerializeError(
|
|
2902
|
+
`cannot serialize to XML, because ${String(locations.length)} location(s) carry a name with a colon, which XML reads as a namespace prefix and this model carries no binding to declare, so the output would not be namespace-well-formed; this refusal does not reach serializeResource`,
|
|
2903
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_PREFIXED_NAME,
|
|
2904
|
+
locations
|
|
2905
|
+
);
|
|
2906
|
+
}
|
|
2907
|
+
function refuseUnboundDivPrefixes(locations) {
|
|
2908
|
+
throw new FhirSerializeError(
|
|
2909
|
+
`cannot serialize to XML, because ${String(locations.length)} div location(s) carry markup naming a namespace prefix that no declaration inside the string binds, so the output would not be namespace-well-formed; serializeResource carries the string as a string, so this refusal never reaches it`,
|
|
2910
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_DIV_PREFIX,
|
|
2911
|
+
locations
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
var XML_NAME = /^[:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}][\u{300}-\u{36F}\-.0-9:A-Z_a-z\u{B7}\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}\u{200C}-\u{200D}\u{203F}-\u{2040}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}]*$/u;
|
|
2915
|
+
function isXmlName(name) {
|
|
2916
|
+
return XML_NAME.test(name);
|
|
2917
|
+
}
|
|
2918
|
+
function refuseNonXmlNames(locations) {
|
|
2919
|
+
throw new FhirSerializeError(
|
|
2920
|
+
`cannot serialize to XML, because ${String(locations.length)} location(s) carry a name that is not an XML 1.0 Name, so a conforming XML processor must reject the output; serializeResource escapes a member name, so this refusal never reaches it`,
|
|
2921
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_XML_NAME,
|
|
2922
|
+
locations
|
|
2923
|
+
);
|
|
2924
|
+
}
|
|
2925
|
+
var NON_XML_CHARACTER = /[^\t\n\r\u{20}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}]/u;
|
|
2926
|
+
function carriesNonXmlCharacter(text) {
|
|
2927
|
+
return NON_XML_CHARACTER.test(text);
|
|
2928
|
+
}
|
|
2929
|
+
function refuseNonXmlCharacters(locations) {
|
|
2930
|
+
throw new FhirSerializeError(
|
|
2931
|
+
`cannot serialize to XML, because ${String(locations.length)} location(s) carry a character outside the XML 1.0 Char production, which a conforming XML processor must reject and which this writer neither escapes, replaces nor drops; this refusal does not reach serializeResource`,
|
|
2932
|
+
SERIALIZE_ERROR_CODES.UNSERIALIZABLE_XML_CHARACTER,
|
|
2933
|
+
locations
|
|
2934
|
+
);
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
// src/codec/write.ts
|
|
2938
|
+
function emitScalar(value) {
|
|
2939
|
+
if (value === void 0) return "null";
|
|
2940
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
2941
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
2942
|
+
return value.raw;
|
|
2943
|
+
}
|
|
2944
|
+
function emitPrimitiveValue(node) {
|
|
2945
|
+
return node.nestedArraySource ?? emitScalar(node.value);
|
|
2946
|
+
}
|
|
2947
|
+
function hasValue(node) {
|
|
2948
|
+
return node.value !== void 0 || node.nestedArraySource !== void 0 || node.undefinedNull === true;
|
|
2949
|
+
}
|
|
2950
|
+
function hasMeta(node) {
|
|
2951
|
+
return node.id !== void 0 || node.extension !== void 0 && node.extension.length > 0 || node.nestedArrayMetaSource !== void 0 || node.nonObjectMetaSource !== void 0;
|
|
2952
|
+
}
|
|
2953
|
+
function emitMeta(node) {
|
|
2954
|
+
if (node.nestedArrayMetaSource !== void 0) return node.nestedArrayMetaSource;
|
|
2955
|
+
if (node.nonObjectMetaSource !== void 0) return node.nonObjectMetaSource;
|
|
2956
|
+
const parts = [];
|
|
2957
|
+
if (node.id !== void 0) parts.push(`"id":${JSON.stringify(node.id)}`);
|
|
2958
|
+
if (node.extension !== void 0 && node.extension.length > 0) {
|
|
2959
|
+
parts.push(`"extension":[${node.extension.map(emitComplex).join(",")}]`);
|
|
2960
|
+
}
|
|
2961
|
+
return `{${parts.join(",")}}`;
|
|
2962
|
+
}
|
|
2963
|
+
function emitPrimitiveProperty(name, node) {
|
|
2964
|
+
const entries = [];
|
|
2965
|
+
if (hasValue(node)) {
|
|
2966
|
+
entries.push(`${JSON.stringify(name)}:${emitPrimitiveValue(node)}`);
|
|
2967
|
+
}
|
|
2968
|
+
if (hasMeta(node)) {
|
|
2969
|
+
entries.push(`${JSON.stringify(`_${name}`)}:${emitMeta(node)}`);
|
|
2970
|
+
}
|
|
2971
|
+
return entries;
|
|
2972
|
+
}
|
|
2973
|
+
function emitListProperty(name, node) {
|
|
2974
|
+
if (node.items.length === 0) return [];
|
|
2975
|
+
const allPrimitive = node.items.every(isPrimitive);
|
|
2976
|
+
if (!allPrimitive) {
|
|
2977
|
+
return [`${JSON.stringify(name)}:[${node.items.map(emitNode).join(",")}]`];
|
|
2978
|
+
}
|
|
2979
|
+
const primitives = node.items.filter(isPrimitive);
|
|
2980
|
+
const anyValue = primitives.some(hasValue);
|
|
2981
|
+
const anyMeta = primitives.some(hasMeta);
|
|
2982
|
+
const entries = [];
|
|
2983
|
+
if (anyValue) {
|
|
2984
|
+
const values = primitives.map(emitPrimitiveValue).join(",");
|
|
2985
|
+
entries.push(`${JSON.stringify(name)}:[${values}]`);
|
|
2986
|
+
}
|
|
2987
|
+
if (anyMeta) {
|
|
2988
|
+
const metas = primitives.map((item) => hasMeta(item) ? emitMeta(item) : "null").join(",");
|
|
2989
|
+
entries.push(`${JSON.stringify(`_${name}`)}:[${metas}]`);
|
|
2990
|
+
}
|
|
2991
|
+
return entries;
|
|
2992
|
+
}
|
|
2993
|
+
function emitNode(node) {
|
|
2994
|
+
switch (node.kind) {
|
|
2995
|
+
case "complex":
|
|
2996
|
+
return emitComplex(node);
|
|
2997
|
+
case "list":
|
|
2998
|
+
return `[${node.items.map(emitNode).join(",")}]`;
|
|
2999
|
+
case "primitive":
|
|
3000
|
+
return emitPrimitiveValue(node);
|
|
3001
|
+
}
|
|
2367
3002
|
}
|
|
2368
|
-
function
|
|
2369
|
-
|
|
2370
|
-
|
|
3003
|
+
function emitComplex(node) {
|
|
3004
|
+
if (node.nestedArraySource !== void 0) return node.nestedArraySource;
|
|
3005
|
+
if (node.nonObjectSource !== void 0) return node.nonObjectSource;
|
|
3006
|
+
const parts = [];
|
|
3007
|
+
const rt = node.properties.find((p) => p.name === "resourceType");
|
|
3008
|
+
const hoisted = rt !== void 0 && isPrimitive(rt.value) && typeof rt.value.value === "string";
|
|
3009
|
+
if (hoisted) parts.push(`"resourceType":${JSON.stringify(rt.value.value)}`);
|
|
3010
|
+
for (const property of node.properties) {
|
|
3011
|
+
if (property.name === "resourceType" && hoisted) {
|
|
3012
|
+
if (isPrimitive(property.value) && hasMeta(property.value)) {
|
|
3013
|
+
parts.push(`"_resourceType":${emitMeta(property.value)}`);
|
|
3014
|
+
}
|
|
3015
|
+
continue;
|
|
3016
|
+
}
|
|
3017
|
+
const value = property.value;
|
|
3018
|
+
switch (value.kind) {
|
|
3019
|
+
case "primitive":
|
|
3020
|
+
parts.push(...emitPrimitiveProperty(property.name, value));
|
|
3021
|
+
break;
|
|
3022
|
+
case "list":
|
|
3023
|
+
parts.push(...emitListProperty(property.name, value));
|
|
3024
|
+
break;
|
|
3025
|
+
case "complex":
|
|
3026
|
+
parts.push(`${JSON.stringify(property.name)}:${emitComplex(value)}`);
|
|
3027
|
+
break;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
return `{${parts.join(",")}}`;
|
|
3031
|
+
}
|
|
3032
|
+
function serializeResource(node) {
|
|
3033
|
+
assertSerializable(node);
|
|
3034
|
+
assertNoShadowedProperty(node);
|
|
3035
|
+
return emitComplex(node);
|
|
2371
3036
|
}
|
|
2372
3037
|
|
|
2373
3038
|
// src/validate/operation-outcome.ts
|
|
@@ -2378,14 +3043,26 @@ function issueNode(issue) {
|
|
|
2378
3043
|
{ name: "diagnostics", value: primitive(diagnosticFor(issue.code)) },
|
|
2379
3044
|
{ name: "expression", value: list([primitive(issue.expression)]) }
|
|
2380
3045
|
];
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
name: "details",
|
|
2384
|
-
value: complex([{ name: "text", value: primitive(issue.constraint) }])
|
|
2385
|
-
});
|
|
2386
|
-
}
|
|
3046
|
+
const details = detailsNode(issue);
|
|
3047
|
+
if (details !== void 0) properties.splice(3, 0, { name: "details", value: details });
|
|
2387
3048
|
return complex(properties);
|
|
2388
3049
|
}
|
|
3050
|
+
function detailsNode(issue) {
|
|
3051
|
+
const members = [];
|
|
3052
|
+
const record = issue.codeSystemVersion;
|
|
3053
|
+
if (record !== void 0) members.push({ name: "coding", value: list([recordCoding(record)]) });
|
|
3054
|
+
let text = issue.constraint;
|
|
3055
|
+
if (record !== void 0) text = record.declared ? record.version : void 0;
|
|
3056
|
+
if (text !== void 0) members.push({ name: "text", value: primitive(text) });
|
|
3057
|
+
return members.length === 0 ? void 0 : complex(members);
|
|
3058
|
+
}
|
|
3059
|
+
function recordCoding(record) {
|
|
3060
|
+
const marker = record.declared ? CODE_SYSTEM_VERSION_RECORD_CODES.DECLARED : CODE_SYSTEM_VERSION_RECORD_CODES.UNDECLARED;
|
|
3061
|
+
return complex([
|
|
3062
|
+
{ name: "system", value: primitive(CODE_SYSTEM_VERSION_RECORD_SYSTEM) },
|
|
3063
|
+
{ name: "code", value: primitive(marker) }
|
|
3064
|
+
]);
|
|
3065
|
+
}
|
|
2389
3066
|
function allClearNode() {
|
|
2390
3067
|
return complex([
|
|
2391
3068
|
{ name: "severity", value: primitive(ISSUE_SEVERITIES.INFORMATION) },
|
|
@@ -2739,76 +3416,6 @@ function collectBundleIssues(resource) {
|
|
|
2739
3416
|
return issues;
|
|
2740
3417
|
}
|
|
2741
3418
|
|
|
2742
|
-
// src/quantity/ucum.ts
|
|
2743
|
-
var UCUM_SYSTEM = "http://unitsofmeasure.org";
|
|
2744
|
-
var LOINC_SYSTEM = "http://loinc.org";
|
|
2745
|
-
var OBSERVATION_CATEGORY_SYSTEM = "http://terminology.hl7.org/CodeSystem/observation-category";
|
|
2746
|
-
var VITAL_SIGNS_CATEGORY = "vital-signs";
|
|
2747
|
-
var VITAL_SIGNS_PROFILE = "http://hl7.org/fhir/StructureDefinition/vitalsigns";
|
|
2748
|
-
var VITAL_SIGN_UNITS = /* @__PURE__ */ new Map([
|
|
2749
|
-
["29463-7", ["g", "kg", "[lb_av]"]],
|
|
2750
|
-
// Body weight
|
|
2751
|
-
["8302-2", ["cm", "[in_i]"]],
|
|
2752
|
-
// Body height
|
|
2753
|
-
["8306-3", ["cm", "[in_i]"]],
|
|
2754
|
-
// Body height (lying) / body length
|
|
2755
|
-
["8287-5", ["cm", "[in_i]"]],
|
|
2756
|
-
// Head circumference (Occipital-frontal, by tape)
|
|
2757
|
-
["9843-4", ["cm", "[in_i]"]],
|
|
2758
|
-
// Head circumference
|
|
2759
|
-
["8310-5", ["Cel", "[degF]"]],
|
|
2760
|
-
// Body temperature
|
|
2761
|
-
["8867-4", ["/min"]],
|
|
2762
|
-
// Heart rate
|
|
2763
|
-
["9279-1", ["/min"]],
|
|
2764
|
-
// Respiratory rate
|
|
2765
|
-
["8480-6", ["mm[Hg]"]],
|
|
2766
|
-
// Systolic blood pressure
|
|
2767
|
-
["8462-4", ["mm[Hg]"]],
|
|
2768
|
-
// Diastolic blood pressure
|
|
2769
|
-
["2708-6", ["%"]],
|
|
2770
|
-
// Oxygen saturation in Arterial blood
|
|
2771
|
-
["59408-5", ["%"]],
|
|
2772
|
-
// Oxygen saturation (SpO2) by pulse oximetry
|
|
2773
|
-
["39156-5", ["kg/m2"]]
|
|
2774
|
-
// Body mass index
|
|
2775
|
-
]);
|
|
2776
|
-
function requiredVitalSignUnits(loincCode) {
|
|
2777
|
-
return VITAL_SIGN_UNITS.get(loincCode);
|
|
2778
|
-
}
|
|
2779
|
-
function decimalValue(node) {
|
|
2780
|
-
if (node === void 0 || !isPrimitive(node)) return void 0;
|
|
2781
|
-
const { value } = node;
|
|
2782
|
-
if (typeof value === "object") return value;
|
|
2783
|
-
return typeof value === "string" ? decimalFromLexical(value) : void 0;
|
|
2784
|
-
}
|
|
2785
|
-
function readQuantity(node) {
|
|
2786
|
-
if (node === void 0 || node.kind !== "complex") return void 0;
|
|
2787
|
-
const q = node;
|
|
2788
|
-
return {
|
|
2789
|
-
value: decimalValue(getProperty(q, "value")),
|
|
2790
|
-
comparator: primitiveString(getProperty(q, "comparator")),
|
|
2791
|
-
unit: primitiveString(getProperty(q, "unit")),
|
|
2792
|
-
system: primitiveString(getProperty(q, "system")),
|
|
2793
|
-
code: primitiveString(getProperty(q, "code"))
|
|
2794
|
-
};
|
|
2795
|
-
}
|
|
2796
|
-
function validateUcumShape(code) {
|
|
2797
|
-
if (code.length === 0) return "invalid";
|
|
2798
|
-
const skeleton = code.replace(/\{[^{}]*\}/g, "");
|
|
2799
|
-
if (skeleton.includes("{") || skeleton.includes("}")) return "invalid";
|
|
2800
|
-
if (/\s/.test(skeleton)) return "invalid";
|
|
2801
|
-
const pairs = { "]": "[", ")": "(" };
|
|
2802
|
-
const stack = [];
|
|
2803
|
-
for (const ch of skeleton) {
|
|
2804
|
-
if (ch === "[" || ch === "(") stack.push(ch);
|
|
2805
|
-
else if (ch === "]" || ch === ")") {
|
|
2806
|
-
if (stack.pop() !== pairs[ch]) return "invalid";
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
return stack.length === 0 ? "ok" : "invalid";
|
|
2810
|
-
}
|
|
2811
|
-
|
|
2812
3419
|
// src/quantity/dose.ts
|
|
2813
3420
|
function dosageProperty(rt) {
|
|
2814
3421
|
if (rt === "MedicationRequest") return "dosageInstruction";
|
|
@@ -2849,60 +3456,6 @@ function readMedicationDoses(resource, rt) {
|
|
|
2849
3456
|
return doses;
|
|
2850
3457
|
}
|
|
2851
3458
|
|
|
2852
|
-
// src/quantity/value.ts
|
|
2853
|
-
var OBSERVATION_VALUE_TYPES = [
|
|
2854
|
-
"Quantity",
|
|
2855
|
-
"CodeableConcept",
|
|
2856
|
-
"String",
|
|
2857
|
-
"Boolean",
|
|
2858
|
-
"Integer",
|
|
2859
|
-
"Range",
|
|
2860
|
-
"Ratio",
|
|
2861
|
-
"SampledData",
|
|
2862
|
-
"Time",
|
|
2863
|
-
"DateTime",
|
|
2864
|
-
"Period"
|
|
2865
|
-
];
|
|
2866
|
-
function presentValues(node) {
|
|
2867
|
-
const byName = new Map(node.properties.map((p) => [p.name, p.value]));
|
|
2868
|
-
const out = [];
|
|
2869
|
-
for (const type of OBSERVATION_VALUE_TYPES) {
|
|
2870
|
-
const value = byName.get(`value${type}`);
|
|
2871
|
-
if (value !== void 0) out.push({ type, node: value });
|
|
2872
|
-
}
|
|
2873
|
-
return out;
|
|
2874
|
-
}
|
|
2875
|
-
function readObservationValue(node) {
|
|
2876
|
-
const [first, ...rest] = presentValues(node);
|
|
2877
|
-
if (first === void 0) return void 0;
|
|
2878
|
-
return {
|
|
2879
|
-
type: first.type,
|
|
2880
|
-
property: `value${first.type}`,
|
|
2881
|
-
node: first.node,
|
|
2882
|
-
quantity: first.type === "Quantity" ? readQuantity(first.node) : void 0,
|
|
2883
|
-
ambiguous: rest.map((r) => r.type)
|
|
2884
|
-
};
|
|
2885
|
-
}
|
|
2886
|
-
function readReferenceRanges(observation) {
|
|
2887
|
-
const node = getProperty(observation, "referenceRange");
|
|
2888
|
-
if (node === void 0) return [];
|
|
2889
|
-
const items = isList(node) ? node.items : [node];
|
|
2890
|
-
const ranges = [];
|
|
2891
|
-
for (const item of items) {
|
|
2892
|
-
if (item.kind !== "complex") continue;
|
|
2893
|
-
ranges.push({
|
|
2894
|
-
low: readQuantity(getProperty(item, "low")),
|
|
2895
|
-
high: readQuantity(getProperty(item, "high")),
|
|
2896
|
-
type: codingsOf(getProperty(item, "type")),
|
|
2897
|
-
text: primitiveString(getProperty(item, "text"))
|
|
2898
|
-
});
|
|
2899
|
-
}
|
|
2900
|
-
return ranges;
|
|
2901
|
-
}
|
|
2902
|
-
function readInterpretations(observation) {
|
|
2903
|
-
return codingsOf(getProperty(observation, "interpretation"));
|
|
2904
|
-
}
|
|
2905
|
-
|
|
2906
3459
|
// src/validate/quantity.ts
|
|
2907
3460
|
function collectQuantityIssues(resource, rt) {
|
|
2908
3461
|
const issues = [];
|
|
@@ -2912,13 +3465,14 @@ function collectQuantityIssues(resource, rt) {
|
|
|
2912
3465
|
forEachComponent(resource, (component, path) => {
|
|
2913
3466
|
checkValueChoice(component, path, vital, issues);
|
|
2914
3467
|
});
|
|
2915
|
-
|
|
2916
|
-
}
|
|
2917
|
-
if (rt === "MedicationRequest" || rt === "MedicationStatement") {
|
|
3468
|
+
} else if (rt === "MedicationRequest" || rt === "MedicationStatement") {
|
|
2918
3469
|
for (const { node, path } of locateDoseQuantities(resource, rt)) {
|
|
2919
3470
|
checkUcumShape(readQuantity(node), path, issues);
|
|
2920
3471
|
}
|
|
2921
3472
|
}
|
|
3473
|
+
for (const location of arrayWrappedValueChoices(resource, rootPath(rt))) {
|
|
3474
|
+
issues.push(validationIssue("ARRAY_WRAPPED_CHOICE", ISSUE_SEVERITIES.ERROR, location));
|
|
3475
|
+
}
|
|
2922
3476
|
return issues;
|
|
2923
3477
|
}
|
|
2924
3478
|
function isVitalSign(observation) {
|
|
@@ -3153,13 +3707,13 @@ function buildBindingRegistry(extra = []) {
|
|
|
3153
3707
|
|
|
3154
3708
|
// src/validate/terminology.ts
|
|
3155
3709
|
function collectTerminologyIssues(resource, rt, options = {}) {
|
|
3156
|
-
const
|
|
3710
|
+
const registry2 = buildBindingRegistry(options.bindings ?? []);
|
|
3157
3711
|
const service = options.terminology;
|
|
3158
3712
|
const issues = [];
|
|
3159
3713
|
const root = rootPath(rt);
|
|
3160
3714
|
for (const property of resource.properties) {
|
|
3161
3715
|
if (property.name === "resourceType") continue;
|
|
3162
|
-
const binding =
|
|
3716
|
+
const binding = registry2(`${rt}.${property.name}`);
|
|
3163
3717
|
if (binding === void 0) continue;
|
|
3164
3718
|
for (const coding of locatedCodings(property.value, childPath(root, property.name))) {
|
|
3165
3719
|
checkCoding(coding, binding, service, issues);
|
|
@@ -3211,13 +3765,20 @@ function checkCoding(coding, binding, service, issues) {
|
|
|
3211
3765
|
return;
|
|
3212
3766
|
}
|
|
3213
3767
|
if (service === void 0 || code === void 0) return;
|
|
3214
|
-
const
|
|
3215
|
-
if (
|
|
3768
|
+
const answer = service.validateCode({ valueSet: binding.valueSet, system, code });
|
|
3769
|
+
if (answer.membership !== "not-in") return;
|
|
3216
3770
|
const severity = notInSeverity(binding.strength);
|
|
3217
3771
|
if (severity !== void 0) {
|
|
3218
|
-
issues.push(
|
|
3772
|
+
issues.push(
|
|
3773
|
+
validationIssue("CODE_NOT_IN_VALUESET", severity, path, void 0, declaredRelease(answer))
|
|
3774
|
+
);
|
|
3219
3775
|
}
|
|
3220
3776
|
}
|
|
3777
|
+
function declaredRelease(answer) {
|
|
3778
|
+
const declared = answer.systemVersion;
|
|
3779
|
+
if (typeof declared !== "string" || declared.trim() === "") return { declared: false };
|
|
3780
|
+
return { declared: true, version: declared };
|
|
3781
|
+
}
|
|
3221
3782
|
function systemUnexpectedSeverity(strength) {
|
|
3222
3783
|
switch (strength) {
|
|
3223
3784
|
case "required":
|
|
@@ -3335,11 +3896,249 @@ var OBSERVATION_ELEMENTS = {
|
|
|
3335
3896
|
referenceRange: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] },
|
|
3336
3897
|
hasMember: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
3337
3898
|
derivedFrom: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
3338
|
-
component: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] }
|
|
3899
|
+
component: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] }
|
|
3900
|
+
};
|
|
3901
|
+
var DIAGNOSTIC_REPORT_STATUS = [
|
|
3902
|
+
"registered",
|
|
3903
|
+
"partial",
|
|
3904
|
+
"preliminary",
|
|
3905
|
+
"final",
|
|
3906
|
+
"amended",
|
|
3907
|
+
"corrected",
|
|
3908
|
+
"appended",
|
|
3909
|
+
"cancelled",
|
|
3910
|
+
"entered-in-error",
|
|
3911
|
+
"unknown"
|
|
3912
|
+
];
|
|
3913
|
+
var DIAGNOSTIC_REPORT_ELEMENTS = {
|
|
3914
|
+
identifier: { min: 0, max: UNBOUNDED, types: ["Identifier"] },
|
|
3915
|
+
basedOn: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
3916
|
+
status: {
|
|
3917
|
+
min: 1,
|
|
3918
|
+
max: 1,
|
|
3919
|
+
types: ["code"],
|
|
3920
|
+
binding: { strength: "required", codes: [...DIAGNOSTIC_REPORT_STATUS] }
|
|
3921
|
+
},
|
|
3922
|
+
category: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
3923
|
+
code: { min: 1, max: 1, types: ["CodeableConcept"] },
|
|
3924
|
+
subject: { min: 0, max: 1, types: ["Reference"] },
|
|
3925
|
+
encounter: { min: 0, max: 1, types: ["Reference"] },
|
|
3926
|
+
effective: { min: 0, max: 1, types: ["dateTime", "Period"] },
|
|
3927
|
+
issued: { min: 0, max: 1, types: ["instant"] },
|
|
3928
|
+
performer: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
3929
|
+
resultsInterpreter: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
3930
|
+
specimen: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
3931
|
+
result: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
3932
|
+
imagingStudy: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
3933
|
+
media: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] },
|
|
3934
|
+
conclusion: { min: 0, max: 1, types: ["string"] },
|
|
3935
|
+
conclusionCode: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
3936
|
+
presentedForm: { min: 0, max: UNBOUNDED, types: ["Attachment"] }
|
|
3937
|
+
};
|
|
3938
|
+
var CONDITION_ELEMENTS = {
|
|
3939
|
+
identifier: { min: 0, max: UNBOUNDED, types: ["Identifier"] },
|
|
3940
|
+
clinicalStatus: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
3941
|
+
verificationStatus: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
3942
|
+
category: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
3943
|
+
severity: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
3944
|
+
code: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
3945
|
+
bodySite: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
3946
|
+
subject: { min: 1, max: 1, types: ["Reference"] },
|
|
3947
|
+
encounter: { min: 0, max: 1, types: ["Reference"] },
|
|
3948
|
+
onset: { min: 0, max: 1, types: ["dateTime", "Age", "Period", "Range", "string"] },
|
|
3949
|
+
abatement: { min: 0, max: 1, types: ["dateTime", "Age", "Period", "Range", "string"] },
|
|
3950
|
+
recordedDate: { min: 0, max: 1, types: ["dateTime"] },
|
|
3951
|
+
recorder: { min: 0, max: 1, types: ["Reference"] },
|
|
3952
|
+
asserter: { min: 0, max: 1, types: ["Reference"] },
|
|
3953
|
+
stage: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] },
|
|
3954
|
+
evidence: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] },
|
|
3955
|
+
note: { min: 0, max: UNBOUNDED, types: ["Annotation"] }
|
|
3956
|
+
};
|
|
3957
|
+
var MEDICATION_REQUEST_STATUS = [
|
|
3958
|
+
"active",
|
|
3959
|
+
"on-hold",
|
|
3960
|
+
"cancelled",
|
|
3961
|
+
"completed",
|
|
3962
|
+
"entered-in-error",
|
|
3963
|
+
"stopped",
|
|
3964
|
+
"draft",
|
|
3965
|
+
"unknown"
|
|
3966
|
+
];
|
|
3967
|
+
var MEDICATION_REQUEST_INTENT = [
|
|
3968
|
+
"proposal",
|
|
3969
|
+
"plan",
|
|
3970
|
+
"order",
|
|
3971
|
+
"original-order",
|
|
3972
|
+
"reflex-order",
|
|
3973
|
+
"filler-order",
|
|
3974
|
+
"instance-order",
|
|
3975
|
+
"option"
|
|
3976
|
+
];
|
|
3977
|
+
var REQUEST_PRIORITY = ["routine", "urgent", "asap", "stat"];
|
|
3978
|
+
var MEDICATION_REQUEST_ELEMENTS = {
|
|
3979
|
+
identifier: { min: 0, max: UNBOUNDED, types: ["Identifier"] },
|
|
3980
|
+
status: {
|
|
3981
|
+
min: 1,
|
|
3982
|
+
max: 1,
|
|
3983
|
+
types: ["code"],
|
|
3984
|
+
binding: { strength: "required", codes: [...MEDICATION_REQUEST_STATUS] }
|
|
3985
|
+
},
|
|
3986
|
+
statusReason: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
3987
|
+
intent: {
|
|
3988
|
+
min: 1,
|
|
3989
|
+
max: 1,
|
|
3990
|
+
types: ["code"],
|
|
3991
|
+
binding: { strength: "required", codes: [...MEDICATION_REQUEST_INTENT] }
|
|
3992
|
+
},
|
|
3993
|
+
category: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
3994
|
+
priority: {
|
|
3995
|
+
min: 0,
|
|
3996
|
+
max: 1,
|
|
3997
|
+
types: ["code"],
|
|
3998
|
+
binding: { strength: "required", codes: [...REQUEST_PRIORITY] }
|
|
3999
|
+
},
|
|
4000
|
+
doNotPerform: { min: 0, max: 1, types: ["boolean"] },
|
|
4001
|
+
reported: { min: 0, max: 1, types: ["boolean", "Reference"] },
|
|
4002
|
+
medication: { min: 1, max: 1, types: ["CodeableConcept", "Reference"] },
|
|
4003
|
+
subject: { min: 1, max: 1, types: ["Reference"] },
|
|
4004
|
+
encounter: { min: 0, max: 1, types: ["Reference"] },
|
|
4005
|
+
supportingInformation: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4006
|
+
authoredOn: { min: 0, max: 1, types: ["dateTime"] },
|
|
4007
|
+
requester: { min: 0, max: 1, types: ["Reference"] },
|
|
4008
|
+
performer: { min: 0, max: 1, types: ["Reference"] },
|
|
4009
|
+
performerType: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4010
|
+
recorder: { min: 0, max: 1, types: ["Reference"] },
|
|
4011
|
+
reasonCode: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
4012
|
+
reasonReference: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4013
|
+
instantiatesCanonical: { min: 0, max: UNBOUNDED, types: ["canonical"] },
|
|
4014
|
+
instantiatesUri: { min: 0, max: UNBOUNDED, types: ["uri"] },
|
|
4015
|
+
basedOn: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4016
|
+
groupIdentifier: { min: 0, max: 1, types: ["Identifier"] },
|
|
4017
|
+
courseOfTherapyType: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4018
|
+
insurance: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4019
|
+
note: { min: 0, max: UNBOUNDED, types: ["Annotation"] },
|
|
4020
|
+
dosageInstruction: { min: 0, max: UNBOUNDED, types: ["Dosage"] },
|
|
4021
|
+
dispenseRequest: { min: 0, max: 1, types: ["BackboneElement"] },
|
|
4022
|
+
substitution: { min: 0, max: 1, types: ["BackboneElement"] },
|
|
4023
|
+
priorPrescription: { min: 0, max: 1, types: ["Reference"] },
|
|
4024
|
+
detectedIssue: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4025
|
+
eventHistory: { min: 0, max: UNBOUNDED, types: ["Reference"] }
|
|
4026
|
+
};
|
|
4027
|
+
var ALLERGY_INTOLERANCE_TYPE = ["allergy", "intolerance"];
|
|
4028
|
+
var ALLERGY_INTOLERANCE_CATEGORY = ["food", "medication", "environment", "biologic"];
|
|
4029
|
+
var ALLERGY_INTOLERANCE_CRITICALITY = ["low", "high", "unable-to-assess"];
|
|
4030
|
+
var ALLERGY_INTOLERANCE_ELEMENTS = {
|
|
4031
|
+
identifier: { min: 0, max: UNBOUNDED, types: ["Identifier"] },
|
|
4032
|
+
clinicalStatus: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4033
|
+
verificationStatus: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4034
|
+
type: {
|
|
4035
|
+
min: 0,
|
|
4036
|
+
max: 1,
|
|
4037
|
+
types: ["code"],
|
|
4038
|
+
binding: { strength: "required", codes: [...ALLERGY_INTOLERANCE_TYPE] }
|
|
4039
|
+
},
|
|
4040
|
+
category: {
|
|
4041
|
+
min: 0,
|
|
4042
|
+
max: UNBOUNDED,
|
|
4043
|
+
types: ["code"],
|
|
4044
|
+
binding: { strength: "required", codes: [...ALLERGY_INTOLERANCE_CATEGORY] }
|
|
4045
|
+
},
|
|
4046
|
+
criticality: {
|
|
4047
|
+
min: 0,
|
|
4048
|
+
max: 1,
|
|
4049
|
+
types: ["code"],
|
|
4050
|
+
binding: { strength: "required", codes: [...ALLERGY_INTOLERANCE_CRITICALITY] }
|
|
4051
|
+
},
|
|
4052
|
+
code: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4053
|
+
patient: { min: 1, max: 1, types: ["Reference"] },
|
|
4054
|
+
encounter: { min: 0, max: 1, types: ["Reference"] },
|
|
4055
|
+
onset: { min: 0, max: 1, types: ["dateTime", "Age", "Period", "Range", "string"] },
|
|
4056
|
+
recordedDate: { min: 0, max: 1, types: ["dateTime"] },
|
|
4057
|
+
recorder: { min: 0, max: 1, types: ["Reference"] },
|
|
4058
|
+
asserter: { min: 0, max: 1, types: ["Reference"] },
|
|
4059
|
+
lastOccurrence: { min: 0, max: 1, types: ["dateTime"] },
|
|
4060
|
+
note: { min: 0, max: UNBOUNDED, types: ["Annotation"] },
|
|
4061
|
+
reaction: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] }
|
|
4062
|
+
};
|
|
4063
|
+
var IMMUNIZATION_STATUS = ["completed", "entered-in-error", "not-done"];
|
|
4064
|
+
var IMMUNIZATION_ELEMENTS = {
|
|
4065
|
+
identifier: { min: 0, max: UNBOUNDED, types: ["Identifier"] },
|
|
4066
|
+
status: {
|
|
4067
|
+
min: 1,
|
|
4068
|
+
max: 1,
|
|
4069
|
+
types: ["code"],
|
|
4070
|
+
binding: { strength: "required", codes: [...IMMUNIZATION_STATUS] }
|
|
4071
|
+
},
|
|
4072
|
+
statusReason: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4073
|
+
vaccineCode: { min: 1, max: 1, types: ["CodeableConcept"] },
|
|
4074
|
+
patient: { min: 1, max: 1, types: ["Reference"] },
|
|
4075
|
+
encounter: { min: 0, max: 1, types: ["Reference"] },
|
|
4076
|
+
occurrence: { min: 1, max: 1, types: ["dateTime", "string"] },
|
|
4077
|
+
recorded: { min: 0, max: 1, types: ["dateTime"] },
|
|
4078
|
+
primarySource: { min: 0, max: 1, types: ["boolean"] },
|
|
4079
|
+
reportOrigin: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4080
|
+
location: { min: 0, max: 1, types: ["Reference"] },
|
|
4081
|
+
manufacturer: { min: 0, max: 1, types: ["Reference"] },
|
|
4082
|
+
lotNumber: { min: 0, max: 1, types: ["string"] },
|
|
4083
|
+
expirationDate: { min: 0, max: 1, types: ["date"] },
|
|
4084
|
+
site: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4085
|
+
route: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4086
|
+
doseQuantity: { min: 0, max: 1, types: ["Quantity"] },
|
|
4087
|
+
performer: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] },
|
|
4088
|
+
note: { min: 0, max: UNBOUNDED, types: ["Annotation"] },
|
|
4089
|
+
reasonCode: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
4090
|
+
reasonReference: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4091
|
+
isSubpotent: { min: 0, max: 1, types: ["boolean"] },
|
|
4092
|
+
subpotentReason: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
4093
|
+
education: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] },
|
|
4094
|
+
programEligibility: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
4095
|
+
fundingSource: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4096
|
+
reaction: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] },
|
|
4097
|
+
protocolApplied: { min: 0, max: UNBOUNDED, types: ["BackboneElement"] }
|
|
4098
|
+
};
|
|
4099
|
+
var MEDICATION_STATEMENT_STATUS = [
|
|
4100
|
+
"active",
|
|
4101
|
+
"completed",
|
|
4102
|
+
"entered-in-error",
|
|
4103
|
+
"intended",
|
|
4104
|
+
"stopped",
|
|
4105
|
+
"on-hold",
|
|
4106
|
+
"unknown",
|
|
4107
|
+
"not-taken"
|
|
4108
|
+
];
|
|
4109
|
+
var MEDICATION_STATEMENT_ELEMENTS = {
|
|
4110
|
+
identifier: { min: 0, max: UNBOUNDED, types: ["Identifier"] },
|
|
4111
|
+
basedOn: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4112
|
+
partOf: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4113
|
+
status: {
|
|
4114
|
+
min: 1,
|
|
4115
|
+
max: 1,
|
|
4116
|
+
types: ["code"],
|
|
4117
|
+
binding: { strength: "required", codes: [...MEDICATION_STATEMENT_STATUS] }
|
|
4118
|
+
},
|
|
4119
|
+
statusReason: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
4120
|
+
category: { min: 0, max: 1, types: ["CodeableConcept"] },
|
|
4121
|
+
medication: { min: 1, max: 1, types: ["CodeableConcept", "Reference"] },
|
|
4122
|
+
subject: { min: 1, max: 1, types: ["Reference"] },
|
|
4123
|
+
context: { min: 0, max: 1, types: ["Reference"] },
|
|
4124
|
+
effective: { min: 0, max: 1, types: ["dateTime", "Period"] },
|
|
4125
|
+
dateAsserted: { min: 0, max: 1, types: ["dateTime"] },
|
|
4126
|
+
informationSource: { min: 0, max: 1, types: ["Reference"] },
|
|
4127
|
+
derivedFrom: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4128
|
+
reasonCode: { min: 0, max: UNBOUNDED, types: ["CodeableConcept"] },
|
|
4129
|
+
reasonReference: { min: 0, max: UNBOUNDED, types: ["Reference"] },
|
|
4130
|
+
note: { min: 0, max: UNBOUNDED, types: ["Annotation"] },
|
|
4131
|
+
dosage: { min: 0, max: UNBOUNDED, types: ["Dosage"] }
|
|
3339
4132
|
};
|
|
3340
4133
|
var BUILTIN_SCHEMAS = [
|
|
3341
4134
|
{ type: "Patient", elements: PATIENT_ELEMENTS },
|
|
3342
|
-
{ type: "Observation", elements: OBSERVATION_ELEMENTS }
|
|
4135
|
+
{ type: "Observation", elements: OBSERVATION_ELEMENTS },
|
|
4136
|
+
{ type: "DiagnosticReport", elements: DIAGNOSTIC_REPORT_ELEMENTS },
|
|
4137
|
+
{ type: "Condition", elements: CONDITION_ELEMENTS },
|
|
4138
|
+
{ type: "MedicationRequest", elements: MEDICATION_REQUEST_ELEMENTS },
|
|
4139
|
+
{ type: "AllergyIntolerance", elements: ALLERGY_INTOLERANCE_ELEMENTS },
|
|
4140
|
+
{ type: "Immunization", elements: IMMUNIZATION_ELEMENTS },
|
|
4141
|
+
{ type: "MedicationStatement", elements: MEDICATION_STATEMENT_ELEMENTS }
|
|
3343
4142
|
];
|
|
3344
4143
|
function withBase(schema) {
|
|
3345
4144
|
return { type: schema.type, elements: { ...BASE_ELEMENTS, ...schema.elements } };
|
|
@@ -3748,6 +4547,370 @@ var UnsupportedFhirPathError = class extends Error {
|
|
|
3748
4547
|
}
|
|
3749
4548
|
};
|
|
3750
4549
|
|
|
4550
|
+
// src/fhirpath/matches.ts
|
|
4551
|
+
var META = /* @__PURE__ */ new Set([
|
|
4552
|
+
"\\",
|
|
4553
|
+
"^",
|
|
4554
|
+
"$",
|
|
4555
|
+
".",
|
|
4556
|
+
"|",
|
|
4557
|
+
"?",
|
|
4558
|
+
"*",
|
|
4559
|
+
"+",
|
|
4560
|
+
"(",
|
|
4561
|
+
")",
|
|
4562
|
+
"[",
|
|
4563
|
+
"]",
|
|
4564
|
+
"{",
|
|
4565
|
+
"}"
|
|
4566
|
+
]);
|
|
4567
|
+
var CLASS_ESCAPABLE = /* @__PURE__ */ new Set(["\\", "]", "[", "-", "^"]);
|
|
4568
|
+
var MAX_REPEAT = 1e3;
|
|
4569
|
+
function refuse() {
|
|
4570
|
+
throw new UnsupportedFhirPathError("matches() pattern outside the portable subset");
|
|
4571
|
+
}
|
|
4572
|
+
var PatternCheck = class {
|
|
4573
|
+
constructor(chars) {
|
|
4574
|
+
this.chars = chars;
|
|
4575
|
+
}
|
|
4576
|
+
chars;
|
|
4577
|
+
pos = 0;
|
|
4578
|
+
run() {
|
|
4579
|
+
this.alternation();
|
|
4580
|
+
if (this.pos !== this.chars.length) refuse();
|
|
4581
|
+
}
|
|
4582
|
+
peek() {
|
|
4583
|
+
return this.chars[this.pos];
|
|
4584
|
+
}
|
|
4585
|
+
alternation() {
|
|
4586
|
+
let shape = this.sequence();
|
|
4587
|
+
let alternates = false;
|
|
4588
|
+
while (this.peek() === "|") {
|
|
4589
|
+
this.pos += 1;
|
|
4590
|
+
alternates = true;
|
|
4591
|
+
const next = this.sequence();
|
|
4592
|
+
shape = { quantified: shape.quantified || next.quantified, alternates: true };
|
|
4593
|
+
}
|
|
4594
|
+
return { quantified: shape.quantified, alternates: alternates || shape.alternates };
|
|
4595
|
+
}
|
|
4596
|
+
sequence() {
|
|
4597
|
+
let quantified = false;
|
|
4598
|
+
let alternates = false;
|
|
4599
|
+
for (let c = this.peek(); c !== void 0 && c !== "|" && c !== ")"; c = this.peek()) {
|
|
4600
|
+
if (c === "^" || c === "$") {
|
|
4601
|
+
this.pos += 1;
|
|
4602
|
+
if (this.isQuantifierStart()) refuse();
|
|
4603
|
+
continue;
|
|
4604
|
+
}
|
|
4605
|
+
const atom = this.atom();
|
|
4606
|
+
if (this.isQuantifierStart()) {
|
|
4607
|
+
if (atom.quantified || atom.alternates) refuse();
|
|
4608
|
+
this.quantifier();
|
|
4609
|
+
quantified = true;
|
|
4610
|
+
}
|
|
4611
|
+
quantified ||= atom.quantified;
|
|
4612
|
+
alternates ||= atom.alternates;
|
|
4613
|
+
}
|
|
4614
|
+
return { quantified, alternates };
|
|
4615
|
+
}
|
|
4616
|
+
isQuantifierStart() {
|
|
4617
|
+
const c = this.peek();
|
|
4618
|
+
return c === "*" || c === "+" || c === "?" || c === "{";
|
|
4619
|
+
}
|
|
4620
|
+
atom() {
|
|
4621
|
+
const c = this.peek();
|
|
4622
|
+
if (c === void 0) refuse();
|
|
4623
|
+
if (c === "(") {
|
|
4624
|
+
this.pos += 1;
|
|
4625
|
+
if (this.peek() === "?") refuse();
|
|
4626
|
+
const inner = this.alternation();
|
|
4627
|
+
if (this.peek() !== ")") refuse();
|
|
4628
|
+
this.pos += 1;
|
|
4629
|
+
return inner;
|
|
4630
|
+
}
|
|
4631
|
+
if (c === "[") {
|
|
4632
|
+
this.characterClass();
|
|
4633
|
+
return { quantified: false, alternates: false };
|
|
4634
|
+
}
|
|
4635
|
+
if (c === "\\") {
|
|
4636
|
+
const escaped = this.chars[this.pos + 1];
|
|
4637
|
+
if (escaped === void 0 || !(META.has(escaped) || escaped === "/")) refuse();
|
|
4638
|
+
this.pos += 2;
|
|
4639
|
+
return { quantified: false, alternates: false };
|
|
4640
|
+
}
|
|
4641
|
+
if (c === ".") {
|
|
4642
|
+
this.pos += 1;
|
|
4643
|
+
return { quantified: false, alternates: false };
|
|
4644
|
+
}
|
|
4645
|
+
if (META.has(c)) refuse();
|
|
4646
|
+
this.pos += 1;
|
|
4647
|
+
return { quantified: false, alternates: false };
|
|
4648
|
+
}
|
|
4649
|
+
quantifier() {
|
|
4650
|
+
const c = this.peek();
|
|
4651
|
+
this.pos += 1;
|
|
4652
|
+
if (c === "{") {
|
|
4653
|
+
const low = this.count();
|
|
4654
|
+
let high = low;
|
|
4655
|
+
if (this.peek() === ",") {
|
|
4656
|
+
this.pos += 1;
|
|
4657
|
+
high = this.peek() === "}" ? MAX_REPEAT : this.count();
|
|
4658
|
+
}
|
|
4659
|
+
if (this.peek() !== "}" || high < low) refuse();
|
|
4660
|
+
this.pos += 1;
|
|
4661
|
+
}
|
|
4662
|
+
if (this.isQuantifierStart()) refuse();
|
|
4663
|
+
}
|
|
4664
|
+
count() {
|
|
4665
|
+
let digits = "";
|
|
4666
|
+
for (let c = this.peek(); c !== void 0 && c >= "0" && c <= "9"; c = this.peek()) {
|
|
4667
|
+
digits += c;
|
|
4668
|
+
this.pos += 1;
|
|
4669
|
+
}
|
|
4670
|
+
const value = digits === "" ? Number.NaN : Number(digits);
|
|
4671
|
+
if (!(value <= MAX_REPEAT)) refuse();
|
|
4672
|
+
return value;
|
|
4673
|
+
}
|
|
4674
|
+
characterClass() {
|
|
4675
|
+
this.pos += 1;
|
|
4676
|
+
if (this.peek() === "^") this.pos += 1;
|
|
4677
|
+
let members = 0;
|
|
4678
|
+
while (this.peek() !== "]") {
|
|
4679
|
+
this.classCharacter();
|
|
4680
|
+
if (this.peek() === "-" && this.chars[this.pos + 1] !== "]") {
|
|
4681
|
+
this.pos += 1;
|
|
4682
|
+
this.classCharacter();
|
|
4683
|
+
}
|
|
4684
|
+
members += 1;
|
|
4685
|
+
}
|
|
4686
|
+
if (members === 0) refuse();
|
|
4687
|
+
this.pos += 1;
|
|
4688
|
+
}
|
|
4689
|
+
classCharacter() {
|
|
4690
|
+
const c = this.peek();
|
|
4691
|
+
if (c === void 0 || c === "[" || c === "-") refuse();
|
|
4692
|
+
if (c === "&" && this.chars[this.pos + 1] === "&") refuse();
|
|
4693
|
+
if (c === "\\") {
|
|
4694
|
+
const escaped = this.chars[this.pos + 1];
|
|
4695
|
+
if (escaped === void 0 || !CLASS_ESCAPABLE.has(escaped)) refuse();
|
|
4696
|
+
this.pos += 2;
|
|
4697
|
+
return;
|
|
4698
|
+
}
|
|
4699
|
+
this.pos += 1;
|
|
4700
|
+
}
|
|
4701
|
+
};
|
|
4702
|
+
function compileMatchesPattern(pattern) {
|
|
4703
|
+
new PatternCheck([...pattern]).run();
|
|
4704
|
+
try {
|
|
4705
|
+
return new RegExp(pattern, "su");
|
|
4706
|
+
} catch {
|
|
4707
|
+
return refuse();
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
// src/fhirpath/types.ts
|
|
4712
|
+
var FHIR_PRIMITIVES = /* @__PURE__ */ new Set([
|
|
4713
|
+
"boolean",
|
|
4714
|
+
"integer",
|
|
4715
|
+
"string",
|
|
4716
|
+
"decimal",
|
|
4717
|
+
"uri",
|
|
4718
|
+
"url",
|
|
4719
|
+
"canonical",
|
|
4720
|
+
"base64Binary",
|
|
4721
|
+
"instant",
|
|
4722
|
+
"date",
|
|
4723
|
+
"dateTime",
|
|
4724
|
+
"time",
|
|
4725
|
+
"code",
|
|
4726
|
+
"oid",
|
|
4727
|
+
"id",
|
|
4728
|
+
"markdown",
|
|
4729
|
+
"unsignedInt",
|
|
4730
|
+
"positiveInt",
|
|
4731
|
+
"uuid"
|
|
4732
|
+
]);
|
|
4733
|
+
var FHIR_COMPLEX = /* @__PURE__ */ new Set([
|
|
4734
|
+
"Address",
|
|
4735
|
+
"Age",
|
|
4736
|
+
"Annotation",
|
|
4737
|
+
"Attachment",
|
|
4738
|
+
"CodeableConcept",
|
|
4739
|
+
"Coding",
|
|
4740
|
+
"ContactPoint",
|
|
4741
|
+
"Count",
|
|
4742
|
+
"Distance",
|
|
4743
|
+
"Duration",
|
|
4744
|
+
"Extension",
|
|
4745
|
+
"HumanName",
|
|
4746
|
+
"Identifier",
|
|
4747
|
+
"Money",
|
|
4748
|
+
"Narrative",
|
|
4749
|
+
"Period",
|
|
4750
|
+
"Quantity",
|
|
4751
|
+
"Range",
|
|
4752
|
+
"Ratio",
|
|
4753
|
+
"Reference",
|
|
4754
|
+
"SampledData",
|
|
4755
|
+
"Signature",
|
|
4756
|
+
"Timing"
|
|
4757
|
+
]);
|
|
4758
|
+
var SPECIALIZES = /* @__PURE__ */ new Map([
|
|
4759
|
+
["Age", "Quantity"],
|
|
4760
|
+
["Count", "Quantity"],
|
|
4761
|
+
["Distance", "Quantity"],
|
|
4762
|
+
["Duration", "Quantity"]
|
|
4763
|
+
]);
|
|
4764
|
+
var FHIR_ABSTRACT = /* @__PURE__ */ new Set(["Resource"]);
|
|
4765
|
+
var SYSTEM_TYPES = /* @__PURE__ */ new Set([
|
|
4766
|
+
"Boolean",
|
|
4767
|
+
"String",
|
|
4768
|
+
"Integer",
|
|
4769
|
+
"Decimal",
|
|
4770
|
+
"Date",
|
|
4771
|
+
"DateTime",
|
|
4772
|
+
"Time",
|
|
4773
|
+
"Quantity"
|
|
4774
|
+
]);
|
|
4775
|
+
var registry;
|
|
4776
|
+
function tables() {
|
|
4777
|
+
registry ??= buildRegistry();
|
|
4778
|
+
return registry;
|
|
4779
|
+
}
|
|
4780
|
+
function fhirKind(name) {
|
|
4781
|
+
if (FHIR_PRIMITIVES.has(name)) return "primitive";
|
|
4782
|
+
if (FHIR_COMPLEX.has(name)) return "complex";
|
|
4783
|
+
if (FHIR_ABSTRACT.has(name)) return "abstract";
|
|
4784
|
+
if (tables()(name) !== void 0) return "resource";
|
|
4785
|
+
return void 0;
|
|
4786
|
+
}
|
|
4787
|
+
function resolveTypeSpecifier(specifier) {
|
|
4788
|
+
const dot = specifier.indexOf(".");
|
|
4789
|
+
if (dot >= 0) {
|
|
4790
|
+
const model = specifier.slice(0, dot);
|
|
4791
|
+
const name = specifier.slice(dot + 1);
|
|
4792
|
+
if (model === "FHIR" && fhirKind(name) !== void 0) return { model, name };
|
|
4793
|
+
if (model === "System" && SYSTEM_TYPES.has(name)) return { model, name };
|
|
4794
|
+
return void 0;
|
|
4795
|
+
}
|
|
4796
|
+
if (fhirKind(specifier) !== void 0) return { model: "FHIR", name: specifier };
|
|
4797
|
+
if (SYSTEM_TYPES.has(specifier)) return { model: "System", name: specifier };
|
|
4798
|
+
return void 0;
|
|
4799
|
+
}
|
|
4800
|
+
function typeSpecifierOf(arg) {
|
|
4801
|
+
const names = [];
|
|
4802
|
+
let at = arg;
|
|
4803
|
+
while (at !== null) {
|
|
4804
|
+
if (at.kind !== "member") return void 0;
|
|
4805
|
+
names.unshift(at.name);
|
|
4806
|
+
at = at.target;
|
|
4807
|
+
}
|
|
4808
|
+
return names.join(".");
|
|
4809
|
+
}
|
|
4810
|
+
var POSITIONS = /* @__PURE__ */ new WeakMap();
|
|
4811
|
+
function positionsIn(resource) {
|
|
4812
|
+
const cached = POSITIONS.get(resource);
|
|
4813
|
+
if (cached !== void 0) return cached;
|
|
4814
|
+
const out = /* @__PURE__ */ new Map();
|
|
4815
|
+
const pending = [resource];
|
|
4816
|
+
const seen = /* @__PURE__ */ new Set([resource]);
|
|
4817
|
+
const record = (value, parent, property, inList) => {
|
|
4818
|
+
if (isList(value)) {
|
|
4819
|
+
for (const item of value.items) record(item, parent, property, true);
|
|
4820
|
+
return;
|
|
4821
|
+
}
|
|
4822
|
+
const list2 = out.get(value);
|
|
4823
|
+
if (list2 === void 0) out.set(value, [{ parent, property, inList }]);
|
|
4824
|
+
else list2.push({ parent, property, inList });
|
|
4825
|
+
if (!seen.has(value)) {
|
|
4826
|
+
seen.add(value);
|
|
4827
|
+
pending.push(value);
|
|
4828
|
+
}
|
|
4829
|
+
};
|
|
4830
|
+
for (let node = pending.pop(); node !== void 0; node = pending.pop()) {
|
|
4831
|
+
if (isComplex(node)) {
|
|
4832
|
+
for (const p of node.properties) record(p.value, node, p.name, false);
|
|
4833
|
+
for (const p of node.duplicates ?? []) record(p.value, node, p.name, false);
|
|
4834
|
+
} else if (isPrimitive(node)) {
|
|
4835
|
+
for (const ext of node.extension ?? []) record(ext, node, "extension", true);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
POSITIONS.set(resource, out);
|
|
4839
|
+
return out;
|
|
4840
|
+
}
|
|
4841
|
+
function suffixContradicts(property, node) {
|
|
4842
|
+
for (let i = 1; i < property.length; i += 1) {
|
|
4843
|
+
const first = property.charAt(i);
|
|
4844
|
+
if (first < "A" || first > "Z") continue;
|
|
4845
|
+
const suffix = property.slice(i);
|
|
4846
|
+
const kind = fhirKind(suffix);
|
|
4847
|
+
if (isPrimitive(node) && (kind === "complex" || kind === "resource")) return true;
|
|
4848
|
+
if (isComplex(node) && FHIR_PRIMITIVES.has(first.toLowerCase() + suffix.slice(1))) return true;
|
|
4849
|
+
}
|
|
4850
|
+
return false;
|
|
4851
|
+
}
|
|
4852
|
+
function choiceVariantType(position, resource) {
|
|
4853
|
+
if (position.inList || position.parent !== resource || !isComplex(resource)) return void 0;
|
|
4854
|
+
const type = resourceType(resource);
|
|
4855
|
+
const schema = type === void 0 ? void 0 : tables()(type);
|
|
4856
|
+
if (schema === void 0) return void 0;
|
|
4857
|
+
const resolved = resolveElement(schema.elements, position.property);
|
|
4858
|
+
if (resolved === void 0 || !isChoice(resolved.element)) return void 0;
|
|
4859
|
+
return resolved.datatype;
|
|
4860
|
+
}
|
|
4861
|
+
function establishedType(node, resource) {
|
|
4862
|
+
const positions = positionsIn(resource).get(node) ?? [];
|
|
4863
|
+
if (node === resource) {
|
|
4864
|
+
if (positions.length > 0 || !isComplex(node)) return void 0;
|
|
4865
|
+
const type = resourceType(node);
|
|
4866
|
+
return type !== void 0 && fhirKind(type) === "resource" ? type : void 0;
|
|
4867
|
+
}
|
|
4868
|
+
let established;
|
|
4869
|
+
for (const position of positions) {
|
|
4870
|
+
const type = choiceVariantType(position, resource);
|
|
4871
|
+
if (type === void 0 || established !== void 0 && established !== type) {
|
|
4872
|
+
return void 0;
|
|
4873
|
+
}
|
|
4874
|
+
established = type;
|
|
4875
|
+
}
|
|
4876
|
+
return established;
|
|
4877
|
+
}
|
|
4878
|
+
function isOfType(actual, tested) {
|
|
4879
|
+
if (actual === tested) return true;
|
|
4880
|
+
const actualKind = fhirKind(actual);
|
|
4881
|
+
const testedKind = fhirKind(tested);
|
|
4882
|
+
if (actualKind === void 0 || testedKind === void 0 || testedKind === "abstract") {
|
|
4883
|
+
throw new UnsupportedFhirPathError("type test between types this engine cannot relate");
|
|
4884
|
+
}
|
|
4885
|
+
if (actualKind === "primitive" || testedKind === "primitive") {
|
|
4886
|
+
throw new UnsupportedFhirPathError("type test between two different FHIR primitive types");
|
|
4887
|
+
}
|
|
4888
|
+
return SPECIALIZES.get(actual) === tested;
|
|
4889
|
+
}
|
|
4890
|
+
function nodeIsOfType(node, type, resource) {
|
|
4891
|
+
if (isList(node)) throw new UnsupportedFhirPathError("type test on a repeating element");
|
|
4892
|
+
const positions = positionsIn(resource).get(node) ?? [];
|
|
4893
|
+
if (positions.some((p) => suffixContradicts(p.property, node))) {
|
|
4894
|
+
throw new UnsupportedFhirPathError(
|
|
4895
|
+
"type test on an element whose property name contradicts its shape"
|
|
4896
|
+
);
|
|
4897
|
+
}
|
|
4898
|
+
if (type.model === "System") {
|
|
4899
|
+
if (isComplex(node)) return false;
|
|
4900
|
+
throw new UnsupportedFhirPathError(`type test '${type.name}' on a model primitive`);
|
|
4901
|
+
}
|
|
4902
|
+
const kind = fhirKind(type.name);
|
|
4903
|
+
if (isPrimitive(node) && kind !== "primitive") return false;
|
|
4904
|
+
if (isComplex(node) && kind === "primitive") return false;
|
|
4905
|
+
const actual = establishedType(node, resource);
|
|
4906
|
+
if (actual === void 0) {
|
|
4907
|
+
throw new UnsupportedFhirPathError(
|
|
4908
|
+
"type test on an element whose type the instance does not establish"
|
|
4909
|
+
);
|
|
4910
|
+
}
|
|
4911
|
+
return isOfType(actual, type.name);
|
|
4912
|
+
}
|
|
4913
|
+
|
|
3751
4914
|
// src/fhirpath/evaluate.ts
|
|
3752
4915
|
function wrap(node) {
|
|
3753
4916
|
if (isList(node)) return node.items.flatMap(wrap);
|
|
@@ -3841,7 +5004,12 @@ function childrenOf(item) {
|
|
|
3841
5004
|
return item.node.properties.filter((p) => p.name !== "resourceType").flatMap((p) => wrap(p.value));
|
|
3842
5005
|
}
|
|
3843
5006
|
if (isPrimitive(item.node)) {
|
|
3844
|
-
|
|
5007
|
+
const id = item.node.id === void 0 ? [] : [{ t: "str", value: item.node.id }];
|
|
5008
|
+
const extensions = (item.node.extension ?? []).map((ext) => ({
|
|
5009
|
+
t: "node",
|
|
5010
|
+
node: ext
|
|
5011
|
+
}));
|
|
5012
|
+
return [...id, ...extensions];
|
|
3845
5013
|
}
|
|
3846
5014
|
return [];
|
|
3847
5015
|
}
|
|
@@ -3994,28 +5162,114 @@ function systemTypeOf(item) {
|
|
|
3994
5162
|
return void 0;
|
|
3995
5163
|
}
|
|
3996
5164
|
var SYSTEM_TYPE_NAMES = /* @__PURE__ */ new Set(["Boolean", "String", "Integer", "Decimal"]);
|
|
3997
|
-
function itemIsType(item, typeName) {
|
|
3998
|
-
const
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
)
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
}
|
|
4008
|
-
|
|
4009
|
-
|
|
5165
|
+
function itemIsType(item, typeName, ctx) {
|
|
5166
|
+
const resolved = resolveTypeSpecifier(typeName);
|
|
5167
|
+
const complexNode = item.t === "node" && isComplex(item.node);
|
|
5168
|
+
if (resolved?.model === "System" && SYSTEM_TYPE_NAMES.has(resolved.name) && !complexNode) {
|
|
5169
|
+
const actual = systemTypeOf(item);
|
|
5170
|
+
if (actual === void 0) {
|
|
5171
|
+
throw new UnsupportedFhirPathError(`type test '${typeName}' on a non-System value`);
|
|
5172
|
+
}
|
|
5173
|
+
if (resolved.name === "Decimal" && actual === "Integer") return true;
|
|
5174
|
+
return actual === resolved.name;
|
|
5175
|
+
}
|
|
5176
|
+
return fhirTypeTest(item, typeName, resolved, ctx);
|
|
5177
|
+
}
|
|
5178
|
+
function fhirTypeTest(item, typeName, resolved, ctx) {
|
|
5179
|
+
if (resolved === void 0) {
|
|
5180
|
+
throw new UnsupportedFhirPathError(`type test '${typeName}' names no known type`);
|
|
5181
|
+
}
|
|
5182
|
+
if (item.t !== "node") {
|
|
5183
|
+
throw new UnsupportedFhirPathError(`type test '${typeName}' on a computed value`);
|
|
5184
|
+
}
|
|
5185
|
+
return nodeIsOfType(item.node, resolved, ctx.resource);
|
|
5186
|
+
}
|
|
5187
|
+
function functionFormTypeTest(input, arg, construct, ctx) {
|
|
5188
|
+
const typeName = typeNameOf(req(arg));
|
|
5189
|
+
const resolved = resolveTypeSpecifier(typeName);
|
|
5190
|
+
if (resolved === void 0) {
|
|
5191
|
+
throw new UnsupportedFhirPathError(`type test '${typeName}' names no known type`);
|
|
5192
|
+
}
|
|
5193
|
+
if (input.length === 0) return "empty";
|
|
5194
|
+
const item = singleItem(input, construct);
|
|
5195
|
+
if (resolved.model === "System" && !(item.t === "node" && isComplex(item.node))) {
|
|
5196
|
+
throw new UnsupportedFhirPathError(`function-form type test '${typeName}' on a primitive`);
|
|
5197
|
+
}
|
|
5198
|
+
return fhirTypeTest(item, typeName, resolved, ctx) ? item : void 0;
|
|
5199
|
+
}
|
|
5200
|
+
function ofTypeKeeps(item, arg, ctx) {
|
|
5201
|
+
const typeName = typeNameOf(arg);
|
|
5202
|
+
const qualified = arg.kind === "member" && arg.target !== null;
|
|
5203
|
+
const complexNode = item.t === "node" && isComplex(item.node);
|
|
5204
|
+
if (qualified && resolveTypeSpecifier(typeName)?.model === "System" && !complexNode) {
|
|
5205
|
+
throw new UnsupportedFhirPathError(`qualified type test '${typeName}' on a primitive`);
|
|
5206
|
+
}
|
|
5207
|
+
return itemIsType(item, typeName, ctx);
|
|
5208
|
+
}
|
|
5209
|
+
function singleItem(input, construct) {
|
|
5210
|
+
if (input.length !== 1) {
|
|
5211
|
+
throw new UnsupportedFhirPathError(`${construct} over more than one item`);
|
|
5212
|
+
}
|
|
5213
|
+
return input[0];
|
|
5214
|
+
}
|
|
5215
|
+
function applyMatches(input, args, callFocus, ctx) {
|
|
5216
|
+
if (input.length === 0) return [];
|
|
5217
|
+
const patternColl = evaluate(req(args[0]), callFocus, ctx);
|
|
5218
|
+
if (patternColl.length === 0) return [];
|
|
5219
|
+
const value = stringOf2(singleItem(input, "matches()"));
|
|
5220
|
+
if (value === void 0) throw new UnsupportedFhirPathError("matches() on a non-string value");
|
|
5221
|
+
const pattern = patternColl.length === 1 ? stringOf2(patternColl[0]) : void 0;
|
|
5222
|
+
if (pattern === void 0)
|
|
5223
|
+
throw new UnsupportedFhirPathError("matches() needs one string pattern");
|
|
5224
|
+
return [{ t: "bool", value: compileMatchesPattern(pattern).test(value) }];
|
|
4010
5225
|
}
|
|
4011
5226
|
function typeNameOf(arg) {
|
|
4012
|
-
|
|
4013
|
-
throw new UnsupportedFhirPathError("expected a type name argument");
|
|
5227
|
+
const specifier = typeSpecifierOf(arg);
|
|
5228
|
+
if (specifier === void 0) throw new UnsupportedFhirPathError("expected a type name argument");
|
|
5229
|
+
return specifier;
|
|
4014
5230
|
}
|
|
4015
5231
|
function filterWhere(focus, criteria, ctx) {
|
|
4016
5232
|
return focus.filter((item) => convertToBoolean(evaluate(criteria, [item], ctx)));
|
|
4017
5233
|
}
|
|
5234
|
+
var SUBSET_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
5235
|
+
"exists",
|
|
5236
|
+
"empty",
|
|
5237
|
+
"not",
|
|
5238
|
+
"where",
|
|
5239
|
+
"select",
|
|
5240
|
+
"all",
|
|
5241
|
+
"count",
|
|
5242
|
+
"first",
|
|
5243
|
+
"last",
|
|
5244
|
+
"distinct",
|
|
5245
|
+
"hasValue",
|
|
5246
|
+
"children",
|
|
5247
|
+
"extension",
|
|
5248
|
+
"intersect",
|
|
5249
|
+
"ofType",
|
|
5250
|
+
"is",
|
|
5251
|
+
"as",
|
|
5252
|
+
"matches"
|
|
5253
|
+
]);
|
|
5254
|
+
var SUBSET_OPERATORS = /* @__PURE__ */ new Set([
|
|
5255
|
+
"and",
|
|
5256
|
+
"or",
|
|
5257
|
+
"xor",
|
|
5258
|
+
"implies",
|
|
5259
|
+
"=",
|
|
5260
|
+
"!=",
|
|
5261
|
+
"<",
|
|
5262
|
+
">",
|
|
5263
|
+
"<=",
|
|
5264
|
+
">=",
|
|
5265
|
+
"|",
|
|
5266
|
+
"in",
|
|
5267
|
+
"contains"
|
|
5268
|
+
]);
|
|
4018
5269
|
function applyFunction(name, input, args, callFocus, ctx) {
|
|
5270
|
+
if (!SUBSET_FUNCTIONS.has(name)) {
|
|
5271
|
+
throw new UnsupportedFhirPathError(`unsupported function '${name}()'`);
|
|
5272
|
+
}
|
|
4019
5273
|
switch (name) {
|
|
4020
5274
|
case "exists":
|
|
4021
5275
|
return [
|
|
@@ -4059,7 +5313,17 @@ function applyFunction(name, input, args, callFocus, ctx) {
|
|
|
4059
5313
|
return distinctItems(input).filter((item) => other.some((o) => itemsEqual(item, o)));
|
|
4060
5314
|
}
|
|
4061
5315
|
case "ofType":
|
|
4062
|
-
return input.filter((item) =>
|
|
5316
|
+
return input.filter((item) => ofTypeKeeps(item, req(args[0]), ctx));
|
|
5317
|
+
case "is": {
|
|
5318
|
+
const matched = functionFormTypeTest(input, args[0], "is()", ctx);
|
|
5319
|
+
return matched === "empty" ? [] : [{ t: "bool", value: matched !== void 0 }];
|
|
5320
|
+
}
|
|
5321
|
+
case "as": {
|
|
5322
|
+
const matched = functionFormTypeTest(input, args[0], "as()", ctx);
|
|
5323
|
+
return matched === "empty" || matched === void 0 ? [] : [matched];
|
|
5324
|
+
}
|
|
5325
|
+
case "matches":
|
|
5326
|
+
return applyMatches(input, args, callFocus, ctx);
|
|
4063
5327
|
default:
|
|
4064
5328
|
throw new UnsupportedFhirPathError(`unsupported function '${name}()'`);
|
|
4065
5329
|
}
|
|
@@ -4082,6 +5346,7 @@ function req(arg) {
|
|
|
4082
5346
|
return arg;
|
|
4083
5347
|
}
|
|
4084
5348
|
function evaluateBinary(op, node, focus, ctx) {
|
|
5349
|
+
if (!SUBSET_OPERATORS.has(op)) throw new UnsupportedFhirPathError(`unsupported operator '${op}'`);
|
|
4085
5350
|
if (op === "and" || op === "or" || op === "xor" || op === "implies") {
|
|
4086
5351
|
const a = toTrit(evaluate(node.left, focus, ctx));
|
|
4087
5352
|
const b = toTrit(evaluate(node.right, focus, ctx));
|
|
@@ -4189,9 +5454,9 @@ function evaluate(expr, focus, ctx) {
|
|
|
4189
5454
|
if (expr.op === "is") {
|
|
4190
5455
|
if (operand.length === 0) return [];
|
|
4191
5456
|
if (operand.length !== 1) return [{ t: "bool", value: false }];
|
|
4192
|
-
return [{ t: "bool", value: itemIsType(operand[0], expr.type) }];
|
|
5457
|
+
return [{ t: "bool", value: itemIsType(operand[0], expr.type, ctx) }];
|
|
4193
5458
|
}
|
|
4194
|
-
return operand.filter((item) => itemIsType(item, expr.type));
|
|
5459
|
+
return operand.filter((item) => itemIsType(item, expr.type, ctx));
|
|
4195
5460
|
}
|
|
4196
5461
|
}
|
|
4197
5462
|
}
|
|
@@ -4656,11 +5921,14 @@ function fociFor(resource, rt, root, elementPath) {
|
|
|
4656
5921
|
}));
|
|
4657
5922
|
}
|
|
4658
5923
|
function collectInvariantIssues(resource, profile, options = {}) {
|
|
5924
|
+
return invariantFindings(resource, profile, options).map((finding) => finding.issue);
|
|
5925
|
+
}
|
|
5926
|
+
function invariantFindings(resource, profile, options = {}) {
|
|
4659
5927
|
const rt = resourceType(resource);
|
|
4660
5928
|
if (rt === void 0 || rt !== profile.type) return [];
|
|
4661
5929
|
const root = rootPath(rt);
|
|
4662
5930
|
const snapshot = snapshotElements(profile, options.resolve ?? (() => void 0));
|
|
4663
|
-
const
|
|
5931
|
+
const findings = [];
|
|
4664
5932
|
for (const el of snapshot) {
|
|
4665
5933
|
if (el.constraint === void 0 || el.constraint.length === 0) continue;
|
|
4666
5934
|
if (el.sliceName !== void 0) continue;
|
|
@@ -4676,21 +5944,217 @@ function collectInvariantIssues(resource, profile, options = {}) {
|
|
|
4676
5944
|
resource
|
|
4677
5945
|
);
|
|
4678
5946
|
if (unchecked) {
|
|
4679
|
-
|
|
4680
|
-
validationIssue(
|
|
5947
|
+
findings.push({
|
|
5948
|
+
issue: validationIssue(
|
|
4681
5949
|
"INVARIANT_UNCHECKED",
|
|
4682
5950
|
ISSUE_SEVERITIES.INFORMATION,
|
|
4683
5951
|
focus.path,
|
|
4684
5952
|
constraint.key
|
|
4685
|
-
)
|
|
4686
|
-
|
|
5953
|
+
),
|
|
5954
|
+
focus: focus.node,
|
|
5955
|
+
expression: constraint.expression
|
|
5956
|
+
});
|
|
4687
5957
|
} else if (!satisfied) {
|
|
4688
|
-
|
|
5958
|
+
findings.push({
|
|
5959
|
+
issue: validationIssue("INVARIANT_VIOLATED", severity, focus.path, constraint.key),
|
|
5960
|
+
focus: focus.node,
|
|
5961
|
+
expression: constraint.expression
|
|
5962
|
+
});
|
|
4689
5963
|
}
|
|
4690
5964
|
}
|
|
4691
5965
|
}
|
|
4692
5966
|
}
|
|
4693
|
-
return
|
|
5967
|
+
return findings;
|
|
5968
|
+
}
|
|
5969
|
+
|
|
5970
|
+
// src/validate/base-invariants.ts
|
|
5971
|
+
var BASE_CONSTRAINT_TYPES = /* @__PURE__ */ new Set([
|
|
5972
|
+
"AllergyIntolerance",
|
|
5973
|
+
"Condition",
|
|
5974
|
+
"DiagnosticReport",
|
|
5975
|
+
"Immunization",
|
|
5976
|
+
"MedicationRequest",
|
|
5977
|
+
"MedicationStatement",
|
|
5978
|
+
"Observation",
|
|
5979
|
+
"Patient"
|
|
5980
|
+
]);
|
|
5981
|
+
var BASE_CONSTRAINTS = [
|
|
5982
|
+
{
|
|
5983
|
+
key: "dom-2",
|
|
5984
|
+
anchor: { kind: "root" },
|
|
5985
|
+
expression: "contained.contained.empty()"
|
|
5986
|
+
},
|
|
5987
|
+
{
|
|
5988
|
+
key: "dom-3",
|
|
5989
|
+
anchor: { kind: "root" },
|
|
5990
|
+
expression: "contained.where((('#'+id in (%resource.descendants().reference | %resource.descendants().as(canonical) | %resource.descendants().as(uri) | %resource.descendants().as(url))) or descendants().where(reference = '#').exists() or descendants().where(as(canonical) = '#').exists() or descendants().where(as(canonical) = '#').exists()).not()).trace('unmatched', id).empty()"
|
|
5991
|
+
},
|
|
5992
|
+
{
|
|
5993
|
+
key: "dom-4",
|
|
5994
|
+
anchor: { kind: "root" },
|
|
5995
|
+
expression: "contained.meta.versionId.empty() and contained.meta.lastUpdated.empty()"
|
|
5996
|
+
},
|
|
5997
|
+
{
|
|
5998
|
+
key: "dom-5",
|
|
5999
|
+
anchor: { kind: "root" },
|
|
6000
|
+
expression: "contained.meta.security.empty()"
|
|
6001
|
+
},
|
|
6002
|
+
{
|
|
6003
|
+
key: "pat-1",
|
|
6004
|
+
anchor: { kind: "element", type: "Patient", element: "contact" },
|
|
6005
|
+
expression: "name.exists() or telecom.exists() or address.exists() or organization.exists()"
|
|
6006
|
+
},
|
|
6007
|
+
{
|
|
6008
|
+
key: "obs-3",
|
|
6009
|
+
anchor: { kind: "element", type: "Observation", element: "referenceRange" },
|
|
6010
|
+
expression: "low.exists() or high.exists() or text.exists()"
|
|
6011
|
+
},
|
|
6012
|
+
{
|
|
6013
|
+
key: "con-1",
|
|
6014
|
+
anchor: { kind: "element", type: "Condition", element: "stage" },
|
|
6015
|
+
expression: "summary.exists() or assessment.exists()"
|
|
6016
|
+
},
|
|
6017
|
+
{
|
|
6018
|
+
key: "con-2",
|
|
6019
|
+
anchor: { kind: "element", type: "Condition", element: "evidence" },
|
|
6020
|
+
expression: "code.exists() or detail.exists()"
|
|
6021
|
+
},
|
|
6022
|
+
{
|
|
6023
|
+
key: "imm-1",
|
|
6024
|
+
anchor: { kind: "element", type: "Immunization", element: "education" },
|
|
6025
|
+
expression: "documentType.exists() or reference.exists()"
|
|
6026
|
+
},
|
|
6027
|
+
{
|
|
6028
|
+
key: "ele-1",
|
|
6029
|
+
anchor: { kind: "every-element" },
|
|
6030
|
+
expression: "hasValue() or (children().count() > id.count())"
|
|
6031
|
+
},
|
|
6032
|
+
{
|
|
6033
|
+
key: "ext-1",
|
|
6034
|
+
anchor: { kind: "every-extension" },
|
|
6035
|
+
expression: "extension.exists() != value.exists()"
|
|
6036
|
+
}
|
|
6037
|
+
];
|
|
6038
|
+
function compile(expression) {
|
|
6039
|
+
try {
|
|
6040
|
+
return parseFhirPath(expression);
|
|
6041
|
+
} catch {
|
|
6042
|
+
return void 0;
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
var COMPILED = BASE_CONSTRAINTS.map((row) => ({
|
|
6046
|
+
row,
|
|
6047
|
+
ast: compile(row.expression)
|
|
6048
|
+
}));
|
|
6049
|
+
var EXPRESSION_OF = new Map(
|
|
6050
|
+
BASE_CONSTRAINTS.map((row) => [row.key, row.expression])
|
|
6051
|
+
);
|
|
6052
|
+
var CONTAINED_SELECTION = compile("contained");
|
|
6053
|
+
function outcomeOf(ast, focus, resource) {
|
|
6054
|
+
if (ast === void 0) return "unchecked";
|
|
6055
|
+
try {
|
|
6056
|
+
const at = [{ t: "node", node: focus }];
|
|
6057
|
+
return convertToBoolean(evaluate(ast, at, { resource, context: at })) ? "satisfied" : "violated";
|
|
6058
|
+
} catch {
|
|
6059
|
+
return "unchecked";
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
6062
|
+
function decideDom3(resource) {
|
|
6063
|
+
if (CONTAINED_SELECTION === void 0) return "unchecked";
|
|
6064
|
+
try {
|
|
6065
|
+
const at = [{ t: "node", node: resource }];
|
|
6066
|
+
const selected = evaluate(CONTAINED_SELECTION, at, { resource, context: at });
|
|
6067
|
+
return selected.length === 0 ? "satisfied" : "unchecked";
|
|
6068
|
+
} catch {
|
|
6069
|
+
return "unchecked";
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
function holdsExtensions(name) {
|
|
6073
|
+
return name === "extension" || name === "modifierExtension";
|
|
6074
|
+
}
|
|
6075
|
+
function occurrencesOf2(node, path) {
|
|
6076
|
+
if (!isList(node)) return [{ node, path }];
|
|
6077
|
+
return node.items.flatMap((item, i) => occurrencesOf2(item, `${path}[${String(i)}]`));
|
|
6078
|
+
}
|
|
6079
|
+
function walkElements(value, path, extension, typedElement, visit2) {
|
|
6080
|
+
for (const occurrence of occurrencesOf2(value, path)) {
|
|
6081
|
+
const { node } = occurrence;
|
|
6082
|
+
visit2(node, occurrence.path, extension, typedElement);
|
|
6083
|
+
if (isComplex(node)) {
|
|
6084
|
+
walkChildren(node, occurrence.path, extension, visit2);
|
|
6085
|
+
} else if (!isList(node)) {
|
|
6086
|
+
(node.extension ?? []).forEach((ext, i) => {
|
|
6087
|
+
walkElements(ext, `${occurrence.path}.extension[${String(i)}]`, true, true, visit2);
|
|
6088
|
+
});
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
}
|
|
6092
|
+
function walkChildren(node, path, isExtension, visit2) {
|
|
6093
|
+
for (const property of node.properties) {
|
|
6094
|
+
if (property.name === "resourceType") continue;
|
|
6095
|
+
const plainString = property.name === "id" || isExtension && property.name === "url";
|
|
6096
|
+
walkElements(
|
|
6097
|
+
property.value,
|
|
6098
|
+
childPath(path, property.name),
|
|
6099
|
+
holdsExtensions(property.name),
|
|
6100
|
+
!plainString,
|
|
6101
|
+
visit2
|
|
6102
|
+
);
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
function collectBaseInvariantFindings(resource, rt) {
|
|
6106
|
+
const findings = [];
|
|
6107
|
+
const decided = /* @__PURE__ */ new Map();
|
|
6108
|
+
const result = {
|
|
6109
|
+
findings,
|
|
6110
|
+
decides: (focus, key, expression) => EXPRESSION_OF.get(key) === expression && decided.get(focus)?.has(key) === true
|
|
6111
|
+
};
|
|
6112
|
+
if (!BASE_CONSTRAINT_TYPES.has(rt)) return result;
|
|
6113
|
+
const root = rootPath(rt);
|
|
6114
|
+
const report = (outcome, key, focus, path) => {
|
|
6115
|
+
if (outcome !== "unchecked") {
|
|
6116
|
+
const keys = decided.get(focus) ?? /* @__PURE__ */ new Set();
|
|
6117
|
+
keys.add(key);
|
|
6118
|
+
decided.set(focus, keys);
|
|
6119
|
+
}
|
|
6120
|
+
if (outcome === "satisfied") return;
|
|
6121
|
+
const issue = outcome === "violated" ? validationIssue("INVARIANT_VIOLATED", ISSUE_SEVERITIES.ERROR, path, key) : validationIssue("INVARIANT_UNCHECKED", ISSUE_SEVERITIES.INFORMATION, path, key);
|
|
6122
|
+
findings.push({ issue, focus });
|
|
6123
|
+
};
|
|
6124
|
+
for (const { row, ast } of COMPILED) {
|
|
6125
|
+
const { anchor } = row;
|
|
6126
|
+
if (anchor.kind === "root") {
|
|
6127
|
+
const outcome = row.key === "dom-3" ? decideDom3(resource) : outcomeOf(ast, resource, resource);
|
|
6128
|
+
report(outcome, row.key, resource, root);
|
|
6129
|
+
} else if (anchor.kind === "element" && anchor.type === rt) {
|
|
6130
|
+
const value = getProperty(resource, anchor.element);
|
|
6131
|
+
if (value === void 0) continue;
|
|
6132
|
+
for (const { node, path } of occurrencesOf2(value, childPath(root, anchor.element))) {
|
|
6133
|
+
report(outcomeOf(ast, node, resource), row.key, node, path);
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
const elementRow = COMPILED.find(({ row }) => row.anchor.kind === "every-element");
|
|
6138
|
+
const extensionRow = COMPILED.find(({ row }) => row.anchor.kind === "every-extension");
|
|
6139
|
+
const visit2 = (node, path, extension, typedElement) => {
|
|
6140
|
+
if (elementRow !== void 0 && typedElement) {
|
|
6141
|
+
report(outcomeOf(elementRow.ast, node, resource), elementRow.row.key, node, path);
|
|
6142
|
+
}
|
|
6143
|
+
if (extensionRow !== void 0 && extension) {
|
|
6144
|
+
report(outcomeOf(extensionRow.ast, node, resource), extensionRow.row.key, node, path);
|
|
6145
|
+
}
|
|
6146
|
+
};
|
|
6147
|
+
for (const property of resource.properties) {
|
|
6148
|
+
if (property.name === "resourceType" || property.name === "contained") continue;
|
|
6149
|
+
walkElements(
|
|
6150
|
+
property.value,
|
|
6151
|
+
childPath(root, property.name),
|
|
6152
|
+
holdsExtensions(property.name),
|
|
6153
|
+
property.name !== "id",
|
|
6154
|
+
visit2
|
|
6155
|
+
);
|
|
6156
|
+
}
|
|
6157
|
+
return result;
|
|
4694
6158
|
}
|
|
4695
6159
|
|
|
4696
6160
|
// src/validate/validate.ts
|
|
@@ -4748,8 +6212,8 @@ function validateResource(resource, options = {}) {
|
|
|
4748
6212
|
return finalize(ctx);
|
|
4749
6213
|
}
|
|
4750
6214
|
const root = rootPath(rt);
|
|
4751
|
-
const
|
|
4752
|
-
const modeled =
|
|
6215
|
+
const registry2 = buildRegistry(options.schemas ?? []);
|
|
6216
|
+
const modeled = registry2(rt);
|
|
4753
6217
|
const schema = modeled ?? baseSchema(rt);
|
|
4754
6218
|
if (modeled === void 0) emit(ctx, "RESOURCE_NOT_MODELED", root);
|
|
4755
6219
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -4762,19 +6226,19 @@ function validateResource(resource, options = {}) {
|
|
|
4762
6226
|
if (modeled !== void 0) emit(ctx, "UNKNOWN_ELEMENT", path);
|
|
4763
6227
|
continue;
|
|
4764
6228
|
}
|
|
4765
|
-
const { element, datatype, base } = match;
|
|
6229
|
+
const { element, datatype, base: base2 } = match;
|
|
4766
6230
|
const occ = occurrences(property.value, path);
|
|
4767
|
-
counts.set(
|
|
6231
|
+
counts.set(base2, (counts.get(base2) ?? 0) + occ.length);
|
|
4768
6232
|
if (isChoice(element)) {
|
|
4769
|
-
const seen = choiceVariants.get(
|
|
6233
|
+
const seen = choiceVariants.get(base2) ?? /* @__PURE__ */ new Set();
|
|
4770
6234
|
seen.add(property.name);
|
|
4771
|
-
choiceVariants.set(
|
|
6235
|
+
choiceVariants.set(base2, seen);
|
|
4772
6236
|
}
|
|
4773
6237
|
if (occ.length > element.max) emit(ctx, "CARDINALITY_MAX", path);
|
|
4774
6238
|
for (const { leaf, path: leafPath } of occ) checkLeaf(ctx, leaf, datatype, element, leafPath);
|
|
4775
6239
|
}
|
|
4776
|
-
for (const [
|
|
4777
|
-
if (seen.size > 1) emit(ctx, "CHOICE_AMBIGUOUS", `${root}.${
|
|
6240
|
+
for (const [base2, seen] of choiceVariants) {
|
|
6241
|
+
if (seen.size > 1) emit(ctx, "CHOICE_AMBIGUOUS", `${root}.${base2}[x]`);
|
|
4778
6242
|
}
|
|
4779
6243
|
for (const [name, element] of Object.entries(schema.elements)) {
|
|
4780
6244
|
if (element.min >= 1 && (counts.get(name) ?? 0) < element.min) {
|
|
@@ -4788,6 +6252,9 @@ function validateResource(resource, options = {}) {
|
|
|
4788
6252
|
for (const issue of collectBundleIssues(resource)) ctx.issues.push(issue);
|
|
4789
6253
|
}
|
|
4790
6254
|
for (const issue of collectTerminologyIssues(resource, rt, options)) ctx.issues.push(issue);
|
|
6255
|
+
const base = collectBaseInvariantFindings(resource, rt);
|
|
6256
|
+
for (const finding of base.findings) ctx.issues.push(finding.issue);
|
|
6257
|
+
const reportedByBase = alreadyReported(base.findings);
|
|
4791
6258
|
if (options.profiles !== void 0 && options.profiles.length > 0) {
|
|
4792
6259
|
const profileOptions = options.resolveBase === void 0 ? {} : { resolve: options.resolveBase };
|
|
4793
6260
|
for (const profile of options.profiles) {
|
|
@@ -4795,8 +6262,12 @@ function validateResource(resource, options = {}) {
|
|
|
4795
6262
|
for (const issue of collectProfileIssues(resource, profile, profileOptions)) {
|
|
4796
6263
|
ctx.issues.push(issue);
|
|
4797
6264
|
}
|
|
4798
|
-
for (const
|
|
4799
|
-
|
|
6265
|
+
for (const finding of invariantFindings(resource, profile, profileOptions)) {
|
|
6266
|
+
if (reportedByBase(finding.focus, finding.issue)) continue;
|
|
6267
|
+
if (finding.issue.code === "INVARIANT_UNCHECKED" && base.decides(finding.focus, finding.issue.constraint ?? "", finding.expression)) {
|
|
6268
|
+
continue;
|
|
6269
|
+
}
|
|
6270
|
+
ctx.issues.push(finding.issue);
|
|
4800
6271
|
}
|
|
4801
6272
|
}
|
|
4802
6273
|
for (const issue of collectProfileVersionIssues(resource, options.profiles)) {
|
|
@@ -4805,6 +6276,15 @@ function validateResource(resource, options = {}) {
|
|
|
4805
6276
|
}
|
|
4806
6277
|
return finalize(ctx);
|
|
4807
6278
|
}
|
|
6279
|
+
function alreadyReported(base) {
|
|
6280
|
+
const byFocus = /* @__PURE__ */ new Map();
|
|
6281
|
+
for (const { focus, issue } of base) {
|
|
6282
|
+
const seen = byFocus.get(focus) ?? /* @__PURE__ */ new Set();
|
|
6283
|
+
seen.add(`${issue.code} ${issue.constraint ?? ""}`);
|
|
6284
|
+
byFocus.set(focus, seen);
|
|
6285
|
+
}
|
|
6286
|
+
return (focus, issue) => byFocus.get(focus)?.has(`${issue.code} ${issue.constraint ?? ""}`) === true;
|
|
6287
|
+
}
|
|
4808
6288
|
function finalize(ctx) {
|
|
4809
6289
|
const issues = [...ctx.issues];
|
|
4810
6290
|
const valid = !issues.some(
|
|
@@ -5201,9 +6681,12 @@ function isWs(c) {
|
|
|
5201
6681
|
}
|
|
5202
6682
|
var RawXmlReader = class {
|
|
5203
6683
|
#src;
|
|
6684
|
+
/** Told the code point of each numeric character reference as it is decoded; never alters the parse. */
|
|
6685
|
+
#onReference;
|
|
5204
6686
|
#pos = 0;
|
|
5205
|
-
constructor(src) {
|
|
6687
|
+
constructor(src, onReference) {
|
|
5206
6688
|
this.#src = src;
|
|
6689
|
+
this.#onReference = onReference;
|
|
5207
6690
|
}
|
|
5208
6691
|
/** Parse the whole document: optional prolog, exactly one root element, optional trailing misc. */
|
|
5209
6692
|
parse() {
|
|
@@ -5451,6 +6934,7 @@ var RawXmlReader = class {
|
|
|
5451
6934
|
start
|
|
5452
6935
|
);
|
|
5453
6936
|
}
|
|
6937
|
+
this.#onReference?.(code);
|
|
5454
6938
|
return String.fromCodePoint(code);
|
|
5455
6939
|
}
|
|
5456
6940
|
fail(message) {
|
|
@@ -5460,6 +6944,13 @@ var RawXmlReader = class {
|
|
|
5460
6944
|
function readRawXml(src) {
|
|
5461
6945
|
return new RawXmlReader(src).parse();
|
|
5462
6946
|
}
|
|
6947
|
+
function readRawXmlReferences(src) {
|
|
6948
|
+
const references = [];
|
|
6949
|
+
const root = new RawXmlReader(src, (codePoint) => {
|
|
6950
|
+
references.push(codePoint);
|
|
6951
|
+
}).parse();
|
|
6952
|
+
return { root, references };
|
|
6953
|
+
}
|
|
5463
6954
|
|
|
5464
6955
|
// src/xml/read.ts
|
|
5465
6956
|
var FHIR_XML_NAMESPACE = "http://hl7.org/fhir";
|
|
@@ -5501,6 +6992,9 @@ function isForeign(resolved, parentNamespace) {
|
|
|
5501
6992
|
function isNarrativeDiv(resolved) {
|
|
5502
6993
|
return resolved.name === "div" && resolved.namespace === XHTML_NAMESPACE;
|
|
5503
6994
|
}
|
|
6995
|
+
function rootVocabularyIsForeign(resolved) {
|
|
6996
|
+
return !resolved.unboundPrefix && resolved.namespace !== FHIR_XML_NAMESPACE && resolved.namespace !== NO_NAMESPACE;
|
|
6997
|
+
}
|
|
5504
6998
|
function modelNameOf(element, resolved, parentNamespace) {
|
|
5505
6999
|
if (isNarrativeDiv(resolved)) return resolved.name;
|
|
5506
7000
|
return isForeign(resolved, parentNamespace) ? element.name : resolved.name;
|
|
@@ -5630,6 +7124,12 @@ function readComplex2(self, path, issues, opts) {
|
|
|
5630
7124
|
function hasStrayText(children) {
|
|
5631
7125
|
return children.some((node) => node.type === "text" && node.value.trim() !== "");
|
|
5632
7126
|
}
|
|
7127
|
+
function elementTextValue(children) {
|
|
7128
|
+
const runs = children.flatMap(
|
|
7129
|
+
(node) => node.type === "text" && node.value.trim() !== "" ? [node.value.trim()] : []
|
|
7130
|
+
);
|
|
7131
|
+
return runs.length === 1 ? runs[0] : void 0;
|
|
7132
|
+
}
|
|
5633
7133
|
function unexpectedXmlContentAt(issues, path) {
|
|
5634
7134
|
return issues.some((i) => i.code === ISSUE_CODES.UNEXPECTED_XML_CONTENT && i.expression === path);
|
|
5635
7135
|
}
|
|
@@ -5689,7 +7189,7 @@ function buildSingle(self, path, issues, parentNamespace) {
|
|
|
5689
7189
|
}
|
|
5690
7190
|
}
|
|
5691
7191
|
const dropped = flagStrayText(element.children, path, issues);
|
|
5692
|
-
const node = primitive(valueAttribute(element), meta);
|
|
7192
|
+
const node = primitive(valueAttribute(element) ?? elementTextValue(element.children), meta);
|
|
5693
7193
|
return dropped ? markDroppedText(node) : node;
|
|
5694
7194
|
}
|
|
5695
7195
|
return readComplex2(self, path, issues, { isResource: false });
|
|
@@ -5701,26 +7201,69 @@ function parseResourceXml(input) {
|
|
|
5701
7201
|
const resolved = resolveName(root.name, scope);
|
|
5702
7202
|
const self = { element: root, scope, resolved, modelName: resolved.name };
|
|
5703
7203
|
const path = rootPath(self.modelName);
|
|
5704
|
-
const rootIsForeign = resolved.unboundPrefix || resolved
|
|
7204
|
+
const rootIsForeign = resolved.unboundPrefix || rootVocabularyIsForeign(resolved);
|
|
5705
7205
|
if (rootIsForeign) issues.push(unexpectedXmlContent(path));
|
|
5706
|
-
const
|
|
7206
|
+
const read = readComplex2(self, path, issues, { isResource: true });
|
|
7207
|
+
const resource = rootVocabularyIsForeign(resolved) ? markForeignRoot(read) : read;
|
|
5707
7208
|
return { resource, issues };
|
|
5708
7209
|
}
|
|
5709
7210
|
|
|
5710
7211
|
// src/xml/write.ts
|
|
5711
7212
|
function tag(name, path, sink) {
|
|
5712
7213
|
if (breaksTag(name)) sink.refusedNames.push(path);
|
|
7214
|
+
else if (carriesUndeclarablePrefix(name)) sink.refusedPrefixes.push(path);
|
|
7215
|
+
else if (!isXmlName(name)) sink.refusedXmlNames.push(path);
|
|
5713
7216
|
return name;
|
|
5714
7217
|
}
|
|
7218
|
+
function attributeValue(text, path, sink) {
|
|
7219
|
+
if (carriesNonXmlCharacter(text)) sink.refusedCharacters.push(path);
|
|
7220
|
+
return escapeAttr(text);
|
|
7221
|
+
}
|
|
7222
|
+
function refersToNonXmlCharacter(references) {
|
|
7223
|
+
return references.some((codePoint) => carriesNonXmlCharacter(String.fromCodePoint(codePoint)));
|
|
7224
|
+
}
|
|
5715
7225
|
function emitsOneDivElement(value) {
|
|
5716
|
-
let
|
|
7226
|
+
let parse;
|
|
5717
7227
|
try {
|
|
5718
|
-
|
|
7228
|
+
parse = readRawXmlReferences(value);
|
|
5719
7229
|
} catch {
|
|
5720
|
-
return
|
|
7230
|
+
return void 0;
|
|
5721
7231
|
}
|
|
7232
|
+
const { name } = parse.root;
|
|
5722
7233
|
const colon = name.indexOf(":");
|
|
5723
|
-
return (colon === -1 ? name : name.slice(colon + 1)) === "div";
|
|
7234
|
+
return (colon === -1 ? name : name.slice(colon + 1)) === "div" ? parse : void 0;
|
|
7235
|
+
}
|
|
7236
|
+
var XML_PREFIX2 = "xml";
|
|
7237
|
+
var XMLNS_PREFIX = "xmlns";
|
|
7238
|
+
var DECLARATION = `${XMLNS_PREFIX}:`;
|
|
7239
|
+
var NOTHING_BOUND = /* @__PURE__ */ new Set();
|
|
7240
|
+
function prefixesInScope(element, inherited) {
|
|
7241
|
+
const declarations = element.attributes.filter(({ name }) => name.startsWith(DECLARATION));
|
|
7242
|
+
if (declarations.length === 0) return inherited;
|
|
7243
|
+
const scope = new Set(inherited);
|
|
7244
|
+
for (const { name, value } of declarations) {
|
|
7245
|
+
const prefix = name.slice(DECLARATION.length);
|
|
7246
|
+
if (value === "") scope.delete(prefix);
|
|
7247
|
+
else scope.add(prefix);
|
|
7248
|
+
}
|
|
7249
|
+
return scope;
|
|
7250
|
+
}
|
|
7251
|
+
function nameIsBound(name, scope) {
|
|
7252
|
+
const colon = name.indexOf(":");
|
|
7253
|
+
if (colon === -1) return true;
|
|
7254
|
+
const prefix = name.slice(0, colon);
|
|
7255
|
+
const local = name.slice(colon + 1);
|
|
7256
|
+
if (prefix === "" || prefix === XMLNS_PREFIX || local === "" || local.includes(":")) return false;
|
|
7257
|
+
return prefix === XML_PREFIX2 || scope.has(prefix);
|
|
7258
|
+
}
|
|
7259
|
+
function bindsEveryPrefix(element, inherited = NOTHING_BOUND) {
|
|
7260
|
+
const scope = prefixesInScope(element, inherited);
|
|
7261
|
+
if (!nameIsBound(element.name, scope)) return false;
|
|
7262
|
+
for (const { name } of element.attributes) {
|
|
7263
|
+
if (name === XMLNS_PREFIX || name.startsWith(DECLARATION)) continue;
|
|
7264
|
+
if (!nameIsBound(name, scope)) return false;
|
|
7265
|
+
}
|
|
7266
|
+
return element.children.every((child) => child.type === "text" || bindsEveryPrefix(child, scope));
|
|
5724
7267
|
}
|
|
5725
7268
|
function scalarText(value) {
|
|
5726
7269
|
if (typeof value === "string") return value;
|
|
@@ -5738,14 +7281,18 @@ function resourceTypeOf(node) {
|
|
|
5738
7281
|
return void 0;
|
|
5739
7282
|
}
|
|
5740
7283
|
function attributeText(node) {
|
|
5741
|
-
if (isPrimitive(node) && node.value !== void 0) return
|
|
7284
|
+
if (isPrimitive(node) && node.value !== void 0) return scalarText(node.value);
|
|
5742
7285
|
return void 0;
|
|
5743
7286
|
}
|
|
5744
7287
|
function writePrimitiveElement(path, name, node, sink) {
|
|
5745
7288
|
const tagName = tag(name, path, sink);
|
|
5746
7289
|
let attrs = "";
|
|
5747
|
-
if (node.id !== void 0)
|
|
5748
|
-
|
|
7290
|
+
if (node.id !== void 0) {
|
|
7291
|
+
attrs += ` id="${attributeValue(node.id, childPath(path, "id"), sink)}"`;
|
|
7292
|
+
}
|
|
7293
|
+
if (node.value !== void 0) {
|
|
7294
|
+
attrs += ` value="${attributeValue(scalarText(node.value), path, sink)}"`;
|
|
7295
|
+
}
|
|
5749
7296
|
const extensions = node.extension ?? [];
|
|
5750
7297
|
if (extensions.length === 0) return `<${tagName}${attrs}/>`;
|
|
5751
7298
|
const extensionPath = childPath(path, "extension");
|
|
@@ -5756,9 +7303,20 @@ function writePrimitiveElement(path, name, node, sink) {
|
|
|
5756
7303
|
}
|
|
5757
7304
|
function writeItem(path, name, node, inExtension, sink) {
|
|
5758
7305
|
if (name === "div" && node.kind === "primitive" && typeof node.value === "string") {
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
7306
|
+
const parse = emitsOneDivElement(node.value);
|
|
7307
|
+
if (parse === void 0) {
|
|
7308
|
+
sink.refusedDivs.push(path);
|
|
7309
|
+
return "";
|
|
7310
|
+
}
|
|
7311
|
+
if (!bindsEveryPrefix(parse.root)) {
|
|
7312
|
+
sink.refusedDivPrefixes.push(path);
|
|
7313
|
+
return "";
|
|
7314
|
+
}
|
|
7315
|
+
if (carriesNonXmlCharacter(node.value) || refersToNonXmlCharacter(parse.references)) {
|
|
7316
|
+
sink.refusedCharacters.push(path);
|
|
7317
|
+
return "";
|
|
7318
|
+
}
|
|
7319
|
+
return node.value;
|
|
5762
7320
|
}
|
|
5763
7321
|
if (node.kind === "primitive") return writePrimitiveElement(path, name, node, sink);
|
|
5764
7322
|
if (node.kind === "list")
|
|
@@ -5779,27 +7337,29 @@ function writeProperty(parentPath, name, node, inExtension, sink) {
|
|
|
5779
7337
|
function writeElement(path, tagName, node, isRoot, inExtension, sink) {
|
|
5780
7338
|
const written = tag(tagName, path, sink);
|
|
5781
7339
|
const isResource = resourceTypeOf(node) !== void 0;
|
|
7340
|
+
const asId = (name) => name === "id" && !isResource;
|
|
7341
|
+
const asUrl = (name) => name === "url" && inExtension;
|
|
5782
7342
|
let attrs = isRoot ? ` xmlns="${FHIR_XML_NAMESPACE}"` : "";
|
|
5783
|
-
let
|
|
5784
|
-
let
|
|
7343
|
+
let idText;
|
|
7344
|
+
let urlText;
|
|
7345
|
+
for (const { name, value } of node.properties) {
|
|
7346
|
+
const text = attributeText(value);
|
|
7347
|
+
if (text === void 0) continue;
|
|
7348
|
+
if (asId(name)) idText = text;
|
|
7349
|
+
else if (asUrl(name)) urlText = text;
|
|
7350
|
+
}
|
|
7351
|
+
if (idText !== void 0) {
|
|
7352
|
+
attrs += ` id="${attributeValue(idText, childPath(path, "id"), sink)}"`;
|
|
7353
|
+
}
|
|
7354
|
+
if (urlText !== void 0) {
|
|
7355
|
+
attrs += ` url="${attributeValue(urlText, childPath(path, "url"), sink)}"`;
|
|
7356
|
+
}
|
|
5785
7357
|
const children = [];
|
|
5786
|
-
for (const
|
|
5787
|
-
|
|
5788
|
-
if (name === "resourceType") continue;
|
|
5789
|
-
if (name === "id" && !isResource) {
|
|
5790
|
-
const text = attributeText(value);
|
|
5791
|
-
if (text !== void 0) idAttr = ` id="${text}"`;
|
|
5792
|
-
continue;
|
|
5793
|
-
}
|
|
5794
|
-
if (name === "url" && inExtension) {
|
|
5795
|
-
const text = attributeText(value);
|
|
5796
|
-
if (text !== void 0) urlAttr = ` url="${text}"`;
|
|
5797
|
-
continue;
|
|
5798
|
-
}
|
|
7358
|
+
for (const { name, value } of node.properties) {
|
|
7359
|
+
if (name === "resourceType" || asId(name) || asUrl(name)) continue;
|
|
5799
7360
|
const childInExtension = name === "extension" || name === "modifierExtension";
|
|
5800
7361
|
children.push(writeProperty(path, name, value, childInExtension, sink));
|
|
5801
7362
|
}
|
|
5802
|
-
attrs += idAttr + urlAttr;
|
|
5803
7363
|
const inner = children.join("");
|
|
5804
7364
|
return inner === "" ? `<${written}${attrs}/>` : `<${written}${attrs}>${inner}</${written}>`;
|
|
5805
7365
|
}
|
|
@@ -5807,7 +7367,14 @@ function serializeResourceXml(node) {
|
|
|
5807
7367
|
assertSerializable(node);
|
|
5808
7368
|
const rt = resourceTypeOf(node);
|
|
5809
7369
|
const tagName = rt ?? "Resource";
|
|
5810
|
-
const sink = {
|
|
7370
|
+
const sink = {
|
|
7371
|
+
refusedNames: [],
|
|
7372
|
+
refusedDivs: [],
|
|
7373
|
+
refusedPrefixes: [],
|
|
7374
|
+
refusedDivPrefixes: [],
|
|
7375
|
+
refusedXmlNames: [],
|
|
7376
|
+
refusedCharacters: []
|
|
7377
|
+
};
|
|
5811
7378
|
const xml = writeElement(rootPath(tagName), tagName, node, true, false, sink);
|
|
5812
7379
|
if (sink.refusedNames.length > 0) refuseUnserializableNames([...new Set(sink.refusedNames)]);
|
|
5813
7380
|
if (sink.refusedDivs.length > 0) refuseUnserializableDivMarkup([...new Set(sink.refusedDivs)]);
|
|
@@ -5815,6 +7382,18 @@ function serializeResourceXml(node) {
|
|
|
5815
7382
|
assertXmlArrayWrapper(node);
|
|
5816
7383
|
assertNoShadowedProperty(node);
|
|
5817
7384
|
assertXmlResourceType(node);
|
|
7385
|
+
assertXmlForeignRoot(node);
|
|
7386
|
+
assertXmlValueChoiceWrapper(node);
|
|
7387
|
+
if (sink.refusedPrefixes.length > 0) {
|
|
7388
|
+
refuseUndeclarablePrefixes([...new Set(sink.refusedPrefixes)]);
|
|
7389
|
+
}
|
|
7390
|
+
if (sink.refusedDivPrefixes.length > 0) {
|
|
7391
|
+
refuseUnboundDivPrefixes([...new Set(sink.refusedDivPrefixes)]);
|
|
7392
|
+
}
|
|
7393
|
+
if (sink.refusedXmlNames.length > 0) refuseNonXmlNames([...new Set(sink.refusedXmlNames)]);
|
|
7394
|
+
if (sink.refusedCharacters.length > 0) {
|
|
7395
|
+
refuseNonXmlCharacters([...new Set(sink.refusedCharacters)]);
|
|
7396
|
+
}
|
|
5818
7397
|
return xml;
|
|
5819
7398
|
}
|
|
5820
7399
|
|
|
@@ -5969,8 +7548,8 @@ async function* streamNdjson(source, options = {}) {
|
|
|
5969
7548
|
}
|
|
5970
7549
|
|
|
5971
7550
|
// src/index.ts
|
|
5972
|
-
var VERSION = "0.0
|
|
7551
|
+
var VERSION = "0.1.0";
|
|
5973
7552
|
|
|
5974
|
-
export { ABSENCE_CODES, ALLERGY_CLINICAL_SYSTEM, ALLERGY_SUBSTANCE_VALUESET, ALLERGY_VERIFICATION_SYSTEM, BINDING_STRENGTHS, BUNDLE_TYPES, CONDITION_CATEGORY_SYSTEM, CONDITION_CLINICAL_SYSTEM, CONDITION_VERIFICATION_SYSTEM, CPT_SYSTEM, CVX_SYSTEM, DATA_ABSENT_REASON_URL, DISCRIMINATOR_TYPES, ENTERED_IN_ERROR, FATAL_CODES, FHIR_XML_NAMESPACE, FhirCodecError, FhirDecimal, FhirInteger64, FhirProfileError, FhirSafetyError, FhirSerializeError, FhirXmlError, ICD10CM_SYSTEM, ICD9CM_SYSTEM, ISSUE_CODES, ISSUE_SEVERITIES, ISSUE_TYPES, InvalidProfileError, KNOWN_MODIFIER_EXTENSION_URLS, KNOWN_SYSTEMS, LOINC_SYSTEM, MAX_REFERENCE_DEPTH, MEDICATION_VALUESET, MODIFIER_ELEMENT_ROOT_TYPES, NDC_SYSTEM, NDJSON_ERROR_CODES, NOT_DONE, NOT_TAKEN, NO_KNOWN_ALLERGY, OBSERVATION_CATEGORY_SYSTEM, OBSERVATION_VALUE_TYPES, PATIENT_IDENTIFIER_STARTER, PRIMITIVE_TYPES, REFUTED, RXNORM_SYSTEM, SAFETY_RESOURCE_TYPES, SERIALIZE_ERROR_CODES, SNOMED_SCT, STARTER_PROFILES, STARTER_PROFILE_BASE_URL, TERMINOLOGY_BINDINGS, UCUM_SYSTEM, UNBOUNDED, UnsupportedFhirPathError, VALIDATION_CODES, VERSION, VITAL_SIGNS_CATEGORY, VITAL_SIGNS_PROFILE, VITAL_SIGN_OBSERVATION_STARTER, VITAL_SIGN_UNITS, WITHHELD, XHTML_NAMESPACE, XML_FATAL_CODES, absenceMarkers, arrayWrappedScalars, assertSafeToSummarize, baseSchema, buildBindingRegistry, buildBundleIndex, buildRegistry, codeOf, codingsOf, collectBundleIssues, collectInvariantIssues, collectProfileIssues, collectProfileVersionIssues, collectQuantityIssues, collectSafetyIssues, collectTerminologyIssues, complex, conflictingAbsenceMarkers, containedIndex, convertToBoolean, decimal, decimalPrecisionAtRisk, defineProfile, diagnosticFor, droppedText, duplicateProperty, entryProcessing, evaluateInvariant, generateSnapshot, getAllProperties, getProperty, hasCodeAnySystem, hasCoding, hasContainedCycle, integer64, isAbsenceCode, isAtomicBundle, isChoice, isComplex, isDroppedText, isKnownSystem, isList, isNestedArray, isPrimitive, isPrimitiveType, isRetracted, isUndefinedNull, list, loadStructureDefinition, locateDoseQuantities, matchSlices, matchesFixed, matchesPattern, misplacedPrimitiveExtension, mixedXmlSpelling, modifierElements, nearMissNegationCodes, nestedArray, nestedArrayContent, nestedArrays, nodesEquivalent, parseFhirPath, parseNdjsonLine, parseReference, parseResource, parseResourceXml, pathExists, primitive, readBundle, readInterpretations, readMedicationDoses, readObservationValue, readQuantity, readRawJson, readRawXml, readReferenceRanges, readSafety, requiredVitalSignUnits, resolveElement, resolvePath, resolveReference, resolveSlices, resourceType, serializeResource, serializeResourceXml, shadowedProperties, snapshotElements, starterProfile, streamNdjson, toOperationOutcome, tokenize, undefinedJsonNull, unexpectedXmlContent, unhandledModifierExtensions, unknownProperty, unreadableAbsenceMarkers, unreadableBooleans, unreadableNegationCodes, validatePrimitiveValue, validateResource, validateUcumShape, validationIssue, wouldLosePrecisionAsDouble };
|
|
7553
|
+
export { ABSENCE_CODES, ALLERGY_CLINICAL_SYSTEM, ALLERGY_SUBSTANCE_VALUESET, ALLERGY_VERIFICATION_SYSTEM, BINDING_STRENGTHS, BUNDLE_TYPES, CODE_SYSTEM_VERSION_RECORD_CODES, CODE_SYSTEM_VERSION_RECORD_SYSTEM, CONDITION_CATEGORY_SYSTEM, CONDITION_CLINICAL_SYSTEM, CONDITION_VERIFICATION_SYSTEM, CPT_SYSTEM, CVX_SYSTEM, DATA_ABSENT_REASON_URL, DISCRIMINATOR_TYPES, ENTERED_IN_ERROR, FATAL_CODES, FHIR_XML_NAMESPACE, FhirCodecError, FhirDecimal, FhirInteger64, FhirProfileError, FhirSafetyError, FhirSerializeError, FhirXmlError, ICD10CM_SYSTEM, ICD9CM_SYSTEM, ISSUE_CODES, ISSUE_SEVERITIES, ISSUE_TYPES, InvalidProfileError, KNOWN_MODIFIER_EXTENSION_URLS, KNOWN_SYSTEMS, LOINC_SYSTEM, MAX_REFERENCE_DEPTH, MEDICATION_VALUESET, MODIFIER_ELEMENT_ROOT_TYPES, NDC_SYSTEM, NDJSON_ERROR_CODES, NOT_DONE, NOT_TAKEN, NO_KNOWN_ALLERGY, OBSERVATION_CATEGORY_SYSTEM, OBSERVATION_VALUE_TYPES, PATIENT_IDENTIFIER_STARTER, PRIMITIVE_TYPES, REFUTED, RXNORM_SYSTEM, SAFETY_RESOURCE_TYPES, SERIALIZE_ERROR_CODES, SNOMED_SCT, STARTER_PROFILES, STARTER_PROFILE_BASE_URL, TERMINOLOGY_BINDINGS, UCUM_SYSTEM, UNBOUNDED, UnsupportedFhirPathError, VALIDATION_CODES, VERSION, VITAL_SIGNS_CATEGORY, VITAL_SIGNS_PROFILE, VITAL_SIGN_OBSERVATION_STARTER, VITAL_SIGN_UNITS, WITHHELD, XHTML_NAMESPACE, XML_FATAL_CODES, absenceMarkers, arrayWrappedScalars, assertSafeToSummarize, baseSchema, buildBindingRegistry, buildBundleIndex, buildRegistry, codeOf, codingsOf, collectBundleIssues, collectInvariantIssues, collectProfileIssues, collectProfileVersionIssues, collectQuantityIssues, collectSafetyIssues, collectTerminologyIssues, complex, conflictingAbsenceMarkers, containedIndex, convertToBoolean, decimal, decimalPrecisionAtRisk, defineProfile, diagnosticFor, droppedText, duplicateProperty, entryProcessing, evaluateInvariant, generateSnapshot, getAllProperties, getProperty, hasCodeAnySystem, hasCoding, hasContainedCycle, integer64, isAbsenceCode, isAtomicBundle, isChoice, isComplex, isDroppedText, isForeignRoot, isKnownSystem, isList, isNestedArray, isPrimitive, isPrimitiveType, isRetracted, isUndefinedNull, list, loadStructureDefinition, locateDoseQuantities, matchSlices, matchesFixed, matchesPattern, misplacedPrimitiveExtension, mixedXmlSpelling, modifierElements, nearMissNegationCodes, nestedArray, nestedArrayContent, nestedArrays, nodesEquivalent, parseFhirPath, parseNdjsonLine, parseReference, parseResource, parseResourceXml, pathExists, primitive, readBundle, readInterpretations, readMedicationDoses, readObservationValue, readQuantity, readRawJson, readRawXml, readReferenceRanges, readSafety, requiredVitalSignUnits, resolveElement, resolvePath, resolveReference, resolveSlices, resourceType, serializeResource, serializeResourceXml, shadowedProperties, snapshotElements, starterProfile, streamNdjson, toOperationOutcome, tokenize, undefinedJsonNull, unexpectedXmlContent, unhandledModifierExtensions, unknownProperty, unreadableAbsenceMarkers, unreadableBooleans, unreadableNegationCodes, validatePrimitiveValue, validateResource, validateUcumShape, validationIssue, wouldLosePrecisionAsDouble };
|
|
5975
7554
|
//# sourceMappingURL=index.mjs.map
|
|
5976
7555
|
//# sourceMappingURL=index.mjs.map
|