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,513 @@
1
+ // registry-model.js
2
+ // Data model for terminology server registry
3
+
4
+ class ServerVersionInformation {
5
+ constructor() {
6
+ this.version = '';
7
+ this.address = '';
8
+ this.security = '';
9
+ this.error = '';
10
+ this.lastSuccess = null; // Date object
11
+ this.lastTat = '';
12
+ this.software = ''; // what software is running
13
+ this.codeSystems = []; // Array of strings (sorted, unique)
14
+ this.valueSets = []; // Array of strings (sorted, unique)
15
+ }
16
+
17
+ update(source) {
18
+ this.address = source.address;
19
+ this.error = source.error;
20
+ if (!source.error) {
21
+ this.security = source.security;
22
+ this.lastSuccess = source.lastSuccess;
23
+ this.codeSystems = [...source.codeSystems];
24
+ this.valueSets = [...source.valueSets];
25
+ this.lastTat = source.lastTat;
26
+ }
27
+ }
28
+
29
+ getDetails() {
30
+ let result = this.error ? this.error : 'Server Processed Ok';
31
+ const lastSeenStr = this.lastSuccess ? this.lastSuccess.toISOString() : 'never';
32
+ result += ` (last seen ${lastSeenStr}, last tat = ${this.lastTat})`;
33
+ return result;
34
+ }
35
+
36
+ getCsListHtml() {
37
+ if (this.codeSystems.length === 0) return '<ul></ul>';
38
+ return '<ul>' + this.codeSystems.map(cs =>
39
+ `<li>${this._escapeHtml(cs)}</li>`
40
+ ).join('') + '</ul>';
41
+ }
42
+
43
+ getVsListHtml() {
44
+ if (this.valueSets.length === 0) return '<ul></ul>';
45
+ return '<ul>' + this.valueSets.map(vs =>
46
+ `<li>${this._escapeHtml(vs)}</li>`
47
+ ).join('') + '</ul>';
48
+ }
49
+
50
+ _escapeHtml(text) {
51
+ const map = {
52
+ '&': '&amp;',
53
+ '<': '&lt;',
54
+ '>': '&gt;',
55
+ '"': '&quot;',
56
+ "'": '&#039;'
57
+ };
58
+ return text.replace(/[&<>"']/g, m => map[m]);
59
+ }
60
+
61
+ toJSON() {
62
+ return {
63
+ version: this.version,
64
+ address: this.address,
65
+ security: this.security,
66
+ error: this.error,
67
+ 'last-success': this.lastSuccess?.toISOString() || '',
68
+ lastTat: this.lastTat,
69
+ terminologies: this.codeSystems,
70
+ software: this.software,
71
+ valuesets: this.valueSets
72
+ };
73
+ }
74
+
75
+ static fromJSON(json) {
76
+ const instance = new ServerVersionInformation();
77
+ instance.version = json.version || '';
78
+ instance.address = json.address || '';
79
+ instance.security = typeof json.security === 'string' ? json.security : "";
80
+ instance.error = json.error || '';
81
+ instance.lastSuccess = json['last-success'] ? new Date(json['last-success']) : null;
82
+ instance.lastTat = json.lastTat || '';
83
+ instance.software = json.software;
84
+ instance.codeSystems = json.terminologies || [];
85
+ instance.valueSets = json.valuesets || [];
86
+ return instance;
87
+ }
88
+ }
89
+
90
+ class ServerInformation {
91
+ constructor() {
92
+ this.code = '';
93
+ this.name = '';
94
+ this.address = '';
95
+ this.accessInfo = '';
96
+ this.authCSList = []; // Authoritative code systems (with wildcards)
97
+ this.authVSList = []; // Authoritative value sets (with wildcards)
98
+ this.usageList = []; // Usage tags
99
+ this.versions = []; // Array of ServerVersionInformation
100
+ }
101
+
102
+ getVersion(ver) {
103
+ return this.versions.find(v => v.version === ver) || null;
104
+ }
105
+
106
+ update(source) {
107
+ this.name = source.name;
108
+ this.address = source.address;
109
+ this.accessInfo = source.accessInfo;
110
+ this.authCSList = [...source.authCSList];
111
+ this.authVSList = [...source.authVSList];
112
+ this.usageList = [...source.usageList];
113
+
114
+ source.versions.forEach(sourceVersion => {
115
+ const existing = this.getVersion(sourceVersion.version);
116
+ if (existing) {
117
+ existing.update(sourceVersion);
118
+ } else {
119
+ this.versions.push(sourceVersion);
120
+ }
121
+ });
122
+ }
123
+
124
+ getDetails() {
125
+ return this.accessInfo;
126
+ }
127
+
128
+ isAuthCS(codeSystem) {
129
+ return this.authCSList.some(mask => this._passesMask(mask, codeSystem));
130
+ }
131
+
132
+ isAuthVS(valueSet) {
133
+ return this.authVSList.some(mask => this._passesMask(mask, valueSet));
134
+ }
135
+
136
+ _passesMask(mask, value) {
137
+ if (mask.endsWith('*')) {
138
+ return value.startsWith(mask.slice(0, -1));
139
+ }
140
+ return value === mask;
141
+ }
142
+
143
+ getDescription() {
144
+ let result = '';
145
+
146
+ if (this.usageList.length > 0) {
147
+ result = `Usage Tags: ${this.usageList.join(', ')}`;
148
+ }
149
+
150
+ if (this.authCSList.length > 0) {
151
+ if (result) result += '. ';
152
+ result += 'Authoritative for the following CodeSystems: <ul>';
153
+ this.authCSList.forEach(cs => {
154
+ const escaped = this._escapeHtml(cs).replace('*', '<b>*</b>');
155
+ result += `<li>${escaped}</li>`;
156
+ });
157
+ result += '</ul>';
158
+ }
159
+
160
+ if (this.authVSList.length > 0) {
161
+ if (result) result += '. ';
162
+ result += 'Authoritative for the following ValueSets: <ul>';
163
+ this.authVSList.forEach(vs => {
164
+ const escaped = this._escapeHtml(vs).replace('*', '<b>*</b>');
165
+ result += `<li>${escaped}</li>`;
166
+ });
167
+ result += '</ul>';
168
+ }
169
+
170
+ return result;
171
+ }
172
+
173
+ _escapeHtml(text) {
174
+ const map = {
175
+ '&': '&amp;',
176
+ '<': '&lt;',
177
+ '>': '&gt;',
178
+ '"': '&quot;',
179
+ "'": '&#039;'
180
+ };
181
+ return text.replace(/[&<>"']/g, m => map[m]);
182
+ }
183
+
184
+ toJSON() {
185
+ return {
186
+ code: this.code,
187
+ name: this.name,
188
+ address: this.address,
189
+ 'access-info': this.accessInfo,
190
+ authoritative: this.authCSList.join(','),
191
+ 'authoritative-valuesets': this.authVSList.join(','),
192
+ usageList: this.usageList,
193
+ versions: this.versions.map(v => v.toJSON())
194
+ };
195
+ }
196
+
197
+ static fromJSON(json) {
198
+ const instance = new ServerInformation();
199
+ instance.code = json.code || '';
200
+ instance.name = json.name || '';
201
+ instance.address = json.address || '';
202
+ instance.accessInfo = json['access-info'] || '';
203
+ instance.authCSList = json.authoritative
204
+ ? json.authoritative.split(',').filter(s => s)
205
+ : [];
206
+ instance.authVSList = json['authoritative-valuesets']
207
+ ? json['authoritative-valuesets'].split(',').filter(s => s)
208
+ : [];
209
+ instance.usageList = json.usageList || [];
210
+ instance.versions = (json.versions || []).map(v => ServerVersionInformation.fromJSON(v));
211
+ return instance;
212
+ }
213
+ }
214
+
215
+ class ServerRegistry {
216
+ constructor() {
217
+ this.code = '';
218
+ this.name = '';
219
+ this.address = '';
220
+ this.authority = '';
221
+ this.error = '';
222
+ this.servers = []; // Array of ServerInformation
223
+ }
224
+
225
+ getServer(code) {
226
+ return this.servers.find(s => s.code === code) || null;
227
+ }
228
+
229
+ update(source) {
230
+ this.name = source.name;
231
+ this.address = source.address;
232
+ this.authority = source.authority;
233
+ this.error = source.error;
234
+
235
+ source.servers.forEach(sourceServer => {
236
+ const existing = this.getServer(sourceServer.code);
237
+ if (existing) {
238
+ existing.update(sourceServer);
239
+ } else {
240
+ this.servers.push(sourceServer);
241
+ }
242
+ });
243
+ }
244
+
245
+ toJSON() {
246
+ return {
247
+ code: this.code,
248
+ name: this.name,
249
+ address: this.address,
250
+ authority: this.authority,
251
+ error: this.error,
252
+ servers: this.servers.map(s => s.toJSON())
253
+ };
254
+ }
255
+
256
+ static fromJSON(json) {
257
+ const instance = new ServerRegistry();
258
+ instance.code = json.code || '';
259
+ instance.name = json.name || '';
260
+ instance.address = json.address || '';
261
+ instance.authority = json.authority || '';
262
+ instance.error = json.error || '';
263
+ instance.servers = (json.servers || []).map(s => ServerInformation.fromJSON(s));
264
+ return instance;
265
+ }
266
+ }
267
+
268
+ class ServerRegistries {
269
+ constructor() {
270
+ this.address = '';
271
+ this.doco = '';
272
+ this.lastRun = null; // Date object
273
+ this.outcome = '';
274
+ this.registries = []; // Array of ServerRegistry
275
+ this._lockName = null; // For tracking lock state
276
+ }
277
+
278
+ lock(name) {
279
+ // In Node.js, we might use async locks or mutexes
280
+ // For now, this is a simple flag-based approach
281
+ this._lockName = name;
282
+ }
283
+
284
+ unlock() {
285
+ this._lockName = null;
286
+ }
287
+
288
+ getRegistry(code) {
289
+ return this.registries.find(r => r.code === code) || null;
290
+ }
291
+
292
+ update(source) {
293
+ this.lastRun = source.lastRun;
294
+ this.outcome = source.outcome;
295
+ this.doco = source.doco;
296
+
297
+ source.registries.forEach(sourceRegistry => {
298
+ const existing = this.getRegistry(sourceRegistry.code);
299
+ if (existing) {
300
+ existing.update(sourceRegistry);
301
+ } else {
302
+ this.registries.push(sourceRegistry);
303
+ }
304
+ });
305
+ }
306
+
307
+ toJSON() {
308
+ return {
309
+ version: '1',
310
+ address: this.address,
311
+ doco: this.doco,
312
+ 'last-run': this.lastRun?.toISOString() || '',
313
+ outcome: this.outcome,
314
+ registries: this.registries.map(r => r.toJSON())
315
+ };
316
+ }
317
+
318
+ static fromJSON(json) {
319
+ if (json.version !== '1') {
320
+ throw new Error(`Unsupported version ${json.version}`);
321
+ }
322
+
323
+ const instance = new ServerRegistries();
324
+ instance.address = json.address || '';
325
+ instance.doco = json.doco || '';
326
+ instance.lastRun = json['last-run'] ? new Date(json['last-run']) : null;
327
+ instance.outcome = json.outcome || '';
328
+ instance.registries = (json.registries || []).map(r => ServerRegistry.fromJSON(r));
329
+ return instance;
330
+ }
331
+ }
332
+
333
+ // ServerRow is a flattened representation for API responses
334
+ class ServerRow {
335
+ constructor() {
336
+ this.serverName = '';
337
+ this.serverCode = '';
338
+ this.registryName = '';
339
+ this.registryCode = '';
340
+ this.registryUrl = '';
341
+ this.authCSList = [];
342
+ this.authVSList = [];
343
+ this.version = '';
344
+ this.url = '';
345
+ this.error = '';
346
+ this.security = '';
347
+ this.lastSuccess = 0; // milliseconds since last success
348
+ this.systems = 0; // count of code systems
349
+ this.sets = 0; // count of value sets
350
+ this.authoritative = false;
351
+ }
352
+
353
+ toJSON() {
354
+ const json = {
355
+ 'server-name': this.serverName,
356
+ 'server-code': this.serverCode,
357
+ 'registry-name': this.registryName,
358
+ 'registry-code': this.registryCode,
359
+ 'registry-url': this.registryUrl,
360
+ url: this.url,
361
+ version: this.version,
362
+ error: this.error,
363
+ 'last-success': this.lastSuccess,
364
+ systems: this.systems,
365
+ security: this.security,
366
+ sets: this.sets
367
+ };
368
+
369
+ if (this.authoritative) {
370
+ json['is-authoritative'] = true;
371
+ }
372
+
373
+ if (this.authCSList.length > 0) {
374
+ json.authoritative = this.authCSList;
375
+ }
376
+
377
+ if (this.authVSList.length > 0) {
378
+ json['authoritative-valuesets'] = this.authVSList;
379
+ }
380
+
381
+
382
+ return json;
383
+ }
384
+
385
+ static fromJSON(json) {
386
+ const instance = new ServerRow();
387
+ instance.serverName = json['server-name'] || '';
388
+ instance.serverCode = json['server-code'] || '';
389
+ instance.registryName = json['registry-name'] || '';
390
+ instance.registryCode = json['registry-code'] || '';
391
+ instance.registryUrl = json['registry-url'] || '';
392
+ instance.url = json.url || '';
393
+ instance.version = json.version || '';
394
+ instance.error = json.error || '';
395
+ instance.lastSuccess = json['last-success'] || 0;
396
+ instance.systems = json.systems || 0;
397
+ instance.sets = json.sets || 0;
398
+ instance.authoritative = json['is-authoritative'] || false;
399
+ instance.authCSList = json.authoritative || [];
400
+ instance.authVSList = json['authoritative-valuesets'] || [];
401
+ instance.security = json['security'] || '';
402
+
403
+ return instance;
404
+ }
405
+ }
406
+
407
+ // Utility functions (similar to TServerRegistryUtilities)
408
+ class ServerRegistryUtilities {
409
+ static passesMask(mask, value) {
410
+ if (mask.endsWith('*')) {
411
+ return value.startsWith(mask.slice(0, -1));
412
+ }
413
+ return value === mask;
414
+ }
415
+
416
+ static hasMatchingCodeSystem(cs, list, supportMask) {
417
+ if (!cs || list.length === 0) return false;
418
+
419
+ // Handle URLs with pipes - extract base URL
420
+ let baseCs = cs;
421
+ if (cs.includes('|')) {
422
+ baseCs = cs.substring(0, cs.indexOf('|'));
423
+ }
424
+
425
+ return list.some(item => {
426
+ // If we support wildcards (masks) and the item ends with "*", do prefix matching
427
+ if (supportMask && item.endsWith('*')) {
428
+ const prefix = item.slice(0, -1);
429
+ return cs.startsWith(prefix) || baseCs.startsWith(prefix);
430
+ }
431
+
432
+ // Otherwise do exact matching on both full and base URL
433
+ return item === cs || item === baseCs;
434
+ });
435
+ }
436
+
437
+ static hasMatchingValueSet(vs, list, supportMask) {
438
+ let baseVs = vs;
439
+ if (vs.includes('|')) {
440
+ baseVs = vs.substring(0, vs.indexOf('|'));
441
+ }
442
+
443
+ return list.some(item => {
444
+ if (supportMask && this.passesMask(item, vs)) {
445
+ return true;
446
+ }
447
+ if (!supportMask && (item === vs || item === baseVs)) {
448
+ return true;
449
+ }
450
+ return false;
451
+ });
452
+ }
453
+
454
+ static versionMatches(requested, available) {
455
+ // Simple semantic version matching
456
+ if (!requested || !available) return true;
457
+
458
+ if (requested === available) return true;
459
+
460
+ // Check if available starts with requested version
461
+ // e.g., "4.0" matches "4.0.1"
462
+ if (available.startsWith(requested + '.')) return true;
463
+
464
+ // More sophisticated semver matching could be added here
465
+ const requestedParts = requested.split('.');
466
+ const availableParts = available.split('.');
467
+
468
+ for (let i = 0; i < requestedParts.length; i++) {
469
+ if (i >= availableParts.length) return false;
470
+ if (requestedParts[i] !== availableParts[i]) return false;
471
+ }
472
+
473
+ return true;
474
+ }
475
+
476
+ static createRow(registry, server, version, isAuthoritative) {
477
+ const row = new ServerRow();
478
+
479
+ row.authoritative = isAuthoritative;
480
+ row.serverName = server.name;
481
+ row.serverCode = server.code;
482
+ row.registryName = registry.name;
483
+ row.registryCode = registry.code;
484
+ row.registryUrl = registry.address;
485
+
486
+ row.url = version.address;
487
+ row.error = version.error;
488
+ row.version = version.version;
489
+ row.security = version.security;
490
+ row.systems = version.codeSystems.length;
491
+ row.sets = version.valueSets.length;
492
+ row.authCSList = [...server.authCSList];
493
+ row.authVSList = [...server.authVSList];
494
+
495
+ // Calculate milliseconds since last success
496
+ if (version.lastSuccess) {
497
+ row.lastSuccess = Math.floor(Date.now() - version.lastSuccess.getTime());
498
+ } else {
499
+ row.lastSuccess = 0;
500
+ }
501
+
502
+ return row;
503
+ }
504
+ }
505
+
506
+ module.exports = {
507
+ ServerVersionInformation,
508
+ ServerInformation,
509
+ ServerRegistry,
510
+ ServerRegistries,
511
+ ServerRow,
512
+ ServerRegistryUtilities
513
+ };