fhirsmith 0.6.0 → 0.7.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +2 -0
  3. package/configurations/projector.json +21 -0
  4. package/configurations/readme.md +5 -0
  5. package/library/package-manager.js +0 -2
  6. package/library/version-utilities.js +85 -0
  7. package/package.json +1 -1
  8. package/packages/package-crawler.js +44 -9
  9. package/packages/packages.js +1 -0
  10. package/registry/crawler.js +35 -14
  11. package/registry/registry.js +3 -0
  12. package/server.js +4 -0
  13. package/tx/README.md +4 -4
  14. package/tx/cs/cs-loinc.js +5 -2
  15. package/tx/cs/cs-provider-api.js +25 -1
  16. package/tx/cs/cs-provider-list.js +2 -2
  17. package/tx/library/canonical-resource.js +6 -1
  18. package/tx/library.js +127 -10
  19. package/tx/ocl/README.md +236 -0
  20. package/tx/ocl/cache/cache-paths.cjs +32 -0
  21. package/tx/ocl/cache/cache-paths.js +2 -0
  22. package/tx/ocl/cache/cache-utils.cjs +43 -0
  23. package/tx/ocl/cache/cache-utils.js +2 -0
  24. package/tx/ocl/cm-ocl.cjs +531 -0
  25. package/tx/ocl/cm-ocl.js +1 -105
  26. package/tx/ocl/cs-ocl.cjs +1779 -0
  27. package/tx/ocl/cs-ocl.js +1 -38
  28. package/tx/ocl/fingerprint/fingerprint.cjs +67 -0
  29. package/tx/ocl/fingerprint/fingerprint.js +2 -0
  30. package/tx/ocl/http/client.cjs +31 -0
  31. package/tx/ocl/http/client.js +2 -0
  32. package/tx/ocl/http/pagination.cjs +98 -0
  33. package/tx/ocl/http/pagination.js +2 -0
  34. package/tx/ocl/jobs/background-queue.cjs +200 -0
  35. package/tx/ocl/jobs/background-queue.js +2 -0
  36. package/tx/ocl/mappers/concept-mapper.cjs +66 -0
  37. package/tx/ocl/mappers/concept-mapper.js +2 -0
  38. package/tx/ocl/model/concept-filter-context.cjs +51 -0
  39. package/tx/ocl/model/concept-filter-context.js +2 -0
  40. package/tx/ocl/shared/constants.cjs +15 -0
  41. package/tx/ocl/shared/constants.js +2 -0
  42. package/tx/ocl/shared/patches.cjs +224 -0
  43. package/tx/ocl/shared/patches.js +2 -0
  44. package/tx/ocl/vs-ocl.cjs +1848 -0
  45. package/tx/ocl/vs-ocl.js +1 -104
  46. package/tx/operation-context.js +8 -1
  47. package/tx/params.js +24 -3
  48. package/tx/provider.js +47 -0
  49. package/tx/tx-html.js +1 -1
  50. package/tx/tx.js +8 -0
  51. package/tx/vs/vs-vsac.js +4 -3
  52. package/tx/workers/batch-validate.js +3 -2
  53. package/tx/workers/batch.js +3 -2
  54. package/tx/workers/expand.js +64 -9
  55. package/tx/workers/lookup.js +5 -4
  56. package/tx/workers/read.js +2 -1
  57. package/tx/workers/related.js +3 -2
  58. package/tx/workers/search.js +4 -9
  59. package/tx/workers/subsumes.js +3 -2
  60. package/tx/workers/translate.js +4 -3
  61. package/tx/workers/validate.js +132 -40
  62. package/tx/workers/worker.js +1 -7
@@ -0,0 +1,224 @@
1
+ const { OCL_CODESYSTEM_MARKER_EXTENSION } = require('./constants');
2
+
3
+ const OCL_SEARCH_PATCH_FLAG = Symbol.for('fhirsmith.ocl.search.codesystem.code.patch');
4
+ const TXPARAMS_HASH_PATCH_FLAG = Symbol.for('fhirsmith.ocl.txparameters.hash.filter.patch');
5
+ const OCL_EXPAND_WHOLE_SYSTEM_PATCH_FLAG = Symbol.for('fhirsmith.ocl.expand.whole-system.patch');
6
+ const OCL_EXPAND_WHOLE_SYSTEM_RETRY_FLAG = Symbol.for('fhirsmith.ocl.expand.whole-system.retry');
7
+
8
+ function hasOCLCodeSystemMarker(resource) {
9
+ const extensions = Array.isArray(resource?.extension) ? resource.extension : [];
10
+ return extensions.some(ext => ext && ext.url === OCL_CODESYSTEM_MARKER_EXTENSION);
11
+ }
12
+
13
+ function filterConceptTreeByCode(concepts, wantedCode) {
14
+ if (!Array.isArray(concepts) || concepts.length === 0) {
15
+ return [];
16
+ }
17
+
18
+ const matches = [];
19
+ for (const concept of concepts) {
20
+ if (!concept || typeof concept !== 'object') {
21
+ continue;
22
+ }
23
+
24
+ const childMatches = filterConceptTreeByCode(concept.concept, wantedCode);
25
+ const isSelfMatch = concept.code != null && String(concept.code) === wantedCode;
26
+ if (!isSelfMatch && childMatches.length === 0) {
27
+ continue;
28
+ }
29
+
30
+ const clone = { ...concept };
31
+ if (childMatches.length > 0) {
32
+ clone.concept = childMatches;
33
+ } else {
34
+ delete clone.concept;
35
+ }
36
+ matches.push(clone);
37
+ }
38
+
39
+ return matches;
40
+ }
41
+
42
+ function filterOCLCodeSystemResourceByCode(resource, code) {
43
+ if (!resource || typeof resource !== 'object') {
44
+ return resource;
45
+ }
46
+
47
+ const filteredConcepts = filterConceptTreeByCode(resource.concept, code);
48
+ return {
49
+ ...resource,
50
+ concept: filteredConcepts
51
+ };
52
+ }
53
+
54
+ function patchSearchWorkerForOCLCodeFiltering() {
55
+ let SearchWorker;
56
+ try {
57
+ SearchWorker = require('../../workers/search');
58
+ } catch (_error) {
59
+ return;
60
+ }
61
+
62
+ if (!SearchWorker || !SearchWorker.prototype) {
63
+ return;
64
+ }
65
+
66
+ const proto = SearchWorker.prototype;
67
+ if (proto[OCL_SEARCH_PATCH_FLAG] === true || typeof proto.searchCodeSystems !== 'function') {
68
+ return;
69
+ }
70
+
71
+ const originalSearchCodeSystems = proto.searchCodeSystems;
72
+ proto.searchCodeSystems = function patchedSearchCodeSystems(params) {
73
+ const matches = originalSearchCodeSystems.call(this, params);
74
+ const requestedCode = params?.code == null ? '' : String(params.code);
75
+
76
+ if (!requestedCode) {
77
+ return matches;
78
+ }
79
+
80
+ const filtered = [];
81
+ for (const resource of matches) {
82
+ if (!hasOCLCodeSystemMarker(resource)) {
83
+ filtered.push(resource);
84
+ continue;
85
+ }
86
+
87
+ const projected = filterOCLCodeSystemResourceByCode(resource, requestedCode);
88
+ if (Array.isArray(projected?.concept) && projected.concept.length > 0) {
89
+ filtered.push(projected);
90
+ }
91
+ }
92
+
93
+ return filtered;
94
+ };
95
+
96
+ Object.defineProperty(proto, OCL_SEARCH_PATCH_FLAG, {
97
+ value: true,
98
+ writable: false,
99
+ configurable: false,
100
+ enumerable: false
101
+ });
102
+ }
103
+
104
+ function normalizeFilterForCacheKey(filter) {
105
+ if (typeof filter !== 'string') {
106
+ return '';
107
+ }
108
+
109
+ return filter.trim().toLowerCase();
110
+ }
111
+
112
+ function ensureTxParametersHashIncludesFilter(TxParameters) {
113
+ const proto = TxParameters && TxParameters.prototype;
114
+ if (!proto || proto[TXPARAMS_HASH_PATCH_FLAG] === true || typeof proto.hashSource !== 'function') {
115
+ return;
116
+ }
117
+
118
+ const originalHashSource = proto.hashSource;
119
+ proto.hashSource = function hashSourceWithFilter() {
120
+ const base = originalHashSource.call(this);
121
+ const normalizedFilter = normalizeFilterForCacheKey(this.filter);
122
+ return `${base}|filter=${normalizedFilter}`;
123
+ };
124
+
125
+ Object.defineProperty(proto, TXPARAMS_HASH_PATCH_FLAG, {
126
+ value: true,
127
+ writable: false,
128
+ configurable: false,
129
+ enumerable: false
130
+ });
131
+ }
132
+
133
+ function isOclWholeSystemRetryCandidate(expander, cset, filter) {
134
+ if (!expander || !cset) {
135
+ return false;
136
+ }
137
+
138
+ // Only touch the OCL ValueSet path where OCL helpers are attached.
139
+ const sourceValueSet = expander?.valueSet;
140
+ if (!sourceValueSet || typeof sourceValueSet.oclFetchConcepts !== 'function') {
141
+ return false;
142
+ }
143
+
144
+ // This fallback is only for whole-system, unfiltered includes.
145
+ if (!cset.system || cset.concept || cset.filter) {
146
+ return false;
147
+ }
148
+ if (!filter || filter.isNull !== true) {
149
+ return false;
150
+ }
151
+
152
+ // Only engage when request did not ask for an explicit page and we have a limit.
153
+ if (!(expander.count < 0 && expander.offset < 0 && expander.limitCount > 0)) {
154
+ return false;
155
+ }
156
+
157
+ // Prevent recursive retries for the same include call path.
158
+ if (expander[OCL_EXPAND_WHOLE_SYSTEM_RETRY_FLAG] === true) {
159
+ return false;
160
+ }
161
+
162
+ return true;
163
+ }
164
+
165
+ function patchValueSetExpandWholeSystemForOcl() {
166
+ let expandModule;
167
+ try {
168
+ expandModule = require('../../workers/expand');
169
+ } catch (_error) {
170
+ return;
171
+ }
172
+
173
+ const ValueSetExpander = expandModule?.ValueSetExpander;
174
+ const proto = ValueSetExpander && ValueSetExpander.prototype;
175
+ if (!proto || proto[OCL_EXPAND_WHOLE_SYSTEM_PATCH_FLAG] === true || typeof proto.includeCodes !== 'function') {
176
+ return;
177
+ }
178
+
179
+ const originalIncludeCodes = proto.includeCodes;
180
+ proto.includeCodes = async function patchedIncludeCodes(cset, path, vsSrc, compose, filter, expansion, excludeInactive, notClosed) {
181
+ try {
182
+ return await originalIncludeCodes.call(this, cset, path, vsSrc, compose, filter, expansion, excludeInactive, notClosed);
183
+ } catch (error) {
184
+ if (!isOclWholeSystemRetryCandidate(this, cset, filter)) {
185
+ throw error;
186
+ }
187
+
188
+ const prevCount = this.count;
189
+ const prevOffset = this.offset;
190
+ this.count = this.limitCount;
191
+ this.offset = 0;
192
+
193
+ // Mirror effective pagination into expansion parameters for transparency.
194
+ if (expansion && typeof this.addParamInt === 'function') {
195
+ this.addParamInt(expansion, 'offset', this.offset);
196
+ this.addParamInt(expansion, 'count', this.count);
197
+ expansion.offset = this.offset;
198
+ }
199
+
200
+ this[OCL_EXPAND_WHOLE_SYSTEM_RETRY_FLAG] = true;
201
+ try {
202
+ return await originalIncludeCodes.call(this, cset, path, vsSrc, compose, filter, expansion, excludeInactive, notClosed);
203
+ } finally {
204
+ this[OCL_EXPAND_WHOLE_SYSTEM_RETRY_FLAG] = false;
205
+ this.count = prevCount;
206
+ this.offset = prevOffset;
207
+ }
208
+ }
209
+ };
210
+
211
+ Object.defineProperty(proto, OCL_EXPAND_WHOLE_SYSTEM_PATCH_FLAG, {
212
+ value: true,
213
+ writable: false,
214
+ configurable: false,
215
+ enumerable: false
216
+ });
217
+ }
218
+
219
+ module.exports = {
220
+ patchSearchWorkerForOCLCodeFiltering,
221
+ ensureTxParametersHashIncludesFilter,
222
+ patchValueSetExpandWholeSystemForOcl,
223
+ normalizeFilterForCacheKey
224
+ };
@@ -0,0 +1,2 @@
1
+ module.exports = require('./patches.cjs');
2
+