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,285 @@
1
+ export type TemporalKind = 'date' | 'dateTime' | 'time'
2
+
3
+ export type TemporalPrecision = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond'
4
+
5
+ const DATE_PATTERN = /^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$/
6
+ const TIME_PATTERN = /^(\d{2})(?::(\d{2})(?::(\d{2})(?:\.(\d+))?)?)?$/
7
+ const TIMEZONE_PATTERN = /(Z|[+-]\d{2}:\d{2})$/
8
+
9
+ /**
10
+ * A Date, DateTime, or Time value with partial precision. Components beyond the
11
+ * value's precision are undefined; comparison across precisions is decided by the
12
+ * operators (spec §6.2/§6.4), not here.
13
+ */
14
+ export class Temporal {
15
+ readonly kind: TemporalKind
16
+ readonly year: number | undefined
17
+ readonly month: number | undefined
18
+ readonly day: number | undefined
19
+ readonly hour: number | undefined
20
+ readonly minute: number | undefined
21
+ readonly second: number | undefined
22
+ /** Fraction-of-second digits as written, e.g. `559` or `5591`; undefined when absent. */
23
+ readonly fraction: string | undefined
24
+ /** Offset in minutes; 0 for `Z`; undefined when the literal has no timezone. */
25
+ readonly timezoneOffsetMinutes: number | undefined
26
+
27
+ private constructor(fields: {
28
+ kind: TemporalKind
29
+ year?: number | undefined
30
+ month?: number | undefined
31
+ day?: number | undefined
32
+ hour?: number | undefined
33
+ minute?: number | undefined
34
+ second?: number | undefined
35
+ fraction?: string | undefined
36
+ timezoneOffsetMinutes?: number | undefined
37
+ }) {
38
+ this.kind = fields.kind
39
+ this.year = fields.year
40
+ this.month = fields.month
41
+ this.day = fields.day
42
+ this.hour = fields.hour
43
+ this.minute = fields.minute
44
+ this.second = fields.second
45
+ this.fraction = fields.fraction
46
+ this.timezoneOffsetMinutes = fields.timezoneOffsetMinutes
47
+ }
48
+
49
+ /** Parse the text of a date literal (`2014-01`) or FHIR date value. */
50
+ static parseDate(text: string): Temporal | undefined {
51
+ const match = DATE_PATTERN.exec(text)
52
+ if (!match) {
53
+ return undefined
54
+ }
55
+ const [, year, month, day] = match
56
+ return Temporal.build('date', {
57
+ year: Number(year),
58
+ month: optionalNumber(month),
59
+ day: optionalNumber(day),
60
+ })
61
+ }
62
+
63
+ /** Parse the text of a dateTime literal (`2014-01-25T14:30:00Z`, `2014T`). */
64
+ static parseDateTime(text: string): Temporal | undefined {
65
+ const timeSeparator = text.indexOf('T')
66
+ if (timeSeparator === -1) {
67
+ const dateOnly = Temporal.parseDate(text)
68
+ return dateOnly ? dateOnly.withKind('dateTime') : undefined
69
+ }
70
+ const datePart = text.slice(0, timeSeparator)
71
+ let timePart = text.slice(timeSeparator + 1)
72
+ const dateMatch = DATE_PATTERN.exec(datePart)
73
+ if (!dateMatch) {
74
+ return undefined
75
+ }
76
+ let timezoneOffsetMinutes: number | undefined
77
+ const timezoneMatch = TIMEZONE_PATTERN.exec(timePart)
78
+ if (timezoneMatch) {
79
+ timezoneOffsetMinutes = parseTimezone(timezoneMatch[1] as string)
80
+ timePart = timePart.slice(0, -(timezoneMatch[1] as string).length)
81
+ }
82
+ let time: RegExpExecArray | null = null
83
+ if (timePart !== '') {
84
+ time = TIME_PATTERN.exec(timePart)
85
+ if (!time) {
86
+ return undefined
87
+ }
88
+ }
89
+ return Temporal.build('dateTime', {
90
+ year: Number(dateMatch[1]),
91
+ month: optionalNumber(dateMatch[2]),
92
+ day: optionalNumber(dateMatch[3]),
93
+ hour: optionalNumber(time?.[1]),
94
+ minute: optionalNumber(time?.[2]),
95
+ second: optionalNumber(time?.[3]),
96
+ fraction: time?.[4],
97
+ timezoneOffsetMinutes,
98
+ })
99
+ }
100
+
101
+ /** Parse the text of a time literal (`14:30:14.559`). Times have no timezone. */
102
+ static parseTime(text: string): Temporal | undefined {
103
+ const match = TIME_PATTERN.exec(text)
104
+ if (!match) {
105
+ return undefined
106
+ }
107
+ const [, hour, minute, second, fraction] = match
108
+ return Temporal.build('time', {
109
+ hour: Number(hour),
110
+ minute: optionalNumber(minute),
111
+ second: optionalNumber(second),
112
+ fraction,
113
+ })
114
+ }
115
+
116
+ /** Build from components with the same range validation as parsing. */
117
+ static fromFields(
118
+ kind: TemporalKind,
119
+ fields: {
120
+ year?: number | undefined
121
+ month?: number | undefined
122
+ day?: number | undefined
123
+ hour?: number | undefined
124
+ minute?: number | undefined
125
+ second?: number | undefined
126
+ fraction?: string | undefined
127
+ timezoneOffsetMinutes?: number | undefined
128
+ }
129
+ ): Temporal | undefined {
130
+ return Temporal.build(kind, fields)
131
+ }
132
+
133
+ private static build(
134
+ kind: TemporalKind,
135
+ fields: {
136
+ year?: number | undefined
137
+ month?: number | undefined
138
+ day?: number | undefined
139
+ hour?: number | undefined
140
+ minute?: number | undefined
141
+ second?: number | undefined
142
+ fraction?: string | undefined
143
+ timezoneOffsetMinutes?: number | undefined
144
+ }
145
+ ): Temporal | undefined {
146
+ if (!(
147
+ inRange(fields.month, 1, 12) &&
148
+ inRange(fields.day, 1, 31) &&
149
+ inRange(fields.hour, 0, 23) &&
150
+ inRange(fields.minute, 0, 59) &&
151
+ inRange(fields.second, 0, 60)
152
+ )) {
153
+ return undefined
154
+ }
155
+ // @2019-02-29 is not a date; timezone offsets only exist within -12:00..+14:00.
156
+ if (
157
+ fields.day !== undefined &&
158
+ fields.month !== undefined &&
159
+ fields.year !== undefined &&
160
+ fields.day > daysInMonth(fields.year, fields.month)
161
+ ) {
162
+ return undefined
163
+ }
164
+ if (fields.timezoneOffsetMinutes !== undefined && Math.abs(fields.timezoneOffsetMinutes) > 14 * 60) {
165
+ return undefined
166
+ }
167
+ return new Temporal({ kind, ...fields })
168
+ }
169
+
170
+ get precision(): TemporalPrecision {
171
+ if (this.fraction !== undefined) {
172
+ return 'millisecond'
173
+ }
174
+ if (this.second !== undefined) {
175
+ return 'second'
176
+ }
177
+ if (this.minute !== undefined) {
178
+ return 'minute'
179
+ }
180
+ if (this.hour !== undefined) {
181
+ return 'hour'
182
+ }
183
+ if (this.day !== undefined) {
184
+ return 'day'
185
+ }
186
+ if (this.month !== undefined) {
187
+ return 'month'
188
+ }
189
+ return 'year'
190
+ }
191
+
192
+ private withKind(kind: TemporalKind): Temporal {
193
+ return new Temporal({
194
+ kind,
195
+ year: this.year,
196
+ month: this.month,
197
+ day: this.day,
198
+ hour: this.hour,
199
+ minute: this.minute,
200
+ second: this.second,
201
+ fraction: this.fraction,
202
+ timezoneOffsetMinutes: this.timezoneOffsetMinutes,
203
+ })
204
+ }
205
+
206
+ toString(): string {
207
+ const parts: string[] = []
208
+ if (this.year !== undefined) {
209
+ parts.push(pad4(this.year))
210
+ if (this.month !== undefined) {
211
+ parts.push(`-${pad2(this.month)}`)
212
+ if (this.day !== undefined) {
213
+ parts.push(`-${pad2(this.day)}`)
214
+ }
215
+ }
216
+ }
217
+ if (this.kind === 'dateTime' && this.hour !== undefined) {
218
+ parts.push('T')
219
+ }
220
+ if (this.hour !== undefined) {
221
+ parts.push(pad2(this.hour))
222
+ if (this.minute !== undefined) {
223
+ parts.push(`:${pad2(this.minute)}`)
224
+ if (this.second !== undefined) {
225
+ parts.push(`:${pad2(this.second)}`)
226
+ if (this.fraction !== undefined) {
227
+ parts.push(`.${this.fraction}`)
228
+ }
229
+ }
230
+ }
231
+ if (this.timezoneOffsetMinutes !== undefined) {
232
+ parts.push(formatTimezone(this.timezoneOffsetMinutes))
233
+ }
234
+ }
235
+ return parts.join('')
236
+ }
237
+
238
+ /** Literal form including the `@` (and `@T` for times); used when printing values. */
239
+ toLiteralString(): string {
240
+ return this.kind === 'time' ? `@T${this.toString()}` : `@${this.toString()}`
241
+ }
242
+ }
243
+
244
+ function optionalNumber(text: string | undefined): number | undefined {
245
+ return text === undefined ? undefined : Number(text)
246
+ }
247
+
248
+ /**
249
+ * Offset added to a FHIRPath year before handing it to `Date.UTC`, which maps
250
+ * years 0–99 to 1900–1999. Shifting by a whole multiple of 400 preserves the
251
+ * proleptic Gregorian leap rule exactly while moving small years out of that
252
+ * remapped range, so date math on years 1–99 stays correct.
253
+ */
254
+ export const GREGORIAN_UTC_YEAR_OFFSET = 400
255
+
256
+ /** Days in a month (1-based), correct across the full FHIRPath year range. */
257
+ export function daysInMonth(year: number, month: number): number {
258
+ return new Date(Date.UTC(year + GREGORIAN_UTC_YEAR_OFFSET, month, 0)).getUTCDate()
259
+ }
260
+
261
+ function inRange(value: number | undefined, min: number, max: number): boolean {
262
+ return value === undefined || (value >= min && value <= max)
263
+ }
264
+
265
+ function parseTimezone(text: string): number {
266
+ if (text === 'Z') {
267
+ return 0
268
+ }
269
+ const sign = text.startsWith('-') ? -1 : 1
270
+ const hours = Number(text.slice(1, 3))
271
+ const minutes = Number(text.slice(4, 6))
272
+ return sign * (hours * 60 + minutes)
273
+ }
274
+
275
+ function formatTimezone(offsetMinutes: number): string {
276
+ if (offsetMinutes === 0) {
277
+ return 'Z'
278
+ }
279
+ const sign = offsetMinutes < 0 ? '-' : '+'
280
+ const absolute = Math.abs(offsetMinutes)
281
+ return `${sign}${pad2(Math.floor(absolute / 60))}:${pad2(absolute % 60)}`
282
+ }
283
+
284
+ const pad2 = (value: number): string => String(value).padStart(2, '0')
285
+ const pad4 = (value: number): string => String(value).padStart(4, '0')
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Exact decimal arithmetic on a BigInt mantissa with an explicit scale (count of
3
+ * fraction digits). The scale doubles as the value's precision: `1.10` keeps scale 2,
4
+ * which `precision()` and the boundary functions rely on. No floats are involved in
5
+ * `+ - * / div mod`, comparison, or rounding, so `0.1 + 0.2 = 0.3` holds exactly.
6
+ */
7
+ export class Decimal {
8
+ /** Scaled integer including the sign: `-1.25` is digits `-125n`, scale 2. */
9
+ readonly digits: bigint
10
+ /** Number of fraction digits; never negative. */
11
+ readonly scale: number
12
+
13
+ constructor(digits: bigint, scale: number) {
14
+ this.digits = digits
15
+ this.scale = scale
16
+ }
17
+
18
+ /** Parse a decimal string. Accepts exponent notation for values arriving from JSON numbers. */
19
+ static fromString(text: string): Decimal | undefined {
20
+ const match = /^([+-]?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/.exec(text.trim())
21
+ if (!match) {
22
+ return undefined
23
+ }
24
+ const [, sign, whole, fraction = '', exponentText] = match
25
+ let digits = BigInt(`${whole}${fraction}`)
26
+ if (sign === '-') {
27
+ digits = -digits
28
+ }
29
+ let scale = fraction.length
30
+ const exponent = exponentText === undefined ? 0 : Number.parseInt(exponentText, 10)
31
+ // JS numbers never stringify past e±308; a larger exponent can only come from
32
+ // hostile input and would allocate a giant BigInt.
33
+ if (Math.abs(exponent) > 400) {
34
+ return undefined
35
+ }
36
+ if (exponent > 0) {
37
+ const shift = Math.min(exponent, scale)
38
+ scale -= shift
39
+ digits *= 10n ** BigInt(exponent - shift)
40
+ } else if (exponent < 0) {
41
+ scale -= exponent
42
+ }
43
+ return new Decimal(digits, scale)
44
+ }
45
+
46
+ static fromNumber(value: number): Decimal | undefined {
47
+ if (!Number.isFinite(value)) {
48
+ return undefined
49
+ }
50
+ return Decimal.fromString(String(value))
51
+ }
52
+
53
+ static zero(): Decimal {
54
+ return new Decimal(0n, 0)
55
+ }
56
+
57
+ isZero(): boolean {
58
+ return this.digits === 0n
59
+ }
60
+
61
+ isNegative(): boolean {
62
+ return this.digits < 0n
63
+ }
64
+
65
+ /** True when the value has no fraction part (trailing fraction zeros count as none). */
66
+ isInteger(): boolean {
67
+ return this.digits % 10n ** BigInt(this.scale) === 0n
68
+ }
69
+
70
+ negate(): Decimal {
71
+ return new Decimal(-this.digits, this.scale)
72
+ }
73
+
74
+ abs(): Decimal {
75
+ return this.digits < 0n ? this.negate() : this
76
+ }
77
+
78
+ add(other: Decimal): Decimal {
79
+ const [a, b, scale] = alignScales(this, other)
80
+ return new Decimal(a + b, scale)
81
+ }
82
+
83
+ subtract(other: Decimal): Decimal {
84
+ const [a, b, scale] = alignScales(this, other)
85
+ return new Decimal(a - b, scale)
86
+ }
87
+
88
+ multiply(other: Decimal): Decimal {
89
+ return new Decimal(this.digits * other.digits, this.scale + other.scale)
90
+ }
91
+
92
+ /** Division; empty (undefined) when dividing by zero. Result is exact up to 24+ fraction digits, then rounded half away from zero and trimmed. */
93
+ divide(other: Decimal): Decimal | undefined {
94
+ if (other.digits === 0n) {
95
+ return undefined
96
+ }
97
+ const targetScale = Math.max(this.scale, other.scale, 8) + 16
98
+ const numerator = this.digits * 10n ** BigInt(targetScale + other.scale - this.scale)
99
+ return new Decimal(divideHalfAwayFromZero(numerator, other.digits), targetScale).trimTrailingZeros()
100
+ }
101
+
102
+ /** Truncated integer division (`div`); empty when dividing by zero. */
103
+ integerDivide(other: Decimal): Decimal | undefined {
104
+ if (other.digits === 0n) {
105
+ return undefined
106
+ }
107
+ const [a, b] = alignScales(this, other)
108
+ return new Decimal(a / b, 0)
109
+ }
110
+
111
+ /** Remainder (`mod`) with the sign of the dividend; empty when dividing by zero. */
112
+ modulo(other: Decimal): Decimal | undefined {
113
+ if (other.digits === 0n) {
114
+ return undefined
115
+ }
116
+ const [a, b, scale] = alignScales(this, other)
117
+ return new Decimal(a % b, scale)
118
+ }
119
+
120
+ compare(other: Decimal): -1 | 0 | 1 {
121
+ const [a, b] = alignScales(this, other)
122
+ if (a < b) {
123
+ return -1
124
+ }
125
+ return a > b ? 1 : 0
126
+ }
127
+
128
+ /** Numeric equality; trailing zeros do not matter (`0.5 = 0.50`). */
129
+ equals(other: Decimal): boolean {
130
+ return this.compare(other) === 0
131
+ }
132
+
133
+ /** Round half away from zero to `fractionDigits` fraction digits. */
134
+ round(fractionDigits = 0): Decimal {
135
+ if (fractionDigits >= this.scale) {
136
+ return this
137
+ }
138
+ const factor = 10n ** BigInt(this.scale - fractionDigits)
139
+ return new Decimal(divideHalfAwayFromZero(this.digits, factor), fractionDigits)
140
+ }
141
+
142
+ /** Drop the fraction part toward zero. */
143
+ truncate(): Decimal {
144
+ return new Decimal(this.digits / 10n ** BigInt(this.scale), 0)
145
+ }
146
+
147
+ ceiling(): Decimal {
148
+ const whole = this.truncate()
149
+ if (this.digits > 0n && !this.isInteger()) {
150
+ return new Decimal(whole.digits + 1n, 0)
151
+ }
152
+ return whole
153
+ }
154
+
155
+ floor(): Decimal {
156
+ const whole = this.truncate()
157
+ if (this.digits < 0n && !this.isInteger()) {
158
+ return new Decimal(whole.digits - 1n, 0)
159
+ }
160
+ return whole
161
+ }
162
+
163
+ /** Reduce the scale by removing trailing fraction zeros, keeping at least `minScale`. */
164
+ trimTrailingZeros(minScale = 0): Decimal {
165
+ let digits = this.digits
166
+ let scale = this.scale
167
+ while (scale > minScale && digits % 10n === 0n) {
168
+ digits /= 10n
169
+ scale -= 1
170
+ }
171
+ return scale === this.scale ? this : new Decimal(digits, scale)
172
+ }
173
+
174
+ /** Rescale to exactly `scale` fraction digits (padding with zeros or rounding). */
175
+ withScale(scale: number): Decimal {
176
+ if (scale <= this.scale) {
177
+ return this.round(scale)
178
+ }
179
+ return new Decimal(this.digits * 10n ** BigInt(scale - this.scale), scale)
180
+ }
181
+
182
+ toString(): string {
183
+ const negative = this.digits < 0n
184
+ const text = (negative ? -this.digits : this.digits).toString().padStart(this.scale + 1, '0')
185
+ const whole = text.slice(0, text.length - this.scale)
186
+ const fraction = this.scale > 0 ? `.${text.slice(text.length - this.scale)}` : ''
187
+ return `${negative ? '-' : ''}${whole}${fraction}`
188
+ }
189
+
190
+ toNumber(): number {
191
+ return Number(this.toString())
192
+ }
193
+ }
194
+
195
+ function alignScales(a: Decimal, b: Decimal): [bigint, bigint, number] {
196
+ const scale = Math.max(a.scale, b.scale)
197
+ return [a.digits * 10n ** BigInt(scale - a.scale), b.digits * 10n ** BigInt(scale - b.scale), scale]
198
+ }
199
+
200
+ function divideHalfAwayFromZero(numerator: bigint, denominator: bigint): bigint {
201
+ const quotient = numerator / denominator
202
+ const remainder = numerator % denominator
203
+ if (remainder === 0n) {
204
+ return quotient
205
+ }
206
+ const absRemainder = remainder < 0n ? -remainder : remainder
207
+ const absDenominator = denominator < 0n ? -denominator : denominator
208
+ if (absRemainder * 2n >= absDenominator) {
209
+ const negativeResult = numerator < 0n !== denominator < 0n
210
+ return quotient + (negativeResult ? -1n : 1n)
211
+ }
212
+ return quotient
213
+ }
@@ -0,0 +1,63 @@
1
+ import { Decimal } from './decimal.ts'
2
+ import { SYSTEM_DECIMAL, SYSTEM_INTEGER, SYSTEM_LONG, systemTypeOf, type TypedValue } from './typed-value.ts'
3
+
4
+ export type NumericKind = 'Integer' | 'Long' | 'Decimal'
5
+
6
+ export interface NumericOperand {
7
+ kind: NumericKind
8
+ value: Decimal
9
+ }
10
+
11
+ const INTEGER_MIN = -2147483648n
12
+ const INTEGER_MAX = 2147483647n
13
+ export const LONG_MIN = -9223372036854775808n
14
+ export const LONG_MAX = 9223372036854775807n
15
+
16
+ /** Read an Integer/Long/Decimal operand as a Decimal, remembering its kind. */
17
+ export function asNumeric(item: TypedValue): NumericOperand | undefined {
18
+ switch (systemTypeOf(item)) {
19
+ case SYSTEM_INTEGER:
20
+ return { kind: 'Integer', value: Decimal.fromString(String(item.value as number)) as Decimal }
21
+ case SYSTEM_LONG:
22
+ return { kind: 'Long', value: Decimal.fromString((item.value as bigint).toString()) as Decimal }
23
+ case SYSTEM_DECIMAL:
24
+ return { kind: 'Decimal', value: item.value as Decimal }
25
+ default:
26
+ return undefined
27
+ }
28
+ }
29
+
30
+ /** The wider of two numeric kinds: Decimal > Long > Integer (spec implicit conversion). */
31
+ export function widerKind(a: NumericKind, b: NumericKind): NumericKind {
32
+ if (a === 'Decimal' || b === 'Decimal') {
33
+ return 'Decimal'
34
+ }
35
+ if (a === 'Long' || b === 'Long') {
36
+ return 'Long'
37
+ }
38
+ return 'Integer'
39
+ }
40
+
41
+ /**
42
+ * Wrap a numeric result as the narrowest type that represents it exactly.
43
+ * Integer is 32-bit and Long is 64-bit, so their arithmetic can produce a whole
44
+ * number too large for the type. When that happens, widen to the next integer
45
+ * type that holds the value (Integer → Long → Decimal), but never below the
46
+ * operands' own kind. This keeps the value exact rather than dropping it (empty)
47
+ * or wrapping it around the way some engines do. Decimal results pass through
48
+ * unchanged.
49
+ */
50
+ export function wrapNumeric(value: Decimal, kind: NumericKind): TypedValue {
51
+ if (kind === 'Decimal') {
52
+ return { type: SYSTEM_DECIMAL, value }
53
+ }
54
+ const whole = value.trimTrailingZeros()
55
+ const big = BigInt(whole.toString())
56
+ if (kind === 'Integer' && big >= INTEGER_MIN && big <= INTEGER_MAX) {
57
+ return { type: SYSTEM_INTEGER, value: Number(big) }
58
+ }
59
+ if (big >= LONG_MIN && big <= LONG_MAX) {
60
+ return { type: SYSTEM_LONG, value: big }
61
+ }
62
+ return { type: SYSTEM_DECIMAL, value: whole }
63
+ }