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,232 @@
1
+ import { Temporal } from '../../values/datetime.ts'
2
+ import { asNumeric } from '../../values/numeric.ts'
3
+ import { coerceQuantityPair, compareQuantities, quantitiesEquivalent } from '../../values/quantity.ts'
4
+ import { compareTemporal } from '../../values/temporal-compare.ts'
5
+ import {
6
+ SYSTEM_BOOLEAN,
7
+ SYSTEM_STRING,
8
+ systemTypeOf,
9
+ type TypedValue,
10
+ typeLocalName,
11
+ } from '../../values/typed-value.ts'
12
+ import type { BinaryOperatorTable } from './index.ts'
13
+
14
+ /**
15
+ * Single-item `=` semantics (spec §6.1.1). Undefined means empty: date/time values
16
+ * whose precisions differ, or quantities whose units cannot be compared yet.
17
+ */
18
+ export function pairEquals(a: TypedValue, b: TypedValue): boolean | undefined {
19
+ if (a.value === undefined || b.value === undefined) {
20
+ // Valueless primitives (extension-only _field elements) never equal anything.
21
+ return false
22
+ }
23
+ const numericA = asNumeric(a)
24
+ const numericB = asNumeric(b)
25
+ if (numericA && numericB) {
26
+ return numericA.value.equals(numericB.value)
27
+ }
28
+ if (a.value instanceof Temporal && b.value instanceof Temporal) {
29
+ const comparison = compareTemporal(a.value, b.value)
30
+ if (comparison === 'differentPrecision') {
31
+ return undefined
32
+ }
33
+ return comparison === 0
34
+ }
35
+ const quantityPair = coerceQuantityPair(a, b)
36
+ if (quantityPair) {
37
+ const comparison = compareQuantities(quantityPair[0], quantityPair[1])
38
+ return comparison === undefined ? undefined : comparison === 0
39
+ }
40
+ if (systemTypeOf(a) === SYSTEM_STRING && systemTypeOf(b) === SYSTEM_STRING) {
41
+ return a.value === b.value
42
+ }
43
+ if (systemTypeOf(a) === SYSTEM_BOOLEAN && systemTypeOf(b) === SYSTEM_BOOLEAN) {
44
+ return a.value === b.value
45
+ }
46
+ if (isComplex(a) && isComplex(b)) {
47
+ return deepEquals(a.value, b.value)
48
+ }
49
+ return false
50
+ }
51
+
52
+ /** Single-item `~` semantics (spec §6.1.3). Never empty. */
53
+ export function pairEquivalent(a: TypedValue, b: TypedValue): boolean {
54
+ if (a.value === undefined || b.value === undefined) {
55
+ // Two valueless primitives are equivalent; a valueless one never matches a value.
56
+ return a.value === undefined && b.value === undefined
57
+ }
58
+ const numericA = asNumeric(a)
59
+ const numericB = asNumeric(b)
60
+ if (numericA && numericB) {
61
+ // Rounded to the least precise operand.
62
+ const scale = Math.min(numericA.value.scale, numericB.value.scale)
63
+ return numericA.value.round(scale).equals(numericB.value.round(scale))
64
+ }
65
+ if (a.value instanceof Temporal && b.value instanceof Temporal) {
66
+ return compareTemporal(a.value, b.value) === 0
67
+ }
68
+ const quantityPair = coerceQuantityPair(a, b)
69
+ if (quantityPair) {
70
+ return quantitiesEquivalent(quantityPair[0], quantityPair[1])
71
+ }
72
+ // One typed side is enough: untyped comparison values (e.g. from %env) still get
73
+ // FHIR semantics when compared against a model-typed Coding/CodeableConcept.
74
+ if ((typeLocalName(a.type) === 'Coding' || typeLocalName(b.type) === 'Coding') && isComplex(a) && isComplex(b)) {
75
+ return codingEquivalent(a.value, b.value)
76
+ }
77
+ if (
78
+ (typeLocalName(a.type) === 'CodeableConcept' || typeLocalName(b.type) === 'CodeableConcept') &&
79
+ isComplex(a) &&
80
+ isComplex(b)
81
+ ) {
82
+ return codeableConceptEquivalent(a.value, b.value)
83
+ }
84
+ if (systemTypeOf(a) === SYSTEM_STRING && systemTypeOf(b) === SYSTEM_STRING) {
85
+ return normalizeString(a.value as string) === normalizeString(b.value as string)
86
+ }
87
+ if (systemTypeOf(a) === SYSTEM_BOOLEAN && systemTypeOf(b) === SYSTEM_BOOLEAN) {
88
+ return a.value === b.value
89
+ }
90
+ if (isComplex(a) && isComplex(b)) {
91
+ return deepEquivalent(a.value, b.value)
92
+ }
93
+ return false
94
+ }
95
+
96
+ /** FHIR equivalence for Coding: system and code only (display and id do not matter). */
97
+ function codingEquivalent(a: unknown, b: unknown): boolean {
98
+ const codingA = a as { system?: unknown; code?: unknown }
99
+ const codingB = b as { system?: unknown; code?: unknown }
100
+ return codingA.system === codingB.system && codingA.code === codingB.code
101
+ }
102
+
103
+ /** FHIR equivalence for CodeableConcept: any pair of equivalent codings. */
104
+ function codeableConceptEquivalent(a: unknown, b: unknown): boolean {
105
+ const codingsA = ((a as { coding?: unknown[] }).coding ?? []) as unknown[]
106
+ const codingsB = ((b as { coding?: unknown[] }).coding ?? []) as unknown[]
107
+ return codingsA.some(one => codingsB.some(other => codingEquivalent(one, other)))
108
+ }
109
+
110
+ function isComplex(item: TypedValue): boolean {
111
+ return typeof item.value === 'object' && item.value !== null && !(item.value instanceof Temporal)
112
+ }
113
+
114
+ function normalizeString(value: string): string {
115
+ // "Normalizing whitespace" (spec §6.1.2) makes tab/newline/space interchangeable;
116
+ // it does not trim or collapse runs — 'a b' and 'a b' stay different.
117
+ return value.replace(/\s/g, ' ').toLowerCase()
118
+ }
119
+
120
+ function deepEquals(a: unknown, b: unknown): boolean {
121
+ if (a === b) {
122
+ return true
123
+ }
124
+ if (Array.isArray(a) || Array.isArray(b)) {
125
+ return (
126
+ Array.isArray(a) &&
127
+ Array.isArray(b) &&
128
+ a.length === b.length &&
129
+ a.every((item, index) => deepEquals(item, b[index]))
130
+ )
131
+ }
132
+ if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {
133
+ // Equality on FHIR complex values ignores element ids and primitive metadata,
134
+ // same as equivalence: a value's identity is its children, not its annotations.
135
+ const keysA = Object.keys(a).filter(isValueKey)
136
+ const keysB = Object.keys(b).filter(isValueKey)
137
+ return (
138
+ keysA.length === keysB.length &&
139
+ keysA.every(key => deepEquals((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]))
140
+ )
141
+ }
142
+ return a === b
143
+ }
144
+
145
+ function deepEquivalent(a: unknown, b: unknown): boolean {
146
+ if (a === b) {
147
+ return true
148
+ }
149
+ if (typeof a === 'string' && typeof b === 'string') {
150
+ return normalizeString(a) === normalizeString(b)
151
+ }
152
+ if (Array.isArray(a) || Array.isArray(b)) {
153
+ return (
154
+ Array.isArray(a) &&
155
+ Array.isArray(b) &&
156
+ a.length === b.length &&
157
+ a.every((item, index) => deepEquivalent(item, b[index]))
158
+ )
159
+ }
160
+ if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {
161
+ const keysA = Object.keys(a).filter(isValueKey)
162
+ const keysB = Object.keys(b).filter(isValueKey)
163
+ return (
164
+ keysA.length === keysB.length &&
165
+ keysA.every(key => deepEquivalent((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key]))
166
+ )
167
+ }
168
+ return a === b
169
+ }
170
+
171
+ /** Excludes a complex value's `id` and `_field` (primitive extension) siblings from comparison. */
172
+ function isValueKey(key: string): boolean {
173
+ return key !== 'id' && !key.startsWith('_')
174
+ }
175
+
176
+ /** Collection `=`: empty operand → empty; different lengths → false; ordered pairwise. */
177
+ export function collectionEquals(left: TypedValue[], right: TypedValue[]): boolean | undefined {
178
+ if (left.length === 0 || right.length === 0) {
179
+ return undefined
180
+ }
181
+ if (left.length !== right.length) {
182
+ return false
183
+ }
184
+ let result = true
185
+ for (let i = 0; i < left.length; i++) {
186
+ const pair = pairEquals(left[i] as TypedValue, right[i] as TypedValue)
187
+ if (pair === undefined) {
188
+ return undefined
189
+ }
190
+ result = result && pair
191
+ }
192
+ return result
193
+ }
194
+
195
+ /** Collection `~`: both empty → true; order-independent multiset matching. */
196
+ export function collectionEquivalent(left: TypedValue[], right: TypedValue[]): boolean {
197
+ if (left.length === 0 && right.length === 0) {
198
+ return true
199
+ }
200
+ if (left.length !== right.length) {
201
+ return false
202
+ }
203
+ const used = new Array<boolean>(right.length).fill(false)
204
+ for (const item of left) {
205
+ let matched = false
206
+ for (let i = 0; i < right.length; i++) {
207
+ if (!used[i] && pairEquivalent(item, right[i] as TypedValue)) {
208
+ used[i] = true
209
+ matched = true
210
+ break
211
+ }
212
+ }
213
+ if (!matched) {
214
+ return false
215
+ }
216
+ }
217
+ return true
218
+ }
219
+
220
+ function wrap(value: boolean | undefined): TypedValue[] {
221
+ return value === undefined ? [] : [{ type: SYSTEM_BOOLEAN, value }]
222
+ }
223
+
224
+ export const equalityOperators = {
225
+ '=': (_context, left, right) => wrap(collectionEquals(left, right)),
226
+ '!=': (_context, left, right) => {
227
+ const result = collectionEquals(left, right)
228
+ return wrap(result === undefined ? undefined : !result)
229
+ },
230
+ '~': (_context, left, right) => wrap(collectionEquivalent(left, right)),
231
+ '!~': (_context, left, right) => wrap(!collectionEquivalent(left, right)),
232
+ } satisfies BinaryOperatorTable
@@ -0,0 +1,65 @@
1
+ import type { BinaryOperator, TypeSpecifier, UnaryOperator } from '../../parser/ast.ts'
2
+ import type { TypedValue } from '../../values/typed-value.ts'
3
+ import type { EvaluationContext } from '../context.ts'
4
+ import { collectionOperators } from './collections.ts'
5
+ import { comparisonOperators } from './comparison.ts'
6
+ import { equalityOperators } from './equality.ts'
7
+ import { logicOperators } from './logic.ts'
8
+ import { arithmeticOperators, unaryArithmeticOperators } from './math.ts'
9
+ import { stringOperators } from './strings.ts'
10
+ import { typeOperator } from './types.ts'
11
+
12
+ export type BinaryOperatorImpl = (context: EvaluationContext, left: TypedValue[], right: TypedValue[]) => TypedValue[]
13
+
14
+ /** What each operator module exports; modules import this type-only, so no cycle. */
15
+ export type BinaryOperatorTable = Partial<Record<BinaryOperator, BinaryOperatorImpl>>
16
+
17
+ export type UnaryOperatorImpl = (context: EvaluationContext, operand: TypedValue[]) => TypedValue[]
18
+
19
+ export type TypeOperatorImpl = (
20
+ context: EvaluationContext,
21
+ operator: 'is' | 'as',
22
+ operand: TypedValue[],
23
+ type: TypeSpecifier
24
+ ) => TypedValue[]
25
+
26
+ /**
27
+ * One entry per BinaryOperator union member, assembled statically — a missing
28
+ * operator is a compile error here, not a runtime throw.
29
+ */
30
+ export const binaryOperators: Readonly<Record<BinaryOperator, BinaryOperatorImpl>> = {
31
+ ...equalityOperators,
32
+ ...comparisonOperators,
33
+ ...logicOperators,
34
+ ...arithmeticOperators,
35
+ ...collectionOperators,
36
+ ...stringOperators,
37
+ }
38
+
39
+ export const unaryOperators: Readonly<Record<UnaryOperator, UnaryOperatorImpl>> = unaryArithmeticOperators
40
+
41
+ export function evaluateBinary(
42
+ context: EvaluationContext,
43
+ operator: BinaryOperator,
44
+ left: TypedValue[],
45
+ right: TypedValue[]
46
+ ): TypedValue[] {
47
+ return binaryOperators[operator](context, left, right)
48
+ }
49
+
50
+ export function evaluateUnary(
51
+ context: EvaluationContext,
52
+ operator: UnaryOperator,
53
+ operand: TypedValue[]
54
+ ): TypedValue[] {
55
+ return unaryOperators[operator](context, operand)
56
+ }
57
+
58
+ export function evaluateTypeOp(
59
+ context: EvaluationContext,
60
+ operator: 'is' | 'as',
61
+ operand: TypedValue[],
62
+ type: TypeSpecifier
63
+ ): TypedValue[] {
64
+ return typeOperator(context, operator, operand, type)
65
+ }
@@ -0,0 +1,53 @@
1
+ import { booleanSingleton, wrapBoolean } from '../../values/collection.ts'
2
+ import type { TypedValue } from '../../values/typed-value.ts'
3
+ import type { BinaryOperatorTable } from './index.ts'
4
+
5
+ /**
6
+ * Three-valued logic straight from the spec §6.5 truth tables. `undefined` is the
7
+ * empty collection. Notable asymmetric cells: `false and empty` is false,
8
+ * `true or empty` is true, `empty implies false` is empty, `false implies x` is true.
9
+ */
10
+ type Tri = boolean | undefined
11
+
12
+ const and = (a: Tri, b: Tri): Tri => {
13
+ if (a === false || b === false) {
14
+ return false
15
+ }
16
+ return a === true && b === true ? true : undefined
17
+ }
18
+
19
+ const or = (a: Tri, b: Tri): Tri => {
20
+ if (a === true || b === true) {
21
+ return true
22
+ }
23
+ return a === false && b === false ? false : undefined
24
+ }
25
+
26
+ const xor = (a: Tri, b: Tri): Tri => {
27
+ if (a === undefined || b === undefined) {
28
+ return undefined
29
+ }
30
+ return a !== b
31
+ }
32
+
33
+ const implies = (a: Tri, b: Tri): Tri => {
34
+ if (a === false) {
35
+ return true
36
+ }
37
+ if (a === true) {
38
+ return b
39
+ }
40
+ return b === true ? true : undefined
41
+ }
42
+
43
+ function logicOperator(table: (a: Tri, b: Tri) => Tri) {
44
+ return (_context: unknown, left: TypedValue[], right: TypedValue[]): TypedValue[] =>
45
+ wrapBoolean(table(booleanSingleton(left), booleanSingleton(right)))
46
+ }
47
+
48
+ export const logicOperators = {
49
+ and: logicOperator(and),
50
+ or: logicOperator(or),
51
+ xor: logicOperator(xor),
52
+ implies: logicOperator(implies),
53
+ } satisfies BinaryOperatorTable
@@ -0,0 +1,225 @@
1
+ import { FhirPathTypeError } from '../../errors.ts'
2
+ import type { UnaryOperator } from '../../parser/ast.ts'
3
+ import { singleton } from '../../values/collection.ts'
4
+ import { Temporal } from '../../values/datetime.ts'
5
+ import type { Decimal } from '../../values/decimal.ts'
6
+ import { asNumeric, widerKind, wrapNumeric } from '../../values/numeric.ts'
7
+ import {
8
+ alignQuantities,
9
+ calendarToUcumLoose,
10
+ coerceQuantity,
11
+ composeUnits,
12
+ promoteQuantity,
13
+ } from '../../values/quantity.ts'
14
+ import { addDuration } from '../../values/temporal-arithmetic.ts'
15
+ import {
16
+ type QuantityValue,
17
+ SYSTEM_QUANTITY,
18
+ SYSTEM_STRING,
19
+ systemTypeOf,
20
+ type TypedValue,
21
+ } from '../../values/typed-value.ts'
22
+ import { canonicalizeUnit, sameDimensions } from '../../values/ucum.ts'
23
+ import type { BinaryOperatorTable, UnaryOperatorImpl } from './index.ts'
24
+
25
+ type ArithmeticOperator = '+' | '-' | '*' | '/' | 'div' | 'mod'
26
+
27
+ function numericArithmetic(operator: ArithmeticOperator, a: TypedValue, b: TypedValue): TypedValue[] {
28
+ const left = asNumeric(a) as NonNullable<ReturnType<typeof asNumeric>>
29
+ const right = asNumeric(b) as NonNullable<ReturnType<typeof asNumeric>>
30
+ let result: Decimal | undefined
31
+ let kind = widerKind(left.kind, right.kind)
32
+ switch (operator) {
33
+ case '+':
34
+ result = left.value.add(right.value)
35
+ break
36
+ case '-':
37
+ result = left.value.subtract(right.value)
38
+ break
39
+ case '*':
40
+ result = left.value.multiply(right.value)
41
+ break
42
+ case '/':
43
+ result = left.value.divide(right.value)
44
+ kind = 'Decimal'
45
+ break
46
+ case 'div':
47
+ result = left.value.integerDivide(right.value)
48
+ kind = kind === 'Decimal' ? 'Integer' : kind
49
+ break
50
+ default:
51
+ result = left.value.modulo(right.value)
52
+ break
53
+ }
54
+ if (result === undefined) {
55
+ return []
56
+ }
57
+ return [wrapNumeric(result, kind)]
58
+ }
59
+
60
+ function temporalArithmetic(operator: ArithmeticOperator, a: TypedValue, b: TypedValue): TypedValue[] {
61
+ if (operator !== '+' && operator !== '-') {
62
+ throw new FhirPathTypeError(`Operator '${operator}' is not defined for date/time values`)
63
+ }
64
+ const result = addDuration(a.value as Temporal, b.value as QuantityValue, operator === '+' ? 1 : -1)
65
+ return result === undefined ? [] : [{ type: a.type, value: result }]
66
+ }
67
+
68
+ function quantityArithmetic(operator: ArithmeticOperator, left: QuantityValue, right: QuantityValue): TypedValue[] {
69
+ if (operator === '+' || operator === '-') {
70
+ // Convertible units align on the more granular one (spec §6.6); else empty.
71
+ const aligned = alignQuantities(left, right)
72
+ if (!aligned) {
73
+ return []
74
+ }
75
+ const value = operator === '+' ? aligned.left.add(aligned.right) : aligned.left.subtract(aligned.right)
76
+ return [{ type: SYSTEM_QUANTITY, value: { value, unit: aligned.unit, calendar: aligned.calendar } }]
77
+ }
78
+ if (operator === '*' || operator === '/') {
79
+ if (left.calendar || right.calendar) {
80
+ return calendarMultiplicative(operator, left, right)
81
+ }
82
+ const value = operator === '*' ? left.value.multiply(right.value) : left.value.divide(right.value)
83
+ if (value === undefined) {
84
+ return []
85
+ }
86
+ const unit = composeUnits(operator, left.unit, right.unit)
87
+ return [{ type: SYSTEM_QUANTITY, value: { value, unit, calendar: false } }]
88
+ }
89
+ throw new FhirPathTypeError(`Operator '${operator}' is not defined for quantities`)
90
+ }
91
+
92
+ /**
93
+ * Calendar durations have no general unit algebra, but two cases are exact:
94
+ * scaling by a dimensionless number (2 year * 3 = 6 years, 1 year / 2 = 6 months)
95
+ * and the ratio of two same-family durations (185 months / 185 months = 1 '1').
96
+ * Everything else is undefined → empty.
97
+ */
98
+ function calendarMultiplicative(operator: '*' | '/', left: QuantityValue, right: QuantityValue): TypedValue[] {
99
+ if (operator === '/') {
100
+ if (left.calendar) {
101
+ const factor = right.calendar ? undefined : dimensionlessValue(right)
102
+ if (factor !== undefined) {
103
+ // Scaling a duration keeps its calendar unit: 1 year / 2 = 0.5 years.
104
+ const scaled = left.value.divide(factor)
105
+ return scaled === undefined ? [] : [{ type: SYSTEM_QUANTITY, value: { ...left, value: scaled } }]
106
+ }
107
+ }
108
+ // Ratios of durations are well defined; both sides drop to their UCUM twins
109
+ // (185 months / 185 'mo' = 1 '1'), computing through exact canonical factors.
110
+ const leftUcum = calendarAsUcum(left)
111
+ const rightUcum = calendarAsUcum(right)
112
+ const leftCanonical = canonicalizeUnit(leftUcum.unit)
113
+ const rightCanonical = canonicalizeUnit(rightUcum.unit)
114
+ if (leftCanonical && rightCanonical && sameDimensions(leftCanonical, rightCanonical)) {
115
+ const ratio = leftUcum.value
116
+ .multiply(leftCanonical.factor)
117
+ .divide(rightUcum.value.multiply(rightCanonical.factor))
118
+ return ratio === undefined ? [] : [{ type: SYSTEM_QUANTITY, value: { value: ratio, unit: '1', calendar: false } }]
119
+ }
120
+ return quantityArithmetic('/', leftUcum, rightUcum)
121
+ }
122
+ const [calendar, scalar] = left.calendar ? [left, right] : [right, left]
123
+ if (scalar.calendar) {
124
+ // year * year has no meaning.
125
+ return []
126
+ }
127
+ const factor = dimensionlessValue(scalar)
128
+ if (factor === undefined) {
129
+ return []
130
+ }
131
+ const value = calendar.value.multiply(factor)
132
+ return [{ type: SYSTEM_QUANTITY, value: { ...calendar, value } }]
133
+ }
134
+
135
+ /** The scalar value of a dimensionless quantity (2000 'g/kg' → 2); else undefined. */
136
+ function dimensionlessValue(quantity: QuantityValue): Decimal | undefined {
137
+ const canonical = canonicalizeUnit(quantity.unit)
138
+ if (!canonical || Object.keys(canonical.dimensions).length > 0) {
139
+ return undefined
140
+ }
141
+ return quantity.value.multiply(canonical.factor)
142
+ }
143
+
144
+ function calendarAsUcum(quantity: QuantityValue): QuantityValue {
145
+ return quantity.calendar ? calendarToUcumLoose(quantity) : quantity
146
+ }
147
+
148
+ function isTemporalType(item: TypedValue): boolean {
149
+ return item.value instanceof Temporal
150
+ }
151
+
152
+ function arithmeticOperator(operator: ArithmeticOperator) {
153
+ return (_context: unknown, leftInput: TypedValue[], rightInput: TypedValue[]): TypedValue[] => {
154
+ const a = singleton(leftInput)
155
+ const b = singleton(rightInput)
156
+ if (a === undefined || b === undefined) {
157
+ return []
158
+ }
159
+ if (systemTypeOf(a) === SYSTEM_STRING || systemTypeOf(b) === SYSTEM_STRING) {
160
+ if (operator === '+' && systemTypeOf(a) === SYSTEM_STRING && systemTypeOf(b) === SYSTEM_STRING) {
161
+ if (a.value === undefined || b.value === undefined) {
162
+ // A primitive present only through its _field sibling has no value; +
163
+ // propagates that like an empty operand.
164
+ return []
165
+ }
166
+ return [{ type: SYSTEM_STRING, value: (a.value as string) + (b.value as string) }]
167
+ }
168
+ throw new FhirPathTypeError(`Operator '${operator}' is not defined for strings`)
169
+ }
170
+ if (isTemporalType(a) && coerceQuantity(b)) {
171
+ return temporalArithmetic(operator, a, { type: SYSTEM_QUANTITY, value: coerceQuantity(b) })
172
+ }
173
+ if (isTemporalType(b) && coerceQuantity(a) && operator === '+') {
174
+ // Addition commutes: 1 year + @2016 works like @2016 + 1 year.
175
+ return temporalArithmetic(operator, b, { type: SYSTEM_QUANTITY, value: coerceQuantity(a) })
176
+ }
177
+ if (asNumeric(a) && asNumeric(b)) {
178
+ return numericArithmetic(operator, a, b)
179
+ }
180
+ if (coerceQuantity(a) || coerceQuantity(b)) {
181
+ const left = promoteQuantity(a)
182
+ const right = promoteQuantity(b)
183
+ if (left && right && operator !== 'div' && operator !== 'mod') {
184
+ return quantityArithmetic(operator, left, right)
185
+ }
186
+ throw new FhirPathTypeError(`Operator '${operator}' is not defined for these quantity operands`)
187
+ }
188
+ throw new FhirPathTypeError(`Operator '${operator}' is not defined for ${a.type} and ${b.type}`)
189
+ }
190
+ }
191
+
192
+ export const arithmeticOperators = {
193
+ '+': arithmeticOperator('+'),
194
+ '-': arithmeticOperator('-'),
195
+ '*': arithmeticOperator('*'),
196
+ '/': arithmeticOperator('/'),
197
+ div: arithmeticOperator('div'),
198
+ mod: arithmeticOperator('mod'),
199
+ } satisfies BinaryOperatorTable
200
+
201
+ function unaryOperator(sign: 1 | -1) {
202
+ return (_context: unknown, input: TypedValue[]): TypedValue[] => {
203
+ const item = singleton(input)
204
+ if (item === undefined) {
205
+ return []
206
+ }
207
+ const numeric = asNumeric(item)
208
+ if (numeric) {
209
+ if (sign === 1) {
210
+ return [item]
211
+ }
212
+ return [wrapNumeric(numeric.value.negate(), numeric.kind)]
213
+ }
214
+ if (item.type === SYSTEM_QUANTITY) {
215
+ const quantity = item.value as QuantityValue
216
+ return sign === 1 ? [item] : [{ type: SYSTEM_QUANTITY, value: { ...quantity, value: quantity.value.negate() } }]
217
+ }
218
+ throw new FhirPathTypeError(`Unary '${sign === 1 ? '+' : '-'}' is not defined for ${item.type}`)
219
+ }
220
+ }
221
+
222
+ export const unaryArithmeticOperators: Readonly<Record<UnaryOperator, UnaryOperatorImpl>> = {
223
+ '+': unaryOperator(1),
224
+ '-': unaryOperator(-1),
225
+ }
@@ -0,0 +1,25 @@
1
+ import { FhirPathTypeError } from '../../errors.ts'
2
+ import { singleton } from '../../values/collection.ts'
3
+ import { SYSTEM_STRING, systemTypeOf, type TypedValue } from '../../values/typed-value.ts'
4
+ import type { BinaryOperatorTable } from './index.ts'
5
+
6
+ /** `&` concatenates strings and, unlike `+`, treats an empty operand as `''` (spec §6.6.2). */
7
+ function concat(_context: unknown, left: TypedValue[], right: TypedValue[]): TypedValue[] {
8
+ return [{ type: SYSTEM_STRING, value: stringOrEmpty(left) + stringOrEmpty(right) }]
9
+ }
10
+
11
+ function stringOrEmpty(input: TypedValue[]): string {
12
+ const item = singleton(input)
13
+ if (item === undefined || item.value === undefined) {
14
+ // Empty operand, or a primitive present only through its _field sibling.
15
+ return ''
16
+ }
17
+ if (systemTypeOf(item) !== SYSTEM_STRING) {
18
+ throw new FhirPathTypeError(`Operator '&' expects strings, got ${item.type}`)
19
+ }
20
+ return item.value as string
21
+ }
22
+
23
+ export const stringOperators = {
24
+ '&': concat,
25
+ } satisfies BinaryOperatorTable
@@ -0,0 +1,16 @@
1
+ import { singleton, wrapBoolean } from '../../values/collection.ts'
2
+ import { itemMatchesType } from '../type-matching.ts'
3
+ import type { TypeOperatorImpl } from './index.ts'
4
+
5
+ export const typeOperator: TypeOperatorImpl = (context, operator, operand, type) => {
6
+ const item = singleton(operand)
7
+ if (item === undefined) {
8
+ return []
9
+ }
10
+ // `is` walks subtypes; the `as` cast demands the exact type (spec + official tests).
11
+ const matches = itemMatchesType(context, item, type.parts, { exact: operator === 'as' })
12
+ if (operator === 'is') {
13
+ return wrapBoolean(matches)
14
+ }
15
+ return matches ? [item] : []
16
+ }