fhirsmith 0.3.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 (277) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/FHIRsmith.png +0 -0
  3. package/README.md +277 -0
  4. package/config-template.json +144 -0
  5. package/library/folder-setup.js +58 -0
  6. package/library/html-server.js +166 -0
  7. package/library/html.js +835 -0
  8. package/library/i18nsupport.js +259 -0
  9. package/library/languages.js +779 -0
  10. package/library/logger-telnet.js +205 -0
  11. package/library/logger.js +279 -0
  12. package/library/package-manager.js +876 -0
  13. package/library/utilities.js +196 -0
  14. package/library/version-utilities.js +1056 -0
  15. package/npmprojector/config-example.json +13 -0
  16. package/npmprojector/indexer.js +394 -0
  17. package/npmprojector/npmprojector.js +395 -0
  18. package/npmprojector/readme.md +174 -0
  19. package/npmprojector/watcher.js +335 -0
  20. package/package.json +119 -0
  21. package/packages/package-crawler.js +846 -0
  22. package/packages/packages-template.html +126 -0
  23. package/packages/packages.js +2838 -0
  24. package/passwords.ini +2 -0
  25. package/publisher/publisher-template.html +208 -0
  26. package/publisher/publisher.js +2167 -0
  27. package/publisher/task-draft.js +458 -0
  28. package/registry/api.js +735 -0
  29. package/registry/crawler.js +637 -0
  30. package/registry/model.js +513 -0
  31. package/registry/readme.md +243 -0
  32. package/registry/registry-data.json +121015 -0
  33. package/registry/registry-template.html +126 -0
  34. package/registry/registry.js +1395 -0
  35. package/registry/test-runner.js +237 -0
  36. package/root-template.html +124 -0
  37. package/server.js +524 -0
  38. package/shl/private-key.pem +5 -0
  39. package/shl/public-key.pem +18 -0
  40. package/shl/shl.js +1125 -0
  41. package/shl/vhl.js +69 -0
  42. package/static/FHIRsmith128.png +0 -0
  43. package/static/FHIRsmith16.png +0 -0
  44. package/static/FHIRsmith32.png +0 -0
  45. package/static/FHIRsmith64.png +0 -0
  46. package/static/assets/css/bootstrap-fhir.css +5302 -0
  47. package/static/assets/css/bootstrap-glyphicons.css +2 -0
  48. package/static/assets/css/bootstrap.css +4097 -0
  49. package/static/assets/css/jquery-ui.css +523 -0
  50. package/static/assets/css/jquery-ui.structure.css +863 -0
  51. package/static/assets/css/jquery-ui.structure.min.css +5 -0
  52. package/static/assets/css/jquery-ui.theme.css +439 -0
  53. package/static/assets/css/jquery-ui.theme.min.css +5 -0
  54. package/static/assets/css/jquery.ui.all.css +7 -0
  55. package/static/assets/css/modules.css +18 -0
  56. package/static/assets/css/project.css +367 -0
  57. package/static/assets/css/pygments-manni.css +66 -0
  58. package/static/assets/css/tags.css +74 -0
  59. package/static/assets/css/xml.css +2 -0
  60. package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
  61. package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
  62. package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
  63. package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
  64. package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
  65. package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
  66. package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
  67. package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
  68. package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
  69. package/static/assets/ico/favicon.ico +0 -0
  70. package/static/assets/ico/favicon.png +0 -0
  71. package/static/assets/images/fhir-logo-www.png +0 -0
  72. package/static/assets/images/fhir-logo.png +0 -0
  73. package/static/assets/images/hl7-logo.png +0 -0
  74. package/static/assets/images/logo_ansinew.jpg +0 -0
  75. package/static/assets/images/search.png +0 -0
  76. package/static/assets/images/stripe.png +0 -0
  77. package/static/assets/images/target.png +0 -0
  78. package/static/assets/images/tx-registry-root.gif +0 -0
  79. package/static/assets/images/tx-registry.png +0 -0
  80. package/static/assets/images/tx-server.png +0 -0
  81. package/static/assets/images/tx-version.png +0 -0
  82. package/static/assets/js/bootstrap.min.js +6 -0
  83. package/static/assets/js/fhir-gw.js +259 -0
  84. package/static/assets/js/fhir.js +2 -0
  85. package/static/assets/js/html5shiv.js +8 -0
  86. package/static/assets/js/jcookie.js +96 -0
  87. package/static/assets/js/jquery-ui.min.js +6 -0
  88. package/static/assets/js/jquery.js +10716 -0
  89. package/static/assets/js/jquery.min.js +2 -0
  90. package/static/assets/js/jquery.ui.core.js +314 -0
  91. package/static/assets/js/jquery.ui.draggable.js +825 -0
  92. package/static/assets/js/jquery.ui.mouse.js +162 -0
  93. package/static/assets/js/jquery.ui.resizable.js +842 -0
  94. package/static/assets/js/jquery.ui.widget.js +268 -0
  95. package/static/assets/js/json2.js +487 -0
  96. package/static/assets/js/jtip.js +97 -0
  97. package/static/assets/js/respond.min.js +6 -0
  98. package/static/assets/js/statuspage.js +70 -0
  99. package/static/assets/js/xml.js +2 -0
  100. package/static/dist/js/bootstrap.js +1964 -0
  101. package/static/favicon.png +0 -0
  102. package/static/fhir.css +626 -0
  103. package/static/icon-fhir-16.png +0 -0
  104. package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  105. package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  106. package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
  107. package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  108. package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  109. package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  110. package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  111. package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  112. package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  113. package/static/images/ui-icons_222222_256x240.png +0 -0
  114. package/static/images/ui-icons_228ef1_256x240.png +0 -0
  115. package/static/images/ui-icons_ef8c08_256x240.png +0 -0
  116. package/static/images/ui-icons_ffd27a_256x240.png +0 -0
  117. package/static/images/ui-icons_ffffff_256x240.png +0 -0
  118. package/static/js/jquery.effects.blind.js +49 -0
  119. package/static/js/jquery.effects.bounce.js +78 -0
  120. package/static/js/jquery.effects.clip.js +54 -0
  121. package/static/js/jquery.effects.core.js +763 -0
  122. package/static/js/jquery.effects.drop.js +50 -0
  123. package/static/js/jquery.effects.explode.js +79 -0
  124. package/static/js/jquery.effects.fade.js +32 -0
  125. package/static/js/jquery.effects.fold.js +56 -0
  126. package/static/js/jquery.effects.highlight.js +50 -0
  127. package/static/js/jquery.effects.pulsate.js +51 -0
  128. package/static/js/jquery.effects.scale.js +178 -0
  129. package/static/js/jquery.effects.shake.js +57 -0
  130. package/static/js/jquery.effects.slide.js +50 -0
  131. package/static/js/jquery.effects.transfer.js +45 -0
  132. package/static/js/jquery.ui.accordion.js +611 -0
  133. package/static/js/jquery.ui.autocomplete.js +612 -0
  134. package/static/js/jquery.ui.button.js +416 -0
  135. package/static/js/jquery.ui.datepicker.js +1823 -0
  136. package/static/js/jquery.ui.dialog.js +878 -0
  137. package/static/js/jquery.ui.droppable.js +296 -0
  138. package/static/js/jquery.ui.position.js +252 -0
  139. package/static/js/jquery.ui.progressbar.js +109 -0
  140. package/static/js/jquery.ui.selectable.js +266 -0
  141. package/static/js/jquery.ui.slider.js +666 -0
  142. package/static/js/jquery.ui.sortable.js +1077 -0
  143. package/static/js/jquery.ui.tabs.js +758 -0
  144. package/stats.js +80 -0
  145. package/test-cache/vsac/vsac-valuesets.db +0 -0
  146. package/token/nginx_passport_setup.md +383 -0
  147. package/token/security_guide.md +294 -0
  148. package/token/token-template.html +330 -0
  149. package/token/token.js +1300 -0
  150. package/translations/Messages.properties +1510 -0
  151. package/translations/Messages_ar.properties +1399 -0
  152. package/translations/Messages_de.properties +836 -0
  153. package/translations/Messages_es.properties +737 -0
  154. package/translations/Messages_fr.properties +1 -0
  155. package/translations/Messages_ja.properties +893 -0
  156. package/translations/Messages_nl.properties +1357 -0
  157. package/translations/Messages_pt.properties +1302 -0
  158. package/translations/Messages_ru.properties +1 -0
  159. package/translations/Messages_uz.properties +1 -0
  160. package/translations/Messages_zh.properties +1 -0
  161. package/translations/rendering-phrases.properties +1128 -0
  162. package/translations/rendering-phrases_ar.properties +1091 -0
  163. package/translations/rendering-phrases_de.properties +6 -0
  164. package/translations/rendering-phrases_es.properties +6 -0
  165. package/translations/rendering-phrases_fr.properties +624 -0
  166. package/translations/rendering-phrases_ja.properties +21 -0
  167. package/translations/rendering-phrases_nl.properties +970 -0
  168. package/translations/rendering-phrases_pt.properties +1020 -0
  169. package/translations/rendering-phrases_ru.properties +1094 -0
  170. package/translations/rendering-phrases_uz.properties +1 -0
  171. package/translations/rendering-phrases_zh.properties +1 -0
  172. package/tx/README.md +418 -0
  173. package/tx/cm/cm-api.js +110 -0
  174. package/tx/cm/cm-database.js +735 -0
  175. package/tx/cm/cm-package.js +325 -0
  176. package/tx/cs/cs-api.js +789 -0
  177. package/tx/cs/cs-areacode.js +615 -0
  178. package/tx/cs/cs-country.js +1110 -0
  179. package/tx/cs/cs-cpt.js +785 -0
  180. package/tx/cs/cs-cs.js +1579 -0
  181. package/tx/cs/cs-currency.js +539 -0
  182. package/tx/cs/cs-db.js +1321 -0
  183. package/tx/cs/cs-hgvs.js +329 -0
  184. package/tx/cs/cs-lang.js +465 -0
  185. package/tx/cs/cs-loinc.js +1485 -0
  186. package/tx/cs/cs-mimetypes.js +238 -0
  187. package/tx/cs/cs-ndc.js +704 -0
  188. package/tx/cs/cs-omop.js +1025 -0
  189. package/tx/cs/cs-provider-api.js +43 -0
  190. package/tx/cs/cs-provider-list.js +37 -0
  191. package/tx/cs/cs-rxnorm.js +808 -0
  192. package/tx/cs/cs-snomed.js +1102 -0
  193. package/tx/cs/cs-ucum.js +514 -0
  194. package/tx/cs/cs-unii.js +271 -0
  195. package/tx/cs/cs-uri.js +218 -0
  196. package/tx/cs/cs-usstates.js +305 -0
  197. package/tx/dev.fhir.org.yml +14 -0
  198. package/tx/fixtures/test-cases-setup.json +18 -0
  199. package/tx/fixtures/test-cases.yml +16 -0
  200. package/tx/html/codesystem-operations.liquid +25 -0
  201. package/tx/html/home-metrics.liquid +247 -0
  202. package/tx/html/operations-form.liquid +148 -0
  203. package/tx/html/search-form.liquid +62 -0
  204. package/tx/html/tx-template.html +133 -0
  205. package/tx/html/valueset-operations.liquid +54 -0
  206. package/tx/importers/atc-to-fhir.js +316 -0
  207. package/tx/importers/import-loinc.module.js +1536 -0
  208. package/tx/importers/import-ndc.module.js +1088 -0
  209. package/tx/importers/import-rxnorm.module.js +898 -0
  210. package/tx/importers/import-sct.module.js +2457 -0
  211. package/tx/importers/import-unii.module.js +601 -0
  212. package/tx/importers/readme.md +453 -0
  213. package/tx/importers/subset-loinc.module.js +1081 -0
  214. package/tx/importers/subset-rxnorm.module.js +938 -0
  215. package/tx/importers/tx-import-base.js +351 -0
  216. package/tx/importers/tx-import-settings.js +310 -0
  217. package/tx/importers/tx-import.js +357 -0
  218. package/tx/library/canonical-resource.js +88 -0
  219. package/tx/library/capabilitystatement.js +292 -0
  220. package/tx/library/codesystem.js +774 -0
  221. package/tx/library/conceptmap.js +568 -0
  222. package/tx/library/designations.js +932 -0
  223. package/tx/library/errors.js +77 -0
  224. package/tx/library/extensions.js +117 -0
  225. package/tx/library/namingsystem.js +322 -0
  226. package/tx/library/operation-outcome.js +127 -0
  227. package/tx/library/parameters.js +105 -0
  228. package/tx/library/renderer.js +1559 -0
  229. package/tx/library/terminologycapabilities.js +418 -0
  230. package/tx/library/ucum-parsers.js +1029 -0
  231. package/tx/library/ucum-service.js +370 -0
  232. package/tx/library/ucum-types.js +1099 -0
  233. package/tx/library/valueset.js +543 -0
  234. package/tx/library.js +676 -0
  235. package/tx/ocl/cm-ocl.js +106 -0
  236. package/tx/ocl/cs-ocl.js +39 -0
  237. package/tx/ocl/vs-ocl.js +105 -0
  238. package/tx/operation-context.js +568 -0
  239. package/tx/params.js +613 -0
  240. package/tx/provider.js +403 -0
  241. package/tx/sct/ecl.js +1560 -0
  242. package/tx/sct/expressions.js +2077 -0
  243. package/tx/sct/structures.js +1396 -0
  244. package/tx/tx-html.js +1063 -0
  245. package/tx/tx.fhir.org.yml +39 -0
  246. package/tx/tx.js +927 -0
  247. package/tx/vs/vs-api.js +112 -0
  248. package/tx/vs/vs-database.js +786 -0
  249. package/tx/vs/vs-package.js +358 -0
  250. package/tx/vs/vs-vsac.js +366 -0
  251. package/tx/workers/batch-validate.js +129 -0
  252. package/tx/workers/batch.js +361 -0
  253. package/tx/workers/closure.js +32 -0
  254. package/tx/workers/expand.js +1845 -0
  255. package/tx/workers/lookup.js +407 -0
  256. package/tx/workers/metadata.js +467 -0
  257. package/tx/workers/operations.js +34 -0
  258. package/tx/workers/read.js +164 -0
  259. package/tx/workers/search.js +384 -0
  260. package/tx/workers/subsumes.js +334 -0
  261. package/tx/workers/translate.js +492 -0
  262. package/tx/workers/validate.js +2504 -0
  263. package/tx/workers/worker.js +904 -0
  264. package/tx/xml/capabilitystatement-xml.js +63 -0
  265. package/tx/xml/codesystem-xml.js +62 -0
  266. package/tx/xml/conceptmap-xml.js +65 -0
  267. package/tx/xml/namingsystem-xml.js +65 -0
  268. package/tx/xml/operationoutcome-xml.js +127 -0
  269. package/tx/xml/parameters-xml.js +312 -0
  270. package/tx/xml/terminologycapabilities-xml.js +64 -0
  271. package/tx/xml/valueset-xml.js +64 -0
  272. package/tx/xml/xml-base.js +603 -0
  273. package/vcl/vcl-parser.js +1098 -0
  274. package/vcl/vcl.js +253 -0
  275. package/windows-install.js +19 -0
  276. package/xig/xig-template.html +124 -0
  277. package/xig/xig.js +3049 -0
@@ -0,0 +1,335 @@
1
+ //
2
+ // Package Watcher
3
+ // Watches a local directory or .tgz file and triggers reloads on changes
4
+ //
5
+
6
+ const chokidar = require('chokidar');
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const os = require('os');
10
+ const tar = require('tar');
11
+
12
+ class PackageWatcher {
13
+ constructor(packagePath, options = {}) {
14
+ this.packagePath = packagePath;
15
+ this.debounceMs = options.debounceMs || 500;
16
+ this.onReload = options.onReload || (() => {});
17
+ this.log = options.log || console;
18
+ this.watcher = null;
19
+ this.debounceTimer = null;
20
+ this.extractDir = null;
21
+ this.isTgz = false;
22
+
23
+ // Folder options (paths relative to package root)
24
+ this.resourceFolders = options.resourceFolders || null; // null = load all
25
+ this.searchParametersFolder = options.searchParametersFolder || null; // folder within package
26
+ }
27
+
28
+ /**
29
+ * Start watching the package directory or .tgz file
30
+ */
31
+ start() {
32
+ this.log.info(`Watching package at: ${this.packagePath}`);
33
+
34
+ // Check if this is a .tgz file
35
+ this.isTgz = this.packagePath.endsWith('.tgz') || this.packagePath.endsWith('.tar.gz');
36
+
37
+ if (this.isTgz) {
38
+ // Create temp directory for extraction
39
+ this.extractDir = fs.mkdtempSync(path.join(os.tmpdir(), 'npmprojector-'));
40
+ this.log.info(`Using temp directory: ${this.extractDir}`);
41
+ }
42
+
43
+ this.watcher = chokidar.watch(this.packagePath, {
44
+ persistent: true,
45
+ ignoreInitial: true,
46
+ awaitWriteFinish: {
47
+ stabilityThreshold: 300,
48
+ pollInterval: 100
49
+ }
50
+ });
51
+
52
+ this.watcher
53
+ .on('add', filePath => this.handleChange('add', filePath))
54
+ .on('change', filePath => this.handleChange('change', filePath))
55
+ .on('unlink', filePath => this.handleChange('unlink', filePath))
56
+ .on('error', error => this.log.error('Watcher error:', error));
57
+
58
+ // Do initial load
59
+ this.triggerReload();
60
+ }
61
+
62
+ /**
63
+ * Handle a file change event
64
+ */
65
+ handleChange(event, filePath) {
66
+ this.log.info(`File ${event}: ${filePath}`);
67
+
68
+ // Debounce rapid changes
69
+ if (this.debounceTimer) {
70
+ clearTimeout(this.debounceTimer);
71
+ }
72
+
73
+ this.debounceTimer = setTimeout(() => {
74
+ this.triggerReload();
75
+ }, this.debounceMs);
76
+ }
77
+
78
+ /**
79
+ * Load all data and trigger the reload callback
80
+ */
81
+ triggerReload() {
82
+ this.log.info('Reloading package data...');
83
+
84
+ try {
85
+ // If .tgz, extract first
86
+ if (this.isTgz) {
87
+ this.extractTgz();
88
+ }
89
+
90
+ // Determine which directory to read from
91
+ const readPath = this.getReadPath();
92
+ this.log.info(`Reading from: ${readPath}`);
93
+
94
+ const data = this.loadPackageDataFrom(readPath);
95
+ this.onReload(data);
96
+ this.log.info(`Reload complete: ${data.resources.length} resources, ${data.searchParameters.length} search parameters`);
97
+ } catch (error) {
98
+ this.log.error('Error loading package data:', error);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Extract .tgz file to temp directory
104
+ */
105
+ extractTgz() {
106
+ // Clear existing extracted content
107
+ if (fs.existsSync(this.extractDir)) {
108
+ fs.rmSync(this.extractDir, { recursive: true, force: true });
109
+ }
110
+ fs.mkdirSync(this.extractDir, { recursive: true });
111
+
112
+ // Extract synchronously
113
+ tar.extract({
114
+ file: this.packagePath,
115
+ cwd: this.extractDir,
116
+ sync: true
117
+ });
118
+
119
+ this.log.info(`Extracted .tgz to ${this.extractDir}`);
120
+ }
121
+
122
+ /**
123
+ * Get the actual directory to read from
124
+ */
125
+ getReadPath() {
126
+ if (this.isTgz) {
127
+ // npm packages typically extract to a 'package' subdirectory
128
+ const packageSubdir = path.join(this.extractDir, 'package');
129
+ if (fs.existsSync(packageSubdir)) {
130
+ return packageSubdir;
131
+ }
132
+ return this.extractDir;
133
+ }
134
+ return this.packagePath;
135
+ }
136
+
137
+ /**
138
+ * Load all FHIR resources and search parameters from a directory
139
+ */
140
+ loadPackageDataFrom(dirPath) {
141
+ const resources = [];
142
+ const searchParameters = [];
143
+
144
+ // Load search parameters from specified folder within package
145
+ if (this.searchParametersFolder) {
146
+ const spPath = path.join(dirPath, this.searchParametersFolder);
147
+ if (fs.existsSync(spPath)) {
148
+ const spFiles = this.findJsonFiles(spPath);
149
+ this.log.info(`Loading search parameters from ${this.searchParametersFolder}: ${spFiles.length} files`);
150
+ for (const filePath of spFiles) {
151
+ this.loadSearchParamsFromFile(filePath, searchParameters);
152
+ }
153
+ } else {
154
+ this.log.warn(`Search parameters folder not found: ${spPath}`);
155
+ }
156
+ }
157
+
158
+ // Determine which folders to load resources from
159
+ let foldersToLoad = [];
160
+ if (this.resourceFolders && this.resourceFolders.length > 0) {
161
+ // Load only from specified folders
162
+ for (const folder of this.resourceFolders) {
163
+ const folderPath = path.join(dirPath, folder);
164
+ if (fs.existsSync(folderPath)) {
165
+ foldersToLoad.push(folderPath);
166
+ } else {
167
+ this.log.warn(`Resource folder not found: ${folderPath}`);
168
+ }
169
+ }
170
+ } else {
171
+ // Load from entire package
172
+ foldersToLoad = [dirPath];
173
+ }
174
+
175
+ // Load all JSON files from the designated folders
176
+ let totalFiles = 0;
177
+ for (const folder of foldersToLoad) {
178
+ const jsonFiles = this.findJsonFiles(folder);
179
+ totalFiles += jsonFiles.length;
180
+
181
+ for (const filePath of jsonFiles) {
182
+ // Skip if this is in the search parameters folder (already loaded)
183
+ if (this.searchParametersFolder) {
184
+ const spPath = path.join(dirPath, this.searchParametersFolder);
185
+ if (filePath.startsWith(spPath)) {
186
+ continue;
187
+ }
188
+ }
189
+
190
+ try {
191
+ const content = fs.readFileSync(filePath, 'utf-8');
192
+ const parsed = JSON.parse(content);
193
+
194
+ // Handle Bundles
195
+ if (parsed.resourceType === 'Bundle' && parsed.entry) {
196
+ for (const entry of parsed.entry) {
197
+ if (entry.resource) {
198
+ this.categorizeResource(entry.resource, resources, searchParameters);
199
+ }
200
+ }
201
+ } else if (parsed.resourceType) {
202
+ this.categorizeResource(parsed, resources, searchParameters);
203
+ }
204
+ } catch (err) {
205
+ // Skip files that can't be parsed as FHIR
206
+ }
207
+ }
208
+ }
209
+
210
+ this.log.info(`Found ${totalFiles} JSON files in ${foldersToLoad.length} folder(s)`);
211
+
212
+ return { resources, searchParameters };
213
+ }
214
+
215
+ /**
216
+ * Load search parameters from a specific path
217
+ */
218
+ loadSearchParametersFrom(searchParamPath) {
219
+ const searchParameters = [];
220
+
221
+ if (!fs.existsSync(searchParamPath)) {
222
+ this.log.warn(`Search parameters path does not exist: ${searchParamPath}`);
223
+ return searchParameters;
224
+ }
225
+
226
+ const stat = fs.statSync(searchParamPath);
227
+
228
+ if (stat.isDirectory()) {
229
+ const jsonFiles = this.findJsonFiles(searchParamPath);
230
+ for (const filePath of jsonFiles) {
231
+ this.loadSearchParamsFromFile(filePath, searchParameters);
232
+ }
233
+ } else if (stat.isFile()) {
234
+ this.loadSearchParamsFromFile(searchParamPath, searchParameters);
235
+ }
236
+
237
+ return searchParameters;
238
+ }
239
+
240
+ /**
241
+ * Load search parameters from a single file
242
+ */
243
+ loadSearchParamsFromFile(filePath, searchParameters) {
244
+ try {
245
+ const content = fs.readFileSync(filePath, 'utf-8');
246
+ const parsed = JSON.parse(content);
247
+
248
+ if (parsed.resourceType === 'Bundle' && parsed.entry) {
249
+ for (const entry of parsed.entry) {
250
+ if (entry.resource && entry.resource.resourceType === 'SearchParameter') {
251
+ searchParameters.push(entry.resource);
252
+ }
253
+ }
254
+ } else if (parsed.resourceType === 'SearchParameter') {
255
+ searchParameters.push(parsed);
256
+ }
257
+ } catch (err) {
258
+ this.log.warn(`Failed to parse search params from ${filePath}: ${err.message}`);
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Categorize a resource as either a SearchParameter or a regular resource
264
+ */
265
+ categorizeResource(resource, resources, searchParameters) {
266
+ if (resource.resourceType === 'SearchParameter') {
267
+ searchParameters.push(resource);
268
+ } else {
269
+ resources.push(resource);
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Recursively find all JSON files in a directory
275
+ */
276
+ findJsonFiles(dir) {
277
+ const files = [];
278
+
279
+ if (!fs.existsSync(dir)) {
280
+ this.log.warn(`Directory does not exist: ${dir}`);
281
+ return files;
282
+ }
283
+
284
+ const stat = fs.statSync(dir);
285
+ if (!stat.isDirectory()) {
286
+ this.log.warn(`Path is not a directory: ${dir}`);
287
+ return files;
288
+ }
289
+
290
+ const entries = fs.readdirSync(dir);
291
+
292
+ for (const entry of entries) {
293
+ // Skip hidden files and node_modules
294
+ if (entry.startsWith('.') || entry === 'node_modules') {
295
+ continue;
296
+ }
297
+
298
+ const fullPath = path.join(dir, entry);
299
+ const entryStat = fs.statSync(fullPath);
300
+
301
+ if (entryStat.isDirectory()) {
302
+ files.push(...this.findJsonFiles(fullPath));
303
+ } else if (entryStat.isFile() && path.extname(entry).toLowerCase() === '.json') {
304
+ files.push(fullPath);
305
+ }
306
+ }
307
+
308
+ return files;
309
+ }
310
+
311
+ /**
312
+ * Stop watching
313
+ */
314
+ stop() {
315
+ if (this.watcher) {
316
+ this.watcher.close();
317
+ this.watcher = null;
318
+ }
319
+ if (this.debounceTimer) {
320
+ clearTimeout(this.debounceTimer);
321
+ this.debounceTimer = null;
322
+ }
323
+ // Clean up temp directory
324
+ if (this.extractDir && fs.existsSync(this.extractDir)) {
325
+ try {
326
+ fs.rmSync(this.extractDir, { recursive: true, force: true });
327
+ this.log.info(`Cleaned up temp directory: ${this.extractDir}`);
328
+ } catch (err) {
329
+ this.log.warn(`Failed to clean up temp directory: ${err.message}`);
330
+ }
331
+ }
332
+ }
333
+ }
334
+
335
+ module.exports = PackageWatcher;
package/package.json ADDED
@@ -0,0 +1,119 @@
1
+ {
2
+ "name": "fhirsmith",
3
+ "version": "0.3.0",
4
+ "description": "A Node.js server that provides a collection of tools to serve the FHIR ecosystem",
5
+ "main": "server.js",
6
+ "engines": {
7
+ "node": ">=24"
8
+ },
9
+ "scripts": {
10
+ "start": "node server.js",
11
+ "dev": "nodemon server.js",
12
+ "test": "jest",
13
+ "test:watch": "jest --watch",
14
+ "test:coverage": "jest --coverage",
15
+ "test:ci": "jest --ci --coverage --watchAll=false --reporters=default --reporters=jest-junit --verbose",
16
+ "test:shl": "jest --testPathPattern=shl",
17
+ "test:vcl": "jest --testPathPattern=vcl",
18
+ "test:xig": "jest --testPathPattern=xig",
19
+ "test:packages": "jest --testPathPattern=packages",
20
+ "test:token": "jest --testPathPattern=token",
21
+ "test:integration": "jest --testPathPattern=integration",
22
+ "test:unit": "jest --testPathPattern=unit",
23
+ "lint": "eslint . --ext .js --ignore-path .gitignore",
24
+ "lint:fix": "eslint . --fix --ignore-pattern 'static/assets/js/html5shiv.js'",
25
+ "lint:relaxed": "eslint . --config .eslintrc.relaxed.js",
26
+ "install-global": "npm link",
27
+ "list": "node tx-import.js list",
28
+ "import": "node tx-import.js",
29
+ "tx-import": "node tx/importers/tx-import.js"
30
+ },
31
+ "dependencies": {
32
+ "axios": "^1.13.4",
33
+ "base45": "^3.0.0",
34
+ "bcrypt": "^6.0.0",
35
+ "cbor": "^9.0.1",
36
+ "chalk": "^4.1.2",
37
+ "chokidar": "^4.0.3",
38
+ "cli-progress": "^3.12.0",
39
+ "commander": "^14.0.3",
40
+ "commonmark": "^0.31.2",
41
+ "connect-sqlite3": "^0.9.16",
42
+ "cors": "^2.8.6",
43
+ "express": "^5.2.1",
44
+ "express-rate-limit": "^7.4.1",
45
+ "express-session": "^1.19.0",
46
+ "fast-xml-parser": "^5.3.4",
47
+ "fhir-validator-wrapper": "1.2.1",
48
+ "fhirpath": "^4.8.3",
49
+ "fs-extra": "^11.3.3",
50
+ "inquirer": "^8.2.5",
51
+ "liquidjs": "^10.24.0",
52
+ "lusca": "^1.7.0",
53
+ "natural": "^6.12.0",
54
+ "node-cron": "^3.0.3",
55
+ "pako": "^2.1.0",
56
+ "passport": "^0.7.0",
57
+ "passport-facebook": "^3.0.0",
58
+ "passport-github2": "^0.1.12",
59
+ "passport-google-oauth20": "^2.0.0",
60
+ "properties-file": "^3.6.4",
61
+ "rimraf": "^5.0.10",
62
+ "sqlite3": "^5.1.7",
63
+ "tar": "^7.5.7",
64
+ "winston": "^3.19.0",
65
+ "winston-daily-rotate-file": "^4.7.1",
66
+ "yaml": "^2.8.2"
67
+ },
68
+ "devDependencies": {
69
+ "@types/jest": "^29.5.8",
70
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
71
+ "@typescript-eslint/parser": "^8.54.0",
72
+ "eslint": "^8.57.1",
73
+ "eslint-plugin-promise": "^7.2.1",
74
+ "jest": "^29.7.0",
75
+ "jest-environment-node": "^29.7.0",
76
+ "jest-junit": "^16.0.0",
77
+ "mock-fs": "^5.5.0",
78
+ "nock": "^13.4.0",
79
+ "nodemon": "^3.1.11",
80
+ "supertest": "^7.2.2",
81
+ "tmp": "^0.2.5"
82
+ },
83
+ "jest-junit": {
84
+ "outputDirectory": "./test-results",
85
+ "outputName": "junit.xml",
86
+ "classNameTemplate": "{classname}",
87
+ "titleTemplate": "{title}",
88
+ "ancestorSeparator": " › "
89
+ },
90
+ "keywords": [
91
+ "fhir",
92
+ "shc",
93
+ "shl",
94
+ "xig",
95
+ "token",
96
+ "oauth",
97
+ "api-keys",
98
+ "smart-health-link",
99
+ "smart-health-card"
100
+ ],
101
+ "author": "Health Intersections Pty Ltd",
102
+ "license": "BSD-3",
103
+ "overrides": {
104
+ "tar": "^7.5.7",
105
+ "fast-xml-parser": "^5.3.4"
106
+ },
107
+ "bin": {
108
+ "tx-import": "./tx/importers/tx-import.js",
109
+ "fhirsmith": "./server.js"
110
+ },
111
+ "repository": {
112
+ "type": "git",
113
+ "url": "git+https://github.com/HealthIntersections/fhirsmith.git"
114
+ },
115
+ "bugs": {
116
+ "url": "https://github.com/HealthIntersections/fhirsmith/issues"
117
+ },
118
+ "homepage": "https://github.com/HealthIntersections/fhirsmith#readme"
119
+ }