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.
- package/CHANGELOG.md +42 -0
- package/FHIRsmith.png +0 -0
- package/README.md +277 -0
- package/config-template.json +144 -0
- package/library/folder-setup.js +58 -0
- package/library/html-server.js +166 -0
- package/library/html.js +835 -0
- package/library/i18nsupport.js +259 -0
- package/library/languages.js +779 -0
- package/library/logger-telnet.js +205 -0
- package/library/logger.js +279 -0
- package/library/package-manager.js +876 -0
- package/library/utilities.js +196 -0
- package/library/version-utilities.js +1056 -0
- package/npmprojector/config-example.json +13 -0
- package/npmprojector/indexer.js +394 -0
- package/npmprojector/npmprojector.js +395 -0
- package/npmprojector/readme.md +174 -0
- package/npmprojector/watcher.js +335 -0
- package/package.json +119 -0
- package/packages/package-crawler.js +846 -0
- package/packages/packages-template.html +126 -0
- package/packages/packages.js +2838 -0
- package/passwords.ini +2 -0
- package/publisher/publisher-template.html +208 -0
- package/publisher/publisher.js +2167 -0
- package/publisher/task-draft.js +458 -0
- package/registry/api.js +735 -0
- package/registry/crawler.js +637 -0
- package/registry/model.js +513 -0
- package/registry/readme.md +243 -0
- package/registry/registry-data.json +121015 -0
- package/registry/registry-template.html +126 -0
- package/registry/registry.js +1395 -0
- package/registry/test-runner.js +237 -0
- package/root-template.html +124 -0
- package/server.js +524 -0
- package/shl/private-key.pem +5 -0
- package/shl/public-key.pem +18 -0
- package/shl/shl.js +1125 -0
- package/shl/vhl.js +69 -0
- package/static/FHIRsmith128.png +0 -0
- package/static/FHIRsmith16.png +0 -0
- package/static/FHIRsmith32.png +0 -0
- package/static/FHIRsmith64.png +0 -0
- package/static/assets/css/bootstrap-fhir.css +5302 -0
- package/static/assets/css/bootstrap-glyphicons.css +2 -0
- package/static/assets/css/bootstrap.css +4097 -0
- package/static/assets/css/jquery-ui.css +523 -0
- package/static/assets/css/jquery-ui.structure.css +863 -0
- package/static/assets/css/jquery-ui.structure.min.css +5 -0
- package/static/assets/css/jquery-ui.theme.css +439 -0
- package/static/assets/css/jquery-ui.theme.min.css +5 -0
- package/static/assets/css/jquery.ui.all.css +7 -0
- package/static/assets/css/modules.css +18 -0
- package/static/assets/css/project.css +367 -0
- package/static/assets/css/pygments-manni.css +66 -0
- package/static/assets/css/tags.css +74 -0
- package/static/assets/css/xml.css +2 -0
- package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
- package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
- package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
- package/static/assets/ico/favicon.ico +0 -0
- package/static/assets/ico/favicon.png +0 -0
- package/static/assets/images/fhir-logo-www.png +0 -0
- package/static/assets/images/fhir-logo.png +0 -0
- package/static/assets/images/hl7-logo.png +0 -0
- package/static/assets/images/logo_ansinew.jpg +0 -0
- package/static/assets/images/search.png +0 -0
- package/static/assets/images/stripe.png +0 -0
- package/static/assets/images/target.png +0 -0
- package/static/assets/images/tx-registry-root.gif +0 -0
- package/static/assets/images/tx-registry.png +0 -0
- package/static/assets/images/tx-server.png +0 -0
- package/static/assets/images/tx-version.png +0 -0
- package/static/assets/js/bootstrap.min.js +6 -0
- package/static/assets/js/fhir-gw.js +259 -0
- package/static/assets/js/fhir.js +2 -0
- package/static/assets/js/html5shiv.js +8 -0
- package/static/assets/js/jcookie.js +96 -0
- package/static/assets/js/jquery-ui.min.js +6 -0
- package/static/assets/js/jquery.js +10716 -0
- package/static/assets/js/jquery.min.js +2 -0
- package/static/assets/js/jquery.ui.core.js +314 -0
- package/static/assets/js/jquery.ui.draggable.js +825 -0
- package/static/assets/js/jquery.ui.mouse.js +162 -0
- package/static/assets/js/jquery.ui.resizable.js +842 -0
- package/static/assets/js/jquery.ui.widget.js +268 -0
- package/static/assets/js/json2.js +487 -0
- package/static/assets/js/jtip.js +97 -0
- package/static/assets/js/respond.min.js +6 -0
- package/static/assets/js/statuspage.js +70 -0
- package/static/assets/js/xml.js +2 -0
- package/static/dist/js/bootstrap.js +1964 -0
- package/static/favicon.png +0 -0
- package/static/fhir.css +626 -0
- package/static/icon-fhir-16.png +0 -0
- package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
- package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- package/static/images/ui-icons_222222_256x240.png +0 -0
- package/static/images/ui-icons_228ef1_256x240.png +0 -0
- package/static/images/ui-icons_ef8c08_256x240.png +0 -0
- package/static/images/ui-icons_ffd27a_256x240.png +0 -0
- package/static/images/ui-icons_ffffff_256x240.png +0 -0
- package/static/js/jquery.effects.blind.js +49 -0
- package/static/js/jquery.effects.bounce.js +78 -0
- package/static/js/jquery.effects.clip.js +54 -0
- package/static/js/jquery.effects.core.js +763 -0
- package/static/js/jquery.effects.drop.js +50 -0
- package/static/js/jquery.effects.explode.js +79 -0
- package/static/js/jquery.effects.fade.js +32 -0
- package/static/js/jquery.effects.fold.js +56 -0
- package/static/js/jquery.effects.highlight.js +50 -0
- package/static/js/jquery.effects.pulsate.js +51 -0
- package/static/js/jquery.effects.scale.js +178 -0
- package/static/js/jquery.effects.shake.js +57 -0
- package/static/js/jquery.effects.slide.js +50 -0
- package/static/js/jquery.effects.transfer.js +45 -0
- package/static/js/jquery.ui.accordion.js +611 -0
- package/static/js/jquery.ui.autocomplete.js +612 -0
- package/static/js/jquery.ui.button.js +416 -0
- package/static/js/jquery.ui.datepicker.js +1823 -0
- package/static/js/jquery.ui.dialog.js +878 -0
- package/static/js/jquery.ui.droppable.js +296 -0
- package/static/js/jquery.ui.position.js +252 -0
- package/static/js/jquery.ui.progressbar.js +109 -0
- package/static/js/jquery.ui.selectable.js +266 -0
- package/static/js/jquery.ui.slider.js +666 -0
- package/static/js/jquery.ui.sortable.js +1077 -0
- package/static/js/jquery.ui.tabs.js +758 -0
- package/stats.js +80 -0
- package/test-cache/vsac/vsac-valuesets.db +0 -0
- package/token/nginx_passport_setup.md +383 -0
- package/token/security_guide.md +294 -0
- package/token/token-template.html +330 -0
- package/token/token.js +1300 -0
- package/translations/Messages.properties +1510 -0
- package/translations/Messages_ar.properties +1399 -0
- package/translations/Messages_de.properties +836 -0
- package/translations/Messages_es.properties +737 -0
- package/translations/Messages_fr.properties +1 -0
- package/translations/Messages_ja.properties +893 -0
- package/translations/Messages_nl.properties +1357 -0
- package/translations/Messages_pt.properties +1302 -0
- package/translations/Messages_ru.properties +1 -0
- package/translations/Messages_uz.properties +1 -0
- package/translations/Messages_zh.properties +1 -0
- package/translations/rendering-phrases.properties +1128 -0
- package/translations/rendering-phrases_ar.properties +1091 -0
- package/translations/rendering-phrases_de.properties +6 -0
- package/translations/rendering-phrases_es.properties +6 -0
- package/translations/rendering-phrases_fr.properties +624 -0
- package/translations/rendering-phrases_ja.properties +21 -0
- package/translations/rendering-phrases_nl.properties +970 -0
- package/translations/rendering-phrases_pt.properties +1020 -0
- package/translations/rendering-phrases_ru.properties +1094 -0
- package/translations/rendering-phrases_uz.properties +1 -0
- package/translations/rendering-phrases_zh.properties +1 -0
- package/tx/README.md +418 -0
- package/tx/cm/cm-api.js +110 -0
- package/tx/cm/cm-database.js +735 -0
- package/tx/cm/cm-package.js +325 -0
- package/tx/cs/cs-api.js +789 -0
- package/tx/cs/cs-areacode.js +615 -0
- package/tx/cs/cs-country.js +1110 -0
- package/tx/cs/cs-cpt.js +785 -0
- package/tx/cs/cs-cs.js +1579 -0
- package/tx/cs/cs-currency.js +539 -0
- package/tx/cs/cs-db.js +1321 -0
- package/tx/cs/cs-hgvs.js +329 -0
- package/tx/cs/cs-lang.js +465 -0
- package/tx/cs/cs-loinc.js +1485 -0
- package/tx/cs/cs-mimetypes.js +238 -0
- package/tx/cs/cs-ndc.js +704 -0
- package/tx/cs/cs-omop.js +1025 -0
- package/tx/cs/cs-provider-api.js +43 -0
- package/tx/cs/cs-provider-list.js +37 -0
- package/tx/cs/cs-rxnorm.js +808 -0
- package/tx/cs/cs-snomed.js +1102 -0
- package/tx/cs/cs-ucum.js +514 -0
- package/tx/cs/cs-unii.js +271 -0
- package/tx/cs/cs-uri.js +218 -0
- package/tx/cs/cs-usstates.js +305 -0
- package/tx/dev.fhir.org.yml +14 -0
- package/tx/fixtures/test-cases-setup.json +18 -0
- package/tx/fixtures/test-cases.yml +16 -0
- package/tx/html/codesystem-operations.liquid +25 -0
- package/tx/html/home-metrics.liquid +247 -0
- package/tx/html/operations-form.liquid +148 -0
- package/tx/html/search-form.liquid +62 -0
- package/tx/html/tx-template.html +133 -0
- package/tx/html/valueset-operations.liquid +54 -0
- package/tx/importers/atc-to-fhir.js +316 -0
- package/tx/importers/import-loinc.module.js +1536 -0
- package/tx/importers/import-ndc.module.js +1088 -0
- package/tx/importers/import-rxnorm.module.js +898 -0
- package/tx/importers/import-sct.module.js +2457 -0
- package/tx/importers/import-unii.module.js +601 -0
- package/tx/importers/readme.md +453 -0
- package/tx/importers/subset-loinc.module.js +1081 -0
- package/tx/importers/subset-rxnorm.module.js +938 -0
- package/tx/importers/tx-import-base.js +351 -0
- package/tx/importers/tx-import-settings.js +310 -0
- package/tx/importers/tx-import.js +357 -0
- package/tx/library/canonical-resource.js +88 -0
- package/tx/library/capabilitystatement.js +292 -0
- package/tx/library/codesystem.js +774 -0
- package/tx/library/conceptmap.js +568 -0
- package/tx/library/designations.js +932 -0
- package/tx/library/errors.js +77 -0
- package/tx/library/extensions.js +117 -0
- package/tx/library/namingsystem.js +322 -0
- package/tx/library/operation-outcome.js +127 -0
- package/tx/library/parameters.js +105 -0
- package/tx/library/renderer.js +1559 -0
- package/tx/library/terminologycapabilities.js +418 -0
- package/tx/library/ucum-parsers.js +1029 -0
- package/tx/library/ucum-service.js +370 -0
- package/tx/library/ucum-types.js +1099 -0
- package/tx/library/valueset.js +543 -0
- package/tx/library.js +676 -0
- package/tx/ocl/cm-ocl.js +106 -0
- package/tx/ocl/cs-ocl.js +39 -0
- package/tx/ocl/vs-ocl.js +105 -0
- package/tx/operation-context.js +568 -0
- package/tx/params.js +613 -0
- package/tx/provider.js +403 -0
- package/tx/sct/ecl.js +1560 -0
- package/tx/sct/expressions.js +2077 -0
- package/tx/sct/structures.js +1396 -0
- package/tx/tx-html.js +1063 -0
- package/tx/tx.fhir.org.yml +39 -0
- package/tx/tx.js +927 -0
- package/tx/vs/vs-api.js +112 -0
- package/tx/vs/vs-database.js +786 -0
- package/tx/vs/vs-package.js +358 -0
- package/tx/vs/vs-vsac.js +366 -0
- package/tx/workers/batch-validate.js +129 -0
- package/tx/workers/batch.js +361 -0
- package/tx/workers/closure.js +32 -0
- package/tx/workers/expand.js +1845 -0
- package/tx/workers/lookup.js +407 -0
- package/tx/workers/metadata.js +467 -0
- package/tx/workers/operations.js +34 -0
- package/tx/workers/read.js +164 -0
- package/tx/workers/search.js +384 -0
- package/tx/workers/subsumes.js +334 -0
- package/tx/workers/translate.js +492 -0
- package/tx/workers/validate.js +2504 -0
- package/tx/workers/worker.js +904 -0
- package/tx/xml/capabilitystatement-xml.js +63 -0
- package/tx/xml/codesystem-xml.js +62 -0
- package/tx/xml/conceptmap-xml.js +65 -0
- package/tx/xml/namingsystem-xml.js +65 -0
- package/tx/xml/operationoutcome-xml.js +127 -0
- package/tx/xml/parameters-xml.js +312 -0
- package/tx/xml/terminologycapabilities-xml.js +64 -0
- package/tx/xml/valueset-xml.js +64 -0
- package/tx/xml/xml-base.js +603 -0
- package/vcl/vcl-parser.js +1098 -0
- package/vcl/vcl.js +253 -0
- package/windows-install.js +19 -0
- package/xig/xig-template.html +124 -0
- 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 };
|