fhirsmith 0.18.1 → 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 (214) hide show
  1. package/ACCESSIBILITY.md +119 -0
  2. package/CHANGELOG.md +190 -9
  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 +331 -33
  15. package/ips/jurisdictions.js +65 -0
  16. package/ips/nationality.js +120 -0
  17. package/ips/pipeline.js +131 -17
  18. package/ips/presenter.js +1 -1
  19. package/ips/profile-editor.js +283 -4
  20. package/ips/readme.md +170 -20
  21. package/ips/render.js +769 -109
  22. package/ips/scripts.js +46 -0
  23. package/ips/store.js +123 -11
  24. package/ips/structure.js +247 -0
  25. package/ips/terminology.js +1 -1
  26. package/library/body-limit.js +22 -0
  27. package/library/cache-generation.js +15 -0
  28. package/library/cgroup-memory.js +133 -1
  29. package/library/cluster-counts.js +121 -0
  30. package/library/cluster-events.js +153 -0
  31. package/library/cluster-metrics.js +193 -0
  32. package/library/cluster-status.js +468 -0
  33. package/library/config-reader.js +96 -0
  34. package/library/daily-report.js +100 -2
  35. package/library/html-server.js +27 -1
  36. package/library/i18nsupport.js +23 -1
  37. package/library/keep-alive.js +18 -0
  38. package/library/languages.js +17 -3
  39. package/library/logger.js +31 -0
  40. package/library/memory-guard.js +428 -0
  41. package/library/native-memory.js +52 -0
  42. package/library/npm-audit.js +51 -25
  43. package/library/openapi-doc.js +7 -5
  44. package/library/package-manager.js +8 -0
  45. package/library/role.js +197 -0
  46. package/library/server-credentials.js +169 -0
  47. package/library/sqlite-utilities.js +59 -0
  48. package/library/ssrf-guard.js +100 -0
  49. package/library/static-response-stats.js +16 -0
  50. package/library/version-utilities.js +50 -0
  51. package/library/worker-count.js +192 -0
  52. package/nginx.md +30 -11
  53. package/npmprojector/indexer.js +18 -10
  54. package/package.json +11 -8
  55. package/packages/feed-check-page.js +117 -0
  56. package/packages/feed-check.js +279 -0
  57. package/packages/npm-check.js +57 -3
  58. package/packages/npm-signing.js +219 -0
  59. package/packages/openapi.yaml +207 -1
  60. package/packages/package-crawler.js +322 -41
  61. package/packages/packages-template.html +6 -8
  62. package/packages/packages.js +604 -57
  63. package/packages/readme.md +113 -1
  64. package/publisher/publisher-template.html +2 -5
  65. package/publisher/publisher.js +121 -110
  66. package/publisher/readme.md +75 -0
  67. package/registry/api.js +179 -1
  68. package/registry/crawler.js +86 -108
  69. package/registry/documentation/api.md +68 -2
  70. package/registry/documentation/configuration.md +34 -0
  71. package/registry/documentation/internals.md +4 -1
  72. package/registry/documentation/readme.md +3 -2
  73. package/registry/model.js +8 -0
  74. package/registry/openapi.yaml +84 -0
  75. package/registry/registry-template.html +5 -8
  76. package/registry/registry.js +58 -6
  77. package/root-bare-template.html +7 -4
  78. package/root-template.html +5 -8
  79. package/server.js +398 -198
  80. package/shl/shl.js +38 -37
  81. package/static/a11y/a11y-check.js +832 -0
  82. package/static/a11y/axe.min.js +12 -0
  83. package/static/assets/css/bootstrap-fhir.css +15 -15
  84. package/static/assets/images/flags/au.svg +18 -0
  85. package/static/assets/images/flags/unknown.svg +11 -0
  86. package/static/assets/images/flags/us.svg +63 -0
  87. package/static/assets/images/icon_audit.png +0 -0
  88. package/static/assets/images/icon_clinical.png +0 -0
  89. package/static/assets/images/icon_document.png +0 -0
  90. package/static/assets/images/icon_external.png +0 -0
  91. package/static/assets/images/icon_medication.png +0 -0
  92. package/static/assets/images/icon_organization.png +0 -0
  93. package/static/assets/images/icon_other.png +0 -0
  94. package/static/assets/images/icon_person.png +0 -0
  95. package/static/assets/images/icon_section.png +0 -0
  96. package/stats-db.js +45 -0
  97. package/stats.js +197 -10
  98. package/testing/testing-template.html +5 -8
  99. package/token/nginx_passport_setup.md +1 -1
  100. package/token/security_guide.md +1 -1
  101. package/token/token-template.html +2 -5
  102. package/token/token.js +40 -36
  103. package/translations/Messages.properties +19 -0
  104. package/tx/README.md +158 -4
  105. package/tx/closure/closure-store.js +126 -3
  106. package/tx/cm/cm-database.js +69 -57
  107. package/tx/cm/cm-package.js +50 -16
  108. package/tx/cs/cs-api.js +284 -19
  109. package/tx/cs/cs-api.md +41 -4
  110. package/tx/cs/cs-areacode.js +1 -1
  111. package/tx/cs/cs-country.js +17 -5
  112. package/tx/cs/cs-cpt.js +33 -16
  113. package/tx/cs/cs-cs.js +199 -24
  114. package/tx/cs/cs-currency.js +1 -1
  115. package/tx/cs/cs-icd11.js +21 -13
  116. package/tx/cs/cs-icf.js +22 -3
  117. package/tx/cs/cs-lang.js +2 -2
  118. package/tx/cs/cs-loinc.js +193 -67
  119. package/tx/cs/cs-mimetypes.js +12 -2
  120. package/tx/cs/cs-ndc.js +40 -25
  121. package/tx/cs/cs-omop.js +54 -29
  122. package/tx/cs/cs-rxnorm.js +300 -196
  123. package/tx/cs/cs-snomed.js +861 -95
  124. package/tx/cs/cs-ucum.js +14 -4
  125. package/tx/cs/cs-unii.js +18 -14
  126. package/tx/cs/cs-virtual.js +210 -0
  127. package/tx/ecosystem-resolver.js +413 -0
  128. package/tx/html/cluster-dash-metrics.liquid +26 -0
  129. package/tx/html/cluster-home-metrics.liquid +31 -0
  130. package/tx/html/cluster-metrics-script.liquid +251 -0
  131. package/tx/html/codesystem-operations.liquid +3 -3
  132. package/tx/html/ecl-panel.liquid +1 -1
  133. package/tx/html/external-metrics.liquid +17 -0
  134. package/tx/html/operations-form.liquid +17 -17
  135. package/tx/html/search-form.liquid +11 -11
  136. package/tx/html/status-line.liquid +58 -0
  137. package/tx/html/status-panel.liquid +140 -0
  138. package/tx/html/status-tasks.liquid +25 -0
  139. package/tx/html/tx-template.html +2 -5
  140. package/tx/importers/atc-to-fhir.js +17 -16
  141. package/tx/importers/export-icd11-codesystem.js +3 -2
  142. package/tx/importers/import-icd11.js +4 -3
  143. package/tx/importers/import-loinc.module.js +118 -56
  144. package/tx/importers/import-ndc.module.js +56 -44
  145. package/tx/importers/import-rxnorm.module.js +118 -79
  146. package/tx/importers/import-sct.module.js +125 -61
  147. package/tx/importers/import-unii.module.js +37 -36
  148. package/tx/importers/subset-loinc.module.js +49 -48
  149. package/tx/importers/subset-rxnorm.module.js +29 -28
  150. package/tx/importers/terminal.js +18 -0
  151. package/tx/importers/tx-import-base.js +13 -12
  152. package/tx/importers/tx-import-settings.js +5 -4
  153. package/tx/importers/tx-import.js +53 -52
  154. package/tx/library/canonical-resource.js +49 -0
  155. package/tx/library/codesystem.js +94 -5
  156. package/tx/library/designations.js +45 -2
  157. package/tx/library/extensions.js +13 -7
  158. package/tx/library/implicit-valuesets.js +66 -0
  159. package/tx/library/renderer.js +77 -21
  160. package/tx/library/sqlite-utilities.js +2 -0
  161. package/tx/library/tho-precedence.js +40 -0
  162. package/tx/library/ucum-parsers.js +11 -1
  163. package/tx/library/wait-for-cache.js +43 -0
  164. package/tx/library.js +321 -22
  165. package/tx/openapi-operations.js +6 -4
  166. package/tx/operation-context.js +460 -99
  167. package/tx/params.js +25 -0
  168. package/tx/problems.js +3 -5
  169. package/tx/provider.js +314 -67
  170. package/tx/resource-cache-sqlite.js +1050 -0
  171. package/tx/sct/documentation.md +45 -21
  172. package/tx/sct/ecl.js +81 -17
  173. package/tx/sct/editions.js +39 -0
  174. package/tx/sct/expressions.js +10 -1
  175. package/tx/sct/load-config.js +107 -0
  176. package/tx/sct/mmap.js +109 -0
  177. package/tx/sct/structures.js +175 -15
  178. package/tx/sct/text-index.js +5 -1
  179. package/tx/syndication/syndication-feed.js +2 -2
  180. package/tx/tx-html.js +44 -8
  181. package/tx/tx.fhir.org.yml +8 -8
  182. package/tx/tx.js +282 -80
  183. package/tx/usage-tracker.js +39 -11
  184. package/tx/vs/vs-api.js +19 -0
  185. package/tx/vs/vs-database.js +386 -62
  186. package/tx/vs/vs-package.js +44 -17
  187. package/tx/vs/vs-vsac.js +649 -101
  188. package/tx/workers/batch-validate.js +37 -10
  189. package/tx/workers/batch.js +6 -1
  190. package/tx/workers/cache-control.js +15 -4
  191. package/tx/workers/closure.js +156 -13
  192. package/tx/workers/compare.js +8 -5
  193. package/tx/workers/expand.js +1173 -289
  194. package/tx/workers/metadata.js +35 -0
  195. package/tx/workers/search.js +53 -1
  196. package/tx/workers/supplement-filters.js +145 -0
  197. package/tx/workers/translate.js +169 -4
  198. package/tx/workers/validate.js +223 -119
  199. package/tx/workers/worker.js +123 -31
  200. package/tx/xversion/xv-valueset.js +7 -4
  201. package/utilities/submit-test-report.js +3 -1
  202. package/utilities/tx-stress-test.js +482 -0
  203. package/vcl/readme.md +37 -0
  204. package/vcl/vcl-parser.js +15 -0
  205. package/xig/readme.md +52 -0
  206. package/xig/xig-template.html +5 -8
  207. package/xig/xig.js +238 -422
  208. package/changelog-template.md +0 -22
  209. package/registry/test-runner.js +0 -237
  210. package/release.sh +0 -80
  211. package/test-scripts/repro-re2-wasm-leak.js +0 -34
  212. package/tsconfig.eslint.json +0 -9
  213. package/tx/html/dash-metrics.liquid +0 -147
  214. package/tx/html/home-metrics.liquid +0 -225
@@ -4,6 +4,7 @@ const path = require('path');
4
4
  const readline = require('readline');
5
5
  const { csvRecords } = require('./csv-records');
6
6
  const chalk = require('chalk');
7
+ const terminal = require('./terminal');
7
8
 
8
9
  class LoincSubsetModule extends BaseTerminologyModule {
9
10
  constructor() {
@@ -84,7 +85,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
84
85
  } catch (error) {
85
86
  this.logError(`Subset operation failed: ${error.message}`);
86
87
  if (options.verbose) {
87
- console.error(error.stack);
88
+ terminal.error(error.stack);
88
89
  }
89
90
  throw error;
90
91
  }
@@ -220,15 +221,15 @@ class LoincSubsetModule extends BaseTerminologyModule {
220
221
  }
221
222
 
222
223
  async confirmSubset(config) {
223
- console.log(chalk.cyan(`\nšŸ“‹ LOINC Subset Configuration:`));
224
- console.log(` Source: ${chalk.white(config.source)}`);
225
- console.log(` Destination: ${chalk.white(config.dest)}`);
226
- console.log(` Codes File: ${chalk.white(config.codes)}`);
227
- console.log(` Expand PartLinks: ${chalk.white(config.expandPartLinks ? 'Yes' : 'No')}`);
228
- console.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
224
+ terminal.log(chalk.cyan(`\nšŸ“‹ LOINC Subset Configuration:`));
225
+ terminal.log(` Source: ${chalk.white(config.source)}`);
226
+ terminal.log(` Destination: ${chalk.white(config.dest)}`);
227
+ terminal.log(` Codes File: ${chalk.white(config.codes)}`);
228
+ terminal.log(` Expand PartLinks: ${chalk.white(config.expandPartLinks ? 'Yes' : 'No')}`);
229
+ terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
229
230
 
230
231
  if (config.estimatedDuration) {
231
- console.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
232
+ terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
232
233
  }
233
234
 
234
235
  const { confirmed } = await require('inquirer').prompt({
@@ -243,14 +244,14 @@ class LoincSubsetModule extends BaseTerminologyModule {
243
244
 
244
245
  async runSubset(config) {
245
246
  try {
246
- console.log(chalk.blue.bold(`šŸ”¬ Starting LOINC Subset Creation...\n`));
247
+ terminal.log(chalk.blue.bold(`šŸ”¬ Starting LOINC Subset Creation...\n`));
247
248
 
248
249
  if (config.verbose) {
249
- console.log('Debug - Final config values:');
250
- console.log(` Source: ${config.source}`);
251
- console.log(` Dest: ${config.dest}`);
252
- console.log(` Codes: ${config.codes}`);
253
- console.log('');
250
+ terminal.log('Debug - Final config values:');
251
+ terminal.log(` Source: ${config.source}`);
252
+ terminal.log(` Dest: ${config.dest}`);
253
+ terminal.log(` Codes: ${config.codes}`);
254
+ terminal.log('');
254
255
  }
255
256
 
256
257
  // Pre-flight checks
@@ -270,7 +271,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
270
271
  this.stopProgress();
271
272
  this.logError(`LOINC subset creation failed: ${error.message}`);
272
273
  if (config.verbose) {
273
- console.error(error.stack);
274
+ terminal.error(error.stack);
274
275
  }
275
276
  throw error;
276
277
  }
@@ -303,13 +304,13 @@ class LoincSubsetModule extends BaseTerminologyModule {
303
304
  const stats = await this.validateSubsetInputs(options.source, options.codes);
304
305
 
305
306
  this.logSuccess('Validation passed');
306
- console.log(` Source files found: ${stats.filesFound.length}`);
307
- console.log(` Codes in list: ${stats.codeCount.toLocaleString()}`);
308
- console.log(` Unique codes: ${stats.uniqueCodes.toLocaleString()}`);
307
+ terminal.log(` Source files found: ${stats.filesFound.length}`);
308
+ terminal.log(` Codes in list: ${stats.codeCount.toLocaleString()}`);
309
+ terminal.log(` Unique codes: ${stats.uniqueCodes.toLocaleString()}`);
309
310
 
310
311
  if (stats.warnings.length > 0) {
311
312
  this.logWarning('Validation warnings:');
312
- stats.warnings.forEach(warning => console.log(` ${warning}`));
313
+ stats.warnings.forEach(warning => terminal.log(` ${warning}`));
313
314
  }
314
315
 
315
316
  } catch (error) {
@@ -324,7 +325,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
324
325
  check: () => {
325
326
  const exists = fs.existsSync(config.source);
326
327
  if (!exists && config.verbose) {
327
- console.log(` Source path being checked: ${config.source}`);
328
+ terminal.log(` Source path being checked: ${config.source}`);
328
329
  }
329
330
  return exists;
330
331
  }
@@ -334,7 +335,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
334
335
  check: () => {
335
336
  const exists = fs.existsSync(config.codes);
336
337
  if (!exists && config.verbose) {
337
- console.log(` Codes path being checked: ${config.codes}`);
338
+ terminal.log(` Codes path being checked: ${config.codes}`);
338
339
  }
339
340
  return exists;
340
341
  }
@@ -351,7 +352,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
351
352
  const fullPath = path.join(config.source, file);
352
353
  const exists = fs.existsSync(fullPath);
353
354
  if (!exists && config.verbose) {
354
- console.log(` Missing required file: ${fullPath}`);
355
+ terminal.log(` Missing required file: ${fullPath}`);
355
356
  }
356
357
  return exists;
357
358
  });
@@ -390,14 +391,14 @@ class LoincSubsetModule extends BaseTerminologyModule {
390
391
 
391
392
  if (config.verbose) {
392
393
  const sampleCodes = Array.from(initialTargetCodes).slice(0, 20);
393
- console.log(`First 20 codes from file: ${sampleCodes.join(', ')}`);
394
+ terminal.log(`First 20 codes from file: ${sampleCodes.join(', ')}`);
394
395
 
395
396
  // Validate some codes exist in the main LOINC file
396
397
  this.logInfo('Validating codes exist in LOINC...');
397
398
  const validationResults = await this.validateCodesExist(config.source, initialTargetCodes);
398
- console.log(`Found ${validationResults.found} of ${validationResults.checked} codes in LOINC main table`);
399
+ terminal.log(`Found ${validationResults.found} of ${validationResults.checked} codes in LOINC main table`);
399
400
  if (validationResults.notFound.length > 0) {
400
- console.log(`Sample missing codes: ${validationResults.notFound.slice(0, 5).join(', ')}`);
401
+ terminal.log(`Sample missing codes: ${validationResults.notFound.slice(0, 5).join(', ')}`);
401
402
  }
402
403
  }
403
404
 
@@ -414,7 +415,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
414
415
  if (config.verbose && addedCodes > 0) {
415
416
  const newCodes = Array.from(finalTargetCodes).filter(code => !initialTargetCodes.has(code));
416
417
  const sampleNewCodes = newCodes.slice(0, 10);
417
- console.log(`Sample newly added codes: ${sampleNewCodes.join(', ')}`);
418
+ terminal.log(`Sample newly added codes: ${sampleNewCodes.join(', ')}`);
418
419
  }
419
420
  } else {
420
421
  this.logInfo('Skipping PartLink expansion (disabled)');
@@ -449,7 +450,7 @@ class LoincSubsetModule extends BaseTerminologyModule {
449
450
 
450
451
  if (!fs.existsSync(partLinkPath)) {
451
452
  if (verbose) {
452
- console.log(` PartLink file not found: ${partLinkPath}`);
453
+ terminal.log(` PartLink file not found: ${partLinkPath}`);
453
454
  }
454
455
  return new Set(initialCodes);
455
456
  }
@@ -457,8 +458,8 @@ class LoincSubsetModule extends BaseTerminologyModule {
457
458
  const expandedCodes = new Set(initialCodes);
458
459
 
459
460
  if (verbose) {
460
- console.log(` Processing PartLink relationships (single pass)...`);
461
- console.log(` Original codes count: ${initialCodes.size}`);
461
+ terminal.log(` Processing PartLink relationships (single pass)...`);
462
+ terminal.log(` Original codes count: ${initialCodes.size}`);
462
463
  }
463
464
 
464
465
  const rl = readline.createInterface({
@@ -495,19 +496,19 @@ class LoincSubsetModule extends BaseTerminologyModule {
495
496
  addedCodes += (sizeAfter - sizeBefore);
496
497
 
497
498
  if (verbose && matchedLines <= 10) {
498
- console.log(` Match ${matchedLines}: "${firstCode}" (${firstInOriginal ? 'in original' : 'new'}) <-> "${thirdCode}" (${thirdInOriginal ? 'in original' : 'new'})`);
499
+ terminal.log(` Match ${matchedLines}: "${firstCode}" (${firstInOriginal ? 'in original' : 'new'}) <-> "${thirdCode}" (${thirdInOriginal ? 'in original' : 'new'})`);
499
500
  }
500
501
  }
501
502
  }
502
503
 
503
504
  if (verbose) {
504
- console.log(` Single pass completed: ${matchedLines} matching relationships found`);
505
- console.log(` Added ${addedCodes} new codes`);
506
- console.log(` Final expanded codes count: ${expandedCodes.size}`);
505
+ terminal.log(` Single pass completed: ${matchedLines} matching relationships found`);
506
+ terminal.log(` Added ${addedCodes} new codes`);
507
+ terminal.log(` Final expanded codes count: ${expandedCodes.size}`);
507
508
 
508
509
  // Show what percentage this represents
509
510
  const expansionRatio = expandedCodes.size / initialCodes.size;
510
- console.log(` Expansion ratio: ${expansionRatio.toFixed(1)}x original size`);
511
+ terminal.log(` Expansion ratio: ${expansionRatio.toFixed(1)}x original size`);
511
512
  }
512
513
 
513
514
  return expandedCodes;
@@ -837,12 +838,12 @@ class LoincSubsetProcessor {
837
838
 
838
839
  async processParts(sourcePath, destPath) {
839
840
  if (this.verbose) {
840
- console.log(` Processing parts file: ${sourcePath}`);
841
- console.log(` Target codes size: ${this.targetCodes.size}`);
841
+ terminal.log(` Processing parts file: ${sourcePath}`);
842
+ terminal.log(` Target codes size: ${this.targetCodes.size}`);
842
843
 
843
844
  // Show first few target codes for comparison
844
845
  const sampleCodes = Array.from(this.targetCodes).slice(0, 5);
845
- console.log(` Sample target codes: ${sampleCodes.join(', ')}`);
846
+ terminal.log(` Sample target codes: ${sampleCodes.join(', ')}`);
846
847
  }
847
848
 
848
849
  await this.processFileWithFilter(sourcePath, destPath, (line, lineNum) => {
@@ -856,7 +857,7 @@ class LoincSubsetProcessor {
856
857
 
857
858
  // Debug first few lines
858
859
  if (this.verbose && lineNum <= 5) {
859
- console.log(` Line ${lineNum}: raw="${items[0]}", clean="${code}", match=${hasCode}`);
860
+ terminal.log(` Line ${lineNum}: raw="${items[0]}", clean="${code}", match=${hasCode}`);
860
861
  }
861
862
 
862
863
  return hasCode;
@@ -865,12 +866,12 @@ class LoincSubsetProcessor {
865
866
 
866
867
  async processConsumerNames(sourcePath, destPath) {
867
868
  if (this.verbose) {
868
- console.log(` Processing consumer names file: ${sourcePath}`);
869
- console.log(` Target codes size: ${this.targetCodes.size}`);
869
+ terminal.log(` Processing consumer names file: ${sourcePath}`);
870
+ terminal.log(` Target codes size: ${this.targetCodes.size}`);
870
871
 
871
872
  // Show first few target codes for comparison
872
873
  const sampleCodes = Array.from(this.targetCodes).slice(0, 5);
873
- console.log(` Sample target codes: ${sampleCodes.join(', ')}`);
874
+ terminal.log(` Sample target codes: ${sampleCodes.join(', ')}`);
874
875
  }
875
876
 
876
877
  await this.processFileWithFilter(sourcePath, destPath, (line, lineNum) => {
@@ -884,7 +885,7 @@ class LoincSubsetProcessor {
884
885
 
885
886
  // Debug first few lines
886
887
  if (this.verbose && lineNum <= 5) {
887
- console.log(` Line ${lineNum}: raw="${items[0]}", clean="${code}", match=${hasCode}`);
888
+ terminal.log(` Line ${lineNum}: raw="${items[0]}", clean="${code}", match=${hasCode}`);
888
889
  }
889
890
 
890
891
  return hasCode;
@@ -917,10 +918,10 @@ class LoincSubsetProcessor {
917
918
 
918
919
  async processPartLinks(sourcePath, destPath) {
919
920
  if (this.verbose) {
920
- console.log(` Processing part links file: ${sourcePath}`);
921
- console.log(` Target codes size: ${this.targetCodes.size}`);
922
- console.log(` Original codes size: ${this.originalCodes.size}`);
923
- console.log(` Using ORIGINAL codes for PartLink filtering to prevent expansion explosion`);
921
+ terminal.log(` Processing part links file: ${sourcePath}`);
922
+ terminal.log(` Target codes size: ${this.targetCodes.size}`);
923
+ terminal.log(` Original codes size: ${this.originalCodes.size}`);
924
+ terminal.log(` Using ORIGINAL codes for PartLink filtering to prevent expansion explosion`);
924
925
  }
925
926
 
926
927
  let debugCount = 0;
@@ -941,7 +942,7 @@ class LoincSubsetProcessor {
941
942
  // Debug first few included lines
942
943
  if (this.verbose && shouldInclude && debugCount < 10) {
943
944
  debugCount++;
944
- console.log(` Include ${debugCount}: "${sourceCode}" (${hasSource ? 'original' : 'no'}) <-> "${targetCode}" (${hasTarget ? 'original' : 'no'})`);
945
+ terminal.log(` Include ${debugCount}: "${sourceCode}" (${hasSource ? 'original' : 'no'}) <-> "${targetCode}" (${hasTarget ? 'original' : 'no'})`);
945
946
  }
946
947
 
947
948
  return shouldInclude;
@@ -1051,7 +1052,7 @@ class LoincSubsetProcessor {
1051
1052
  writeStream.end();
1052
1053
 
1053
1054
  if (this.verbose && lineNum > 1) {
1054
- console.log(` Included ${includedLines - 1} of ${lineNum - 1} data rows`);
1055
+ terminal.log(` Included ${includedLines - 1} of ${lineNum - 1} data rows`);
1055
1056
  }
1056
1057
  }
1057
1058
 
@@ -3,6 +3,7 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const readline = require('readline');
5
5
  const chalk = require('chalk');
6
+ const terminal = require('./terminal');
6
7
 
7
8
  class RxNormSubsetModule extends BaseTerminologyModule {
8
9
  constructor() {
@@ -86,7 +87,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
86
87
  } catch (error) {
87
88
  this.logError(`Subset operation failed: ${error.message}`);
88
89
  if (options.verbose) {
89
- console.error(error.stack);
90
+ terminal.error(error.stack);
90
91
  }
91
92
  throw error;
92
93
  }
@@ -237,18 +238,18 @@ class RxNormSubsetModule extends BaseTerminologyModule {
237
238
  }
238
239
 
239
240
  async confirmSubset(config) {
240
- console.log(chalk.cyan(`\nšŸ“‹ RxNorm Subset Configuration:`));
241
- console.log(` Source: ${chalk.white(config.source)}`);
242
- console.log(` Destination: ${chalk.white(config.dest)}`);
243
- console.log(` Codes File: ${chalk.white(config.codes)}`);
244
- console.log(` Expand Relationships: ${chalk.white(config.expandRelationships ? 'Yes' : 'No')}`);
245
- console.log(` Include Synonyms: ${chalk.white(config.includeSynonyms ? 'Yes' : 'No')}`);
246
- console.log(` Include Archived: ${chalk.white(config.includeArchived ? 'Yes' : 'No')}`);
247
- console.log(` Max Iterations: ${chalk.white(config.maxIterations)}`);
248
- console.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
241
+ terminal.log(chalk.cyan(`\nšŸ“‹ RxNorm Subset Configuration:`));
242
+ terminal.log(` Source: ${chalk.white(config.source)}`);
243
+ terminal.log(` Destination: ${chalk.white(config.dest)}`);
244
+ terminal.log(` Codes File: ${chalk.white(config.codes)}`);
245
+ terminal.log(` Expand Relationships: ${chalk.white(config.expandRelationships ? 'Yes' : 'No')}`);
246
+ terminal.log(` Include Synonyms: ${chalk.white(config.includeSynonyms ? 'Yes' : 'No')}`);
247
+ terminal.log(` Include Archived: ${chalk.white(config.includeArchived ? 'Yes' : 'No')}`);
248
+ terminal.log(` Max Iterations: ${chalk.white(config.maxIterations)}`);
249
+ terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
249
250
 
250
251
  if (config.estimatedDuration) {
251
- console.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
252
+ terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
252
253
  }
253
254
 
254
255
  const { confirmed } = await require('inquirer').prompt({
@@ -263,7 +264,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
263
264
 
264
265
  async runSubset(config) {
265
266
  try {
266
- console.log(chalk.blue.bold(`🧬 Starting RxNorm Subset Creation...\n`));
267
+ terminal.log(chalk.blue.bold(`🧬 Starting RxNorm Subset Creation...\n`));
267
268
 
268
269
  // Pre-flight checks
269
270
  this.logInfo('Running pre-flight checks...');
@@ -282,7 +283,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
282
283
  this.stopProgress();
283
284
  this.logError(`RxNorm subset creation failed: ${error.message}`);
284
285
  if (config.verbose) {
285
- console.error(error.stack);
286
+ terminal.error(error.stack);
286
287
  }
287
288
  throw error;
288
289
  }
@@ -315,14 +316,14 @@ class RxNormSubsetModule extends BaseTerminologyModule {
315
316
  const stats = await this.validateSubsetInputs(options.source, options.codes);
316
317
 
317
318
  this.logSuccess('Validation passed');
318
- console.log(` Required files found: ${stats.requiredFiles.length}/3`);
319
- console.log(` Optional files found: ${stats.optionalFiles.length}/3`);
320
- console.log(` Codes in list: ${stats.codeCount.toLocaleString()}`);
321
- console.log(` Unique codes: ${stats.uniqueCodes.toLocaleString()}`);
319
+ terminal.log(` Required files found: ${stats.requiredFiles.length}/3`);
320
+ terminal.log(` Optional files found: ${stats.optionalFiles.length}/3`);
321
+ terminal.log(` Codes in list: ${stats.codeCount.toLocaleString()}`);
322
+ terminal.log(` Unique codes: ${stats.uniqueCodes.toLocaleString()}`);
322
323
 
323
324
  if (stats.warnings.length > 0) {
324
325
  this.logWarning('Validation warnings:');
325
- stats.warnings.forEach(warning => console.log(` ${warning}`));
326
+ stats.warnings.forEach(warning => terminal.log(` ${warning}`));
326
327
  }
327
328
 
328
329
  } catch (error) {
@@ -380,7 +381,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
380
381
 
381
382
  if (config.verbose) {
382
383
  const sampleCodes = Array.from(initialTargetCodes).slice(0, 10);
383
- console.log(`Sample codes: ${sampleCodes.join(', ')}`);
384
+ terminal.log(`Sample codes: ${sampleCodes.join(', ')}`);
384
385
  }
385
386
 
386
387
  let finalTargetCodes = initialTargetCodes;
@@ -403,7 +404,7 @@ class RxNormSubsetModule extends BaseTerminologyModule {
403
404
  if (config.verbose && addedCodes > 0) {
404
405
  const newCodes = Array.from(finalTargetCodes).filter(code => !initialTargetCodes.has(code));
405
406
  const sampleNewCodes = newCodes.slice(0, 10);
406
- console.log(`Sample newly added codes: ${sampleNewCodes.join(', ')}`);
407
+ terminal.log(`Sample newly added codes: ${sampleNewCodes.join(', ')}`);
407
408
  }
408
409
  }
409
410
 
@@ -529,7 +530,7 @@ class RxNormRelationshipExpander {
529
530
 
530
531
  async expandCodes(initialCodes) {
531
532
  if (this.verbose) {
532
- console.log(` Starting relationship expansion with ${initialCodes.size} codes`);
533
+ terminal.log(` Starting relationship expansion with ${initialCodes.size} codes`);
533
534
  }
534
535
 
535
536
  let currentCodes = new Set(initialCodes);
@@ -540,7 +541,7 @@ class RxNormRelationshipExpander {
540
541
  const sizeBefore = currentCodes.size;
541
542
 
542
543
  if (this.verbose) {
543
- console.log(` Iteration ${iteration}: Starting with ${sizeBefore} codes`);
544
+ terminal.log(` Iteration ${iteration}: Starting with ${sizeBefore} codes`);
544
545
  }
545
546
 
546
547
  const newCodes = await this.findRelatedCodes(currentCodes);
@@ -554,13 +555,13 @@ class RxNormRelationshipExpander {
554
555
  const added = sizeAfter - sizeBefore;
555
556
 
556
557
  if (this.verbose) {
557
- console.log(` Iteration ${iteration}: Added ${added} codes (total: ${sizeAfter})`);
558
+ terminal.log(` Iteration ${iteration}: Added ${added} codes (total: ${sizeAfter})`);
558
559
  }
559
560
 
560
561
  // Stop if no new codes were found
561
562
  if (added === 0) {
562
563
  if (this.verbose) {
563
- console.log(` Expansion converged after ${iteration} iterations`);
564
+ terminal.log(` Expansion converged after ${iteration} iterations`);
564
565
  }
565
566
  break;
566
567
  }
@@ -575,7 +576,7 @@ class RxNormRelationshipExpander {
575
576
 
576
577
  if (!fs.existsSync(rxnrelPath)) {
577
578
  if (this.verbose) {
578
- console.log(` RXNREL file not found: ${rxnrelPath}`);
579
+ terminal.log(` RXNREL file not found: ${rxnrelPath}`);
579
580
  }
580
581
  return relatedCodes;
581
582
  }
@@ -624,12 +625,12 @@ class RxNormRelationshipExpander {
624
625
  }
625
626
 
626
627
  if (processedLines % 100000 === 0 && this.verbose) {
627
- console.log(` Processed ${processedLines} relationships, found ${matchedRelationships} matches`);
628
+ terminal.log(` Processed ${processedLines} relationships, found ${matchedRelationships} matches`);
628
629
  }
629
630
  }
630
631
 
631
632
  if (this.verbose) {
632
- console.log(` Found ${relatedCodes.size} related codes from ${matchedRelationships} relationships`);
633
+ terminal.log(` Found ${relatedCodes.size} related codes from ${matchedRelationships} relationships`);
633
634
  }
634
635
 
635
636
  return relatedCodes;
@@ -890,7 +891,7 @@ class RxNormSubsetProcessor {
890
891
 
891
892
  if (this.verbose && lineNum > 0) {
892
893
  const filename = path.basename(sourcePath);
893
- console.log(` Included ${includedLines.toLocaleString()} of ${lineNum.toLocaleString()} lines in ${filename}`);
894
+ terminal.log(` Included ${includedLines.toLocaleString()} of ${lineNum.toLocaleString()} lines in ${filename}`);
894
895
  }
895
896
  }
896
897
 
@@ -0,0 +1,18 @@
1
+ /**
2
+ * What the importers print: progress and results for the person running them (stdout), and
3
+ * problems (stderr). The importers are command line tools, so this is the console - but
4
+ * everything goes through here, so console calls stay out of the importer code (and out of a
5
+ * search for console.log).
6
+ *
7
+ * Each function looks console up when it is called, so whatever has replaced console.log (a
8
+ * test, say) still gets the output. Code that wants to see what an importer prints can replace
9
+ * terminal.log itself, and put it back after (see import-unii.module.js).
10
+ */
11
+ /* eslint-disable no-console */
12
+ const terminal = {
13
+ log: (...args) => console.log(...args),
14
+ warn: (...args) => console.warn(...args),
15
+ error: (...args) => console.error(...args)
16
+ };
17
+
18
+ module.exports = terminal;
@@ -4,6 +4,7 @@ const cliProgress = require('cli-progress');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const { getConfigManager } = require('./tx-import-settings');
7
+ const terminal = require('./terminal');
7
8
 
8
9
  /**
9
10
  * Base class for terminology import modules
@@ -167,14 +168,14 @@ class BaseTerminologyModule {
167
168
  }
168
169
 
169
170
  async confirmImport(config) {
170
- console.log(chalk.cyan(`\nšŸ“‹ ${this.getName()} Import Configuration:`));
171
- console.log(` Source: ${chalk.white(config.source)}`);
172
- console.log(` Destination: ${chalk.white(config.dest)}`);
173
- console.log(` Version: ${chalk.white(config.version || 'Not specified')}`);
174
- console.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
171
+ terminal.log(chalk.cyan(`\nšŸ“‹ ${this.getName()} Import Configuration:`));
172
+ terminal.log(` Source: ${chalk.white(config.source)}`);
173
+ terminal.log(` Destination: ${chalk.white(config.dest)}`);
174
+ terminal.log(` Version: ${chalk.white(config.version || 'Not specified')}`);
175
+ terminal.log(` Overwrite: ${chalk.white(config.overwrite ? 'Yes' : 'No')}`);
175
176
 
176
177
  if (config.estimatedDuration) {
177
- console.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
178
+ terminal.log(` Estimated Duration: ${chalk.white(config.estimatedDuration)}`);
178
179
  }
179
180
 
180
181
  const { confirmed } = await inquirer.prompt({
@@ -218,19 +219,19 @@ class BaseTerminologyModule {
218
219
  }
219
220
 
220
221
  logInfo(message) {
221
- console.log(chalk.blue('ℹ'), message);
222
+ terminal.log(chalk.blue('ℹ'), message);
222
223
  }
223
224
 
224
225
  logSuccess(message) {
225
- console.log(chalk.green('āœ“'), message);
226
+ terminal.log(chalk.green('āœ“'), message);
226
227
  }
227
228
 
228
229
  logWarning(message) {
229
- console.log(chalk.yellow('⚠'), message);
230
+ terminal.log(chalk.yellow('⚠'), message);
230
231
  }
231
232
 
232
233
  logError(message) {
233
- console.log(chalk.red('āœ—'), message);
234
+ terminal.log(chalk.red('āœ—'), message);
234
235
  }
235
236
 
236
237
  async validatePrerequisites(config) {
@@ -299,7 +300,7 @@ class BaseTerminologyModule {
299
300
  // Common import workflow
300
301
  async runImport(config) {
301
302
  try {
302
- console.log(chalk.blue.bold(`šŸ„ Starting ${this.getName()} Import...\n`));
303
+ terminal.log(chalk.blue.bold(`šŸ„ Starting ${this.getName()} Import...\n`));
303
304
 
304
305
  // Pre-flight checks
305
306
  this.logInfo('Running pre-flight checks...');
@@ -321,7 +322,7 @@ class BaseTerminologyModule {
321
322
  this.stopProgress(); // Ensure progress bar is cleaned up
322
323
  this.logError(`${this.getName()} import failed: ${error.message}`);
323
324
  if (config.verbose) {
324
- console.error(error.stack);
325
+ terminal.error(error.stack);
325
326
  }
326
327
  process.exit(1);
327
328
  }
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const os = require('os');
4
+ const terminal = require('./terminal');
4
5
 
5
6
  /**
6
7
  * Manages persistent configuration for terminology import tools
@@ -31,7 +32,7 @@ class ConfigManager {
31
32
  this.config = JSON.parse(fs.readFileSync(this.configFile, 'utf8'));
32
33
  }
33
34
  } catch (error) {
34
- console.warn(`Warning: Could not load config: ${error.message}`);
35
+ terminal.warn(`Warning: Could not load config: ${error.message}`);
35
36
  this.config = {};
36
37
  }
37
38
  }
@@ -42,7 +43,7 @@ class ConfigManager {
42
43
  this.history = JSON.parse(fs.readFileSync(this.historyFile, 'utf8'));
43
44
  }
44
45
  } catch (error) {
45
- console.warn(`Warning: Could not load history: ${error.message}`);
46
+ terminal.warn(`Warning: Could not load history: ${error.message}`);
46
47
  this.history = {};
47
48
  }
48
49
  }
@@ -51,7 +52,7 @@ class ConfigManager {
51
52
  try {
52
53
  fs.writeFileSync(this.configFile, JSON.stringify(this.config, null, 2));
53
54
  } catch (error) {
54
- console.warn(`Warning: Could not save config: ${error.message}`);
55
+ terminal.warn(`Warning: Could not save config: ${error.message}`);
55
56
  }
56
57
  }
57
58
 
@@ -59,7 +60,7 @@ class ConfigManager {
59
60
  try {
60
61
  fs.writeFileSync(this.historyFile, JSON.stringify(this.history, null, 2));
61
62
  } catch (error) {
62
- console.warn(`Warning: Could not save history: ${error.message}`);
63
+ terminal.warn(`Warning: Could not save history: ${error.message}`);
63
64
  }
64
65
  }
65
66