fhirsmith 0.10.0 → 0.11.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/CHANGELOG.md +48 -1
- package/changelog-template.md +22 -0
- package/library/logger.js +8 -1
- package/library/package-manager.js +40 -2
- package/npmprojector/watcher.js +1 -1
- package/package.json +3 -2
- package/packages/packages.js +28 -15
- package/publisher/publisher.js +8 -8
- package/registry/api.js +92 -31
- package/registry/crawler.js +15 -1
- package/registry/model.js +158 -8
- package/registry/readme.md +5 -13
- package/registry/registry.js +87 -46
- package/registry/test-runner.js +1 -1
- package/server.js +8 -1
- package/token/token.js +1 -1
- package/translations/Messages.properties +3 -1
- package/translations/rendering-phrases.properties +1227 -1218
- package/translations/rendering-phrases_ar.properties +1097 -1091
- package/translations/rendering-phrases_fr.properties +626 -624
- package/translations/rendering-phrases_nl.properties +976 -970
- package/translations/rendering-phrases_pt.properties +1026 -1020
- package/translations/rendering-phrases_ru.properties +1100 -1094
- package/tsconfig.eslint.json +9 -0
- package/tx/cs/cs-api.js +57 -4
- package/tx/cs/cs-cs.js +98 -25
- package/tx/cs/cs-hgvs.js +4 -0
- package/tx/cs/cs-lang.js +8 -0
- package/tx/cs/cs-mimetypes.js +4 -0
- package/tx/cs/cs-snomed.js +530 -75
- package/tx/cs/cs-uri.js +4 -0
- package/tx/data/snomed-subset.txt +40 -3
- package/tx/importers/import-sct.module.js +18 -0
- package/tx/library/designations.js +3 -5
- package/tx/library/renderer.js +235 -38
- package/tx/library.js +5 -1
- package/tx/operation-context.js +40 -3
- package/tx/provider.js +41 -12
- package/tx/sct/ecl.js +30 -11
- package/tx/sct/expressions.js +28 -1
- package/tx/sct/structures.js +5 -5
- package/tx/tx-html.js +24 -15
- package/tx/tx.fhir.org.yml +1 -4
- package/tx/vs/vs-database.js +41 -8
- package/tx/vs/vs-package.js +55 -0
- package/tx/vs/vs-vsac.js +4 -0
- package/tx/workers/batch-validate.js +125 -18
- package/tx/workers/cache-control.js +31 -2
- package/tx/workers/expand.js +50 -32
- package/tx/workers/metadata.js +27 -6
- package/tx/workers/search.js +7 -1
- package/tx/workers/validate.js +30 -18
- package/tx/workers/worker.js +30 -7
- package/tx/xversion/xv-valueset.js +5 -2
- package/xig/xig.js +3 -3
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true, "checkJs": false, "noEmit": true,
|
|
4
|
+
"moduleResolution": "node", "target": "es2022", "module": "commonjs",
|
|
5
|
+
"strict": false, "skipLibCheck": true
|
|
6
|
+
},
|
|
7
|
+
"include": ["**/*.js"],
|
|
8
|
+
"exclude": ["node_modules", "static", "**/*.test.js", "tests", "coverage"]
|
|
9
|
+
}
|
package/tx/cs/cs-api.js
CHANGED
|
@@ -118,6 +118,19 @@ class CodeSystemProvider {
|
|
|
118
118
|
*/
|
|
119
119
|
totalCount() { throw new Error("Must override"); }
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* The number of codes an "all codes" expansion of this code system would
|
|
123
|
+
* contain, computed WITHOUT materialising them - used by the count=0 (count
|
|
124
|
+
* only) expansion fast path. `excludeInactive` mirrors the expansion's
|
|
125
|
+
* active-only handling. Defaults to totalCount(); providers that distinguish
|
|
126
|
+
* active from inactive override this to honour excludeInactive.
|
|
127
|
+
* @param {boolean} excludeInactive
|
|
128
|
+
* @returns {integer}
|
|
129
|
+
*/
|
|
130
|
+
async countAllCodes(excludeInactive) { // eslint-disable-line no-unused-vars
|
|
131
|
+
return await this.totalCount();
|
|
132
|
+
}
|
|
133
|
+
|
|
121
134
|
/**
|
|
122
135
|
* @returns {CodeSystem.property[]} defined properties for the code system
|
|
123
136
|
*/
|
|
@@ -131,6 +144,18 @@ class CodeSystemProvider {
|
|
|
131
144
|
return false;
|
|
132
145
|
}
|
|
133
146
|
|
|
147
|
+
/**
|
|
148
|
+
* returns false if the code system cannot be iterated / enumerated in any
|
|
149
|
+
* meaningful sense - as distinct from isNotClosed(), which marks a code system
|
|
150
|
+
* that has a grammar whose base set may still be partially expanded. An
|
|
151
|
+
* expansion that would need to iterate a system for which this is false
|
|
152
|
+
* returns an OperationOutcome rather than attempting the iteration.
|
|
153
|
+
* @returns {boolean}
|
|
154
|
+
*/
|
|
155
|
+
canBeExpanded() {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
134
159
|
/**
|
|
135
160
|
* returns true if the code system is case sensitive when comparing codes.
|
|
136
161
|
* this is true by default
|
|
@@ -372,11 +397,21 @@ class CodeSystemProvider {
|
|
|
372
397
|
}
|
|
373
398
|
|
|
374
399
|
/**
|
|
400
|
+
* List the designations for a concept, adding them to `displays`.
|
|
401
|
+
*
|
|
375
402
|
* @param {string | CodeSystemProviderContext} code
|
|
376
|
-
* @param {ConceptDesignations} designation list
|
|
403
|
+
* @param {ConceptDesignations} displays - designation list to add to
|
|
404
|
+
* @param {boolean} [significantOnly=false] - when true, emit only the
|
|
405
|
+
* designations that are significant for a value set expansion (the display,
|
|
406
|
+
* and - for code systems that distinguish them - the fully specified name
|
|
407
|
+
* and the preferred term), rather than every designation in every language.
|
|
408
|
+
* The $expand worker passes true; $lookup uses the default (false) to return
|
|
409
|
+
* the complete set. Providers that do not distinguish significant
|
|
410
|
+
* designations may ignore this parameter and return all designations either
|
|
411
|
+
* way.
|
|
377
412
|
* @returns {Designation[]} whatever designations exist (in all languages)
|
|
378
413
|
*/
|
|
379
|
-
async designations(code, displays) { return null; }
|
|
414
|
+
async designations(code, displays, significantOnly = false) { return null; }
|
|
380
415
|
|
|
381
416
|
_listSupplementDesignations(code, displays) {
|
|
382
417
|
assert(typeof code === 'string', 'code must be string');
|
|
@@ -582,9 +617,27 @@ class CodeSystemProvider {
|
|
|
582
617
|
* @param {boolean} forIteration - whether this filter is going to be iterated
|
|
583
618
|
* @param {String} prop
|
|
584
619
|
* @param {ValueSetFilterOperator} op
|
|
585
|
-
* @param {String}
|
|
620
|
+
* @param {String} value
|
|
621
|
+
* @param {Object} [fc] the ValueSet compose filter element
|
|
622
|
+
* (ValueSet.compose.include.filter) this filter was derived from. It is a stable
|
|
623
|
+
* sub-object of the (cached) ValueSet resource, so a provider MAY use it as a place
|
|
624
|
+
* to memoise the resolved analysis of this filter (e.g. the enumerated concept set),
|
|
625
|
+
* which is then reused on subsequent expand/validate calls for as long as the
|
|
626
|
+
* ValueSet stays cached - trading memory for the cost of re-preparing the filter.
|
|
627
|
+
*
|
|
628
|
+
* Two rules govern any such cache:
|
|
629
|
+
* - It MUST be pinned to the resolved code system VERSION. The same ValueSet can be
|
|
630
|
+
* resolved against different versions/editions, and a resolved filter (a set of
|
|
631
|
+
* version-specific concept references) is only valid for the version it was
|
|
632
|
+
* computed against.
|
|
633
|
+
* - It must NOT be pinned to forIteration. The same ValueSet is used both ways
|
|
634
|
+
* (iterated for $expand, non-iterated for validate), so if the iterated and
|
|
635
|
+
* non-iterated resolved forms differ, store BOTH rather than assuming one.
|
|
636
|
+
*
|
|
637
|
+
* Providers that gain nothing from this (the SQL-backed providers, and the trivial
|
|
638
|
+
* in-memory enumerations) simply ignore fc.
|
|
586
639
|
**/
|
|
587
|
-
async filter(filterContext, forIteration, prop, op, value) { throw new Error("Must override"); } // well, only if any filters are actually supported
|
|
640
|
+
async filter(filterContext, forIteration, prop, op, value, fc) { throw new Error("Must override"); } // well, only if any filters are actually supported
|
|
588
641
|
|
|
589
642
|
/**
|
|
590
643
|
* called once all the filters have been handled, and iteration is about to happen.
|
package/tx/cs/cs-cs.js
CHANGED
|
@@ -8,6 +8,17 @@ const {Extensions} = require("../library/extensions");
|
|
|
8
8
|
const {BaseCSServices} = require("./cs-base");
|
|
9
9
|
const regexUtilities = require("../../library/regex-utilities");
|
|
10
10
|
|
|
11
|
+
// Symbol key under which resolved filter analysis is memoised directly on the
|
|
12
|
+
// ValueSet compose.include.filter element (`fc`). Symbol-keyed and defined
|
|
13
|
+
// non-enumerable, so it never serialises, clones (structuredClone), or spreads
|
|
14
|
+
// into a response - it simply lives and dies with the filter element (and thus
|
|
15
|
+
// the cached ValueSet). The value is Map(codeSystemVersionKey -> concepts[]):
|
|
16
|
+
// the resolved concept list is only valid for the code system version it was
|
|
17
|
+
// computed against. Only the expensive full-scan / subtree-walk filters are
|
|
18
|
+
// cached (see _isCacheableFilter); cheap ones (=, in, child-of) are not. cs-cs
|
|
19
|
+
// filter results do not depend on forIteration, so one form is stored per version.
|
|
20
|
+
const FILTER_ANALYSIS = Symbol('filterAnalysis');
|
|
21
|
+
|
|
11
22
|
/**
|
|
12
23
|
* Context class for FHIR CodeSystem provider concepts
|
|
13
24
|
*/
|
|
@@ -1218,40 +1229,70 @@ class FhirCodeSystemProvider extends BaseCSServices {
|
|
|
1218
1229
|
* @param {string} value - Filter value
|
|
1219
1230
|
* @returns {Promise<FhirCodeSystemProviderFilterContext>} Filter results
|
|
1220
1231
|
*/
|
|
1221
|
-
async filter(filterContext, forIteration, prop, op, value) {
|
|
1222
|
-
|
|
1223
|
-
|
|
1232
|
+
async filter(filterContext, forIteration, prop, op, value, fc) {
|
|
1224
1233
|
let results = null;
|
|
1225
1234
|
|
|
1226
|
-
//
|
|
1227
|
-
|
|
1228
|
-
|
|
1235
|
+
// Reuse previously resolved analysis when this same cached value set filter
|
|
1236
|
+
// is resolved again against the same code system version (see filter()/fc).
|
|
1237
|
+
// Only expensive filters are cached; the concept list is shared read-only,
|
|
1238
|
+
// wrapped in a fresh context so iteration state (the cursor) is per-use.
|
|
1239
|
+
// Caching needs a stable code system identity. When the code system has no
|
|
1240
|
+
// url (odd corner cases) _filterCacheVersionKey returns null and caching is
|
|
1241
|
+
// skipped entirely - there's no safe key to store or look up under.
|
|
1242
|
+
const vkey = fc ? this._filterCacheVersionKey() : null;
|
|
1243
|
+
const cacheable = vkey !== null && this._isCacheableFilter(prop, op);
|
|
1244
|
+
if (cacheable) {
|
|
1245
|
+
const byVersion = fc[FILTER_ANALYSIS];
|
|
1246
|
+
const cachedConcepts = byVersion ? byVersion.get(vkey) : null;
|
|
1247
|
+
if (cachedConcepts) {
|
|
1248
|
+
results = new FhirCodeSystemProviderFilterContext();
|
|
1249
|
+
results.concepts = cachedConcepts; // immutable: read-only from here
|
|
1250
|
+
}
|
|
1229
1251
|
}
|
|
1230
1252
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1253
|
+
if (!results) {
|
|
1254
|
+
// Handle concept/code hierarchy filters
|
|
1255
|
+
if ((prop === 'concept' || prop === 'code')) {
|
|
1256
|
+
results = await this._handleConceptFilter(filterContext, prop, op, value);
|
|
1257
|
+
}
|
|
1235
1258
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
const propertyDef = propertyDefs.find(p => p.code === prop);
|
|
1240
|
-
if (propertyDef) {
|
|
1241
|
-
results = await this._handlePropertyFilter(filterContext, propertyDef, op, value);
|
|
1259
|
+
// Handle child existence filter
|
|
1260
|
+
if (prop === 'child' && op === 'exists') {
|
|
1261
|
+
results = await this._handleChildExistsFilter(filterContext, value);
|
|
1242
1262
|
}
|
|
1243
|
-
}
|
|
1244
1263
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1264
|
+
// Handle property-based filters
|
|
1265
|
+
const propertyDefs = this.propertyDefinitions();
|
|
1266
|
+
if (propertyDefs) {
|
|
1267
|
+
const propertyDef = propertyDefs.find(p => p.code === prop);
|
|
1268
|
+
if (propertyDef) {
|
|
1269
|
+
results = await this._handlePropertyFilter(filterContext, propertyDef, op, value);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1250
1272
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1273
|
+
// Handle known special properties
|
|
1274
|
+
const knownProperties = ['notSelectable', 'status', 'inactive', 'deprecated'];
|
|
1275
|
+
if (knownProperties.includes(prop)) {
|
|
1276
|
+
results = await this._handleKnownPropertyFilter(filterContext, prop, op, value);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if (!results) {
|
|
1280
|
+
throw new Issue('error', 'exception', null, 'FILTER_NOT_UNDERSTOOD',
|
|
1281
|
+
this.opContext.i18n.translate('FILTER_NOT_UNDERSTOOD', this.opContext.langs, [prop, op, value]), 'vs-invalid', 422);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// Store a private copy of the resolved concept list for reuse. A slice
|
|
1285
|
+
// isolates the cache from any later mutation of this request's context.
|
|
1286
|
+
if (cacheable) {
|
|
1287
|
+
let byVersion = fc[FILTER_ANALYSIS];
|
|
1288
|
+
if (!byVersion) {
|
|
1289
|
+
byVersion = new Map();
|
|
1290
|
+
Object.defineProperty(fc, FILTER_ANALYSIS, { value: byVersion, enumerable: false, configurable: true, writable: true });
|
|
1291
|
+
}
|
|
1292
|
+
byVersion.set(vkey, results.concepts.slice());
|
|
1293
|
+
}
|
|
1254
1294
|
}
|
|
1295
|
+
|
|
1255
1296
|
// Add to filter context
|
|
1256
1297
|
if (!filterContext.filters) {
|
|
1257
1298
|
filterContext.filters = [];
|
|
@@ -1261,6 +1302,38 @@ class FhirCodeSystemProvider extends BaseCSServices {
|
|
|
1261
1302
|
return results;
|
|
1262
1303
|
}
|
|
1263
1304
|
|
|
1305
|
+
/**
|
|
1306
|
+
* Whether a filter is expensive enough to be worth memoising: the full-scan
|
|
1307
|
+
* or subtree-walk families. Cheap lookups (=, in on a code list, child-of)
|
|
1308
|
+
* are not cached. Mirrors the dispatch in filter().
|
|
1309
|
+
* @private
|
|
1310
|
+
*/
|
|
1311
|
+
_isCacheableFilter(prop, op) {
|
|
1312
|
+
if (prop === 'concept' || prop === 'code') {
|
|
1313
|
+
return op === 'is-a' || op === 'descendent-of' || op === 'is-not-a' || op === 'regex';
|
|
1314
|
+
}
|
|
1315
|
+
if (prop === 'child' && op === 'exists') {
|
|
1316
|
+
return true;
|
|
1317
|
+
}
|
|
1318
|
+
const propertyDefs = this.propertyDefinitions();
|
|
1319
|
+
if (propertyDefs && propertyDefs.find(p => p.code === prop)) {
|
|
1320
|
+
return true;
|
|
1321
|
+
}
|
|
1322
|
+
return ['notSelectable', 'status', 'inactive', 'deprecated'].includes(prop);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* Cache key pinning resolved analysis to the code system identity + version.
|
|
1327
|
+
* @private
|
|
1328
|
+
*/
|
|
1329
|
+
_filterCacheVersionKey() {
|
|
1330
|
+
const url = this.codeSystem.jsonObj.url;
|
|
1331
|
+
if (!url) {
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
return url + '|' + (this.codeSystem.jsonObj.version || '');
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1264
1337
|
/**
|
|
1265
1338
|
* Handle concept/code filters (is-a, descendent-of, etc.)
|
|
1266
1339
|
* @param {FilterExecutionContext} filterContext - Filter context
|
package/tx/cs/cs-hgvs.js
CHANGED
package/tx/cs/cs-lang.js
CHANGED
|
@@ -430,6 +430,14 @@ class IETFLanguageCodeProvider extends CodeSystemProvider {
|
|
|
430
430
|
return null;
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
specialEnumeration() {
|
|
434
|
+
// The BCP-47 language grammar is unbounded, but a useful base expansion is
|
|
435
|
+
// the common languages enumerated by the THO "Languages" value set. A whole-
|
|
436
|
+
// code-system expansion returns that value set marked as an incomplete
|
|
437
|
+
// (unclosed) expansion, mirroring how UCUM uses its common-units value set.
|
|
438
|
+
return 'http://terminology.hl7.org/ValueSet/Languages';
|
|
439
|
+
}
|
|
440
|
+
|
|
433
441
|
isNotClosed() {
|
|
434
442
|
return true;
|
|
435
443
|
}
|
package/tx/cs/cs-mimetypes.js
CHANGED
|
@@ -188,6 +188,10 @@ class MimeTypeServices extends CodeSystemProvider {
|
|
|
188
188
|
return true;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
canBeExpanded() {
|
|
192
|
+
return false; // MIME types cannot be iterated / enumerated
|
|
193
|
+
}
|
|
194
|
+
|
|
191
195
|
}
|
|
192
196
|
|
|
193
197
|
class MimeTypeServicesFactory extends CodeSystemFactoryProvider {
|