fhirsmith 0.4.2 → 0.5.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 (92) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/library/cron-utilities.js +136 -0
  4. package/library/html-server.js +13 -29
  5. package/library/html.js +3 -8
  6. package/library/languages.js +160 -37
  7. package/library/package-manager.js +48 -1
  8. package/library/utilities.js +100 -19
  9. package/package.json +2 -2
  10. package/packages/package-crawler.js +6 -1
  11. package/packages/packages.js +38 -54
  12. package/publisher/publisher.js +19 -27
  13. package/registry/api.js +11 -10
  14. package/registry/crawler.js +31 -29
  15. package/registry/model.js +5 -26
  16. package/registry/registry.js +32 -41
  17. package/server.js +53 -5
  18. package/shl/shl.js +0 -18
  19. package/static/assets/js/statuspage.js +1 -9
  20. package/stats.js +39 -1
  21. package/token/token.js +14 -9
  22. package/translations/Messages.properties +2 -1
  23. package/tx/README.md +17 -6
  24. package/tx/cs/cs-api.js +19 -1
  25. package/tx/cs/cs-base.js +77 -0
  26. package/tx/cs/cs-country.js +46 -0
  27. package/tx/cs/cs-cpt.js +9 -5
  28. package/tx/cs/cs-cs.js +27 -13
  29. package/tx/cs/cs-lang.js +60 -22
  30. package/tx/cs/cs-loinc.js +69 -98
  31. package/tx/cs/cs-mimetypes.js +4 -0
  32. package/tx/cs/cs-ndc.js +6 -0
  33. package/tx/cs/cs-omop.js +16 -15
  34. package/tx/cs/cs-rxnorm.js +23 -1
  35. package/tx/cs/cs-snomed.js +283 -40
  36. package/tx/cs/cs-ucum.js +90 -70
  37. package/tx/importers/import-sct.module.js +371 -35
  38. package/tx/importers/readme.md +117 -7
  39. package/tx/library/bundle.js +5 -0
  40. package/tx/library/capabilitystatement.js +3 -142
  41. package/tx/library/codesystem.js +19 -173
  42. package/tx/library/conceptmap.js +4 -218
  43. package/tx/library/designations.js +14 -1
  44. package/tx/library/extensions.js +7 -0
  45. package/tx/library/namingsystem.js +3 -89
  46. package/tx/library/operation-outcome.js +8 -3
  47. package/tx/library/parameters.js +3 -2
  48. package/tx/library/renderer.js +10 -6
  49. package/tx/library/terminologycapabilities.js +3 -243
  50. package/tx/library/valueset.js +3 -235
  51. package/tx/library.js +100 -13
  52. package/tx/operation-context.js +23 -4
  53. package/tx/params.js +35 -38
  54. package/tx/provider.js +6 -5
  55. package/tx/sct/expressions.js +12 -3
  56. package/tx/tx-html.js +80 -89
  57. package/tx/tx.fhir.org.yml +6 -5
  58. package/tx/tx.js +163 -13
  59. package/tx/vs/vs-database.js +56 -39
  60. package/tx/vs/vs-package.js +21 -2
  61. package/tx/vs/vs-vsac.js +175 -39
  62. package/tx/workers/batch-validate.js +2 -0
  63. package/tx/workers/batch.js +2 -0
  64. package/tx/workers/expand.js +132 -112
  65. package/tx/workers/lookup.js +33 -14
  66. package/tx/workers/metadata.js +2 -2
  67. package/tx/workers/read.js +3 -2
  68. package/tx/workers/related.js +574 -0
  69. package/tx/workers/search.js +46 -9
  70. package/tx/workers/subsumes.js +13 -3
  71. package/tx/workers/translate.js +7 -3
  72. package/tx/workers/validate.js +258 -285
  73. package/tx/workers/worker.js +43 -39
  74. package/tx/xml/bundle-xml.js +237 -0
  75. package/tx/xml/xml-base.js +215 -64
  76. package/tx/xversion/xv-bundle.js +71 -0
  77. package/tx/xversion/xv-capabiliityStatement.js +137 -0
  78. package/tx/xversion/xv-codesystem.js +169 -0
  79. package/tx/xversion/xv-conceptmap.js +224 -0
  80. package/tx/xversion/xv-namingsystem.js +88 -0
  81. package/tx/xversion/xv-operationoutcome.js +27 -0
  82. package/tx/xversion/xv-parameters.js +87 -0
  83. package/tx/xversion/xv-resource.js +45 -0
  84. package/tx/xversion/xv-terminologyCapabilities.js +214 -0
  85. package/tx/xversion/xv-valueset.js +234 -0
  86. package/utilities/dev-proxy-server.js +126 -0
  87. package/utilities/explode-results.js +58 -0
  88. package/utilities/split-by-system.js +198 -0
  89. package/utilities/vsac-cs-fetcher.js +0 -0
  90. package/{windows-install.js → utilities/windows-install.js} +2 -0
  91. package/vcl/vcl.js +0 -18
  92. package/xig/xig.js +108 -99
@@ -0,0 +1,198 @@
1
+ import { createReadStream, writeFileSync } from 'fs';
2
+ import { createInterface } from 'readline';
3
+ import { createHash } from 'crypto';
4
+
5
+ const inputFile = '/Users/grahamegrieve/temp/tx-comp/comparison.ndjson';
6
+ const outDir = '/Users/grahamegrieve/temp/tx-comp/';
7
+
8
+ // Map well-known system URLs to short names
9
+ const systemNames = {
10
+ 'http://snomed.info/sct': 'snomed',
11
+ 'http://loinc.org': 'loinc',
12
+ 'http://unitsofmeasure.org': 'ucum',
13
+ 'http://hl7.org/fhir/sid/icd-10': 'icd10',
14
+ 'http://hl7.org/fhir/sid/icd-10-cm': 'icd10cm',
15
+ 'http://hl7.org/fhir/sid/icd-9-cm': 'icd9cm',
16
+ 'http://www.nlm.nih.gov/research/umls/rxnorm': 'rxnorm',
17
+ 'http://hl7.org/fhir/sid/ndc': 'ndc',
18
+ 'http://www.ama-assn.org/go/cpt': 'cpt',
19
+ 'urn:ietf:bcp:13': 'mimetypes',
20
+ 'urn:ietf:bcp:47': 'bcp47',
21
+ 'urn:iso:std:iso:3166': 'iso3166',
22
+ 'urn:iso:std:iso:4217': 'iso4217',
23
+ };
24
+
25
+ let unknownCounter = 0;
26
+ const unknownMap = new Map(); // url -> assigned name
27
+
28
+ function nameForSystem(url) {
29
+ if (!url) return null;
30
+ // exact match
31
+ if (systemNames[url]) return systemNames[url];
32
+ // check if it starts with a known prefix (for versioned URLs like snomed with editions)
33
+ for (const [key, name] of Object.entries(systemNames)) {
34
+ if (url.startsWith(key)) return name;
35
+ }
36
+ // try to derive a name from the URL
37
+ if (url.startsWith('http://hl7.org/fhir/')) {
38
+ // e.g. http://hl7.org/fhir/administrative-gender -> administrative-gender
39
+ const parts = url.replace('http://hl7.org/fhir/', '').split('/');
40
+ const last = parts[parts.length - 1];
41
+ if (last && last.length > 0 && last.length < 60) return 'fhir-' + last;
42
+ }
43
+ if (url.startsWith('http://terminology.hl7.org/')) {
44
+ const parts = url.replace('http://terminology.hl7.org/', '').split('/');
45
+ const last = parts[parts.length - 1];
46
+ if (last && last.length > 0 && last.length < 60) return 'tho-' + last;
47
+ }
48
+ if (url.startsWith('http://hl7.org/fhir/v2/')) {
49
+ return 'v2-' + url.replace('http://hl7.org/fhir/v2/', '').replace(/\//g, '-');
50
+ }
51
+ if (url.startsWith('http://hl7.org/fhir/v3/') || url.startsWith('http://terminology.hl7.org/CodeSystem/v3-')) {
52
+ const tail = url.includes('v3/') ? url.split('v3/').pop() : url.split('v3-').pop();
53
+ return 'v3-' + tail.replace(/\//g, '-');
54
+ }
55
+ // fall back to numbered
56
+ if (unknownMap.has(url)) return unknownMap.get(url);
57
+ unknownCounter++;
58
+ const name = 'n' + String(unknownCounter).padStart(3, '0');
59
+ unknownMap.set(url, name);
60
+ return name;
61
+ }
62
+
63
+ function extractSystems(line) {
64
+ let obj;
65
+ try { obj = JSON.parse(line); } catch { return null; }
66
+
67
+ const systems = new Set();
68
+ const reqBody = obj.requestBody;
69
+ if (!reqBody) return { systems: new Set(), obj };
70
+
71
+ let req;
72
+ try { req = JSON.parse(reqBody); } catch { return { systems: new Set(), obj }; }
73
+
74
+ // Walk the parameters looking for system values, codings, codeableConcepts
75
+ if (req.parameter) {
76
+ for (const p of req.parameter) {
77
+ if (p.name === 'system' && p.valueUri) {
78
+ systems.add(p.valueUri);
79
+ }
80
+ if (p.name === 'coding' && p.valueCoding?.system) {
81
+ systems.add(p.valueCoding.system);
82
+ }
83
+ if (p.name === 'codeableConcept' && p.valueCodeableConcept?.coding) {
84
+ for (const c of p.valueCodeableConcept.coding) {
85
+ if (c.system) systems.add(c.system);
86
+ }
87
+ }
88
+ if (p.name === 'url' && p.valueUri) {
89
+ // This is a ValueSet URL, not a system - skip
90
+ }
91
+ // For batch-validate, look inside nested resources
92
+ if (p.name === 'validation' && p.resource?.parameter) {
93
+ for (const inner of p.resource.parameter) {
94
+ if (inner.name === 'system' && inner.valueUri) systems.add(inner.valueUri);
95
+ if (inner.name === 'coding' && inner.valueCoding?.system) systems.add(inner.valueCoding.system);
96
+ if (inner.name === 'codeableConcept' && inner.valueCodeableConcept?.coding) {
97
+ for (const c of inner.valueCodeableConcept.coding) {
98
+ if (c.system) systems.add(c.system);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ // Also check if it's a Bundle (batch)
107
+ if (req.entry) {
108
+ for (const entry of req.entry) {
109
+ const res = entry.resource;
110
+ if (res?.parameter) {
111
+ for (const p of res.parameter) {
112
+ if (p.name === 'system' && p.valueUri) systems.add(p.valueUri);
113
+ if (p.name === 'coding' && p.valueCoding?.system) systems.add(p.valueCoding.system);
114
+ if (p.name === 'codeableConcept' && p.valueCodeableConcept?.coding) {
115
+ for (const c of p.valueCodeableConcept.coding) {
116
+ if (c.system) systems.add(c.system);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ return { systems, obj };
125
+ }
126
+
127
+ async function run() {
128
+ const seenHashes = new Set();
129
+ const fileLines = new Map(); // filename -> lines[]
130
+ let totalLines = 0;
131
+ let dupes = 0;
132
+ let noSystem = 0;
133
+
134
+ const rl = createInterface({
135
+ input: createReadStream(inputFile),
136
+ crlfDelay: Infinity
137
+ });
138
+
139
+ for await (const line of rl) {
140
+ if (!line.trim()) continue;
141
+ totalLines++;
142
+
143
+ // Hash the requestBody for dedup (not the whole line, since prod/dev responses differ)
144
+ let parsed;
145
+ try { parsed = JSON.parse(line); } catch { continue; }
146
+
147
+ const hashInput = (parsed.method || '') + '|' + (parsed.url || '') + '|' + (parsed.requestBody || '');
148
+ const hash = createHash('md5').update(hashInput).digest('hex');
149
+
150
+ if (seenHashes.has(hash)) {
151
+ dupes++;
152
+ continue;
153
+ }
154
+ seenHashes.add(hash);
155
+
156
+ const result = extractSystems(line);
157
+ if (!result) continue;
158
+
159
+ const { systems } = result;
160
+
161
+ let filename;
162
+ if (systems.size === 0) {
163
+ noSystem++;
164
+ filename = 'system-unknown.ndjson';
165
+ } else if (systems.size === 1) {
166
+ const sysUrl = [...systems][0];
167
+ const name = nameForSystem(sysUrl);
168
+ filename = `system-${name}.ndjson`;
169
+ } else {
170
+ filename = 'system-multiple.ndjson';
171
+ }
172
+
173
+ if (!fileLines.has(filename)) fileLines.set(filename, []);
174
+ fileLines.get(filename).push(line);
175
+
176
+ if (totalLines % 50000 === 0) {
177
+ console.log(` processed ${totalLines} lines...`);
178
+ }
179
+ }
180
+
181
+ // Write all files
182
+ for (const [filename, lines] of fileLines) {
183
+ const path = outDir + filename;
184
+ writeFileSync(path, lines.join('\n') + '\n');
185
+ console.log(` ${filename}: ${lines.length} entries`);
186
+ }
187
+
188
+ console.log(`\nDone. ${totalLines} total lines, ${dupes} duplicates removed, ${noSystem} with no system found.`);
189
+
190
+ // Write the unknown system mapping
191
+ if (unknownMap.size > 0) {
192
+ const mapping = Object.fromEntries(unknownMap);
193
+ writeFileSync(outDir + 'system-mapping.json', JSON.stringify(mapping, null, 2));
194
+ console.log(`\nUnknown system mapping written to system-mapping.json (${unknownMap.size} systems)`);
195
+ }
196
+ }
197
+
198
+ run().catch(e => { console.error(e); process.exit(1); });
File without changes
@@ -1,6 +1,8 @@
1
1
  // npm install -g node-windows
2
2
  // npm link node-windows
3
3
 
4
+ // the idea here is that you hack this into shape for what you want
5
+
4
6
  var Service = require('node-windows').Service;
5
7
 
6
8
  // Create a new service object
package/vcl/vcl.js CHANGED
@@ -112,24 +112,6 @@ class VCLModule {
112
112
  };
113
113
  }
114
114
 
115
- // Enhanced HTML escaping
116
- escapeHtml(str) {
117
- if (!str || typeof str !== 'string') return '';
118
-
119
- const escapeMap = {
120
- '&': '&amp;',
121
- '<': '&lt;',
122
- '>': '&gt;',
123
- '"': '&quot;',
124
- "'": '&#x27;',
125
- '/': '&#x2F;',
126
- '`': '&#x60;',
127
- '=': '&#x3D;'
128
- };
129
-
130
- return str.replace(/[&<>"'`=/]/g, (match) => escapeMap[match]);
131
- }
132
-
133
115
  // VCL expression validation
134
116
  validateVCLInput(vcl) {
135
117
  if (!vcl || typeof vcl !== 'string') {