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,370 @@
1
+ /**
2
+ * UCUM Service - Main service class for UCUM operations
3
+ * BSD 3-Clause License
4
+ * Copyright (c) 2006+, Health Intersections Pty Ltd
5
+ */
6
+
7
+ const {
8
+ UcumException, Pair, Registry, UcumVersionDetails
9
+ } = require('./ucum-types.js');
10
+
11
+ const {
12
+ ExpressionParser, ExpressionComposer, FormalStructureComposer,
13
+ UcumEssenceParser, Search, Converter, UcumValidator
14
+ } = require('./ucum-parsers.js');
15
+
16
+ // UCUM Service - main service class for UCUM operations
17
+ class UcumService {
18
+ constructor() {
19
+ this.model = null;
20
+ this.handlers = new Registry();
21
+ }
22
+
23
+ init(xmlContent) {
24
+ const parser = new UcumEssenceParser();
25
+ this.model = parser.parse(xmlContent);
26
+ }
27
+
28
+ /**
29
+ * Given a unit, return a formal description of what the units stand for using
30
+ * full names
31
+ * @param {string} unit the unit code
32
+ * @return {string} formal description
33
+ * @throws {UcumException}
34
+ */
35
+ analyse(unit) {
36
+ if (!unit || !unit.trim()) {
37
+ return "(unity)";
38
+ }
39
+
40
+ if (!this.model) {
41
+ throw new UcumException("UCUM service not initialized - call init() first");
42
+ }
43
+
44
+ const term = new ExpressionParser(this.model).parse(unit);
45
+ return new FormalStructureComposer().compose(term);
46
+ }
47
+
48
+ /**
49
+ * Convert a unit to its canonical form
50
+ * @param {string} unit the unit code
51
+ * @return {string} canonical units
52
+ * @throws {UcumException}
53
+ */
54
+ getCanonicalUnits(unit) {
55
+ if (!unit || !unit.trim()) {
56
+ throw new UcumException("getCanonicalUnits: unit must not be null or empty");
57
+ }
58
+
59
+ if (!this.model) {
60
+ throw new UcumException("UCUM service not initialized - call init() first");
61
+ }
62
+
63
+ try {
64
+ const term = new ExpressionParser(this.model).parse(unit);
65
+ const convertedTerm = new Converter(this.model, this.handlers).convert(term);
66
+ return new ExpressionComposer().compose(convertedTerm, false);
67
+ } catch (e) {
68
+ e.message = `Error processing ${unit}: ${e.message}`;
69
+ throw e;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Check if two units are comparable (can be converted to same canonical form)
75
+ * @param {string} units1 first unit
76
+ * @param {string} units2 second unit
77
+ * @return {boolean} true if comparable
78
+ * @throws {UcumException}
79
+ */
80
+ isComparable(units1, units2) {
81
+ if (!units1 || !units2) {
82
+ return false;
83
+ }
84
+
85
+ try {
86
+ const u1 = this.getCanonicalUnits(units1);
87
+ const u2 = this.getCanonicalUnits(units2);
88
+ return u1 === u2;
89
+ } catch (e) {
90
+ console.error('Error message:', e.message);
91
+ console.error('Stack trace:', e.stack);
92
+ return false;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Divide one quantity by another
98
+ * @param {Pair} dividend the base
99
+ * @param {Pair} divisor the multiplier (not that order matters)
100
+ * @return {Pair} the result
101
+ * @throws {UcumException}
102
+ */
103
+ multiply(o1, o2) {
104
+ const res = new Pair(o1.getValue().multiply(o2.getValue()), o1.getCode() +"."+o2.getCode());
105
+ return this.getCanonicalForm(res);
106
+ }
107
+ /**
108
+ * Divide one quantity by another
109
+ * @param {Pair} dividend the dividend
110
+ * @param {Pair} divisor the divisor
111
+ * @return {Pair} the result
112
+ * @throws {UcumException}
113
+ */
114
+ divideBy(dividend, divisor) {
115
+ const dividendCode = dividend.getCode();
116
+ const divisorCode = divisor.getCode();
117
+
118
+ const resultValue = dividend.getValue().divide(divisor.getValue());
119
+ const resultCode = (dividendCode.includes("/") || dividendCode.includes("*") ?
120
+ "(" + dividendCode + ")" : dividendCode) + "/" +
121
+ (divisorCode.includes("/") || divisorCode.includes("*") ?
122
+ "(" + divisorCode + ")" : divisorCode);
123
+
124
+ const result = new Pair(resultValue, resultCode);
125
+ return this.getCanonicalForm(result);
126
+ }
127
+
128
+ /**
129
+ * Convert a Pair to its canonical form
130
+ * @param {Pair} pair the pair to convert
131
+ * @return {Pair} canonical form pair
132
+ * @throws {UcumException}
133
+ */
134
+ getCanonicalForm(pair) {
135
+ if (!pair) {
136
+ throw new UcumException("getCanonicalForm: value must not be null");
137
+ }
138
+
139
+ const code = pair.getCode();
140
+ if (!code || !code.trim()) {
141
+ throw new UcumException("getCanonicalForm: value.code must not be null or empty");
142
+ }
143
+
144
+ if (!this.model) {
145
+ throw new UcumException("UCUM service not initialized - call init() first");
146
+ }
147
+
148
+ const term = new ExpressionParser(this.model).parse(code);
149
+ const c = new Converter(this.model, this.handlers).convert(term);
150
+
151
+ let p = null;
152
+ if (pair.getValue() === null || pair.getValue() === undefined) {
153
+ p = new Pair(null, new ExpressionComposer().compose(c, false));
154
+ } else {
155
+ const newValue = pair.getValue().multiply(c.getValue());
156
+ p = new Pair(newValue, new ExpressionComposer().compose(c, false));
157
+
158
+ if (pair.getValue().isWholeNumber()) {
159
+ // whole numbers are tricky - they have implied infinite precision, but we need to check for digit errors in the last couple of digits
160
+ if (p.getValue().checkForCouldBeWholeNumber) {
161
+ p.getValue().checkForCouldBeWholeNumber();
162
+ }
163
+ }
164
+ }
165
+
166
+ return p;
167
+ }
168
+
169
+ /**
170
+ * Validate the UCUM model
171
+ * @return {Array<string>} list of validation errors
172
+ */
173
+ validateUCUM() {
174
+ if (!this.model) {
175
+ throw new UcumException("UCUM service not initialized - call init() first");
176
+ }
177
+
178
+ const validator = new UcumValidator(this.model, this.handlers);
179
+ return validator.validate();
180
+ }
181
+
182
+ /**
183
+ * Get UCUM version identification
184
+ * @return {UcumVersionDetails} version details
185
+ */
186
+ ucumIdentification() {
187
+ if (!this.model) {
188
+ throw new UcumException("UCUM service not initialized - call init() first");
189
+ }
190
+
191
+ return new UcumVersionDetails(this.model.revisionDate, this.model.version);
192
+ }
193
+
194
+ /**
195
+ * Validate a unit expression
196
+ * @param {string} unit the unit to validate
197
+ * @return {string} null if valid, error message if invalid
198
+ */
199
+ validate(unit) {
200
+ if (!unit) {
201
+ return "unit must not be null";
202
+ }
203
+
204
+ if (!this.model) {
205
+ return "UCUM service not initialized - call init() first";
206
+ }
207
+
208
+ try {
209
+ new ExpressionParser(this.model).parse(unit);
210
+ return null;
211
+ } catch (e) {
212
+ return e.message;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Check if a unit expression is valid
218
+ * @param {string} unit the unit to check
219
+ * @return {boolean} true if valid, false if invalid
220
+ */
221
+ isValidUnit(unit) {
222
+ return this.validate(unit) === null;
223
+ }
224
+
225
+ /**
226
+ * Convert a value from one unit to another
227
+ * @param {Decimal} value the value to convert
228
+ * @param {string} sourceUnit source unit
229
+ * @param {string} destUnit destination unit
230
+ * @return {Decimal} converted value
231
+ * @throws {UcumException}
232
+ */
233
+ convert(value, sourceUnit, destUnit) {
234
+ if (!value) {
235
+ throw new UcumException("convert: value must not be null");
236
+ }
237
+ if (!sourceUnit || !sourceUnit.trim()) {
238
+ throw new UcumException("convert: sourceUnit must not be null or empty");
239
+ }
240
+ if (!destUnit || !destUnit.trim()) {
241
+ throw new UcumException("convert: destUnit must not be null or empty");
242
+ }
243
+
244
+ if (!this.model) {
245
+ throw new UcumException("UCUM service not initialized - call init() first");
246
+ }
247
+
248
+ if (sourceUnit === destUnit) {
249
+ return value;
250
+ }
251
+
252
+ const src = new Converter(this.model, this.handlers).convert(
253
+ new ExpressionParser(this.model).parse(sourceUnit)
254
+ );
255
+ const dst = new Converter(this.model, this.handlers).convert(
256
+ new ExpressionParser(this.model).parse(destUnit)
257
+ );
258
+
259
+ const s = new ExpressionComposer().compose(src, false);
260
+ const d = new ExpressionComposer().compose(dst, false);
261
+
262
+ if (s !== d) {
263
+ throw new UcumException(
264
+ `Unable to convert between units ${sourceUnit} and ${destUnit} as they do not have matching canonical forms (${s} and ${d} respectively)`
265
+ );
266
+ }
267
+
268
+ const canValue = value.multiply(src.getValue());
269
+ let res = canValue.divide(dst.getValue());
270
+
271
+ if (value.isWholeNumber()) {
272
+ // whole numbers are tricky - they have implied infinite precision, but we need to check for digit errors in the last couple of digits
273
+ if (res.checkForCouldBeWholeNumber) {
274
+ res.checkForCouldBeWholeNumber();
275
+ }
276
+ }
277
+
278
+ return res;
279
+ }
280
+
281
+ /**
282
+ * Get all properties from defined units
283
+ * @return {Set<string>} set of properties
284
+ */
285
+ getProperties() {
286
+ if (!this.model) {
287
+ throw new UcumException("UCUM service not initialized - call init() first");
288
+ }
289
+
290
+ const result = new Set();
291
+ for (const unit of this.model.getDefinedUnits()) {
292
+ if (unit.property) {
293
+ result.add(unit.property);
294
+ }
295
+ }
296
+ return result;
297
+ }
298
+
299
+ /**
300
+ * Get all prefixes
301
+ * @return {Array} array of prefixes
302
+ */
303
+ getPrefixes() {
304
+ if (!this.model) {
305
+ throw new UcumException("UCUM service not initialized - call init() first");
306
+ }
307
+
308
+ return this.model.getPrefixes();
309
+ }
310
+
311
+ /**
312
+ * Get all base units
313
+ * @return {Array} array of base units
314
+ */
315
+ getBaseUnits() {
316
+ if (!this.model) {
317
+ throw new UcumException("UCUM service not initialized - call init() first");
318
+ }
319
+
320
+ return this.model.getBaseUnits();
321
+ }
322
+
323
+ /**
324
+ * Get all defined units
325
+ * @return {Array} array of defined units
326
+ */
327
+ getDefinedUnits() {
328
+ if (!this.model) {
329
+ throw new UcumException("UCUM service not initialized - call init() first");
330
+ }
331
+
332
+ return this.model.getDefinedUnits();
333
+ }
334
+
335
+ /**
336
+ * Get the UCUM model
337
+ * @return {UcumModel} the loaded model
338
+ */
339
+ getModel() {
340
+ return this.model;
341
+ }
342
+
343
+ /**
344
+ * Get the handlers registry
345
+ * @return {Registry} the handlers registry
346
+ */
347
+ getHandlers() {
348
+ return this.handlers;
349
+ }
350
+
351
+ /**
352
+ * Search for concepts in the model
353
+ * @param {ConceptKind} kind - type of concept to search for (optional)
354
+ * @param {string} text - text to search for
355
+ * @param {boolean} isRegex - whether text is a regex pattern
356
+ * @return {Array} array of matching concepts
357
+ */
358
+ search(kind = null, text = '', isRegex = false) {
359
+ if (!this.model) {
360
+ throw new UcumException("UCUM service not initialized - call init() first");
361
+ }
362
+
363
+ const search = new Search();
364
+ return search.doSearch(this.model, kind, text, isRegex);
365
+ }
366
+ }
367
+
368
+ module.exports = {
369
+ UcumService
370
+ };