fhirpath-ts 0.0.0-bootstrap.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 (414) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +391 -0
  3. package/THIRD-PARTY-NOTICES.md +296 -0
  4. package/dist/analyzer/analyze-dto.d.ts +45 -0
  5. package/dist/analyzer/analyze-dto.d.ts.map +1 -0
  6. package/dist/analyzer/analyze-dto.js +147 -0
  7. package/dist/analyzer/analyze-dto.js.map +1 -0
  8. package/dist/analyzer/analyze.d.ts +98 -0
  9. package/dist/analyzer/analyze.d.ts.map +1 -0
  10. package/dist/analyzer/analyze.js +925 -0
  11. package/dist/analyzer/analyze.js.map +1 -0
  12. package/dist/analyzer/expression-policy.d.ts +210 -0
  13. package/dist/analyzer/expression-policy.d.ts.map +1 -0
  14. package/dist/analyzer/expression-policy.js +254 -0
  15. package/dist/analyzer/expression-policy.js.map +1 -0
  16. package/dist/analyzer/index.d.ts +3 -0
  17. package/dist/analyzer/index.d.ts.map +1 -0
  18. package/dist/analyzer/index.js +6 -0
  19. package/dist/analyzer/index.js.map +1 -0
  20. package/dist/analyzer/regex-safety.d.ts +10 -0
  21. package/dist/analyzer/regex-safety.d.ts.map +1 -0
  22. package/dist/analyzer/regex-safety.js +84 -0
  23. package/dist/analyzer/regex-safety.js.map +1 -0
  24. package/dist/analyzer/signatures.d.ts +72 -0
  25. package/dist/analyzer/signatures.d.ts.map +1 -0
  26. package/dist/analyzer/signatures.js +195 -0
  27. package/dist/analyzer/signatures.js.map +1 -0
  28. package/dist/api/bundle.d.ts +29 -0
  29. package/dist/api/bundle.d.ts.map +1 -0
  30. package/dist/api/bundle.js +108 -0
  31. package/dist/api/bundle.js.map +1 -0
  32. package/dist/api/cache.d.ts +10 -0
  33. package/dist/api/cache.d.ts.map +1 -0
  34. package/dist/api/cache.js +38 -0
  35. package/dist/api/cache.js.map +1 -0
  36. package/dist/api/column-signature.d.ts +53 -0
  37. package/dist/api/column-signature.d.ts.map +1 -0
  38. package/dist/api/column-signature.js +43 -0
  39. package/dist/api/column-signature.js.map +1 -0
  40. package/dist/api/compile.d.ts +120 -0
  41. package/dist/api/compile.d.ts.map +1 -0
  42. package/dist/api/compile.js +167 -0
  43. package/dist/api/compile.js.map +1 -0
  44. package/dist/api/constraints.d.ts +48 -0
  45. package/dist/api/constraints.d.ts.map +1 -0
  46. package/dist/api/constraints.js +52 -0
  47. package/dist/api/constraints.js.map +1 -0
  48. package/dist/api/dto.d.ts +134 -0
  49. package/dist/api/dto.d.ts.map +1 -0
  50. package/dist/api/dto.js +295 -0
  51. package/dist/api/dto.js.map +1 -0
  52. package/dist/api/engine.d.ts +136 -0
  53. package/dist/api/engine.d.ts.map +1 -0
  54. package/dist/api/engine.js +183 -0
  55. package/dist/api/engine.js.map +1 -0
  56. package/dist/api/evaluate.d.ts +7 -0
  57. package/dist/api/evaluate.d.ts.map +1 -0
  58. package/dist/api/evaluate.js +10 -0
  59. package/dist/api/evaluate.js.map +1 -0
  60. package/dist/api/project.d.ts +89 -0
  61. package/dist/api/project.d.ts.map +1 -0
  62. package/dist/api/project.js +150 -0
  63. package/dist/api/project.js.map +1 -0
  64. package/dist/api/tagged.d.ts +14 -0
  65. package/dist/api/tagged.d.ts.map +1 -0
  66. package/dist/api/tagged.js +13 -0
  67. package/dist/api/tagged.js.map +1 -0
  68. package/dist/cli/dto-check.d.ts +25 -0
  69. package/dist/cli/dto-check.d.ts.map +1 -0
  70. package/dist/cli/dto-check.js +77 -0
  71. package/dist/cli/dto-check.js.map +1 -0
  72. package/dist/cli/fhirpath-check.d.ts +3 -0
  73. package/dist/cli/fhirpath-check.d.ts.map +1 -0
  74. package/dist/cli/fhirpath-check.js +149 -0
  75. package/dist/cli/fhirpath-check.js.map +1 -0
  76. package/dist/cli/ts-loader.d.mts +3 -0
  77. package/dist/cli/ts-loader.d.mts.map +1 -0
  78. package/dist/cli/ts-loader.mjs +48 -0
  79. package/dist/cli/ts-loader.mjs.map +1 -0
  80. package/dist/engine/context.d.ts +173 -0
  81. package/dist/engine/context.d.ts.map +1 -0
  82. package/dist/engine/context.js +118 -0
  83. package/dist/engine/context.js.map +1 -0
  84. package/dist/engine/evaluator.d.ts +7 -0
  85. package/dist/engine/evaluator.d.ts.map +1 -0
  86. package/dist/engine/evaluator.js +196 -0
  87. package/dist/engine/evaluator.js.map +1 -0
  88. package/dist/engine/navigation.d.ts +11 -0
  89. package/dist/engine/navigation.d.ts.map +1 -0
  90. package/dist/engine/navigation.js +102 -0
  91. package/dist/engine/navigation.js.map +1 -0
  92. package/dist/engine/operators/collections.d.ts +12 -0
  93. package/dist/engine/operators/collections.d.ts.map +1 -0
  94. package/dist/engine/operators/collections.js +31 -0
  95. package/dist/engine/operators/collections.js.map +1 -0
  96. package/dist/engine/operators/comparison.d.ts +13 -0
  97. package/dist/engine/operators/comparison.d.ts.map +1 -0
  98. package/dist/engine/operators/comparison.js +77 -0
  99. package/dist/engine/operators/comparison.js.map +1 -0
  100. package/dist/engine/operators/equality.d.ts +19 -0
  101. package/dist/engine/operators/equality.d.ts.map +1 -0
  102. package/dist/engine/operators/equality.js +203 -0
  103. package/dist/engine/operators/equality.js.map +1 -0
  104. package/dist/engine/operators/index.d.ts +18 -0
  105. package/dist/engine/operators/index.d.ts.map +1 -0
  106. package/dist/engine/operators/index.js +30 -0
  107. package/dist/engine/operators/index.js.map +1 -0
  108. package/dist/engine/operators/logic.d.ts +8 -0
  109. package/dist/engine/operators/logic.d.ts.map +1 -0
  110. package/dist/engine/operators/logic.js +38 -0
  111. package/dist/engine/operators/logic.js.map +1 -0
  112. package/dist/engine/operators/math.d.ts +13 -0
  113. package/dist/engine/operators/math.d.ts.map +1 -0
  114. package/dist/engine/operators/math.js +198 -0
  115. package/dist/engine/operators/math.js.map +1 -0
  116. package/dist/engine/operators/strings.d.ts +8 -0
  117. package/dist/engine/operators/strings.d.ts.map +1 -0
  118. package/dist/engine/operators/strings.js +22 -0
  119. package/dist/engine/operators/strings.js.map +1 -0
  120. package/dist/engine/operators/types.d.ts +3 -0
  121. package/dist/engine/operators/types.d.ts.map +1 -0
  122. package/dist/engine/operators/types.js +15 -0
  123. package/dist/engine/operators/types.js.map +1 -0
  124. package/dist/engine/type-matching.d.ts +20 -0
  125. package/dist/engine/type-matching.d.ts.map +1 -0
  126. package/dist/engine/type-matching.js +122 -0
  127. package/dist/engine/type-matching.js.map +1 -0
  128. package/dist/errors.d.ts +25 -0
  129. package/dist/errors.d.ts.map +1 -0
  130. package/dist/errors.js +21 -0
  131. package/dist/errors.js.map +1 -0
  132. package/dist/eslint/index.d.ts +12 -0
  133. package/dist/eslint/index.d.ts.map +1 -0
  134. package/dist/eslint/index.js +316 -0
  135. package/dist/eslint/index.js.map +1 -0
  136. package/dist/fhir/html-checks.d.ts +7 -0
  137. package/dist/fhir/html-checks.d.ts.map +1 -0
  138. package/dist/fhir/html-checks.js +256 -0
  139. package/dist/fhir/html-checks.js.map +1 -0
  140. package/dist/fhir/model-navigation.d.ts +11 -0
  141. package/dist/fhir/model-navigation.d.ts.map +1 -0
  142. package/dist/fhir/model-navigation.js +142 -0
  143. package/dist/fhir/model-navigation.js.map +1 -0
  144. package/dist/functions/aggregate.d.ts +2 -0
  145. package/dist/functions/aggregate.d.ts.map +1 -0
  146. package/dist/functions/aggregate.js +127 -0
  147. package/dist/functions/aggregate.js.map +1 -0
  148. package/dist/functions/boundary.d.ts +2 -0
  149. package/dist/functions/boundary.d.ts.map +1 -0
  150. package/dist/functions/boundary.js +189 -0
  151. package/dist/functions/boundary.js.map +1 -0
  152. package/dist/functions/combining.d.ts +2 -0
  153. package/dist/functions/combining.d.ts.map +1 -0
  154. package/dist/functions/combining.js +13 -0
  155. package/dist/functions/combining.js.map +1 -0
  156. package/dist/functions/control.d.ts +2 -0
  157. package/dist/functions/control.d.ts.map +1 -0
  158. package/dist/functions/control.js +25 -0
  159. package/dist/functions/control.js.map +1 -0
  160. package/dist/functions/conversion.d.ts +3 -0
  161. package/dist/functions/conversion.d.ts.map +1 -0
  162. package/dist/functions/conversion.js +316 -0
  163. package/dist/functions/conversion.js.map +1 -0
  164. package/dist/functions/date-components.d.ts +2 -0
  165. package/dist/functions/date-components.d.ts.map +1 -0
  166. package/dist/functions/date-components.js +81 -0
  167. package/dist/functions/date-components.js.map +1 -0
  168. package/dist/functions/existence.d.ts +4 -0
  169. package/dist/functions/existence.d.ts.map +1 -0
  170. package/dist/functions/existence.js +97 -0
  171. package/dist/functions/existence.js.map +1 -0
  172. package/dist/functions/fhir.d.ts +2 -0
  173. package/dist/functions/fhir.d.ts.map +1 -0
  174. package/dist/functions/fhir.js +222 -0
  175. package/dist/functions/fhir.js.map +1 -0
  176. package/dist/functions/filtering.d.ts +2 -0
  177. package/dist/functions/filtering.d.ts.map +1 -0
  178. package/dist/functions/filtering.js +112 -0
  179. package/dist/functions/filtering.js.map +1 -0
  180. package/dist/functions/install.d.ts +18 -0
  181. package/dist/functions/install.d.ts.map +1 -0
  182. package/dist/functions/install.js +20 -0
  183. package/dist/functions/install.js.map +1 -0
  184. package/dist/functions/iteration.d.ts +7 -0
  185. package/dist/functions/iteration.d.ts.map +1 -0
  186. package/dist/functions/iteration.js +9 -0
  187. package/dist/functions/iteration.js.map +1 -0
  188. package/dist/functions/math.d.ts +2 -0
  189. package/dist/functions/math.d.ts.map +1 -0
  190. package/dist/functions/math.js +152 -0
  191. package/dist/functions/math.js.map +1 -0
  192. package/dist/functions/reflection.d.ts +2 -0
  193. package/dist/functions/reflection.d.ts.map +1 -0
  194. package/dist/functions/reflection.js +23 -0
  195. package/dist/functions/reflection.js.map +1 -0
  196. package/dist/functions/registry.d.ts +28 -0
  197. package/dist/functions/registry.d.ts.map +1 -0
  198. package/dist/functions/registry.js +46 -0
  199. package/dist/functions/registry.js.map +1 -0
  200. package/dist/functions/sort.d.ts +2 -0
  201. package/dist/functions/sort.d.ts.map +1 -0
  202. package/dist/functions/sort.js +48 -0
  203. package/dist/functions/sort.js.map +1 -0
  204. package/dist/functions/string.d.ts +2 -0
  205. package/dist/functions/string.d.ts.map +1 -0
  206. package/dist/functions/string.js +353 -0
  207. package/dist/functions/string.js.map +1 -0
  208. package/dist/functions/subsetting.d.ts +2 -0
  209. package/dist/functions/subsetting.d.ts.map +1 -0
  210. package/dist/functions/subsetting.js +72 -0
  211. package/dist/functions/subsetting.js.map +1 -0
  212. package/dist/functions/tree.d.ts +8 -0
  213. package/dist/functions/tree.d.ts.map +1 -0
  214. package/dist/functions/tree.js +109 -0
  215. package/dist/functions/tree.js.map +1 -0
  216. package/dist/functions/type-specifier.d.ts +8 -0
  217. package/dist/functions/type-specifier.d.ts.map +1 -0
  218. package/dist/functions/type-specifier.js +27 -0
  219. package/dist/functions/type-specifier.js.map +1 -0
  220. package/dist/functions/utility.d.ts +2 -0
  221. package/dist/functions/utility.d.ts.map +1 -0
  222. package/dist/functions/utility.js +76 -0
  223. package/dist/functions/utility.js.map +1 -0
  224. package/dist/functions/variables.d.ts +2 -0
  225. package/dist/functions/variables.d.ts.map +1 -0
  226. package/dist/functions/variables.js +35 -0
  227. package/dist/functions/variables.js.map +1 -0
  228. package/dist/index.d.ts +25 -0
  229. package/dist/index.d.ts.map +1 -0
  230. package/dist/index.js +11 -0
  231. package/dist/index.js.map +1 -0
  232. package/dist/lexer/lexer.d.ts +4 -0
  233. package/dist/lexer/lexer.d.ts.map +1 -0
  234. package/dist/lexer/lexer.js +318 -0
  235. package/dist/lexer/lexer.js.map +1 -0
  236. package/dist/lexer/tokens.d.ts +23 -0
  237. package/dist/lexer/tokens.d.ts.map +1 -0
  238. package/dist/lexer/tokens.js +40 -0
  239. package/dist/lexer/tokens.js.map +1 -0
  240. package/dist/model/provider.d.ts +36 -0
  241. package/dist/model/provider.d.ts.map +1 -0
  242. package/dist/model/provider.js +2 -0
  243. package/dist/model/provider.js.map +1 -0
  244. package/dist/parser/ast.d.ts +104 -0
  245. package/dist/parser/ast.d.ts.map +1 -0
  246. package/dist/parser/ast.js +2 -0
  247. package/dist/parser/ast.js.map +1 -0
  248. package/dist/parser/parser.d.ts +4 -0
  249. package/dist/parser/parser.d.ts.map +1 -0
  250. package/dist/parser/parser.js +299 -0
  251. package/dist/parser/parser.js.map +1 -0
  252. package/dist/parser/precedence.d.ts +24 -0
  253. package/dist/parser/precedence.d.ts.map +1 -0
  254. package/dist/parser/precedence.js +52 -0
  255. package/dist/parser/precedence.js.map +1 -0
  256. package/dist/parser/printer.d.ts +4 -0
  257. package/dist/parser/printer.d.ts.map +1 -0
  258. package/dist/parser/printer.js +112 -0
  259. package/dist/parser/printer.js.map +1 -0
  260. package/dist/r4/generated/resources-data.d.ts +3 -0
  261. package/dist/r4/generated/resources-data.d.ts.map +1 -0
  262. package/dist/r4/generated/resources-data.js +28628 -0
  263. package/dist/r4/generated/resources-data.js.map +1 -0
  264. package/dist/r4/generated/type-maps.d.ts +43153 -0
  265. package/dist/r4/generated/type-maps.d.ts.map +1 -0
  266. package/dist/r4/generated/type-maps.js +4 -0
  267. package/dist/r4/generated/type-maps.js.map +1 -0
  268. package/dist/r4/generated/types-data.d.ts +3 -0
  269. package/dist/r4/generated/types-data.d.ts.map +1 -0
  270. package/dist/r4/generated/types-data.js +2205 -0
  271. package/dist/r4/generated/types-data.js.map +1 -0
  272. package/dist/r4/index.d.ts +9 -0
  273. package/dist/r4/index.d.ts.map +1 -0
  274. package/dist/r4/index.js +94 -0
  275. package/dist/r4/index.js.map +1 -0
  276. package/dist/r4/model-data.d.ts +17 -0
  277. package/dist/r4/model-data.d.ts.map +1 -0
  278. package/dist/r4/model-data.js +2 -0
  279. package/dist/r4/model-data.js.map +1 -0
  280. package/dist/sites/index.d.ts +31 -0
  281. package/dist/sites/index.d.ts.map +1 -0
  282. package/dist/sites/index.js +205 -0
  283. package/dist/sites/index.js.map +1 -0
  284. package/dist/typed/infer.d.ts +234 -0
  285. package/dist/typed/infer.d.ts.map +1 -0
  286. package/dist/typed/infer.js +85 -0
  287. package/dist/typed/infer.js.map +1 -0
  288. package/dist/typed/perf-fixture.types.d.ts +136 -0
  289. package/dist/typed/perf-fixture.types.d.ts.map +1 -0
  290. package/dist/typed/perf-fixture.types.js +2 -0
  291. package/dist/typed/perf-fixture.types.js.map +1 -0
  292. package/dist/values/collection.d.ts +24 -0
  293. package/dist/values/collection.d.ts.map +1 -0
  294. package/dist/values/collection.js +51 -0
  295. package/dist/values/collection.js.map +1 -0
  296. package/dist/values/datetime.d.ts +54 -0
  297. package/dist/values/datetime.d.ts.map +1 -0
  298. package/dist/values/datetime.js +226 -0
  299. package/dist/values/datetime.js.map +1 -0
  300. package/dist/values/decimal.d.ts +48 -0
  301. package/dist/values/decimal.d.ts.map +1 -0
  302. package/dist/values/decimal.js +188 -0
  303. package/dist/values/decimal.js.map +1 -0
  304. package/dist/values/numeric.d.ts +24 -0
  305. package/dist/values/numeric.d.ts.map +1 -0
  306. package/dist/values/numeric.js +53 -0
  307. package/dist/values/numeric.js.map +1 -0
  308. package/dist/values/quantity.d.ts +49 -0
  309. package/dist/values/quantity.d.ts.map +1 -0
  310. package/dist/values/quantity.js +297 -0
  311. package/dist/values/quantity.js.map +1 -0
  312. package/dist/values/temporal-arithmetic.d.ts +17 -0
  313. package/dist/values/temporal-arithmetic.d.ts.map +1 -0
  314. package/dist/values/temporal-arithmetic.js +183 -0
  315. package/dist/values/temporal-arithmetic.js.map +1 -0
  316. package/dist/values/temporal-compare.d.ts +9 -0
  317. package/dist/values/temporal-compare.d.ts.map +1 -0
  318. package/dist/values/temporal-compare.js +59 -0
  319. package/dist/values/temporal-compare.js.map +1 -0
  320. package/dist/values/type-compat.d.ts +47 -0
  321. package/dist/values/type-compat.d.ts.map +1 -0
  322. package/dist/values/type-compat.js +100 -0
  323. package/dist/values/type-compat.js.map +1 -0
  324. package/dist/values/typed-value.d.ts +48 -0
  325. package/dist/values/typed-value.d.ts.map +1 -0
  326. package/dist/values/typed-value.js +117 -0
  327. package/dist/values/typed-value.js.map +1 -0
  328. package/dist/values/ucum.d.ts +14 -0
  329. package/dist/values/ucum.d.ts.map +1 -0
  330. package/dist/values/ucum.js +146 -0
  331. package/dist/values/ucum.js.map +1 -0
  332. package/package.json +137 -0
  333. package/src/analyzer/analyze-dto.ts +195 -0
  334. package/src/analyzer/analyze.ts +1181 -0
  335. package/src/analyzer/expression-policy.ts +434 -0
  336. package/src/analyzer/index.ts +23 -0
  337. package/src/analyzer/regex-safety.ts +85 -0
  338. package/src/analyzer/signatures.ts +260 -0
  339. package/src/api/bundle.ts +132 -0
  340. package/src/api/cache.ts +40 -0
  341. package/src/api/column-signature.ts +71 -0
  342. package/src/api/compile.ts +308 -0
  343. package/src/api/constraints.ts +104 -0
  344. package/src/api/dto.ts +445 -0
  345. package/src/api/engine.ts +328 -0
  346. package/src/api/evaluate.ts +11 -0
  347. package/src/api/project.ts +255 -0
  348. package/src/api/tagged.ts +35 -0
  349. package/src/cli/dto-check.ts +94 -0
  350. package/src/cli/fhirpath-check.ts +168 -0
  351. package/src/cli/ts-loader.mjs +49 -0
  352. package/src/engine/context.ts +282 -0
  353. package/src/engine/evaluator.ts +246 -0
  354. package/src/engine/navigation.ts +106 -0
  355. package/src/engine/operators/collections.ts +34 -0
  356. package/src/engine/operators/comparison.ts +82 -0
  357. package/src/engine/operators/equality.ts +232 -0
  358. package/src/engine/operators/index.ts +65 -0
  359. package/src/engine/operators/logic.ts +53 -0
  360. package/src/engine/operators/math.ts +225 -0
  361. package/src/engine/operators/strings.ts +25 -0
  362. package/src/engine/operators/types.ts +16 -0
  363. package/src/engine/type-matching.ts +148 -0
  364. package/src/errors.ts +33 -0
  365. package/src/eslint/index.ts +388 -0
  366. package/src/fhir/html-checks.ts +266 -0
  367. package/src/fhir/model-navigation.ts +148 -0
  368. package/src/functions/aggregate.ts +136 -0
  369. package/src/functions/boundary.ts +209 -0
  370. package/src/functions/combining.ts +15 -0
  371. package/src/functions/control.ts +25 -0
  372. package/src/functions/conversion.ts +349 -0
  373. package/src/functions/date-components.ts +88 -0
  374. package/src/functions/existence.ts +107 -0
  375. package/src/functions/fhir.ts +247 -0
  376. package/src/functions/filtering.ts +120 -0
  377. package/src/functions/install.ts +19 -0
  378. package/src/functions/iteration.ts +21 -0
  379. package/src/functions/math.ts +167 -0
  380. package/src/functions/reflection.ts +24 -0
  381. package/src/functions/registry.ts +72 -0
  382. package/src/functions/sort.ts +68 -0
  383. package/src/functions/string.ts +413 -0
  384. package/src/functions/subsetting.ts +89 -0
  385. package/src/functions/tree.ts +112 -0
  386. package/src/functions/type-specifier.ts +29 -0
  387. package/src/functions/utility.ts +82 -0
  388. package/src/functions/variables.ts +36 -0
  389. package/src/index.ts +30 -0
  390. package/src/lexer/lexer.ts +341 -0
  391. package/src/lexer/tokens.ts +69 -0
  392. package/src/model/provider.ts +36 -0
  393. package/src/parser/ast.ts +164 -0
  394. package/src/parser/parser.ts +321 -0
  395. package/src/parser/precedence.ts +52 -0
  396. package/src/parser/printer.ts +114 -0
  397. package/src/r4/generated/resources-data.ts +28631 -0
  398. package/src/r4/generated/type-maps.ts +22145 -0
  399. package/src/r4/generated/types-data.ts +2208 -0
  400. package/src/r4/index.ts +107 -0
  401. package/src/r4/model-data.ts +17 -0
  402. package/src/sites/index.ts +285 -0
  403. package/src/typed/infer.ts +433 -0
  404. package/src/typed/perf-fixture.types.ts +143 -0
  405. package/src/values/collection.ts +57 -0
  406. package/src/values/datetime.ts +285 -0
  407. package/src/values/decimal.ts +213 -0
  408. package/src/values/numeric.ts +63 -0
  409. package/src/values/quantity.ts +358 -0
  410. package/src/values/temporal-arithmetic.ts +231 -0
  411. package/src/values/temporal-compare.ts +65 -0
  412. package/src/values/type-compat.ts +133 -0
  413. package/src/values/typed-value.ts +151 -0
  414. package/src/values/ucum.ts +181 -0
@@ -0,0 +1,209 @@
1
+ import { FhirPathTypeError } from '../errors.ts'
2
+ import { singleton } from '../values/collection.ts'
3
+ import { daysInMonth, Temporal } from '../values/datetime.ts'
4
+ import { Decimal } from '../values/decimal.ts'
5
+ import {
6
+ type QuantityValue,
7
+ SYSTEM_DATE,
8
+ SYSTEM_DATETIME,
9
+ SYSTEM_DECIMAL,
10
+ SYSTEM_INTEGER,
11
+ SYSTEM_QUANTITY,
12
+ SYSTEM_TIME,
13
+ systemTypeOf,
14
+ } from '../values/typed-value.ts'
15
+ import { argAt, registerFunction } from './registry.ts'
16
+
17
+ type Edge = 'low' | 'high'
18
+
19
+ const MAX_DECIMAL_PRECISION = 28
20
+ // Spec default for decimal boundaries, and the digit count of a full date (YYYYMMDD).
21
+ const DEFAULT_DECIMAL_BOUNDARY_PRECISION = 8
22
+ const FULL_DATE_DIGITS = 8
23
+ const MAX_DATETIME_DIGITS = 17
24
+ const MAX_TIME_DIGITS = 9
25
+
26
+ /**
27
+ * Decimal boundary: value ± half of the last-digit step, then floored (low) or
28
+ * ceiled (high) to the target precision. Integers behave as scale-0 decimals
29
+ * (`1.lowBoundary(0)` is 0, per the official suite).
30
+ */
31
+ function decimalBoundary(value: Decimal, edge: Edge, targetScale: number): Decimal {
32
+ const halfStep = new Decimal(5n, value.scale + 1)
33
+ const boundary = edge === 'low' ? value.subtract(halfStep) : value.add(halfStep)
34
+ if (targetScale >= boundary.scale) {
35
+ return boundary.withScale(targetScale)
36
+ }
37
+ return edge === 'low' ? floorAt(boundary, targetScale) : ceilAt(boundary, targetScale)
38
+ }
39
+
40
+ function floorAt(value: Decimal, scale: number): Decimal {
41
+ const factor = 10n ** BigInt(value.scale - scale)
42
+ let digits = value.digits / factor
43
+ if (value.digits < 0n && value.digits % factor !== 0n) {
44
+ digits -= 1n
45
+ }
46
+ return new Decimal(digits, scale)
47
+ }
48
+
49
+ function ceilAt(value: Decimal, scale: number): Decimal {
50
+ const factor = 10n ** BigInt(value.scale - scale)
51
+ let digits = value.digits / factor
52
+ if (value.digits > 0n && value.digits % factor !== 0n) {
53
+ digits += 1n
54
+ }
55
+ return new Decimal(digits, scale)
56
+ }
57
+
58
+ /** Digit count of the value's text form, timezone excluded — the spec's precision measure. */
59
+ function temporalPrecisionDigits(value: Temporal): number {
60
+ const text = value.toString().replace(/(Z|[+-]\d{2}:\d{2})$/, '')
61
+ return (text.match(/\d/g) ?? []).length
62
+ }
63
+
64
+ /**
65
+ * The earliest ('low') or latest ('high') moment a partial date/time may mean:
66
+ * missing components fill with their extremes down to `targetDigits`, components
67
+ * beyond the target truncate away, and a dateTime reaching time precision with no
68
+ * timezone gets the earliest (+14:00) or latest (-12:00) offset.
69
+ */
70
+ function temporalBoundary(value: Temporal, edge: Edge, targetDigits: number | undefined): Temporal | undefined {
71
+ const isTime = value.kind === 'time'
72
+ const digits = targetDigits ?? (isTime ? MAX_TIME_DIGITS : MAX_DATETIME_DIGITS)
73
+ if (digits > (isTime ? MAX_TIME_DIGITS : MAX_DATETIME_DIGITS)) {
74
+ return undefined
75
+ }
76
+ const low = edge === 'low'
77
+ const dateDigits = isTime ? 0 : FULL_DATE_DIGITS
78
+ const wantsMonth = !isTime && digits >= 6
79
+ const wantsDay = !isTime && digits >= FULL_DATE_DIGITS
80
+ const wantsHour = digits >= dateDigits + 2 && (isTime || value.kind === 'dateTime')
81
+ const wantsMinute = digits >= dateDigits + 4
82
+ const wantsSecond = digits >= dateDigits + 6
83
+ const wantsFraction = digits >= dateDigits + 9
84
+ const year = value.year
85
+ const month = wantsMonth ? (value.month ?? (low ? 1 : 12)) : undefined
86
+ const day =
87
+ wantsDay && month !== undefined ? (value.day ?? (low ? 1 : daysInMonth(year as number, month))) : undefined
88
+ const hour = wantsHour ? (value.hour ?? (low ? 0 : 23)) : undefined
89
+ const minute = wantsMinute && hour !== undefined ? (value.minute ?? (low ? 0 : 59)) : undefined
90
+ const second = wantsSecond && minute !== undefined ? (value.second ?? (low ? 0 : 59)) : undefined
91
+ const fraction = wantsFraction && second !== undefined ? (value.fraction ?? (low ? '000' : '999')) : undefined
92
+ let timezoneOffsetMinutes = value.timezoneOffsetMinutes
93
+ if (value.kind === 'dateTime' && hour !== undefined && timezoneOffsetMinutes === undefined) {
94
+ timezoneOffsetMinutes = low ? 14 * 60 : -12 * 60
95
+ }
96
+ if (value.kind === 'dateTime' && hour === undefined) {
97
+ timezoneOffsetMinutes = undefined
98
+ }
99
+ const kind = value.kind === 'dateTime' && hour === undefined ? 'date' : value.kind
100
+ return Temporal.fromFields(kind, {
101
+ year,
102
+ month,
103
+ day,
104
+ hour,
105
+ minute,
106
+ second,
107
+ fraction,
108
+ timezoneOffsetMinutes,
109
+ })
110
+ }
111
+
112
+ function boundaryFunction(name: string, edge: Edge): void {
113
+ registerFunction(name, {
114
+ minArity: 0,
115
+ maxArity: 1,
116
+ evaluate: (context, input, args, evaluateNode) => {
117
+ const item = singleton(input)
118
+ if (item === undefined) {
119
+ return []
120
+ }
121
+ let precision: number | undefined
122
+ if (args.length === 1) {
123
+ const argument = singleton(evaluateNode(argAt(args, 0), context, input), SYSTEM_INTEGER)
124
+ if (argument === undefined || typeof argument.value !== 'number') {
125
+ throw new FhirPathTypeError(`${name}() expects an integer precision`)
126
+ }
127
+ // Out-of-range precision means the boundary cannot be represented: empty.
128
+ if (argument.value < 0) {
129
+ return []
130
+ }
131
+ precision = argument.value
132
+ }
133
+ switch (systemTypeOf(item) ?? item.type) {
134
+ case SYSTEM_INTEGER:
135
+ case 'System.Long':
136
+ case SYSTEM_DECIMAL: {
137
+ if (precision !== undefined && precision > MAX_DECIMAL_PRECISION) {
138
+ return []
139
+ }
140
+ const value = item.value instanceof Decimal ? item.value : (Decimal.fromString(String(item.value)) as Decimal)
141
+ return [
142
+ {
143
+ type: SYSTEM_DECIMAL,
144
+ value: decimalBoundary(value, edge, precision ?? DEFAULT_DECIMAL_BOUNDARY_PRECISION),
145
+ },
146
+ ]
147
+ }
148
+ case SYSTEM_QUANTITY: {
149
+ if (precision !== undefined && precision > MAX_DECIMAL_PRECISION) {
150
+ return []
151
+ }
152
+ const quantity = item.value as QuantityValue
153
+ return [
154
+ {
155
+ type: SYSTEM_QUANTITY,
156
+ value: {
157
+ ...quantity,
158
+ value: decimalBoundary(quantity.value, edge, precision ?? DEFAULT_DECIMAL_BOUNDARY_PRECISION),
159
+ },
160
+ },
161
+ ]
162
+ }
163
+ case SYSTEM_DATE:
164
+ case SYSTEM_DATETIME:
165
+ case SYSTEM_TIME: {
166
+ const boundary = temporalBoundary(item.value as Temporal, edge, precision)
167
+ if (boundary === undefined) {
168
+ return []
169
+ }
170
+ const type = boundary.kind === 'date' ? SYSTEM_DATE : boundary.kind === 'time' ? SYSTEM_TIME : SYSTEM_DATETIME
171
+ return [{ type, value: boundary }]
172
+ }
173
+ default:
174
+ throw new FhirPathTypeError(`${name}() is not defined for ${item.type}`)
175
+ }
176
+ },
177
+ })
178
+ }
179
+
180
+ boundaryFunction('lowBoundary', 'low')
181
+ boundaryFunction('highBoundary', 'high')
182
+
183
+ registerFunction('precision', {
184
+ minArity: 0,
185
+ maxArity: 0,
186
+ evaluate: (_context, input) => {
187
+ const item = singleton(input)
188
+ if (item === undefined) {
189
+ return []
190
+ }
191
+ switch (systemTypeOf(item) ?? item.type) {
192
+ case SYSTEM_DECIMAL:
193
+ return [
194
+ {
195
+ type: SYSTEM_INTEGER,
196
+ value: item.value instanceof Decimal ? item.value.scale : 0,
197
+ },
198
+ ]
199
+ case SYSTEM_INTEGER:
200
+ return [{ type: SYSTEM_INTEGER, value: 0 }]
201
+ case SYSTEM_DATE:
202
+ case SYSTEM_DATETIME:
203
+ case SYSTEM_TIME:
204
+ return [{ type: SYSTEM_INTEGER, value: temporalPrecisionDigits(item.value as Temporal) }]
205
+ default:
206
+ throw new FhirPathTypeError(`precision() is not defined for ${item.type}`)
207
+ }
208
+ },
209
+ })
@@ -0,0 +1,15 @@
1
+ import { unionCollections } from '../engine/operators/collections.ts'
2
+ import { argAt, registerFunction } from './registry.ts'
3
+
4
+ registerFunction('union', {
5
+ minArity: 1,
6
+ maxArity: 1,
7
+ evaluate: (context, input, args, evaluateNode) =>
8
+ unionCollections(input, evaluateNode(argAt(args, 0), context, input)),
9
+ })
10
+
11
+ registerFunction('combine', {
12
+ minArity: 1,
13
+ maxArity: 1,
14
+ evaluate: (context, input, args, evaluateNode) => [...input, ...evaluateNode(argAt(args, 0), context, input)],
15
+ })
@@ -0,0 +1,25 @@
1
+ import { withFrame } from '../engine/context.ts'
2
+ import { FhirPathRuntimeError } from '../errors.ts'
3
+ import { booleanSingleton } from '../values/collection.ts'
4
+ import { argAt, registerFunction } from './registry.ts'
5
+
6
+ registerFunction('iif', {
7
+ minArity: 2,
8
+ maxArity: 3,
9
+ evaluate: (context, input, args, evaluateNode) => {
10
+ if (input.length > 1) {
11
+ throw new FhirPathRuntimeError('iif() expects a collection with at most one item as input')
12
+ }
13
+ // $this is the (optional) single input item; branches evaluate lazily.
14
+ return withFrame(context, { thisValue: input }, frameContext => {
15
+ const criterion = booleanSingleton(evaluateNode(argAt(args, 0), frameContext, input))
16
+ if (criterion === true) {
17
+ return evaluateNode(argAt(args, 1), frameContext, input)
18
+ }
19
+ if (args.length === 3) {
20
+ return evaluateNode(argAt(args, 2), frameContext, input)
21
+ }
22
+ return []
23
+ })
24
+ },
25
+ })
@@ -0,0 +1,349 @@
1
+ import { FhirPathTypeError } from '../errors.ts'
2
+ import { CALENDAR_DURATION_UNITS } from '../lexer/tokens.ts'
3
+ import { singleton, wrapBoolean } from '../values/collection.ts'
4
+ import { Temporal } from '../values/datetime.ts'
5
+ import { Decimal } from '../values/decimal.ts'
6
+ import { LONG_MAX, LONG_MIN } from '../values/numeric.ts'
7
+ import { coerceQuantity, convertQuantity } from '../values/quantity.ts'
8
+ import {
9
+ type QuantityValue,
10
+ SYSTEM_BOOLEAN,
11
+ SYSTEM_DATE,
12
+ SYSTEM_DATETIME,
13
+ SYSTEM_DECIMAL,
14
+ SYSTEM_INTEGER,
15
+ SYSTEM_LONG,
16
+ SYSTEM_QUANTITY,
17
+ SYSTEM_STRING,
18
+ SYSTEM_TIME,
19
+ systemTypeOf,
20
+ type TypedValue,
21
+ } from '../values/typed-value.ts'
22
+ import type { FhirPathFunction } from './registry.ts'
23
+ import { registerFunction } from './registry.ts'
24
+
25
+ type Converter = (item: TypedValue) => TypedValue | undefined
26
+
27
+ /** Registers `to<Type>()` plus its `convertsTo<Type>()` twin (spec §5.5). */
28
+ function conversionPair(typeName: string, convert: Converter): void {
29
+ registerFunction(`to${typeName}`, {
30
+ minArity: 0,
31
+ maxArity: 0,
32
+ evaluate: (_context, input) => {
33
+ const item = singleton(input)
34
+ if (item === undefined) {
35
+ return []
36
+ }
37
+ const converted = convert(item)
38
+ return converted === undefined ? [] : [converted]
39
+ },
40
+ })
41
+ registerFunction(`convertsTo${typeName}`, {
42
+ minArity: 0,
43
+ maxArity: 0,
44
+ evaluate: (_context, input) => {
45
+ const item = singleton(input)
46
+ if (item === undefined) {
47
+ return []
48
+ }
49
+ return wrapBoolean(convert(item) !== undefined)
50
+ },
51
+ })
52
+ }
53
+
54
+ const TRUE_STRINGS = new Set(['true', 't', 'yes', 'y', '1', '1.0'])
55
+ const FALSE_STRINGS = new Set(['false', 'f', 'no', 'n', '0', '0.0'])
56
+
57
+ conversionPair('Boolean', item => {
58
+ switch (systemTypeOf(item)) {
59
+ case SYSTEM_BOOLEAN:
60
+ return item
61
+ case SYSTEM_INTEGER: {
62
+ if (item.value === 1) {
63
+ return { type: SYSTEM_BOOLEAN, value: true }
64
+ }
65
+ return item.value === 0 ? { type: SYSTEM_BOOLEAN, value: false } : undefined
66
+ }
67
+ case SYSTEM_LONG: {
68
+ if (item.value === 1n) {
69
+ return { type: SYSTEM_BOOLEAN, value: true }
70
+ }
71
+ return item.value === 0n ? { type: SYSTEM_BOOLEAN, value: false } : undefined
72
+ }
73
+ case SYSTEM_DECIMAL: {
74
+ const value = item.value as Decimal
75
+ if (value.equals(new Decimal(1n, 0))) {
76
+ return { type: SYSTEM_BOOLEAN, value: true }
77
+ }
78
+ return value.isZero() ? { type: SYSTEM_BOOLEAN, value: false } : undefined
79
+ }
80
+ case SYSTEM_STRING: {
81
+ const text = (item.value as string).toLowerCase()
82
+ if (TRUE_STRINGS.has(text)) {
83
+ return { type: SYSTEM_BOOLEAN, value: true }
84
+ }
85
+ return FALSE_STRINGS.has(text) ? { type: SYSTEM_BOOLEAN, value: false } : undefined
86
+ }
87
+ default:
88
+ return undefined
89
+ }
90
+ })
91
+
92
+ conversionPair('Integer', item => {
93
+ switch (systemTypeOf(item)) {
94
+ case SYSTEM_INTEGER:
95
+ return item
96
+ case SYSTEM_LONG: {
97
+ const value = item.value as bigint
98
+ if (value >= -2147483648n && value <= 2147483647n) {
99
+ return { type: SYSTEM_INTEGER, value: Number(value) }
100
+ }
101
+ return undefined
102
+ }
103
+ case SYSTEM_BOOLEAN:
104
+ return { type: SYSTEM_INTEGER, value: item.value === true ? 1 : 0 }
105
+ case SYSTEM_STRING: {
106
+ if (!/^[+-]?\d+$/.test(item.value as string)) {
107
+ return undefined
108
+ }
109
+ const value = Number.parseInt(item.value as string, 10)
110
+ return Number.isSafeInteger(value) ? { type: SYSTEM_INTEGER, value } : undefined
111
+ }
112
+ default:
113
+ return undefined
114
+ }
115
+ })
116
+
117
+ conversionPair('Long', item => {
118
+ switch (systemTypeOf(item)) {
119
+ case SYSTEM_LONG:
120
+ return item
121
+ case SYSTEM_INTEGER:
122
+ return { type: SYSTEM_LONG, value: BigInt(item.value as number) }
123
+ case SYSTEM_BOOLEAN:
124
+ return { type: SYSTEM_LONG, value: item.value === true ? 1n : 0n }
125
+ case SYSTEM_STRING: {
126
+ if (!/^[+-]?\d+$/.test(item.value as string)) {
127
+ return undefined
128
+ }
129
+ const value = BigInt(item.value as string)
130
+ return value >= LONG_MIN && value <= LONG_MAX ? { type: SYSTEM_LONG, value } : undefined
131
+ }
132
+ default:
133
+ return undefined
134
+ }
135
+ })
136
+
137
+ conversionPair('Decimal', item => {
138
+ switch (systemTypeOf(item)) {
139
+ case SYSTEM_DECIMAL:
140
+ return item
141
+ case SYSTEM_INTEGER:
142
+ return { type: SYSTEM_DECIMAL, value: Decimal.fromString(String(item.value as number)) }
143
+ case SYSTEM_LONG:
144
+ return { type: SYSTEM_DECIMAL, value: Decimal.fromString((item.value as bigint).toString()) }
145
+ case SYSTEM_BOOLEAN:
146
+ return { type: SYSTEM_DECIMAL, value: Decimal.fromString(item.value === true ? '1.0' : '0.0') }
147
+ case SYSTEM_STRING: {
148
+ if (!/^[+-]?\d+(\.\d+)?$/.test(item.value as string)) {
149
+ return undefined
150
+ }
151
+ return { type: SYSTEM_DECIMAL, value: Decimal.fromString(item.value as string) }
152
+ }
153
+ default:
154
+ return undefined
155
+ }
156
+ })
157
+
158
+ export function valueToString(item: TypedValue): string | undefined {
159
+ if (item.value === undefined || item.value === null) {
160
+ // A primitive present only through its _field sibling has no value to render.
161
+ return undefined
162
+ }
163
+ switch (systemTypeOf(item) ?? item.type) {
164
+ case SYSTEM_STRING:
165
+ return item.value as string
166
+ case SYSTEM_BOOLEAN:
167
+ return item.value === true ? 'true' : 'false'
168
+ case SYSTEM_INTEGER:
169
+ return String(item.value as number)
170
+ case SYSTEM_LONG:
171
+ return (item.value as bigint).toString()
172
+ case SYSTEM_DECIMAL:
173
+ return (item.value as Decimal).toString()
174
+ case SYSTEM_DATE:
175
+ case SYSTEM_DATETIME:
176
+ case SYSTEM_TIME:
177
+ return (item.value as Temporal).toString()
178
+ case SYSTEM_QUANTITY: {
179
+ const quantity = item.value as QuantityValue
180
+ return quantity.calendar
181
+ ? `${quantity.value.toString()} ${quantity.unit}`
182
+ : `${quantity.value.toString()} '${quantity.unit}'`
183
+ }
184
+ default:
185
+ return undefined
186
+ }
187
+ }
188
+
189
+ conversionPair('String', item => {
190
+ const value = valueToString(item)
191
+ return value === undefined ? undefined : { type: SYSTEM_STRING, value }
192
+ })
193
+
194
+ conversionPair('Date', item => {
195
+ switch (systemTypeOf(item)) {
196
+ case SYSTEM_DATE:
197
+ return item
198
+ case SYSTEM_DATETIME: {
199
+ const value = item.value as Temporal
200
+ const truncated = Temporal.fromFields('date', { year: value.year, month: value.month, day: value.day })
201
+ return truncated === undefined ? undefined : { type: SYSTEM_DATE, value: truncated }
202
+ }
203
+ case SYSTEM_STRING: {
204
+ const parsed = Temporal.parseDate(item.value as string)
205
+ return parsed === undefined ? undefined : { type: SYSTEM_DATE, value: parsed }
206
+ }
207
+ default:
208
+ return undefined
209
+ }
210
+ })
211
+
212
+ conversionPair('DateTime', item => {
213
+ switch (systemTypeOf(item)) {
214
+ case SYSTEM_DATETIME:
215
+ return item
216
+ case SYSTEM_DATE: {
217
+ const value = item.value as Temporal
218
+ const widened = Temporal.fromFields('dateTime', { year: value.year, month: value.month, day: value.day })
219
+ return widened === undefined ? undefined : { type: SYSTEM_DATETIME, value: widened }
220
+ }
221
+ case SYSTEM_STRING: {
222
+ const parsed = Temporal.parseDateTime(item.value as string)
223
+ return parsed === undefined ? undefined : { type: SYSTEM_DATETIME, value: parsed }
224
+ }
225
+ default:
226
+ return undefined
227
+ }
228
+ })
229
+
230
+ conversionPair('Time', item => {
231
+ switch (systemTypeOf(item)) {
232
+ case SYSTEM_TIME:
233
+ return item
234
+ case SYSTEM_STRING: {
235
+ // The conversion format mirrors the literal form, so the leading T is optional.
236
+ const text = item.value as string
237
+ const parsed = Temporal.parseTime(text.startsWith('T') ? text.slice(1) : text)
238
+ return parsed === undefined ? undefined : { type: SYSTEM_TIME, value: parsed }
239
+ }
240
+ default:
241
+ return undefined
242
+ }
243
+ })
244
+
245
+ const QUANTITY_STRING = /^([+-]?\d+(?:\.\d+)?)\s*(?:'([^']+)'|([a-zA-Z]+))?$/
246
+
247
+ function stringToQuantity(text: string): QuantityValue | undefined {
248
+ const match = QUANTITY_STRING.exec(text.trim())
249
+ if (!match) {
250
+ return undefined
251
+ }
252
+ const [, number, ucumUnit, wordUnit] = match
253
+ const value = Decimal.fromString(number as string)
254
+ /* v8 ignore next 3 -- the regex guarantees a parseable number */
255
+ if (!value) {
256
+ return undefined
257
+ }
258
+ if (ucumUnit !== undefined) {
259
+ return { value, unit: ucumUnit, calendar: false }
260
+ }
261
+ if (wordUnit !== undefined) {
262
+ return CALENDAR_DURATION_UNITS.has(wordUnit) ? { value, unit: wordUnit, calendar: true } : undefined
263
+ }
264
+ return { value, unit: '1', calendar: false }
265
+ }
266
+
267
+ function quantityConverter(item: TypedValue): TypedValue | undefined {
268
+ // FHIR Quantity values coerce like they do for operators (incl. UCUM
269
+ // time-valued codes becoming calendar durations).
270
+ const coerced = coerceQuantity(item)
271
+ if (coerced !== undefined) {
272
+ return item.type === SYSTEM_QUANTITY ? item : { type: SYSTEM_QUANTITY, value: coerced }
273
+ }
274
+ switch (systemTypeOf(item) ?? item.type) {
275
+ case SYSTEM_QUANTITY:
276
+ return item
277
+ case SYSTEM_INTEGER:
278
+ case SYSTEM_DECIMAL:
279
+ case SYSTEM_LONG: {
280
+ const text =
281
+ item.type === SYSTEM_LONG ? (item.value as bigint).toString() : String(item.value as number | Decimal)
282
+ const value = Decimal.fromString(text)
283
+ /* v8 ignore next 3 -- numeric values always render parseable text */
284
+ if (!value) {
285
+ return undefined
286
+ }
287
+ return { type: SYSTEM_QUANTITY, value: { value, unit: '1', calendar: false } }
288
+ }
289
+ case SYSTEM_BOOLEAN:
290
+ return {
291
+ type: SYSTEM_QUANTITY,
292
+ value: {
293
+ value: Decimal.fromString(item.value === true ? '1.0' : '0.0') as Decimal,
294
+ unit: '1',
295
+ calendar: false,
296
+ },
297
+ }
298
+ case SYSTEM_STRING: {
299
+ const parsed = stringToQuantity(item.value as string)
300
+ return parsed === undefined ? undefined : { type: SYSTEM_QUANTITY, value: parsed }
301
+ }
302
+ default:
303
+ return undefined
304
+ }
305
+ }
306
+
307
+ const toQuantityImpl: FhirPathFunction['evaluate'] = (context, input, args, evaluateNode) => {
308
+ const item = singleton(input)
309
+ if (item === undefined) {
310
+ return []
311
+ }
312
+ const converted = quantityConverter(item)
313
+ if (converted === undefined) {
314
+ return []
315
+ }
316
+ if (args.length === 1) {
317
+ const unitArg = singleton(evaluateNode(args[0] as NonNullable<(typeof args)[0]>, context, input))
318
+ if (unitArg === undefined || systemTypeOf(unitArg) !== SYSTEM_STRING) {
319
+ throw new FhirPathTypeError('toQuantity() expects a String unit argument')
320
+ }
321
+ const quantity = converted.value as QuantityValue
322
+ const target = unitArg.value as string
323
+ if (quantity.unit !== target || quantity.calendar) {
324
+ const convertedQuantity = convertQuantity(quantity, target)
325
+ return convertedQuantity === undefined ? [] : [{ type: SYSTEM_QUANTITY, value: convertedQuantity }]
326
+ }
327
+ }
328
+ return [converted]
329
+ }
330
+
331
+ registerFunction('toQuantity', {
332
+ minArity: 0,
333
+ maxArity: 1,
334
+ evaluate: toQuantityImpl,
335
+ })
336
+
337
+ registerFunction('convertsToQuantity', {
338
+ minArity: 0,
339
+ maxArity: 1,
340
+ // Same path as toQuantity so the toX/convertsToX contract holds with a unit
341
+ // argument too: convertible exactly when toQuantity(unit) is non-empty.
342
+ evaluate: (context, input, args, evaluateNode) => {
343
+ const item = singleton(input)
344
+ if (item === undefined) {
345
+ return []
346
+ }
347
+ return wrapBoolean(toQuantityImpl(context, input, args, evaluateNode).length > 0)
348
+ },
349
+ })
@@ -0,0 +1,88 @@
1
+ import { singleton } from '../values/collection.ts'
2
+ import { Temporal } from '../values/datetime.ts'
3
+ import { Decimal } from '../values/decimal.ts'
4
+ import { SYSTEM_DATE, SYSTEM_DECIMAL, SYSTEM_INTEGER, SYSTEM_TIME, type TypedValue } from '../values/typed-value.ts'
5
+ import { registerFunction } from './registry.ts'
6
+
7
+ function temporalInput(input: TypedValue[]): Temporal | undefined {
8
+ const item = singleton(input)
9
+ if (item === undefined || !(item.value instanceof Temporal)) {
10
+ // Component extraction from a non-temporal value is not applicable → empty.
11
+ return undefined
12
+ }
13
+ return item.value
14
+ }
15
+
16
+ /** Component extractors (ballot §5.8): empty when the value's precision omits the component. */
17
+ function componentFunction(name: string, extract: (value: Temporal) => number | undefined): void {
18
+ registerFunction(name, {
19
+ minArity: 0,
20
+ maxArity: 0,
21
+ evaluate: (_context, input) => {
22
+ const temporal = temporalInput(input)
23
+ if (temporal === undefined) {
24
+ return []
25
+ }
26
+ const component = extract(temporal)
27
+ return component === undefined ? [] : [{ type: SYSTEM_INTEGER, value: component }]
28
+ },
29
+ })
30
+ }
31
+
32
+ componentFunction('yearOf', value => value.year)
33
+ componentFunction('monthOf', value => value.month)
34
+ componentFunction('dayOf', value => value.day)
35
+ componentFunction('hourOf', value => value.hour)
36
+ componentFunction('minuteOf', value => value.minute)
37
+ componentFunction('secondOf', value => value.second)
38
+ componentFunction('millisecondOf', value =>
39
+ value.fraction === undefined ? undefined : Number.parseInt(value.fraction.padEnd(3, '0').slice(0, 3), 10)
40
+ )
41
+
42
+ registerFunction('timezoneOffsetOf', {
43
+ minArity: 0,
44
+ maxArity: 0,
45
+ evaluate: (_context, input) => {
46
+ const temporal = temporalInput(input)
47
+ if (temporal === undefined || temporal.timezoneOffsetMinutes === undefined) {
48
+ return []
49
+ }
50
+ // Offset in hours as a Decimal: +05:30 is 5.5.
51
+ const hours = Decimal.fromString(String(temporal.timezoneOffsetMinutes / 60))
52
+ /* v8 ignore next -- minutes divided by 60 always renders a parseable number */
53
+ return hours === undefined ? [] : [{ type: SYSTEM_DECIMAL, value: hours }]
54
+ },
55
+ })
56
+
57
+ registerFunction('dateOf', {
58
+ minArity: 0,
59
+ maxArity: 0,
60
+ evaluate: (_context, input) => {
61
+ const temporal = temporalInput(input)
62
+ if (temporal === undefined || temporal.kind === 'time') {
63
+ return []
64
+ }
65
+ const date = Temporal.fromFields('date', { year: temporal.year, month: temporal.month, day: temporal.day })
66
+ /* v8 ignore next -- components come from an already-valid Temporal */
67
+ return date === undefined ? [] : [{ type: SYSTEM_DATE, value: date }]
68
+ },
69
+ })
70
+
71
+ registerFunction('timeOf', {
72
+ minArity: 0,
73
+ maxArity: 0,
74
+ evaluate: (_context, input) => {
75
+ const temporal = temporalInput(input)
76
+ if (temporal === undefined || temporal.kind !== 'dateTime' || temporal.hour === undefined) {
77
+ return []
78
+ }
79
+ const time = Temporal.fromFields('time', {
80
+ hour: temporal.hour,
81
+ minute: temporal.minute,
82
+ second: temporal.second,
83
+ fraction: temporal.fraction,
84
+ })
85
+ /* v8 ignore next -- components come from an already-valid Temporal */
86
+ return time === undefined ? [] : [{ type: SYSTEM_TIME, value: time }]
87
+ },
88
+ })