fhirsmith 0.18.2 → 1.0.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 (203) hide show
  1. package/ACCESSIBILITY.md +119 -0
  2. package/CHANGELOG.md +149 -0
  3. package/CONFIGURATION.md +305 -0
  4. package/LICENSE +29 -0
  5. package/README.md +29 -122
  6. package/cluster.js +1220 -0
  7. package/cluster.md +707 -0
  8. package/config-template.json +77 -8
  9. package/extension-tracker/extension-tracker-template.html +5 -8
  10. package/extension-tracker/extension-tracker.js +0 -2
  11. package/folder/folder.js +7 -6
  12. package/ips/analysis.js +560 -0
  13. package/ips/ips-template.html +5 -8
  14. package/ips/ips.js +132 -59
  15. package/ips/nationality.js +120 -0
  16. package/ips/pipeline.js +74 -21
  17. package/ips/presenter.js +1 -1
  18. package/ips/readme.md +132 -25
  19. package/ips/render.js +466 -77
  20. package/ips/store.js +79 -22
  21. package/ips/structure.js +247 -0
  22. package/ips/terminology.js +1 -1
  23. package/library/body-limit.js +22 -0
  24. package/library/cache-generation.js +15 -0
  25. package/library/cgroup-memory.js +133 -1
  26. package/library/cluster-counts.js +121 -0
  27. package/library/cluster-events.js +153 -0
  28. package/library/cluster-metrics.js +193 -0
  29. package/library/cluster-status.js +468 -0
  30. package/library/config-reader.js +96 -0
  31. package/library/daily-report.js +100 -2
  32. package/library/html-server.js +27 -1
  33. package/library/i18nsupport.js +23 -1
  34. package/library/keep-alive.js +18 -0
  35. package/library/languages.js +17 -3
  36. package/library/logger.js +31 -0
  37. package/library/memory-guard.js +428 -0
  38. package/library/native-memory.js +52 -0
  39. package/library/npm-audit.js +51 -25
  40. package/library/openapi-doc.js +7 -5
  41. package/library/package-manager.js +8 -0
  42. package/library/role.js +197 -0
  43. package/library/server-credentials.js +169 -0
  44. package/library/sqlite-utilities.js +59 -0
  45. package/library/ssrf-guard.js +100 -0
  46. package/library/static-response-stats.js +16 -0
  47. package/library/version-utilities.js +50 -0
  48. package/library/worker-count.js +192 -0
  49. package/nginx.md +30 -11
  50. package/npmprojector/indexer.js +18 -10
  51. package/package.json +11 -8
  52. package/packages/feed-check-page.js +117 -0
  53. package/packages/feed-check.js +279 -0
  54. package/packages/npm-check.js +57 -3
  55. package/packages/npm-signing.js +219 -0
  56. package/packages/openapi.yaml +207 -1
  57. package/packages/package-crawler.js +322 -41
  58. package/packages/packages-template.html +6 -8
  59. package/packages/packages.js +604 -57
  60. package/packages/readme.md +113 -1
  61. package/publisher/publisher-template.html +2 -5
  62. package/publisher/publisher.js +121 -110
  63. package/publisher/readme.md +75 -0
  64. package/registry/crawler.js +84 -108
  65. package/registry/documentation/configuration.md +34 -0
  66. package/registry/registry-template.html +5 -8
  67. package/registry/registry.js +5 -4
  68. package/root-bare-template.html +7 -4
  69. package/root-template.html +5 -8
  70. package/server.js +398 -198
  71. package/shl/shl.js +38 -37
  72. package/static/a11y/a11y-check.js +832 -0
  73. package/static/a11y/axe.min.js +12 -0
  74. package/static/assets/css/bootstrap-fhir.css +15 -15
  75. package/static/assets/images/flags/au.svg +18 -0
  76. package/static/assets/images/flags/unknown.svg +11 -0
  77. package/static/assets/images/flags/us.svg +63 -0
  78. package/static/assets/images/icon_audit.png +0 -0
  79. package/static/assets/images/icon_clinical.png +0 -0
  80. package/static/assets/images/icon_document.png +0 -0
  81. package/static/assets/images/icon_external.png +0 -0
  82. package/static/assets/images/icon_medication.png +0 -0
  83. package/static/assets/images/icon_organization.png +0 -0
  84. package/static/assets/images/icon_other.png +0 -0
  85. package/static/assets/images/icon_person.png +0 -0
  86. package/static/assets/images/icon_section.png +0 -0
  87. package/stats-db.js +45 -0
  88. package/stats.js +197 -10
  89. package/testing/testing-template.html +5 -8
  90. package/token/nginx_passport_setup.md +1 -1
  91. package/token/security_guide.md +1 -1
  92. package/token/token-template.html +2 -5
  93. package/token/token.js +40 -36
  94. package/translations/Messages.properties +19 -0
  95. package/tx/README.md +154 -4
  96. package/tx/closure/closure-store.js +126 -3
  97. package/tx/cm/cm-database.js +69 -57
  98. package/tx/cm/cm-package.js +29 -1
  99. package/tx/cs/cs-api.js +284 -19
  100. package/tx/cs/cs-api.md +41 -4
  101. package/tx/cs/cs-areacode.js +1 -1
  102. package/tx/cs/cs-country.js +17 -5
  103. package/tx/cs/cs-cpt.js +33 -16
  104. package/tx/cs/cs-cs.js +148 -14
  105. package/tx/cs/cs-currency.js +1 -1
  106. package/tx/cs/cs-icd11.js +21 -13
  107. package/tx/cs/cs-icf.js +22 -3
  108. package/tx/cs/cs-lang.js +2 -2
  109. package/tx/cs/cs-loinc.js +185 -66
  110. package/tx/cs/cs-mimetypes.js +12 -2
  111. package/tx/cs/cs-ndc.js +40 -25
  112. package/tx/cs/cs-omop.js +54 -29
  113. package/tx/cs/cs-rxnorm.js +300 -196
  114. package/tx/cs/cs-snomed.js +608 -64
  115. package/tx/cs/cs-ucum.js +14 -4
  116. package/tx/cs/cs-unii.js +18 -14
  117. package/tx/cs/cs-virtual.js +210 -0
  118. package/tx/ecosystem-resolver.js +413 -0
  119. package/tx/html/cluster-dash-metrics.liquid +26 -0
  120. package/tx/html/cluster-home-metrics.liquid +31 -0
  121. package/tx/html/cluster-metrics-script.liquid +251 -0
  122. package/tx/html/codesystem-operations.liquid +3 -3
  123. package/tx/html/ecl-panel.liquid +1 -1
  124. package/tx/html/external-metrics.liquid +17 -0
  125. package/tx/html/operations-form.liquid +17 -17
  126. package/tx/html/search-form.liquid +11 -11
  127. package/tx/html/status-line.liquid +58 -0
  128. package/tx/html/status-panel.liquid +140 -0
  129. package/tx/html/status-tasks.liquid +25 -0
  130. package/tx/html/tx-template.html +2 -5
  131. package/tx/importers/atc-to-fhir.js +17 -16
  132. package/tx/importers/export-icd11-codesystem.js +3 -2
  133. package/tx/importers/import-icd11.js +4 -3
  134. package/tx/importers/import-loinc.module.js +118 -56
  135. package/tx/importers/import-ndc.module.js +56 -44
  136. package/tx/importers/import-rxnorm.module.js +118 -79
  137. package/tx/importers/import-sct.module.js +57 -58
  138. package/tx/importers/import-unii.module.js +37 -36
  139. package/tx/importers/subset-loinc.module.js +49 -48
  140. package/tx/importers/subset-rxnorm.module.js +29 -28
  141. package/tx/importers/terminal.js +18 -0
  142. package/tx/importers/tx-import-base.js +13 -12
  143. package/tx/importers/tx-import-settings.js +5 -4
  144. package/tx/importers/tx-import.js +53 -52
  145. package/tx/library/canonical-resource.js +49 -0
  146. package/tx/library/codesystem.js +94 -5
  147. package/tx/library/designations.js +45 -2
  148. package/tx/library/extensions.js +13 -7
  149. package/tx/library/implicit-valuesets.js +66 -0
  150. package/tx/library/renderer.js +77 -21
  151. package/tx/library/sqlite-utilities.js +2 -0
  152. package/tx/library/ucum-parsers.js +11 -1
  153. package/tx/library/wait-for-cache.js +43 -0
  154. package/tx/library.js +321 -22
  155. package/tx/openapi-operations.js +6 -4
  156. package/tx/operation-context.js +460 -99
  157. package/tx/params.js +25 -0
  158. package/tx/problems.js +3 -5
  159. package/tx/provider.js +278 -42
  160. package/tx/resource-cache-sqlite.js +1050 -0
  161. package/tx/sct/ecl.js +47 -6
  162. package/tx/sct/editions.js +39 -0
  163. package/tx/sct/expressions.js +10 -1
  164. package/tx/sct/load-config.js +107 -0
  165. package/tx/sct/mmap.js +109 -0
  166. package/tx/sct/structures.js +166 -13
  167. package/tx/sct/text-index.js +5 -1
  168. package/tx/syndication/syndication-feed.js +2 -2
  169. package/tx/tx-html.js +44 -8
  170. package/tx/tx.fhir.org.yml +8 -8
  171. package/tx/tx.js +282 -80
  172. package/tx/usage-tracker.js +39 -11
  173. package/tx/vs/vs-api.js +19 -0
  174. package/tx/vs/vs-database.js +386 -62
  175. package/tx/vs/vs-package.js +27 -4
  176. package/tx/vs/vs-vsac.js +648 -100
  177. package/tx/workers/batch-validate.js +37 -10
  178. package/tx/workers/batch.js +6 -1
  179. package/tx/workers/cache-control.js +15 -4
  180. package/tx/workers/closure.js +156 -13
  181. package/tx/workers/compare.js +8 -5
  182. package/tx/workers/expand.js +1173 -289
  183. package/tx/workers/metadata.js +35 -0
  184. package/tx/workers/search.js +53 -1
  185. package/tx/workers/supplement-filters.js +145 -0
  186. package/tx/workers/translate.js +169 -4
  187. package/tx/workers/validate.js +223 -119
  188. package/tx/workers/worker.js +123 -31
  189. package/tx/xversion/xv-valueset.js +7 -4
  190. package/utilities/submit-test-report.js +3 -1
  191. package/utilities/tx-stress-test.js +482 -0
  192. package/vcl/readme.md +37 -0
  193. package/vcl/vcl-parser.js +15 -0
  194. package/xig/readme.md +52 -0
  195. package/xig/xig-template.html +5 -8
  196. package/xig/xig.js +238 -422
  197. package/changelog-template.md +0 -22
  198. package/registry/test-runner.js +0 -237
  199. package/release.sh +0 -80
  200. package/test-scripts/repro-re2-wasm-leak.js +0 -34
  201. package/tsconfig.eslint.json +0 -9
  202. package/tx/html/dash-metrics.liquid +0 -147
  203. package/tx/html/home-metrics.liquid +0 -225
@@ -12,6 +12,7 @@ const {
12
12
  SnomedRelationshipList, SnomedReferenceSetMembers, SnomedReferenceSetIndex
13
13
  } = require('../sct/structures');
14
14
  const {SnomedExpressionServices} = require("../sct/expressions");
15
+ const terminal = require('./terminal');
15
16
 
16
17
  // definitionStatusId values from the RF2 concept file
17
18
  const SNOMED_DEFINITION_STATUS_PRIMITIVE = 900000000000074008n;
@@ -106,16 +107,16 @@ class SnomedModule extends BaseTerminologyModule {
106
107
  } catch (error) {
107
108
  this.logError(`Import command failed: ${error.message}`);
108
109
  if (options.verbose) {
109
- console.error(error.stack);
110
+ terminal.error(error.stack);
110
111
  }
111
112
  throw error;
112
113
  }
113
114
  }
114
115
 
115
116
  async confirmImport(config) {
116
- console.log(chalk.cyan(`\n📋 ${this.getName()} Import Configuration:`));
117
- console.log(` Source: ${chalk.white(config.source)}`);
118
- console.log(` Destination: ${chalk.white(config.dest)}`);
117
+ terminal.log(chalk.cyan(`\n📋 ${this.getName()} Import Configuration:`));
118
+ terminal.log(` Source: ${chalk.white(config.source)}`);
119
+ terminal.log(` Destination: ${chalk.white(config.dest)}`);
119
120
 
120
121
  if (config.edition) {
121
122
  const editions = {
@@ -125,26 +126,26 @@ class SnomedModule extends BaseTerminologyModule {
125
126
  // ... other editions
126
127
  };
127
128
  const editionName = editions[config.edition] || `Edition ${config.edition}`;
128
- console.log(` Edition: ${chalk.white(editionName)} (${config.edition})`);
129
+ terminal.log(` Edition: ${chalk.white(editionName)} (${config.edition})`);
129
130
  }
130
131
 
131
132
  if (config.version) {
132
- console.log(` Version: ${chalk.white(config.version)}`);
133
+ terminal.log(` Version: ${chalk.white(config.version)}`);
133
134
  }
134
135
 
135
136
  if (config.uri) {
136
- console.log(` Version URI: ${chalk.white(config.uri)}`);
137
+ terminal.log(` Version URI: ${chalk.white(config.uri)}`);
137
138
  }
138
139
 
139
140
  if (config.language) {
140
- console.log(` Language: ${chalk.white(config.language)}`);
141
+ terminal.log(` Language: ${chalk.white(config.language)}`);
141
142
  }
142
143
 
143
- console.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
144
- console.log(` Verbose: ${chalk.white(config.verbose ? 'Yes' : 'No')}`);
144
+ terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
145
+ terminal.log(` Verbose: ${chalk.white(config.verbose ? 'Yes' : 'No')}`);
145
146
 
146
147
  if (config.estimatedDuration) {
147
- console.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
148
+ terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
148
149
  }
149
150
 
150
151
  const { confirmed } = await inquirer.prompt({
@@ -357,7 +358,7 @@ class SnomedModule extends BaseTerminologyModule {
357
358
  } catch (error) {
358
359
  // Silent fail - auto-detection is best-effort
359
360
  if (this.config?.verbose) {
360
- console.log(`Auto-detection warning: ${error.message}`);
361
+ terminal.log(`Auto-detection warning: ${error.message}`);
361
362
  }
362
363
  }
363
364
 
@@ -661,7 +662,7 @@ class SnomedModule extends BaseTerminologyModule {
661
662
 
662
663
  async runImportWithoutConfigSaving(config) {
663
664
  try {
664
- console.log(chalk.blue.bold(`🏥 Starting ${this.getName()} Import...\n`));
665
+ terminal.log(chalk.blue.bold(`🏥 Starting ${this.getName()} Import...\n`));
665
666
 
666
667
  // Pre-flight checks
667
668
  this.logInfo('Running pre-flight checks...');
@@ -680,7 +681,7 @@ class SnomedModule extends BaseTerminologyModule {
680
681
  this.stopProgress();
681
682
  this.logError(`${this.getName()} import failed: ${error.message}`);
682
683
  if (config.verbose) {
683
- console.error(error.stack);
684
+ terminal.error(error.stack);
684
685
  }
685
686
  process.exit(1);
686
687
  }
@@ -816,10 +817,10 @@ class SnomedModule extends BaseTerminologyModule {
816
817
  const stats = await this.validateSnomedDirectory(options.source);
817
818
 
818
819
  this.logSuccess('Directory validation passed');
819
- console.log(` Concept files: ${stats.conceptFiles}`);
820
- console.log(` Description files: ${stats.descriptionFiles}`);
821
- console.log(` Relationship files: ${stats.relationshipFiles}`);
822
- console.log(` Refset directories: ${stats.refsetDirectories}`);
820
+ terminal.log(` Concept files: ${stats.conceptFiles}`);
821
+ terminal.log(` Description files: ${stats.descriptionFiles}`);
822
+ terminal.log(` Relationship files: ${stats.relationshipFiles}`);
823
+ terminal.log(` Refset directories: ${stats.refsetDirectories}`);
823
824
 
824
825
  } catch (error) {
825
826
  this.logError(`Validation failed: ${error.message}`);
@@ -843,26 +844,26 @@ class SnomedModule extends BaseTerminologyModule {
843
844
  const data = await reader.loadSnomedData();
844
845
 
845
846
  this.logSuccess('Cache file status:');
846
- console.log(` Cache Version: ${data.cacheVersion}`);
847
- console.log(` Version URI: ${data.versionUri}`);
848
- console.log(` Version Date: ${data.versionDate}`);
849
- console.log(` Edition: ${data.edition}`);
850
- console.log(` SNOMED Version: ${data.version}`);
847
+ terminal.log(` Cache Version: ${data.cacheVersion}`);
848
+ terminal.log(` Version URI: ${data.versionUri}`);
849
+ terminal.log(` Version Date: ${data.versionDate}`);
850
+ terminal.log(` Edition: ${data.edition}`);
851
+ terminal.log(` SNOMED Version: ${data.version}`);
851
852
 
852
853
  // Create structure instances to get counts
853
854
  const concepts = new SnomedConceptList(data.concept);
854
855
  const descriptions = new SnomedDescriptions(data.desc);
855
856
  const relationships = new SnomedRelationshipList(data.rel);
856
857
 
857
- console.log(` Concepts: ${concepts.count().toLocaleString()}`);
858
- console.log(` Descriptions: ${descriptions.count().toLocaleString()}`);
859
- console.log(` Relationships: ${relationships.count().toLocaleString()}`);
860
- console.log(` Active Roots: ${data.activeRoots.length}`);
861
- console.log(` Inactive Roots: ${data.inactiveRoots.length}`);
858
+ terminal.log(` Concepts: ${concepts.count().toLocaleString()}`);
859
+ terminal.log(` Descriptions: ${descriptions.count().toLocaleString()}`);
860
+ terminal.log(` Relationships: ${relationships.count().toLocaleString()}`);
861
+ terminal.log(` Active Roots: ${data.activeRoots.length}`);
862
+ terminal.log(` Inactive Roots: ${data.inactiveRoots.length}`);
862
863
 
863
864
  const fileStat = fs.statSync(cachePath);
864
- console.log(` File Size: ${(fileStat.size / (1024 * 1024 * 1024)).toFixed(2)} GB`);
865
- console.log(` Last Modified: ${fileStat.mtime.toISOString()}`);
865
+ terminal.log(` File Size: ${(fileStat.size / (1024 * 1024 * 1024)).toFixed(2)} GB`);
866
+ terminal.log(` Last Modified: ${fileStat.mtime.toISOString()}`);
866
867
 
867
868
  } catch (error) {
868
869
  this.logError(`Status check failed: ${error.message}`);
@@ -924,7 +925,7 @@ class SnomedImporterWithProgress {
924
925
  message += ` (${rate.toLocaleString()} items/sec)`;
925
926
  }
926
927
 
927
- console.log(message);
928
+ terminal.log(message);
928
929
 
929
930
  // Clean up
930
931
  this.taskStartTimes.delete(taskName);
@@ -1161,7 +1162,7 @@ class SnomedImporter {
1161
1162
  await this.saveCache();
1162
1163
 
1163
1164
  } catch (error) {
1164
- console.error('DEBUG: Import failed with error:', error);
1165
+ terminal.error('DEBUG: Import failed with error:', error);
1165
1166
  throw new Error(`Import failed: ${error.message}`);
1166
1167
  }
1167
1168
  }
@@ -1181,7 +1182,7 @@ class SnomedImporter {
1181
1182
  // extension content is layered on top.
1182
1183
  if (this.config.base) {
1183
1184
  if (this.config.verbose) {
1184
- console.log(`Loading base edition from: ${this.config.base}`);
1185
+ terminal.log(`Loading base edition from: ${this.config.base}`);
1185
1186
  }
1186
1187
  this._scanDirectory(this.config.base, files);
1187
1188
  }
@@ -1232,7 +1233,7 @@ class SnomedImporter {
1232
1233
  files.concreteValues.push(filePath);
1233
1234
  }
1234
1235
  } catch (error) {
1235
- console.log(`DEBUG: Error reading file ${filePath}: ${error.message}`);
1236
+ terminal.log(`DEBUG: Error reading file ${filePath}: ${error.message}`);
1236
1237
  }
1237
1238
  }
1238
1239
 
@@ -1528,14 +1529,15 @@ class SnomedImporter {
1528
1529
  const month = parseInt(dateStr.substring(4, 6));
1529
1530
  const day = parseInt(dateStr.substring(6, 8));
1530
1531
 
1531
- // Create target date
1532
- const targetDate = new Date(year, month - 1, day);
1533
-
1534
- // Pascal TDateTime epoch: December 30, 1899
1535
- const pascalEpoch = new Date(1899, 11, 30); // Month is 0-based in JS
1532
+ // Days since the Pascal TDateTime epoch (December 30, 1899), counted in UTC. Local dates are
1533
+ // not whole days apart: 1899 local time is the zone's historical mean time (e.g. +9:39:52 in
1534
+ // Melbourne) and today's has daylight saving, so the difference came out a day short in zones
1535
+ // ahead of UTC, and every effectiveTime in a cache built there was a day early
1536
+ const targetDate = Date.UTC(year, month - 1, day);
1537
+ const pascalEpoch = Date.UTC(1899, 11, 30); // Month is 0-based in JS
1536
1538
 
1537
1539
  // Calculate days difference
1538
- const daysDiff = Math.floor((targetDate - pascalEpoch) / (1000 * 60 * 60 * 24));
1540
+ const daysDiff = Math.round((targetDate - pascalEpoch) / (1000 * 60 * 60 * 24));
1539
1541
 
1540
1542
  // Ensure it fits in 16 bits (0-65535) and is positive
1541
1543
  if (daysDiff < 0 || daysDiff > 65535) {
@@ -1784,7 +1786,7 @@ class SnomedImporter {
1784
1786
  }
1785
1787
  this.identifiers.sort((a, b) => (a.join('|') < b.join('|') ? -1 : 1));
1786
1788
  if (this.identifiers.length > 0 || missing > 0) {
1787
- console.log(`Alternate identifiers: ${this.identifiers.length}${missing ? ` (${missing} for concepts not in this edition, ignored)` : ''}`);
1789
+ terminal.log(`Alternate identifiers: ${this.identifiers.length}${missing ? ` (${missing} for concepts not in this edition, ignored)` : ''}`);
1788
1790
  }
1789
1791
  }
1790
1792
 
@@ -1836,7 +1838,7 @@ class SnomedImporter {
1836
1838
  }
1837
1839
  this.concreteValues.sort((a, b) => (a.join('|') < b.join('|') ? -1 : 1));
1838
1840
  if (this.concreteValues.length > 0 || missing > 0) {
1839
- console.log(`Concrete values: ${this.concreteValues.length}${missing ? ` (${missing} for concepts not in this edition, ignored)` : ''}`);
1841
+ terminal.log(`Concrete values: ${this.concreteValues.length}${missing ? ` (${missing} for concepts not in this edition, ignored)` : ''}`);
1840
1842
  }
1841
1843
  }
1842
1844
 
@@ -2251,7 +2253,7 @@ class SnomedImporter {
2251
2253
 
2252
2254
  async processRefsets() {
2253
2255
  if (this.files.refsetDirectories.length === 0) {
2254
- console.log('No reference set directories found');
2256
+ terminal.log('No reference set directories found');
2255
2257
  this.refsetIndex.doneBuild();
2256
2258
  this.refsetMembers.doneBuild();
2257
2259
  return;
@@ -2367,7 +2369,7 @@ class SnomedImporter {
2367
2369
  const parts = fileName.split('_');
2368
2370
 
2369
2371
  if (parts.length < 3) {
2370
- console.log(`Skipping file with unexpected name format: ${fileName}`);
2372
+ terminal.log(`Skipping file with unexpected name format: ${fileName}`);
2371
2373
  return;
2372
2374
  }
2373
2375
 
@@ -2391,19 +2393,16 @@ class SnomedImporter {
2391
2393
  lineNumber++;
2392
2394
 
2393
2395
  if (lineNumber === 1) {
2394
- // Parse headers
2396
+ // Parse headers. Map reference sets (simple, complex, extended) are loaded like
2397
+ // any other: ECL ^ and ?fhir_vs=refset/ can name any reference set, and the
2398
+ // ICD-10 maps are among the most used.
2395
2399
  headers = line.split('\t').map(h => h.trim());
2396
-
2397
- // Skip if this looks like a mapping reference set
2398
- if (line.toLowerCase().includes('map')) {
2399
- return;
2400
- }
2401
2400
  continue;
2402
2401
  }
2403
2402
 
2404
2403
  const fields = line.split('\t');
2405
2404
  if (fields.length < 6) {
2406
- console.log(`Skipping line ${lineNumber} with insufficient fields: ${fields.length}`);
2405
+ terminal.log(`Skipping line ${lineNumber} with insufficient fields: ${fields.length}`);
2407
2406
  continue; // Minimum fields required
2408
2407
  }
2409
2408
 
@@ -2439,14 +2438,14 @@ class SnomedImporter {
2439
2438
  try {
2440
2439
  member.id = this.parseGUID(id);
2441
2440
  } catch (error) {
2442
- console.log(`Invalid GUID in ${fileName}: ${id}`);
2441
+ terminal.log(`Invalid GUID in ${fileName}: ${id}`);
2443
2442
  continue;
2444
2443
  }
2445
2444
 
2446
2445
  // Find module concept
2447
2446
  const moduleConcept = this.conceptMap.get(BigInt(moduleId));
2448
2447
  if (!moduleConcept) {
2449
- console.log(`Module concept not found: ${moduleId}`);
2448
+ terminal.log(`Module concept not found: ${moduleId}`);
2450
2449
  continue;
2451
2450
  }
2452
2451
  member.module = moduleConcept.index;
@@ -2479,7 +2478,7 @@ class SnomedImporter {
2479
2478
  // Try to find as relationship (simplified - would need relationship ID lookup)
2480
2479
  member.kind = 3; // Other/unknown
2481
2480
  member.ref = 0;
2482
- console.log(`Component not found: ${referencedComponentId}`);
2481
+ terminal.log(`Component not found: ${referencedComponentId}`);
2483
2482
  continue;
2484
2483
  }
2485
2484
  }
@@ -2502,7 +2501,7 @@ class SnomedImporter {
2502
2501
  }
2503
2502
 
2504
2503
  } catch (error) {
2505
- console.error(`Error processing reference set ${filePath}:`, error);
2504
+ terminal.error(`Error processing reference set ${filePath}:`, error);
2506
2505
  }
2507
2506
  }
2508
2507
 
@@ -2590,7 +2589,7 @@ class SnomedImporter {
2590
2589
  value = descResult.index;
2591
2590
  type = 2; // Description
2592
2591
  } else {
2593
- console.log(`Referenced component not found: ${field}`);
2592
+ terminal.log(`Referenced component not found: ${field}`);
2594
2593
  value = 0;
2595
2594
  type = 1;
2596
2595
  }
@@ -2847,7 +2846,7 @@ class SnomedImporter {
2847
2846
  } catch (error) {
2848
2847
  // Log the error but continue processing other concepts
2849
2848
  if (this.config.verbose) {
2850
- console.warn(`Warning: Could not build normal form for concept ${concept.id}: ${error.message}`);
2849
+ terminal.warn(`Warning: Could not build normal form for concept ${concept.id}: ${error.message}`);
2851
2850
  }
2852
2851
  }
2853
2852
 
@@ -2862,7 +2861,7 @@ class SnomedImporter {
2862
2861
  }
2863
2862
 
2864
2863
  if (this.config.verbose) {
2865
- console.log(`Normal forms: ${normalFormsAdded} concepts have non-trivial normal forms`);
2864
+ terminal.log(`Normal forms: ${normalFormsAdded} concepts have non-trivial normal forms`);
2866
2865
  }
2867
2866
  }
2868
2867
 
@@ -3,6 +3,7 @@ const fs = require('fs');
3
3
  const sqlite3 = require('sqlite3').verbose();
4
4
  const path = require("path");
5
5
  const readline = require("readline");
6
+ const terminal = require('./terminal');
6
7
 
7
8
  class UniiModule extends BaseTerminologyModule {
8
9
  getName() {
@@ -81,16 +82,16 @@ class UniiModule extends BaseTerminologyModule {
81
82
  const inquirer = require('inquirer');
82
83
  const chalk = require('chalk');
83
84
 
84
- console.log(chalk.cyan(`\n📋 ${this.getName()} Import Configuration:`));
85
- console.log(` Source: ${chalk.white(config.source)}`);
86
- console.log(` Destination: ${chalk.white(config.dest)}`);
87
- console.log(` Version: ${chalk.white(config.version)}`);
88
- console.log(` Create Indexes: ${chalk.white(config.createIndexes ? 'Yes' : 'No')}`);
89
- console.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
90
- console.log(` Verbose: ${chalk.white(config.verbose ? 'Yes' : 'No')}`);
85
+ terminal.log(chalk.cyan(`\n📋 ${this.getName()} Import Configuration:`));
86
+ terminal.log(` Source: ${chalk.white(config.source)}`);
87
+ terminal.log(` Destination: ${chalk.white(config.dest)}`);
88
+ terminal.log(` Version: ${chalk.white(config.version)}`);
89
+ terminal.log(` Create Indexes: ${chalk.white(config.createIndexes ? 'Yes' : 'No')}`);
90
+ terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
91
+ terminal.log(` Verbose: ${chalk.white(config.verbose ? 'Yes' : 'No')}`);
91
92
 
92
93
  if (config.estimatedDuration) {
93
- console.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
94
+ terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
94
95
  }
95
96
 
96
97
  const { confirmed } = await inquirer.prompt({
@@ -120,14 +121,14 @@ class UniiModule extends BaseTerminologyModule {
120
121
  const stats = await this.validateUniiFile(options.source, parseInt(options.sample));
121
122
 
122
123
  this.logSuccess('File validation passed');
123
- console.log(` Lines: ${stats.totalLines}`);
124
- console.log(` Estimated UNII codes: ${stats.estimatedCodes}`);
125
- console.log(` Estimated descriptions: ${stats.estimatedDescriptions}`);
126
- console.log(` Sample data format: ${stats.formatValid ? 'Valid' : 'Invalid'}`);
124
+ terminal.log(` Lines: ${stats.totalLines}`);
125
+ terminal.log(` Estimated UNII codes: ${stats.estimatedCodes}`);
126
+ terminal.log(` Estimated descriptions: ${stats.estimatedDescriptions}`);
127
+ terminal.log(` Sample data format: ${stats.formatValid ? 'Valid' : 'Invalid'}`);
127
128
 
128
129
  if (stats.warnings.length > 0) {
129
130
  this.logWarning('Validation warnings:');
130
- stats.warnings.forEach(warning => console.log(` ${warning}`));
131
+ stats.warnings.forEach(warning => terminal.log(` ${warning}`));
131
132
  }
132
133
 
133
134
  } catch (error) {
@@ -149,11 +150,11 @@ class UniiModule extends BaseTerminologyModule {
149
150
  const stats = await this.getDatabaseStats(dbPath);
150
151
 
151
152
  this.logSuccess('Database status:');
152
- console.log(` Version: ${stats.version}`);
153
- console.log(` UNII Codes: ${stats.uniiCount.toLocaleString()}`);
154
- console.log(` Descriptions: ${stats.descCount.toLocaleString()}`);
155
- console.log(` Database Size: ${stats.sizeGB.toFixed(2)} GB`);
156
- console.log(` Last Modified: ${stats.lastModified}`);
153
+ terminal.log(` Version: ${stats.version}`);
154
+ terminal.log(` UNII Codes: ${stats.uniiCount.toLocaleString()}`);
155
+ terminal.log(` Descriptions: ${stats.descCount.toLocaleString()}`);
156
+ terminal.log(` Database Size: ${stats.sizeGB.toFixed(2)} GB`);
157
+ terminal.log(` Last Modified: ${stats.lastModified}`);
157
158
 
158
159
  } catch (error) {
159
160
  this.logError(`Status check failed: ${error.message}`);
@@ -304,7 +305,7 @@ class UniiModule extends BaseTerminologyModule {
304
305
  db.serialize(() => {
305
306
  indexes.forEach(sql => {
306
307
  db.run(sql, (err) => {
307
- if (err) console.warn(`Index creation warning: ${err.message}`);
308
+ if (err) terminal.warn(`Index creation warning: ${err.message}`);
308
309
  });
309
310
  });
310
311
  });
@@ -327,7 +328,7 @@ class UniiDataMigrator {
327
328
  * @returns {Promise<void>}
328
329
  */
329
330
  async migrate(sourceFile, destFile, version = 'unknown', verbose = true) {
330
- if (verbose) console.log('Starting UNII data migration...');
331
+ if (verbose) terminal.log('Starting UNII data migration...');
331
332
 
332
333
  // Ensure destination directory exists
333
334
  const destDir = path.dirname(destFile);
@@ -350,7 +351,7 @@ class UniiDataMigrator {
350
351
  // Process source file
351
352
  await this.#processSourceFile(db, sourceFile, verbose);
352
353
 
353
- if (verbose) console.log('UNII data migration completed successfully');
354
+ if (verbose) terminal.log('UNII data migration completed successfully');
354
355
  } finally {
355
356
  await this.#closeDatabase(db, verbose);
356
357
  }
@@ -361,7 +362,7 @@ class UniiDataMigrator {
361
362
  * @private
362
363
  */
363
364
  async #createTables(db, version, verbose = true) {
364
- if (verbose) console.log('Creating database tables...');
365
+ if (verbose) terminal.log('Creating database tables...');
365
366
 
366
367
  return new Promise((resolve, reject) => {
367
368
  db.serialize(() => {
@@ -400,7 +401,7 @@ class UniiDataMigrator {
400
401
  // Insert version
401
402
  db.run('INSERT INTO UniiVersion (Version) VALUES (?)', [version], (err) => {
402
403
  if (err) return reject(err);
403
- if (verbose) console.log('Database tables created');
404
+ if (verbose) terminal.log('Database tables created');
404
405
  resolve();
405
406
  });
406
407
  });
@@ -412,7 +413,7 @@ class UniiDataMigrator {
412
413
  * @private
413
414
  */
414
415
  async #processSourceFile(db, sourceFile, verbose = true) {
415
- if (verbose) console.log('Processing source file:', sourceFile);
416
+ if (verbose) terminal.log('Processing source file:', sourceFile);
416
417
 
417
418
  if (!fs.existsSync(sourceFile)) {
418
419
  throw new Error(`Source file not found: ${sourceFile}`);
@@ -438,7 +439,7 @@ class UniiDataMigrator {
438
439
  throw new Error('Source file is empty');
439
440
  }
440
441
 
441
- if (verbose) console.log(`Read ${lines.length} lines, processing...`);
442
+ if (verbose) terminal.log(`Read ${lines.length} lines, processing...`);
442
443
 
443
444
  // Track processed codes and auto-increment keys
444
445
  const codeMap = new Map(); // code -> UniiKey
@@ -467,7 +468,7 @@ class UniiDataMigrator {
467
468
 
468
469
  // Need at least 3 columns (Display, Type, Code)
469
470
  if (cols.length < 3) {
470
- if (verbose) console.warn(`Skipping line ${i + 1}: insufficient columns`);
471
+ if (verbose) terminal.warn(`Skipping line ${i + 1}: insufficient columns`);
471
472
  continue;
472
473
  }
473
474
 
@@ -476,7 +477,7 @@ class UniiDataMigrator {
476
477
  const code = cols[2] || '';
477
478
 
478
479
  if (!code) {
479
- if (verbose) console.warn(`Skipping line ${i + 1}: empty UNII code`);
480
+ if (verbose) terminal.warn(`Skipping line ${i + 1}: empty UNII code`);
480
481
  continue;
481
482
  }
482
483
 
@@ -510,14 +511,14 @@ class UniiDataMigrator {
510
511
  });
511
512
 
512
513
  if (processedLines % 10000 === 0) {
513
- console.log(`Processed ${processedLines} lines...`);
514
+ terminal.log(`Processed ${processedLines} lines...`);
514
515
  }
515
516
  }
516
517
 
517
518
  if (verbose) {
518
- console.log(`Processing completed. Total lines processed: ${processedLines}`);
519
- console.log(`Unique UNII codes: ${codeMap.size}`);
520
- console.log(`Total descriptions: ${lastUniiDescKey}`);
519
+ terminal.log(`Processing completed. Total lines processed: ${processedLines}`);
520
+ terminal.log(`Unique UNII codes: ${codeMap.size}`);
521
+ terminal.log(`Total descriptions: ${lastUniiDescKey}`);
521
522
  }
522
523
  }
523
524
 
@@ -529,7 +530,7 @@ class UniiDataMigrator {
529
530
  return new Promise((resolve) => {
530
531
  db.close((err) => {
531
532
  if (err && verbose) {
532
- console.error('Error closing database:', err);
533
+ terminal.error('Error closing database:', err);
533
534
  }
534
535
  resolve();
535
536
  });
@@ -558,8 +559,8 @@ class UniiDataMigratorWithProgress {
558
559
  let processedLines = 0;
559
560
 
560
561
  // Override the progress reporting in the original migrator
561
- const originalConsoleLog = console.log;
562
- console.log = (...args) => {
562
+ const originalLog = terminal.log;
563
+ terminal.log = (...args) => {
563
564
  const message = args.join(' ');
564
565
  if (message.includes('Processed') && message.includes('lines')) {
565
566
  const match = message.match(/Processed (\d+) lines/);
@@ -568,13 +569,13 @@ class UniiDataMigratorWithProgress {
568
569
  this.module.updateProgress(processedLines);
569
570
  }
570
571
  }
571
- if (verbose) originalConsoleLog(...args);
572
+ if (verbose) originalLog(...args);
572
573
  };
573
574
 
574
575
  try {
575
576
  await originalMigrator.migrate(sourceFile, destFile, version, verbose);
576
577
  } finally {
577
- console.log = originalConsoleLog;
578
+ terminal.log = originalLog;
578
579
  this.module.stopProgress();
579
580
  }
580
581
  }