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,24 @@
1
+ import { type TypedValue, typeLocalName } from '../values/typed-value.ts'
2
+ import { registerFunction } from './registry.ts'
3
+
4
+ /**
5
+ * type() reflection (spec §10.2, STU). Returns one TypeInfo per item; the official
6
+ * tests only inspect `namespace` and `name`, so ClassInfo element lists are not
7
+ * materialized.
8
+ */
9
+ registerFunction('type', {
10
+ minArity: 0,
11
+ maxArity: 0,
12
+ evaluate: (context, input) => input.map(item => typeInfoOf(item, context.model?.namespace)),
13
+ })
14
+
15
+ function typeInfoOf(item: TypedValue, modelNamespace: string | undefined): TypedValue {
16
+ const separator = item.type.lastIndexOf('.')
17
+ const namespace = separator === -1 ? (modelNamespace ?? 'FHIR') : item.type.slice(0, separator)
18
+ const name = typeLocalName(item.type)
19
+ const isSystem = namespace === 'System'
20
+ return {
21
+ type: isSystem ? 'System.SimpleTypeInfo' : 'System.ClassInfo',
22
+ value: { namespace, name },
23
+ }
24
+ }
@@ -0,0 +1,72 @@
1
+ import type { EvaluationContext } from '../engine/context.ts'
2
+ import { FhirPathTypeError } from '../errors.ts'
3
+ import type { AstNode } from '../parser/ast.ts'
4
+ import type { TypedValue } from '../values/typed-value.ts'
5
+
6
+ /**
7
+ * A FHIRPath function. Arguments arrive as unevaluated ASTs so functions with
8
+ * expression parameters (`where`, `select`, `iif`, `aggregate`, ...) control their
9
+ * own scoping and laziness; plain-value functions evaluate them via the callback.
10
+ */
11
+ export interface FhirPathFunction {
12
+ /** Inclusive argument count range. */
13
+ minArity: number
14
+ maxArity: number
15
+ evaluate(
16
+ context: EvaluationContext,
17
+ input: TypedValue[],
18
+ args: AstNode[],
19
+ evaluateNode: (node: AstNode, context: EvaluationContext, input: TypedValue[]) => TypedValue[]
20
+ ): TypedValue[]
21
+ }
22
+
23
+ /**
24
+ * The argument AST at `index`. `lookupFunction` has already enforced the arity
25
+ * range, so within a registered `evaluate` the node exists whenever `index` is
26
+ * below the function's minArity; this replaces the unchecked `args[i] as AstNode`
27
+ * casts with one guarded accessor.
28
+ */
29
+ export function argAt(args: AstNode[], index: number): AstNode {
30
+ const node = args[index]
31
+ /* v8 ignore next 3 -- arity is validated before evaluate runs */
32
+ if (node === undefined) {
33
+ throw new FhirPathTypeError(`Missing required argument at position ${index}`)
34
+ }
35
+ return node
36
+ }
37
+
38
+ /** Function table; the per-section function modules add entries (append-only). */
39
+ export const functions = new Map<string, FhirPathFunction>()
40
+
41
+ export function registerFunction(name: string, fn: FhirPathFunction): void {
42
+ if (functions.has(name)) {
43
+ // 100+ names across 20 modules: a copy-paste collision would silently
44
+ // replace an implementation, so fail loudly at import time instead.
45
+ throw new Error(`FHIRPath function '${name}' is registered twice`)
46
+ }
47
+ functions.set(name, fn)
48
+ }
49
+
50
+ export function lookupFunction(name: string, argCount: number): FhirPathFunction {
51
+ const fn = functions.get(name)
52
+ if (!fn) {
53
+ throw new FhirPathTypeError(`Unrecognized function '${name}'`)
54
+ }
55
+ if (argCount < fn.minArity || argCount > fn.maxArity) {
56
+ throw new FhirPathTypeError(
57
+ `Function '${name}' expects ${describeArity(fn.minArity, fn.maxArity)}, got ${argCount} arguments`
58
+ )
59
+ }
60
+ return fn
61
+ }
62
+
63
+ /** Human-readable arity range, e.g. "1 argument", "2 arguments", "1 to 3 arguments". */
64
+ export function describeArity(minArity: number, maxArity: number): string {
65
+ if (minArity === maxArity) {
66
+ return minArity === 1 ? '1 argument' : `${minArity} arguments`
67
+ }
68
+ if (maxArity === Number.POSITIVE_INFINITY) {
69
+ return minArity === 1 ? 'at least 1 argument' : `at least ${minArity} arguments`
70
+ }
71
+ return `${minArity} to ${maxArity} arguments`
72
+ }
@@ -0,0 +1,68 @@
1
+ import type { EvaluationContext } from '../engine/context.ts'
2
+ import { withFrame } from '../engine/context.ts'
3
+ import { compareValues } from '../engine/operators/comparison.ts'
4
+ import type { AstNode } from '../parser/ast.ts'
5
+ import { singleton } from '../values/collection.ts'
6
+ import type { TypedValue } from '../values/typed-value.ts'
7
+ import type { NodeEvaluator } from './iteration.ts'
8
+ import { registerFunction } from './registry.ts'
9
+
10
+ interface SortKey {
11
+ expression: AstNode | undefined
12
+ descending: boolean
13
+ }
14
+
15
+ /** sort([keys...]) — ballot STU. `-key` sorts that key descending; no keys sorts by value. */
16
+ registerFunction('sort', {
17
+ minArity: 0,
18
+ // The ballot puts no limit on sort keys; 8 is a practical cap for arity checking.
19
+ maxArity: 8,
20
+ evaluate: (context, input, args, evaluateNode) => {
21
+ const keys: SortKey[] = args.length
22
+ ? args.map(node =>
23
+ node.kind === 'unary' && node.operator === '-'
24
+ ? { expression: node.operand, descending: true }
25
+ : { expression: node, descending: false }
26
+ )
27
+ : [{ expression: undefined, descending: false }]
28
+ const decorated = input.map((item, index) => ({
29
+ item,
30
+ index,
31
+ values: keys.map(key => keyValue(context, item, key.expression, evaluateNode)),
32
+ }))
33
+ decorated.sort((a, b) => {
34
+ for (let i = 0; i < keys.length; i++) {
35
+ const comparison = compareKey(a.values[i], b.values[i], (keys[i] as SortKey).descending)
36
+ if (comparison !== 0) {
37
+ return comparison
38
+ }
39
+ }
40
+ return a.index - b.index
41
+ })
42
+ return decorated.map(entry => entry.item)
43
+ },
44
+ })
45
+
46
+ function keyValue(
47
+ context: EvaluationContext,
48
+ item: TypedValue,
49
+ expression: AstNode | undefined,
50
+ evaluateNode: NodeEvaluator
51
+ ): TypedValue | undefined {
52
+ if (expression === undefined) {
53
+ return item
54
+ }
55
+ return withFrame(context, { thisValue: [item] }, frameContext =>
56
+ singleton(evaluateNode(expression, frameContext, [item]))
57
+ )
58
+ }
59
+
60
+ function compareKey(a: TypedValue | undefined, b: TypedValue | undefined, descending: boolean): number {
61
+ // Empty keys sort after present ones (ascending); descending reverses everything.
62
+ if (a === undefined || b === undefined) {
63
+ const emptiness = (a === undefined ? 1 : 0) - (b === undefined ? 1 : 0)
64
+ return descending ? -emptiness : emptiness
65
+ }
66
+ const comparison = compareValues(a, b) ?? 0
67
+ return descending ? -comparison : comparison
68
+ }
@@ -0,0 +1,413 @@
1
+ import type { EvaluationContext } from '../engine/context.ts'
2
+ import { FhirPathTypeError } from '../errors.ts'
3
+ import type { AstNode } from '../parser/ast.ts'
4
+ import { singleton, wrapBoolean } from '../values/collection.ts'
5
+ import { SYSTEM_INTEGER, SYSTEM_STRING, systemTypeOf, type TypedValue } from '../values/typed-value.ts'
6
+ import type { NodeEvaluator } from './iteration.ts'
7
+ import { argAt, type FhirPathFunction, registerFunction } from './registry.ts'
8
+
9
+ /** Singleton String input; empty stays empty, anything else is a type error (spec §5.6). */
10
+ function stringInput(name: string, input: TypedValue[]): string | undefined {
11
+ const item = singleton(input)
12
+ if (item === undefined) {
13
+ return undefined
14
+ }
15
+ if (systemTypeOf(item) !== SYSTEM_STRING) {
16
+ throw new FhirPathTypeError(`${name}() expects a String input, found ${item.type}`)
17
+ }
18
+ return item.value as string
19
+ }
20
+
21
+ function stringArgument(
22
+ name: string,
23
+ context: EvaluationContext,
24
+ input: TypedValue[],
25
+ node: AstNode,
26
+ evaluateNode: NodeEvaluator
27
+ ): string | undefined {
28
+ const item = singleton(evaluateNode(node, context, input))
29
+ if (item === undefined) {
30
+ return undefined
31
+ }
32
+ if (systemTypeOf(item) !== SYSTEM_STRING) {
33
+ throw new FhirPathTypeError(`${name}() expects a String argument, found ${item.type}`)
34
+ }
35
+ return item.value as string
36
+ }
37
+
38
+ const str = (value: string): TypedValue[] => [{ type: SYSTEM_STRING, value }]
39
+ const int = (value: number): TypedValue[] => [{ type: SYSTEM_INTEGER, value }]
40
+
41
+ /** Register a function over a singleton String input; empty input propagates. */
42
+ function stringFunction(
43
+ name: string,
44
+ arity: { min: number; max: number },
45
+ body: (value: string, args: (string | undefined)[], context: EvaluationContext) => TypedValue[]
46
+ ): void {
47
+ const fn: FhirPathFunction = {
48
+ minArity: arity.min,
49
+ maxArity: arity.max,
50
+ evaluate: (context, input, args, evaluateNode) => {
51
+ const value = stringInput(name, input)
52
+ if (value === undefined) {
53
+ return []
54
+ }
55
+ const argValues = args.map(arg => stringArgument(name, context, input, arg, evaluateNode))
56
+ return body(value, argValues, context)
57
+ },
58
+ }
59
+ registerFunction(name, fn)
60
+ }
61
+
62
+ stringFunction('indexOf', { min: 1, max: 1 }, (value, [substring]) =>
63
+ substring === undefined ? [] : int(value.indexOf(substring))
64
+ )
65
+
66
+ stringFunction('lastIndexOf', { min: 1, max: 1 }, (value, [substring]) =>
67
+ substring === undefined ? [] : int(value.lastIndexOf(substring))
68
+ )
69
+
70
+ registerFunction('substring', {
71
+ minArity: 1,
72
+ maxArity: 2,
73
+ evaluate: (context, input, args, evaluateNode) => {
74
+ const value = stringInput('substring', input)
75
+ if (value === undefined) {
76
+ return []
77
+ }
78
+ const start = singleton(evaluateNode(argAt(args, 0), context, input), SYSTEM_INTEGER)
79
+ if (start === undefined || typeof start.value !== 'number') {
80
+ return []
81
+ }
82
+ if (start.value < 0 || start.value >= value.length) {
83
+ return []
84
+ }
85
+ if (args.length === 1) {
86
+ return str(value.slice(start.value))
87
+ }
88
+ const lengthCollection = evaluateNode(argAt(args, 1), context, input)
89
+ if (lengthCollection.length === 0) {
90
+ // An empty length means "to the end", like the one-argument form.
91
+ return str(value.slice(start.value))
92
+ }
93
+ const length = singleton(lengthCollection, SYSTEM_INTEGER)
94
+ /* v8 ignore next 3 -- singleton(SYSTEM_INTEGER) only returns integer items */
95
+ if (length === undefined || typeof length.value !== 'number') {
96
+ return []
97
+ }
98
+ return str(value.slice(start.value, start.value + Math.max(0, length.value)))
99
+ },
100
+ })
101
+
102
+ stringFunction('startsWith', { min: 1, max: 1 }, (value, [prefix]) =>
103
+ prefix === undefined ? [] : wrapBoolean(value.startsWith(prefix))
104
+ )
105
+
106
+ stringFunction('endsWith', { min: 1, max: 1 }, (value, [suffix]) =>
107
+ suffix === undefined ? [] : wrapBoolean(value.endsWith(suffix))
108
+ )
109
+
110
+ stringFunction('contains', { min: 1, max: 1 }, (value, [substring]) =>
111
+ substring === undefined ? [] : wrapBoolean(value.includes(substring))
112
+ )
113
+
114
+ stringFunction('upper', { min: 0, max: 0 }, value => str(value.toUpperCase()))
115
+
116
+ stringFunction('lower', { min: 0, max: 0 }, value => str(value.toLowerCase()))
117
+
118
+ stringFunction('replace', { min: 2, max: 2 }, (value, [pattern, substitution]) => {
119
+ if (pattern === undefined || substitution === undefined) {
120
+ return []
121
+ }
122
+ if (pattern === '') {
123
+ // Spec: an empty pattern surrounds every character with the substitution.
124
+ return str(substitution + value.split('').join(substitution) + substitution)
125
+ }
126
+ return str(value.split(pattern).join(substitution))
127
+ })
128
+
129
+ function compileRegex(name: string, pattern: string, flags: string): RegExp {
130
+ try {
131
+ // Unicode mode enables \p{...} property escapes and full code-point matching.
132
+ return new RegExp(pattern, `${flags}u`)
133
+ } catch {
134
+ // Patterns with annex-B quirks (e.g. unescaped braces) only compile without it.
135
+ }
136
+ try {
137
+ return new RegExp(pattern, flags)
138
+ } catch {
139
+ throw new FhirPathTypeError(`${name}() received an invalid regular expression`)
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Compile with the host's regex engine (EvaluateOptions.regex) when supplied —
145
+ * the ReDoS protection for untrusted expressions — and the built-in RegExp
146
+ * otherwise. A custom engine's compile errors become the same
147
+ * invalid-expression type error the built-in path raises.
148
+ */
149
+ function compilePattern(
150
+ name: string,
151
+ context: EvaluationContext,
152
+ pattern: string,
153
+ flags: string
154
+ ): { test(subject: string): boolean; replace(subject: string, substitution: string): string } {
155
+ if (context.regex !== undefined) {
156
+ try {
157
+ return context.regex.compile(pattern, flags)
158
+ } catch {
159
+ throw new FhirPathTypeError(`${name}() received an invalid regular expression`)
160
+ }
161
+ }
162
+ const compiled = compileRegex(name, pattern, flags)
163
+ return {
164
+ test: subject => compiled.test(subject),
165
+ replace: (subject, substitution) => subject.replace(compiled, substitution),
166
+ }
167
+ }
168
+
169
+ stringFunction('matches', { min: 1, max: 1 }, (value, [pattern], context) =>
170
+ // Single-line mode: `.` matches line terminators (spec §5.6.9).
171
+ pattern === undefined ? [] : wrapBoolean(compilePattern('matches', context, pattern, 's').test(value))
172
+ )
173
+
174
+ stringFunction('matchesFull', { min: 1, max: 1 }, (value, [pattern], context) =>
175
+ pattern === undefined ? [] : wrapBoolean(compilePattern('matchesFull', context, `^(?:${pattern})$`, 's').test(value))
176
+ )
177
+
178
+ stringFunction('replaceMatches', { min: 2, max: 2 }, (value, [pattern, substitution], context) => {
179
+ if (pattern === undefined || substitution === undefined) {
180
+ return []
181
+ }
182
+ // An empty regex matches nothing meaningful; the input passes through unchanged.
183
+ if (pattern === '') {
184
+ return str(value)
185
+ }
186
+ return str(compilePattern('replaceMatches', context, pattern, 'gs').replace(value, substitution))
187
+ })
188
+
189
+ stringFunction('length', { min: 0, max: 0 }, value => int(value.length))
190
+
191
+ stringFunction('toChars', { min: 0, max: 0 }, value => value.split('').map(ch => ({ type: SYSTEM_STRING, value: ch })))
192
+
193
+ stringFunction('trim', { min: 0, max: 0 }, value => str(value.trim()))
194
+
195
+ stringFunction('split', { min: 1, max: 1 }, (value, [separator]) =>
196
+ separator === undefined ? [] : value.split(separator).map(part => ({ type: SYSTEM_STRING, value: part }))
197
+ )
198
+
199
+ // join() works on a collection of strings, not a singleton.
200
+ registerFunction('join', {
201
+ minArity: 0,
202
+ maxArity: 1,
203
+ evaluate: (context, input, args, evaluateNode) => {
204
+ const separator = args.length === 1 ? stringArgument('join', context, input, argAt(args, 0), evaluateNode) : ''
205
+ const parts: string[] = []
206
+ for (const item of input) {
207
+ if (systemTypeOf(item) !== SYSTEM_STRING) {
208
+ throw new FhirPathTypeError(`join() expects a collection of strings, found ${item.type}`)
209
+ }
210
+ if (item.value !== undefined) {
211
+ parts.push(item.value as string)
212
+ }
213
+ }
214
+ return str(parts.join(separator ?? ''))
215
+ },
216
+ })
217
+
218
+ const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
219
+
220
+ function encodeBase64(value: string): string {
221
+ const bytes = new TextEncoder().encode(value)
222
+ let result = ''
223
+ for (let i = 0; i < bytes.length; i += 3) {
224
+ const chunk = [bytes[i], bytes[i + 1], bytes[i + 2]]
225
+ const combined = ((chunk[0] as number) << 16) | (((chunk[1] ?? 0) as number) << 8) | ((chunk[2] ?? 0) as number)
226
+ result += BASE64_ALPHABET[(combined >> 18) & 63] as string
227
+ result += BASE64_ALPHABET[(combined >> 12) & 63] as string
228
+ result += chunk[1] === undefined ? '=' : (BASE64_ALPHABET[(combined >> 6) & 63] as string)
229
+ result += chunk[2] === undefined ? '=' : (BASE64_ALPHABET[combined & 63] as string)
230
+ }
231
+ return result
232
+ }
233
+
234
+ function decodeBase64(name: string, value: string): string {
235
+ const normalized = value.replace(/=+$/, '')
236
+ const bytes: number[] = []
237
+ let buffer = 0
238
+ let bits = 0
239
+ for (const ch of normalized) {
240
+ const index = BASE64_ALPHABET.indexOf(ch)
241
+ if (index === -1) {
242
+ throw new FhirPathTypeError(`${name}() received invalid base64 input`)
243
+ }
244
+ buffer = (buffer << 6) | index
245
+ bits += 6
246
+ if (bits >= 8) {
247
+ bits -= 8
248
+ bytes.push((buffer >> bits) & 0xff)
249
+ }
250
+ }
251
+ return new TextDecoder().decode(new Uint8Array(bytes))
252
+ }
253
+
254
+ function encodeHex(value: string): string {
255
+ const bytes = new TextEncoder().encode(value)
256
+ let result = ''
257
+ for (const byte of bytes) {
258
+ result += byte.toString(16).padStart(2, '0')
259
+ }
260
+ return result
261
+ }
262
+
263
+ function decodeHex(name: string, value: string): string {
264
+ if (value.length % 2 !== 0 || /[^0-9a-fA-F]/.test(value)) {
265
+ throw new FhirPathTypeError(`${name}() received invalid hex input`)
266
+ }
267
+ const bytes = new Uint8Array(value.length / 2)
268
+ for (let i = 0; i < bytes.length; i++) {
269
+ bytes[i] = Number.parseInt(value.slice(i * 2, i * 2 + 2), 16)
270
+ }
271
+ return new TextDecoder().decode(bytes)
272
+ }
273
+
274
+ stringFunction('encode', { min: 1, max: 1 }, (value, [format]) => {
275
+ switch (format) {
276
+ case undefined:
277
+ return []
278
+ case 'base64':
279
+ return str(encodeBase64(value))
280
+ case 'urlbase64':
281
+ return str(encodeBase64(value).replace(/\+/g, '-').replace(/\//g, '_'))
282
+ case 'hex':
283
+ return str(encodeHex(value))
284
+ default:
285
+ throw new FhirPathTypeError(`encode() does not support the format '${format}'`)
286
+ }
287
+ })
288
+
289
+ stringFunction('decode', { min: 1, max: 1 }, (value, [format]) => {
290
+ switch (format) {
291
+ case undefined:
292
+ return []
293
+ case 'base64':
294
+ return str(decodeBase64('decode', value))
295
+ case 'urlbase64':
296
+ return str(decodeBase64('decode', value.replace(/-/g, '+').replace(/_/g, '/')))
297
+ case 'hex':
298
+ return str(decodeHex('decode', value))
299
+ default:
300
+ throw new FhirPathTypeError(`decode() does not support the format '${format}'`)
301
+ }
302
+ })
303
+
304
+ const HTML_ESCAPES: Readonly<Record<string, string>> = {
305
+ '&': '&amp;',
306
+ '<': '&lt;',
307
+ '>': '&gt;',
308
+ '"': '&quot;',
309
+ "'": '&#39;',
310
+ }
311
+
312
+ function escapeJson(value: string): string {
313
+ let result = ''
314
+ for (const ch of value) {
315
+ if (ch === '"') {
316
+ result += '\\"'
317
+ } else if (ch === '\\') {
318
+ result += '\\\\'
319
+ } else if (ch === '\n') {
320
+ result += '\\n'
321
+ } else if (ch === '\r') {
322
+ result += '\\r'
323
+ } else if (ch === '\t') {
324
+ result += '\\t'
325
+ } else if (ch < ' ') {
326
+ result += `\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`
327
+ } else {
328
+ result += ch
329
+ }
330
+ }
331
+ return result
332
+ }
333
+
334
+ /** Backslash sequences decode; everything else (including quote characters) is literal content. */
335
+ function unescapeJson(value: string): string {
336
+ let result = ''
337
+ for (let i = 0; i < value.length; i++) {
338
+ const ch = value[i] as string
339
+ if (ch !== '\\') {
340
+ result += ch
341
+ continue
342
+ }
343
+ const next = value[i + 1]
344
+ i += 1
345
+ switch (next) {
346
+ case '"':
347
+ case '\\':
348
+ case '/':
349
+ result += next
350
+ break
351
+ case 'b':
352
+ result += '\b'
353
+ break
354
+ case 'f':
355
+ result += '\f'
356
+ break
357
+ case 'n':
358
+ result += '\n'
359
+ break
360
+ case 'r':
361
+ result += '\r'
362
+ break
363
+ case 't':
364
+ result += '\t'
365
+ break
366
+ case 'u': {
367
+ const hex = value.slice(i + 1, i + 5)
368
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
369
+ throw new FhirPathTypeError('unescape() received invalid JSON string content')
370
+ }
371
+ result += String.fromCharCode(Number.parseInt(hex, 16))
372
+ i += 4
373
+ break
374
+ }
375
+ default:
376
+ throw new FhirPathTypeError('unescape() received invalid JSON string content')
377
+ }
378
+ }
379
+ return result
380
+ }
381
+
382
+ stringFunction('escape', { min: 1, max: 1 }, (value, [target]) => {
383
+ switch (target) {
384
+ case undefined:
385
+ return []
386
+ case 'html':
387
+ return str(value.replace(/[&<>"']/g, ch => HTML_ESCAPES[ch] as string))
388
+ case 'json':
389
+ return str(escapeJson(value))
390
+ default:
391
+ throw new FhirPathTypeError(`escape() does not support the target '${target}'`)
392
+ }
393
+ })
394
+
395
+ stringFunction('unescape', { min: 1, max: 1 }, (value, [target]) => {
396
+ switch (target) {
397
+ case undefined:
398
+ return []
399
+ case 'html':
400
+ return str(
401
+ value
402
+ .replace(/&lt;/g, '<')
403
+ .replace(/&gt;/g, '>')
404
+ .replace(/&quot;/g, '"')
405
+ .replace(/&#39;/g, "'")
406
+ .replace(/&amp;/g, '&')
407
+ )
408
+ case 'json':
409
+ return str(unescapeJson(value))
410
+ default:
411
+ throw new FhirPathTypeError(`unescape() does not support the target '${target}'`)
412
+ }
413
+ })
@@ -0,0 +1,89 @@
1
+ import type { EvaluationContext } from '../engine/context.ts'
2
+ import { pairEquals } from '../engine/operators/equality.ts'
3
+ import { FhirPathRuntimeError, FhirPathTypeError } from '../errors.ts'
4
+ import type { AstNode } from '../parser/ast.ts'
5
+ import { singleton } from '../values/collection.ts'
6
+ import { SYSTEM_INTEGER, type TypedValue } from '../values/typed-value.ts'
7
+ import { distinctItems } from './existence.ts'
8
+ import type { NodeEvaluator } from './iteration.ts'
9
+ import { argAt, registerFunction } from './registry.ts'
10
+
11
+ registerFunction('single', {
12
+ minArity: 0,
13
+ maxArity: 0,
14
+ evaluate: (_context, input) => {
15
+ if (input.length > 1) {
16
+ throw new FhirPathRuntimeError(`single() expects at most one item, found ${input.length}`)
17
+ }
18
+ return input
19
+ },
20
+ })
21
+
22
+ registerFunction('first', {
23
+ minArity: 0,
24
+ maxArity: 0,
25
+ evaluate: (_context, input) => input.slice(0, 1),
26
+ })
27
+
28
+ registerFunction('last', {
29
+ minArity: 0,
30
+ maxArity: 0,
31
+ evaluate: (_context, input) => input.slice(-1),
32
+ })
33
+
34
+ registerFunction('tail', {
35
+ minArity: 0,
36
+ maxArity: 0,
37
+ evaluate: (_context, input) => input.slice(1),
38
+ })
39
+
40
+ function integerArgument(
41
+ name: string,
42
+ context: EvaluationContext,
43
+ input: TypedValue[],
44
+ node: AstNode,
45
+ evaluateNode: NodeEvaluator
46
+ ): number {
47
+ const value = singleton(evaluateNode(node, context, input), SYSTEM_INTEGER)
48
+ if (value === undefined || typeof value.value !== 'number') {
49
+ throw new FhirPathTypeError(`${name}() expects an integer argument`)
50
+ }
51
+ return value.value
52
+ }
53
+
54
+ registerFunction('skip', {
55
+ minArity: 1,
56
+ maxArity: 1,
57
+ evaluate: (context, input, args, evaluateNode) => {
58
+ const count = integerArgument('skip', context, input, argAt(args, 0), evaluateNode)
59
+ return count <= 0 ? input : input.slice(count)
60
+ },
61
+ })
62
+
63
+ registerFunction('take', {
64
+ minArity: 1,
65
+ maxArity: 1,
66
+ evaluate: (context, input, args, evaluateNode) => {
67
+ const count = integerArgument('take', context, input, argAt(args, 0), evaluateNode)
68
+ return count <= 0 ? [] : input.slice(0, count)
69
+ },
70
+ })
71
+
72
+ registerFunction('intersect', {
73
+ minArity: 1,
74
+ maxArity: 1,
75
+ evaluate: (context, input, args, evaluateNode) => {
76
+ const other = evaluateNode(argAt(args, 0), context, input)
77
+ return distinctItems(input).filter(item => other.some(candidate => pairEquals(item, candidate) === true))
78
+ },
79
+ })
80
+
81
+ registerFunction('exclude', {
82
+ minArity: 1,
83
+ maxArity: 1,
84
+ evaluate: (context, input, args, evaluateNode) => {
85
+ const other = evaluateNode(argAt(args, 0), context, input)
86
+ // Keeps duplicates and order, unlike intersect().
87
+ return input.filter(item => !other.some(candidate => pairEquals(item, candidate) === true))
88
+ },
89
+ })