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,1056 @@
1
+ /**
2
+ * VersionUtilities - JavaScript implementation
3
+ * Port of the Java VersionUtilities class for FHIR version handling
4
+ */
5
+
6
+ const { Utilities, validateParameter, validateOptionalParameter} = require('./utilities');
7
+
8
+ // Enums
9
+ const VersionPrecision = {
10
+ MAJOR: 'MAJOR',
11
+ MINOR: 'MINOR',
12
+ PATCH: 'PATCH',
13
+ FULL: 'FULL'
14
+ };
15
+
16
+ class SemverParser {
17
+ static parseSemver(version, allowWildcards = false) {
18
+ const result = new ParseResult();
19
+
20
+ if (Utilities.noString(version)) {
21
+ result.error = 'Empty version';
22
+ return result;
23
+ }
24
+
25
+ // Handle question mark suffix
26
+ let versionStr = version;
27
+ if (versionStr.endsWith('?')) {
28
+ versionStr = versionStr.slice(0, -1);
29
+ }
30
+
31
+ // Split into main version and labels
32
+ let mainVersion = versionStr;
33
+ let releaseLabel = null;
34
+ let build = null;
35
+
36
+ // Extract build metadata (after +)
37
+ const plusIndex = mainVersion.indexOf('+');
38
+ if (plusIndex >= 0) {
39
+ build = mainVersion.substring(plusIndex + 1);
40
+ mainVersion = mainVersion.substring(0, plusIndex);
41
+ }
42
+
43
+ // Extract pre-release (after -)
44
+ const dashIndex = mainVersion.indexOf('-');
45
+ if (dashIndex >= 0) {
46
+ releaseLabel = mainVersion.substring(dashIndex + 1);
47
+ mainVersion = mainVersion.substring(0, dashIndex);
48
+ }
49
+
50
+ // Split version parts
51
+ const parts = mainVersion.split('.');
52
+
53
+ // For semver, we need at least major.minor, but no more than major.minor.patch
54
+ if (parts.length < 1) {
55
+ result.error = 'Version must have at least major';
56
+ return result;
57
+ }
58
+ if (parts.length > 3) {
59
+ result.error = 'Version cannot have more than major.minor.patch';
60
+ return result;
61
+ }
62
+
63
+ // Validate parts
64
+ for (let i = 0; i < parts.length; i++) {
65
+ const part = parts[i];
66
+ if (!allowWildcards && !Utilities.isInteger(part)) {
67
+ result.error = `Invalid version part: ${part}`;
68
+ return result;
69
+ }
70
+ if (allowWildcards && !Utilities.isInteger(part) && !Utilities.existsInList(part, "x", "X", "*")) {
71
+ result.error = `Invalid version part with wildcards: ${part}`;
72
+ return result;
73
+ }
74
+ // Check for leading zeros (except for "0" itself)
75
+ if (Utilities.isInteger(part) && part.length > 1 && part.startsWith('0')) {
76
+ result.error = `Invalid leading zero in version part: ${part}`;
77
+ return result;
78
+ }
79
+ }
80
+
81
+ // Check for invalid pre-release formats
82
+ if (releaseLabel !== null) {
83
+ if (releaseLabel === '') {
84
+ result.error = 'Empty pre-release label';
85
+ return result;
86
+ }
87
+ // Check for leading zeros in numeric pre-release identifiers
88
+ const releaseParts = releaseLabel.split('.');
89
+ for (const part of releaseParts) {
90
+ if (Utilities.isInteger(part) && part.length > 1 && part.startsWith('0')) {
91
+ result.error = `Invalid leading zero in pre-release: ${part}`;
92
+ return result;
93
+ }
94
+ }
95
+ }
96
+
97
+ // Check for invalid build formats
98
+ if (build !== null && build === '') {
99
+ result.error = 'Empty build metadata';
100
+ return result;
101
+ }
102
+
103
+ result.major = parts[0] || null;
104
+ result.minor = parts[1] || null;
105
+ result.patch = parts.length >= 3 ? parts[2] : null;
106
+ result.releaseLabel = releaseLabel;
107
+ result.build = build;
108
+ result.success = true;
109
+
110
+ return result;
111
+ }
112
+ }
113
+
114
+ class ParseResult {
115
+ constructor() {
116
+ this.success = false;
117
+ this.error = null;
118
+ this.major = null;
119
+ this.minor = null;
120
+ this.patch = null;
121
+ this.releaseLabel = null;
122
+ this.build = null;
123
+ }
124
+
125
+ isSuccess() {
126
+ return this.success;
127
+ }
128
+
129
+ getMajor() {
130
+ return this.major;
131
+ }
132
+
133
+ getMinor() {
134
+ return this.minor;
135
+ }
136
+
137
+ getPatch() {
138
+ return this.patch;
139
+ }
140
+
141
+ getReleaseLabel() {
142
+ return this.releaseLabel;
143
+ }
144
+
145
+ getBuild() {
146
+ return this.build;
147
+ }
148
+
149
+ getError() {
150
+ return this.error;
151
+ }
152
+ }
153
+
154
+ class VersionUtilities {
155
+ static SUPPORTED_MAJOR_VERSIONS = ["1.0", "1.4", "3.0", "4.0", "5.0", "6.0"];
156
+ static SUPPORTED_VERSIONS = ["1.0.2", "1.4.0", "3.0.2", "4.0.1", "4.1.0", "4.3.0", "5.0.0", "6.0.0"];
157
+
158
+ /**
159
+ * Returns the package name for the given FHIR version.
160
+ * @param {string} v FHIR version string
161
+ * @return {string|null} package name (e.g., "hl7.fhir.r4.core") or null if version not recognized
162
+ */
163
+ static packageForVersion(v) {
164
+ if (this.isR2Ver(v)) {
165
+ return "hl7.fhir.r2.core";
166
+ }
167
+ if (this.isR2BVer(v)) {
168
+ return "hl7.fhir.r2b.core";
169
+ }
170
+ if (this.isR3Ver(v)) {
171
+ return "hl7.fhir.r3.core";
172
+ }
173
+ if (this.isR4Ver(v)) {
174
+ return "hl7.fhir.r4.core";
175
+ }
176
+ if (this.isR4BVer(v)) {
177
+ return "hl7.fhir.r4b.core";
178
+ }
179
+ if (this.isR5Ver(v)) {
180
+ return "hl7.fhir.r5.core";
181
+ }
182
+ if (this.isR6Ver(v)) {
183
+ return "hl7.fhir.r6.core";
184
+ }
185
+ if (v === "current") {
186
+ return "hl7.fhir.r5.core";
187
+ }
188
+ return null;
189
+ }
190
+
191
+ /**
192
+ * Returns the current/latest version for a given FHIR version family.
193
+ * @param {string} v FHIR version string
194
+ * @return {string} current version string for that family
195
+ */
196
+ static getCurrentVersion(v) {
197
+ if (this.isR2Ver(v)) {
198
+ return "1.0.2";
199
+ }
200
+ if (this.isR2BVer(v)) {
201
+ return "1.4.0";
202
+ }
203
+ if (this.isR3Ver(v)) {
204
+ return "3.0.2";
205
+ }
206
+ if (this.isR4Ver(v)) {
207
+ return "4.0.1";
208
+ }
209
+ if (this.isR5Ver(v)) {
210
+ return "5.0.0";
211
+ }
212
+ if (this.isR6Ver(v)) {
213
+ return "6.0.0";
214
+ }
215
+ return v;
216
+ }
217
+
218
+ /**
219
+ * Returns the current package version for a given FHIR version family.
220
+ * @param {string} v FHIR version string
221
+ * @return {string} package version (major.minor format)
222
+ */
223
+ static getCurrentPackageVersion(v) {
224
+ if (this.isR2Ver(v)) {
225
+ return "1.0";
226
+ }
227
+ if (this.isR2BVer(v)) {
228
+ return "1.4";
229
+ }
230
+ if (this.isR3Ver(v)) {
231
+ return "3.0";
232
+ }
233
+ if (this.isR4Ver(v)) {
234
+ return "4.0";
235
+ }
236
+ if (this.isR5Ver(v)) {
237
+ return "5.0";
238
+ }
239
+ if (this.isR6Ver(v)) {
240
+ return "6.0";
241
+ }
242
+ return v;
243
+ }
244
+
245
+ /**
246
+ * Checks if the given version is in the list of supported FHIR versions.
247
+ * @param {string} version version string to check
248
+ * @return {boolean} true if version is supported
249
+ */
250
+ static isSupportedVersion(version) {
251
+ version = this.checkVersionNotNullAndValid(this.removeLabels(this.fixForSpecialValue(version)));
252
+ return Utilities.existsInList(version, ...this.SUPPORTED_VERSIONS);
253
+ }
254
+
255
+ /**
256
+ * Returns a comma-separated list of all supported FHIR versions.
257
+ * @return {string} string listing supported versions
258
+ */
259
+ static listSupportedVersions() {
260
+ return this.SUPPORTED_VERSIONS.join(", ");
261
+ }
262
+
263
+ /**
264
+ * Returns a comma-separated list of all supported major FHIR versions.
265
+ * @return {string} string listing supported major versions
266
+ */
267
+ static listSupportedMajorVersions() {
268
+ return this.SUPPORTED_MAJOR_VERSIONS.join(", ");
269
+ }
270
+
271
+ /**
272
+ * returns true if version refers to any R6 release (including rX/RX variants)
273
+ */
274
+ static isR6Plus(version) {
275
+ return this.isR6Ver(version);
276
+ }
277
+
278
+ /**
279
+ * Checks if version refers to any R6 release.
280
+ * @param {string} version version string to check
281
+ * @return {boolean} true if R6 version
282
+ */
283
+ static isR6Ver(version) {
284
+ version = this.removeLabels(this.checkVersionValid(this.fixForSpecialValue(version)));
285
+ return version != null && version.startsWith("6.0");
286
+ }
287
+
288
+ /**
289
+ * returns true if version refers to any R5 release (including pre-release versions starting from 4.5) (including rX/RX variants)
290
+ */
291
+ static isR5Plus(version) {
292
+ return this.isR5Ver(version) || this.isR6Plus(version);
293
+ }
294
+
295
+ /**
296
+ * Checks if version refers to any R5 release (including 4.5+ pre-releases).
297
+ * @param {string} version version string to check
298
+ * @return {boolean} true if R5 version
299
+ */
300
+ static isR5Ver(version) {
301
+ version = this.removeLabels(this.checkVersionValid(this.fixForSpecialValue(version)));
302
+ return version != null && (version.startsWith("4.5") || version.startsWith("5.0"));
303
+ }
304
+
305
+ /**
306
+ * Checks if version refers to any R4B release.
307
+ * @param {string} version version string to check
308
+ * @return {boolean} true if R4B version
309
+ */
310
+ static isR4BVer(version) {
311
+ version = this.removeLabels(this.checkVersionValid(this.fixForSpecialValue(version)));
312
+ return version != null && (version.startsWith("4.1") || version.startsWith("4.3"));
313
+ }
314
+
315
+ /**
316
+ * Checks if version refers to any R4 release (including 3.2+ pre-releases).
317
+ * @param {string} version version string to check
318
+ * @return {boolean} true if R4 version
319
+ */
320
+ static isR4Ver(version) {
321
+ version = this.removeLabels(this.checkVersionValid(this.fixForSpecialValue(version)));
322
+ return version != null && (version.startsWith("4.0") ||
323
+ // pre-release versions
324
+ version.startsWith("3.2") || version.startsWith("3.3") ||
325
+ version.startsWith("3.4") || version.startsWith("3.5"));
326
+ }
327
+
328
+ /**
329
+ * returns true if version refers to any R4 release (including pre-release versions starting from 3.2) (including rX/RX variants)
330
+ */
331
+ static isR4Plus(version) {
332
+ return this.isR4Ver(version) || this.isR4BVer(version) || this.isR5Plus(version);
333
+ }
334
+
335
+ /**
336
+ * Checks if version refers to any R3 release.
337
+ * @param {string} version version string to check
338
+ * @return {boolean} true if R3 version
339
+ */
340
+ static isR3Ver(version) {
341
+ version = this.removeLabels(this.checkVersionValid(this.fixForSpecialValue(version)));
342
+ return version != null && version.startsWith("3.0");
343
+ }
344
+
345
+ /**
346
+ * Checks if version refers to any R2B release.
347
+ * @param {string} version version string to check
348
+ * @return {boolean} true if R2B version
349
+ */
350
+ static isR2BVer(version) {
351
+ version = this.removeLabels(this.checkVersionValid(this.fixForSpecialValue(version)));
352
+ return version != null && version.startsWith("1.4");
353
+ }
354
+
355
+ /**
356
+ * Checks if version refers to any R2 release.
357
+ * @param {string} version version string to check
358
+ * @return {boolean} true if R2 version
359
+ */
360
+ static isR2Ver(version) {
361
+ version = this.removeLabels(this.checkVersionValid(this.fixForSpecialValue(version)));
362
+ return version != null && version.startsWith("1.0");
363
+ }
364
+
365
+ /**
366
+ * Checks if the given string is a FHIR core package name.
367
+ * @param {string} s package name to check
368
+ * @return {boolean} true if it's a core package
369
+ */
370
+ static isCorePackage(s) {
371
+ if (s == null) {
372
+ return false;
373
+ }
374
+ if (s.includes("#")) {
375
+ s = s.substring(0, s.indexOf("#"));
376
+ }
377
+ return Utilities.existsInList(s, "hl7.fhir.core", "hl7.fhir.r2.core", "hl7.fhir.r2b.core",
378
+ "hl7.fhir.r3.core", "hl7.fhir.r4.core", "hl7.fhir.r4b.core", "hl7.fhir.r5.core", "hl7.fhir.r6.core");
379
+ }
380
+
381
+ /**
382
+ * Returns version string with labels (pre-release/build info) removed.
383
+ * @param {string} version version string
384
+ * @return {string|null} version without labels, or null if invalid
385
+ */
386
+ static versionWithoutLabels(version) {
387
+ version = this.checkVersionNotNullAndValid(this.fixForSpecialValue(version));
388
+ return this.removeLabels(version);
389
+ }
390
+
391
+ /**
392
+ * given any valid semver string, returns major.minor. Also accepts the special values rX/RX where X is a major FHIR version (2,2B,3,4,4B,5,6)
393
+ *
394
+ * returns null if not a valid semver
395
+ */
396
+ static getMajMin(version) {
397
+ version = this.removeLabels(this.fixForSpecialValue(version));
398
+ if (version == null) {
399
+ return null;
400
+ }
401
+ if (!this.isSemVer(version)) {
402
+ return null;
403
+ }
404
+ return this.getMajMinPriv(version);
405
+ }
406
+
407
+ static getMajMinPriv(version) {
408
+ const p = version.split(".");
409
+ return p[0] + "." + p[1];
410
+ }
411
+
412
+ /**
413
+ * given any valid semver string, returns major.minor.patch. Also accepts the special values rX/RX where X is a major FHIR version (2,2B,3,4,4B,5,6)
414
+ *
415
+ * if there's no patch, it will be assumed to be 0
416
+ *
417
+ * returns null if it's not a valid semver
418
+ */
419
+ static getMajMinPatch(version) {
420
+ version = this.removeLabels(this.fixForSpecialValue(version));
421
+ if (version == null) {
422
+ return null;
423
+ }
424
+ if (!this.isSemVer(version)) {
425
+ return null;
426
+ }
427
+ const p = version.split(".");
428
+ return p[0] + "." + p[1] + (p.length >= 3 ? "." + p[2] : ".0");
429
+ }
430
+
431
+ /**
432
+ * given any valid semver string, returns just the patch version, with no labels. Also accepts the special values rX/RX where X is a major FHIR version (2,2B,3,4,4B,5,6)
433
+ */
434
+ static getPatch(version) {
435
+ version = this.removeLabels(this.checkVersionValid(this.fixForSpecialValue(version)));
436
+ if (version == null)
437
+ return null;
438
+ return this.getPatchPriv(version);
439
+ }
440
+
441
+ static getPatchPriv(version) {
442
+ const p = version.split(".");
443
+ return p.length >= 3 ? p[2] : "0";
444
+ }
445
+
446
+ /**
447
+ * returns true if this is a valid semver. we accept major.minor without a patch. This one does not accept the codes such as RX
448
+ */
449
+ static isSemVer(version) {
450
+ if (Utilities.noString(version)) {
451
+ return false;
452
+ }
453
+ const pr = SemverParser.parseSemver(version, false, false);
454
+ if (!pr.isSuccess()) {
455
+ return false;
456
+ }
457
+ return Utilities.isInteger(pr.getMajor()) && Utilities.isInteger(pr.getMinor()) &&
458
+ (pr.getPatch() == null || Utilities.isInteger(pr.getPatch()));
459
+ }
460
+
461
+ /**
462
+ * Checks if version string is valid semver with wildcard support.
463
+ * @param {string} version version string to validate
464
+ * @return {boolean} true if valid semver with wildcards
465
+ */
466
+ static isSemVerWithWildcards(version) {
467
+ if (Utilities.noString(version)) {
468
+ return false;
469
+ }
470
+ const pr = SemverParser.parseSemver(version, true, false);
471
+ if (!pr.isSuccess()) {
472
+ return false;
473
+ }
474
+ return Utilities.isInteger(pr.getMajor()) && this.isIntegerOrX(pr.getMinor()) &&
475
+ (pr.getPatch() == null || this.isIntegerOrX(pr.getPatch()));
476
+ }
477
+
478
+ static isIntegerOrX(p) {
479
+ return Utilities.existsInList(p, "x", "*", "X") || Utilities.isInteger(p);
480
+ }
481
+
482
+ /**
483
+ * Returns true if the version string contains any wildcard characters.
484
+ * Supported wildcards are: * (asterisk) anywhere, x/X (in major/minor/patch only), and ? (question mark suffix).
485
+ * Note: x and X are only wildcards in version number parts, not in release labels (after -) or build labels (after +).
486
+ *
487
+ * @param {string} version version string to check
488
+ * @return {boolean} true if version contains any wildcard characters, false otherwise
489
+ */
490
+ static versionHasWildcards(version) {
491
+ if (Utilities.noString(version)) {
492
+ return false;
493
+ }
494
+
495
+ // Check for ? suffix wildcard
496
+ if (version.endsWith("?")) {
497
+ return true;
498
+ }
499
+
500
+ // Check for * wildcard anywhere
501
+ if (version.includes("*")) {
502
+ return true;
503
+ }
504
+
505
+ // For x/X wildcards, we need to check only the version number parts (before any - or +)
506
+ let versionPart = version;
507
+ const dashIndex = version.indexOf('-');
508
+ const plusIndex = version.indexOf('+');
509
+
510
+ if (dashIndex >= 0 && plusIndex >= 0) {
511
+ versionPart = version.substring(0, Math.min(dashIndex, plusIndex));
512
+ } else if (dashIndex >= 0) {
513
+ versionPart = version.substring(0, dashIndex);
514
+ } else if (plusIndex >= 0) {
515
+ versionPart = version.substring(0, plusIndex);
516
+ }
517
+
518
+ // Check for x/X wildcards only in the version number part
519
+ return versionPart.includes("x") || versionPart.includes("X");
520
+ }
521
+
522
+ /**
523
+ * return true if the current version equals criteria, or later, based on the degree of precision specified
524
+ *
525
+ * This can be used to check e.g. if a feature is defined in 4.0, if (VersionUtilities.isThisOrLater("4.0", version))
526
+ *
527
+ * this is only applicable to valid semver versions (though patch is optional)
528
+ *
529
+ * the criteria string can contain wildcards - see versionMatches
530
+ *
531
+ * @param {string} criteria The value to compare to
532
+ * @param {string} candidate The value being compared
533
+ * @param {string} precision how far into the version string to consider (usually just set to full if there's wildcards in the test string)
534
+ *
535
+ * @return {boolean} Is candidate later or equal to criteria? For example, if criteria = 0.5 and candidate = 0.6 this method will return true
536
+ */
537
+ static isThisOrLater(criteria, candidate, precision) {
538
+ criteria = this.checkVersionNotNullAndValidWildcards(this.fixForSpecialValue(criteria), "criteria");
539
+ candidate = this.checkVersionNotNullAndValid(this.fixForSpecialValue(candidate), "candidate");
540
+
541
+ let endsWithQ = false;
542
+ if (criteria.endsWith("?")) {
543
+ endsWithQ = true;
544
+ criteria = criteria.substring(0, criteria.length - 1);
545
+ }
546
+
547
+ const parsedCriteria = SemverParser.parseSemver(criteria, true, false);
548
+ if (!parsedCriteria.isSuccess()) {
549
+ throw new Error("Invalid criteria: " + criteria + ": (" + parsedCriteria.getError() + ")");
550
+ }
551
+
552
+ const parsedCandidate = SemverParser.parseSemver(candidate, false, false);
553
+ if (!parsedCandidate.isSuccess()) {
554
+ throw new Error("Invalid candidate: " + candidate + " (" + parsedCandidate.getError() + ")");
555
+ }
556
+
557
+ let thisOrLater;
558
+ thisOrLater = this.partIsThisOrLater(parsedCriteria.getMajor(), parsedCandidate.getMajor(), true);
559
+ if (thisOrLater !== 0) { return thisOrLater < 0 ? true : false; }
560
+ if (endsWithQ && parsedCriteria.getMinor() == null) { return true; }
561
+ if (precision === VersionPrecision.MAJOR) { return true; }
562
+ thisOrLater = this.partIsThisOrLater(parsedCriteria.getMinor(), parsedCandidate.getMinor(), true);
563
+ if (thisOrLater !== 0) { return thisOrLater < 0 ? true : false; }
564
+ if (endsWithQ && parsedCriteria.getPatch() == null) { return true; }
565
+ if (precision === VersionPrecision.MINOR) { return true; }
566
+ thisOrLater = this.partIsThisOrLater(parsedCriteria.getPatch(), parsedCandidate.getPatch(), true);
567
+ if (thisOrLater !== 0) { return thisOrLater < 0 ? true : false; }
568
+ if (precision === VersionPrecision.PATCH) { return true; }
569
+ if (endsWithQ && parsedCriteria.getReleaseLabel() == null && parsedCriteria.getBuild() == null) { return true; }
570
+ thisOrLater = this.partIsThisOrLater(parsedCriteria.getReleaseLabel(), parsedCandidate.getReleaseLabel(), false);
571
+ if (thisOrLater !== 0) { return thisOrLater < 0 ? true : false; }
572
+ thisOrLater = this.partIsThisOrLater(parsedCriteria.getBuild(), parsedCandidate.getBuild(), false);
573
+ if (thisOrLater !== 0) { return thisOrLater < 0 ? true : false; }
574
+ return true;
575
+ }
576
+
577
+ static partIsThisOrLater(criteria, candidate, allowX) {
578
+ if (criteria == null) {
579
+ if (candidate == null) {
580
+ return 0;
581
+ } else {
582
+ return allowX ? -1 : 1;
583
+ }
584
+ } else if (candidate == null) {
585
+ return allowX ? 1 : -1;
586
+ } else if (allowX ? Utilities.existsInList(criteria, "*", "x", "X") : Utilities.existsInList(criteria, "*")) {
587
+ return -1;
588
+ } else if (Utilities.isInteger(criteria) && Utilities.isInteger(candidate)) {
589
+ return parseInt(criteria, 10) - parseInt(candidate, 10);
590
+ } else {
591
+ return criteria.localeCompare(candidate);
592
+ }
593
+ }
594
+
595
+ /**
596
+ * given any semver, increment the major version and reset the minor and patch to .0.0, and remove any labels
597
+ */
598
+ static incMajorVersion(v) {
599
+ v = this.removeLabels(this.checkVersionNotNullAndValid(this.fixForSpecialValue(v)));
600
+ const parts = this.splitParts(this.removeLabels(v));
601
+ return (parts[0] + 1).toString() + ".0.0";
602
+ }
603
+
604
+ /**
605
+ * given any semver, increment the minor version and reset the patch to .0 and remove any labels
606
+ */
607
+ static incMinorVersion(v) {
608
+ v = this.removeLabels(this.checkVersionNotNullAndValid(this.fixForSpecialValue(v)));
609
+ const parts = this.splitParts(this.removeLabels(v));
610
+ return parts[0].toString() + "." + (parts.length === 1 ? "0.0" : (parts[1] + 1).toString() + ".0");
611
+ }
612
+
613
+ /**
614
+ * given any semver, increment the patch and remove any labels
615
+ */
616
+ static incPatchVersion(v) {
617
+ v = this.removeLabels(this.checkVersionNotNullAndValid(this.fixForSpecialValue(v)));
618
+ const parts = this.splitParts(v);
619
+ return parts[0].toString() + "." +
620
+ (parts.length < 2 ? "0" : parts[1].toString()) + "." +
621
+ (parts.length < 3 ? "1" : (parts[2] + 1).toString());
622
+ }
623
+
624
+ static splitParts(v) {
625
+ const p = v.split(".");
626
+ return p.map(part => parseInt(part, 10));
627
+ }
628
+
629
+ /**
630
+ * Converts version code to standard version string.
631
+ * @param {string} version version code or string
632
+ * @return {string} standardized version string
633
+ */
634
+ static versionFromCode(version) {
635
+ return this.checkVersionNotNullAndValid(this.fixForSpecialValue(version));
636
+ }
637
+
638
+ // ... (continuing with remaining methods)
639
+
640
+ /**
641
+ * returns true if v1 and v2 are both semver, and they 'match'
642
+ */
643
+ static versionMatches(criteria, candidate) {
644
+ validateParameter(criteria, "criteria", String);
645
+ validateParameter(candidate, "candidate", String);
646
+ if (Utilities.noString(criteria)) {
647
+ throw new Error("Invalid criteria: null / empty");
648
+ }
649
+ if (Utilities.noString(candidate)) {
650
+ throw new Error("Invalid candidate: null / empty");
651
+ }
652
+ criteria = this.fixForSpecialValue(criteria);
653
+ candidate = this.fixForSpecialValue(candidate);
654
+
655
+ let endsWithQ = false;
656
+ if (criteria.endsWith("?")) {
657
+ endsWithQ = true;
658
+ criteria = criteria.substring(0, criteria.length - 1);
659
+ }
660
+
661
+ const parsedCriteria = SemverParser.parseSemver(criteria, true, false);
662
+ if (!parsedCriteria.isSuccess()) {
663
+ throw new Error("Invalid criteria: " + criteria + ": (" + parsedCriteria.getError() + ")");
664
+ }
665
+
666
+ const parsedCandidate = SemverParser.parseSemver(candidate, false, false);
667
+ if (!parsedCandidate.isSuccess()) {
668
+ throw new Error("Invalid candidate: " + candidate + " (" + parsedCandidate.getError() + ")");
669
+ }
670
+
671
+ if (!this.partMatches(parsedCriteria.getMajor(), parsedCandidate.getMajor(), true)) { return false; }
672
+ if (endsWithQ && parsedCriteria.getMinor() == null) { return true; }
673
+ if (!this.partMatches(parsedCriteria.getMinor(), parsedCandidate.getMinor(), true)) { return false; }
674
+ if (endsWithQ && parsedCriteria.getPatch() == null) { return true; }
675
+ if (!this.partMatches(parsedCriteria.getPatch(), parsedCandidate.getPatch(), true)) { return false; }
676
+ if (endsWithQ && parsedCriteria.getReleaseLabel() == null && parsedCriteria.getBuild() == null) { return true; }
677
+ if (!this.partMatches(parsedCriteria.getReleaseLabel(), parsedCandidate.getReleaseLabel(), false)) { return false; }
678
+ if (!this.partMatches(parsedCriteria.getBuild(), parsedCandidate.getBuild(), false)) { return false; }
679
+ return true;
680
+ }
681
+
682
+ static partMatches(criteria, candidate, allowX) {
683
+ if (criteria == null) {
684
+ return candidate == null;
685
+ } else {
686
+ if (allowX ? Utilities.existsInList(criteria, "*", "x", "X") : Utilities.existsInList(criteria, "*")) {
687
+ return candidate != null;
688
+ } else {
689
+ return criteria === candidate;
690
+ }
691
+ }
692
+ }
693
+
694
+ /**
695
+ * returns true if v1 matches any v2 using the rules for versionMatches()
696
+ */
697
+ static versionMatchesList(v1, v2l) {
698
+ for (const v2 of v2l) {
699
+ if (this.versionMatches(v1, v2)) {
700
+ return true;
701
+ }
702
+ }
703
+ return false;
704
+ }
705
+
706
+ /**
707
+ * Given a canonical URL of format {url}|{version}, remove the version part
708
+ */
709
+ static removeVersionFromCanonical(url) {
710
+ if (url == null) {
711
+ return null;
712
+ }
713
+ if (url.includes("|")) {
714
+ return url.substring(0, url.indexOf("|"));
715
+ } else {
716
+ return url;
717
+ }
718
+ }
719
+
720
+ /**
721
+ * given version ver1 and ver2, compare them as semver strings (special values also accepted).
722
+ * -1 means ver1 is earlier, 0 means they 'match' and 1 means ver2 is later (normal java sort order)
723
+ */
724
+ static compareVersions(ver1, ver2) {
725
+ ver1 = this.checkVersionValid(this.fixForSpecialValue(ver1), "ver1");
726
+ ver2 = this.checkVersionValid(this.fixForSpecialValue(ver2), "ver2");
727
+
728
+ if (ver1 != null && ver2 != null) {
729
+ const pr1 = SemverParser.parseSemver(ver1, false, false);
730
+ const pr2 = SemverParser.parseSemver(ver2, false, false);
731
+ let res = this.compareVersionStrings(pr1.getMajor(), pr2.getMajor(), true, false);
732
+ if (res === 0) {
733
+ res = this.compareVersionStrings(pr1.getMinor(), pr2.getMinor(), true, false);
734
+ }
735
+ if (res === 0) {
736
+ res = this.compareVersionStrings(pr1.getPatch(), pr2.getPatch(), true, false);
737
+ }
738
+ if (res === 0) {
739
+ res = this.compareVersionStrings(pr1.getReleaseLabel(), pr2.getReleaseLabel(), false, true);
740
+ }
741
+ if (res === 0) {
742
+ res = this.compareVersionStrings(pr1.getBuild(), pr2.getBuild(), false, true);
743
+ }
744
+ return res;
745
+ } else if (ver1 == null) {
746
+ return ver2 == null ? 0 : -1;
747
+ } else {
748
+ return 1;
749
+ }
750
+ }
751
+
752
+ static compareVersionStrings(v1, v2, asInteger, inverted) {
753
+ if (v1 == null) {
754
+ if (v2 == null) {
755
+ return 0;
756
+ } else {
757
+ return inverted ? 1 : -1;
758
+ }
759
+ } else if (v2 == null) {
760
+ return inverted ? -1 : 1;
761
+ } else if (asInteger && Utilities.isInteger(v1) && Utilities.isInteger(v2)) {
762
+ const r = parseInt(v1, 10) - parseInt(v2, 10);
763
+ if (r === 0) {
764
+ return 0;
765
+ } else if (r < 0) {
766
+ return -1;
767
+ } else {
768
+ return 1;
769
+ }
770
+ } else {
771
+ const r = v1.localeCompare(v2);
772
+ if (r === 0) {
773
+ return 0;
774
+ } else if (r < 0) {
775
+ return -1;
776
+ } else {
777
+ return 1;
778
+ }
779
+ }
780
+ }
781
+
782
+ // Helper methods
783
+ static removeLabels(version) {
784
+ if (Utilities.noString(version))
785
+ return null;
786
+ if (version.includes("+")) {
787
+ version = version.substring(0, version.indexOf("+"));
788
+ }
789
+ if (version.includes("-")) {
790
+ version = version.substring(0, version.indexOf("-"));
791
+ }
792
+ return version;
793
+ }
794
+
795
+ static checkVersionNotNullAndValid(s, label = null) {
796
+ if (s == null) {
797
+ throw new Error("Invalid" + (label ? " " + label : "") + " version: null");
798
+ } else if (!this.isSemVer(s)) {
799
+ throw new Error("Invalid" + (label ? " " + label : "") + " version: '" + s + "'");
800
+ } else {
801
+ return s;
802
+ }
803
+ }
804
+
805
+ static checkVersionNotNullAndValidWildcards(s, label = null) {
806
+ if (s == null) {
807
+ throw new Error("Invalid" + (label ? " " + label : "") + " version: null");
808
+ } else if (!this.isSemVerWithWildcards(s)) {
809
+ throw new Error("Invalid" + (label ? " " + label : "") + " version: '" + s + "'");
810
+ } else {
811
+ return s;
812
+ }
813
+ }
814
+
815
+ static checkVersionValid(s, label = null) {
816
+ if (s == null) {
817
+ return null;
818
+ } else if (!this.isSemVer(s)) {
819
+ throw new Error("Invalid" + (label ? " " + label : "") + " version: '" + s + "'");
820
+ } else {
821
+ return s;
822
+ }
823
+ }
824
+
825
+ static fixForSpecialValue(version) {
826
+ if (Utilities.noString(version)) {
827
+ return null;
828
+ }
829
+ if (version.startsWith("http://hl7.org/fhir/")) {
830
+ version = version.substring(20);
831
+ if (version.includes("/")) {
832
+ version = version.substring(0, version.indexOf("/"));
833
+ }
834
+ }
835
+
836
+ switch (version.toUpperCase()) {
837
+ case "R2":
838
+ return "1.0.2";
839
+ case "DSTU2":
840
+ return "1.0.2";
841
+ case "R2B":
842
+ return "1.4.0";
843
+ case "R3":
844
+ return "3.0.2";
845
+ case "STU3":
846
+ return "3.0.2";
847
+ case "R4":
848
+ return "4.0.1";
849
+ case "R4B":
850
+ return "4.3.0";
851
+ case "R5":
852
+ return "5.0.0";
853
+ case "R6":
854
+ return "6.0.0-cibuild";
855
+ default:
856
+ return version;
857
+ }
858
+ }
859
+
860
+ static versionMatchesByAlgorithm(criteria, candidate, versionAlgorithm) {
861
+ validateOptionalParameter(criteria, "criteria", String);
862
+ validateOptionalParameter(candidate, "candidate", String);
863
+ validateOptionalParameter(versionAlgorithm, "versionAlgorithm", String);
864
+
865
+ if (criteria == candidate) {
866
+ return true;
867
+ }
868
+ if (!versionAlgorithm) {
869
+ versionAlgorithm = this.guessVersionFormat(candidate);
870
+ }
871
+ if (!criteria || !candidate) {
872
+ return false;
873
+ }
874
+ switch (versionAlgorithm) {
875
+ case 'semver' : return VersionUtilities.isSemVerWithWildcards(criteria) && VersionUtilities.isSemVer(candidate) && VersionUtilities.versionMatches(criteria, candidate);
876
+ case 'integer' : return false;
877
+ default: return candidate.startsWith(criteria);
878
+ }
879
+ }
880
+
881
+ /**
882
+ * Guess the version format algorithm from a version string
883
+ * @param {string} v - Version string
884
+ * @returns {string} One of VersionAlgorithm values
885
+ */
886
+ static guessVersionFormat(v) {
887
+ if (!v || v.length === 0) {
888
+ return null;
889
+ }
890
+
891
+ const isDigit = (c) => c >= '0' && c <= '9';
892
+ const isLetter = (c) => (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
893
+
894
+ const isValidDatePart = (part, minVal, maxVal) => {
895
+ if (!part || part.length === 0 || part.length > 4) return false;
896
+ for (const c of part) {
897
+ if (!isDigit(c)) return false;
898
+ }
899
+ const val = parseInt(part, 10);
900
+ return !isNaN(val) && val >= minVal && val <= maxVal;
901
+ };
902
+
903
+ const checkDateFormat = () => {
904
+ // Look for YYYY-MM-DD format
905
+ if (v.length >= 4 && dashCount > 0) {
906
+ const dashPos1 = v.indexOf('-');
907
+ if (dashPos1 === 4) { // Year should be 4 digits
908
+ const yearPart = v.substring(0, 4);
909
+ if (isValidDatePart(yearPart, 1000, 9999)) {
910
+ if (v.length === 4) { // Just YYYY
911
+ return true;
912
+ } else if (dashPos1 === v.length - 1) { // YYYY- (partial)
913
+ return true;
914
+ } else {
915
+ const dashPos2 = v.indexOf('-', dashPos1 + 1);
916
+ if (dashPos2 === -1) {
917
+ // YYYY-MM format
918
+ const monthPart = v.substring(6);
919
+ return isValidDatePart(monthPart, 1, 12);
920
+ } else if (dashPos2 === dashPos1 + 3) { // YYYY-MM-DD
921
+ const monthPart = v.substring(5, 7);
922
+ const dayPart = v.substring(8);
923
+ return isValidDatePart(monthPart, 1, 12) && isValidDatePart(dayPart, 1, 31);
924
+ }
925
+ }
926
+ }
927
+ }
928
+ }
929
+ // Check YYYYMMDD format
930
+ else if (dashCount === 0 && v.length >= 4 && v.length <= 8) {
931
+ // All digits for date format
932
+ for (const c of v) {
933
+ if (!isDigit(c)) return false;
934
+ }
935
+
936
+ if (v.length === 4) { // YYYY
937
+ return isValidDatePart(v, 1000, 9999);
938
+ } else if (v.length === 6) { // YYYYMM
939
+ const yearPart = v.substring(0, 4);
940
+ const monthPart = v.substring(4, 6);
941
+ return isValidDatePart(yearPart, 1000, 9999) && isValidDatePart(monthPart, 1, 12);
942
+ } else if (v.length === 8) { // YYYYMMDD
943
+ const yearPart = v.substring(0, 4);
944
+ const monthPart = v.substring(4, 6);
945
+ const dayPart = v.substring(6, 8);
946
+ return isValidDatePart(yearPart, 1000, 9999) &&
947
+ isValidDatePart(monthPart, 1, 12) &&
948
+ isValidDatePart(dayPart, 1, 31);
949
+ }
950
+ }
951
+ return false;
952
+ };
953
+
954
+ const checkSemverFormat = () => {
955
+ // Must have exactly 2 dots for basic semver (major.minor.patch)
956
+ if (dotCount !== 2) return false;
957
+
958
+ // Split by dots and validate each part
959
+ const parts = v.split('.');
960
+ if (parts.length !== 3) return false;
961
+
962
+ for (const part of parts) {
963
+ if (part.length === 0) return false;
964
+
965
+ // Allow wildcards
966
+ if (['*', 'x', 'X'].includes(part)) continue;
967
+
968
+ // Each part should be numeric
969
+ for (const c of part) {
970
+ if (!isDigit(c)) return false;
971
+ }
972
+ }
973
+
974
+ return true;
975
+ };
976
+
977
+ // Initialize counters
978
+ let dotCount = 0;
979
+ let dashCount = 0;
980
+ let digitCount = 0;
981
+ let letterCount = 0;
982
+ let hasDigits = false;
983
+ let hasLetters = false;
984
+ let hasDots = false;
985
+
986
+ // Count character types
987
+ for (const c of v) {
988
+ if (isDigit(c)) {
989
+ digitCount++;
990
+ hasDigits = true;
991
+ } else if (isLetter(c)) {
992
+ letterCount++;
993
+ hasLetters = true;
994
+ } else if (c === '.') {
995
+ dotCount++;
996
+ hasDots = true;
997
+ } else if (c === '-') {
998
+ dashCount++;
999
+ }
1000
+ }
1001
+
1002
+ // Check for plain integer first (simplest case)
1003
+ if (digitCount === v.length && v.length > 0) {
1004
+ return 'integer';
1005
+ }
1006
+
1007
+ // Check for date format
1008
+ if (checkDateFormat()) {
1009
+ return 'date';
1010
+ }
1011
+
1012
+ // Check for semver format
1013
+ if (checkSemverFormat()) {
1014
+ return 'semver';
1015
+ }
1016
+
1017
+ // Check for natural version (contains digits and has some version-like structure)
1018
+ if (hasDigits && ((hasDots && dotCount <= 4) ||
1019
+ (hasLetters && letterCount <= digitCount * 2))) {
1020
+ // Basic heuristic: looks like it could be a natural version
1021
+ // Contains digits, maybe some dots, maybe some letters but not too many
1022
+ return 'natural';
1023
+ }
1024
+
1025
+ // Default case
1026
+ return null;
1027
+ }
1028
+
1029
+ static splitCanonical(canonical) {
1030
+ if (!canonical) {
1031
+ return { url: null, version: null };
1032
+ }
1033
+
1034
+ const pipeIndex = canonical.lastIndexOf('|');
1035
+
1036
+ if (pipeIndex === -1) {
1037
+ return { url: canonical, version: null };
1038
+ }
1039
+
1040
+ return {
1041
+ url: canonical.substring(0, pipeIndex),
1042
+ version: canonical.substring(pipeIndex + 1)
1043
+ };
1044
+ }
1045
+
1046
+
1047
+ static vurl(url, version) {
1048
+ if (version) {
1049
+ return url + "|" + version;
1050
+ } else {
1051
+ return url;
1052
+ }
1053
+ }
1054
+ }
1055
+
1056
+ module.exports = { VersionUtilities, VersionPrecision, SemverParser };