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,434 @@
1
+ /**
2
+ * Shared expression-site rules for the TypeScript and ESTree walkers. This file
3
+ * owns call names, argument shapes, receiver checks, and site context. Walkers
4
+ * provide AST access only. It must not import TypeScript or ESLint at runtime.
5
+ */
6
+ import {
7
+ type ColumnFunctionSignature,
8
+ columnSignature,
9
+ type ColumnTypeClaim,
10
+ criteriaSignature,
11
+ } from '../api/column-signature.ts'
12
+
13
+ /** The supported expression argument shapes: one string, columns, constraints, or DTO variables. */
14
+ export type CallSiteShape = 'expression' | 'columns' | 'constraints' | 'dto-vars'
15
+
16
+ export interface CallSitePolicy {
17
+ /** Which argument holds the expression(s): 0 for expression-first calls, 1 for subject-first helpers. */
18
+ argIndex: number
19
+ shape: CallSiteShape
20
+ /**
21
+ * Required receiver evidence. `any` accepts bare or member calls unless a
22
+ * foreign import owns the name. `engine` requires a package import or local
23
+ * `FhirPathEngine`. `import` requires the called name to come from this package.
24
+ */
25
+ receiver: 'any' | 'engine' | 'import'
26
+ /**
27
+ * The argument that names the type the expression runs against, when the call
28
+ * takes one: `fhirpath('status', 'MedicationRequest')` declares it at index 1,
29
+ * `defineDto('Condition', { vars })` at index 0. A site with a declared root is
30
+ * analyzed against it, which is the only way a walker can check a relative
31
+ * expression — see `analyzeSite`.
32
+ */
33
+ rootArg?: number
34
+ /**
35
+ * The root comes from the enclosing class's `extends defineDto('Condition')`
36
+ * clause: a `@column`/`@criteria` field. A class extending anything else (a
37
+ * base class, a root-generic factory) has no statically-known fhirType, and
38
+ * the expression is analyzed without an input type.
39
+ */
40
+ rootFromClass?: true
41
+ /**
42
+ * A DTO member site. Its `%variables` are never judged — they come from the
43
+ * DTO's own `vars`/`env`, from a base class, or from the projecting call, none
44
+ * of which a source walker can see in full — and an unresolved function is
45
+ * reported only when it misspells a column of the same file. `analyzeDto`
46
+ * checks all of it properly.
47
+ */
48
+ dto?: true
49
+ /** Declares a zero-argument function named after the decorated field. */
50
+ declaresField?: 'column' | 'criteria'
51
+ }
52
+
53
+ /** Call names that take FHIRPath expressions, and where/how/on-what they take them. */
54
+ export const CALL_SITES: ReadonlyMap<string, CallSitePolicy> = new Map([
55
+ // Expression-first: the low-level API and the evaluate family of FhirPathEngine.
56
+ // `fhirpath`/`compile` take the type the expression runs against as their
57
+ // second argument, which is what makes an expression held in a `const` — and
58
+ // evaluated somewhere else entirely — checkable.
59
+ ['fhirpath', { argIndex: 0, shape: 'expression', receiver: 'any', rootArg: 1 }],
60
+ ['compile', { argIndex: 0, shape: 'expression', receiver: 'any', rootArg: 1 }],
61
+ ['evaluate', { argIndex: 0, shape: 'expression', receiver: 'any' }],
62
+ ['evaluateTyped', { argIndex: 0, shape: 'expression', receiver: 'any' }],
63
+ ['first', { argIndex: 0, shape: 'expression', receiver: 'engine' }],
64
+ ['analyzeExpression', { argIndex: 0, shape: 'expression', receiver: 'any' }],
65
+ // Subject-first FhirPathEngine helpers: the expression(s) come second.
66
+ ['test', { argIndex: 1, shape: 'expression', receiver: 'engine' }],
67
+ ['filter', { argIndex: 1, shape: 'expression', receiver: 'engine' }],
68
+ ['project', { argIndex: 1, shape: 'columns', receiver: 'engine' }],
69
+ ['checkConstraints', { argIndex: 1, shape: 'constraints', receiver: 'any' }],
70
+ // DTO declarations: the column/criteria expressions of a `@column` field, and
71
+ // the `vars` a DTO binds per row.
72
+ [
73
+ 'column',
74
+ { argIndex: 0, shape: 'expression', receiver: 'import', rootFromClass: true, dto: true, declaresField: 'column' },
75
+ ],
76
+ [
77
+ 'criteria',
78
+ { argIndex: 0, shape: 'expression', receiver: 'import', rootFromClass: true, dto: true, declaresField: 'criteria' },
79
+ ],
80
+ ['defineDto', { argIndex: 1, shape: 'dto-vars', receiver: 'import', rootArg: 0, dto: true }],
81
+ ])
82
+
83
+ /** The `defineDto` call whose first argument fixes a DTO's fhirType. */
84
+ export const DTO_BASE_NAME = 'defineDto'
85
+
86
+ /** The tag name whose no-substitution template holds a FHIRPath expression. */
87
+ export const TAG_NAME = 'fhirpath'
88
+
89
+ /** Imports from this package (any subpath) are the real FHIRPath API, never foreign. */
90
+ export const PACKAGE_PREFIX = 'fhirpath-ts'
91
+
92
+ /** The engine class name: `new FhirPathEngine(...)` locals are trusted bindings. */
93
+ export const ENGINE_CLASS_NAME = 'FhirPathEngine'
94
+
95
+ /**
96
+ * Name bindings a walker collects from a source file before extracting sites.
97
+ * Files without imports (scripts, snippets) have all sets empty: `receiver: 'any'`
98
+ * names are then always checked, `receiver: 'engine'` names only via a
99
+ * `new FhirPathEngine(...)` local.
100
+ */
101
+ export interface SourceBindings {
102
+ /**
103
+ * Local names bound by imports from modules other than this package
104
+ * (`compile` from handlebars is not a FHIRPath entry point).
105
+ */
106
+ foreign: ReadonlySet<string>
107
+ /** Package imports and locals created with `new FhirPathEngine()`. */
108
+ trusted: ReadonlySet<string>
109
+ /**
110
+ * Trusted names also declared for another purpose. Trust is file-wide, so a
111
+ * rebound name is skipped to prevent false positives in another scope.
112
+ */
113
+ rebound: ReadonlySet<string>
114
+ }
115
+
116
+ /**
117
+ * Whether a call site should be checked, given its policy and the file's
118
+ * bindings. `receiverRoot` is the leftmost identifier of a member-expression
119
+ * callee (`Handlebars` in `Handlebars.compile(...)`, `db` in
120
+ * `db.clients.first(...)`), or undefined for a bare-identifier callee or a
121
+ * non-identifier root (`this`, a call result).
122
+ */
123
+ export function isCheckedCall(
124
+ policy: CallSitePolicy,
125
+ calleeName: string,
126
+ receiverRoot: string | undefined,
127
+ bindings: SourceBindings
128
+ ): boolean {
129
+ if (policy.receiver === 'engine') {
130
+ return receiverRoot !== undefined && bindings.trusted.has(receiverRoot) && !bindings.rebound.has(receiverRoot)
131
+ }
132
+ if (policy.receiver === 'import') {
133
+ return receiverRoot === undefined && bindings.trusted.has(calleeName) && !bindings.rebound.has(calleeName)
134
+ }
135
+ return !bindings.foreign.has(receiverRoot ?? calleeName)
136
+ }
137
+
138
+ /** Checks a `fhirpath` tag unless its bare name or namespace comes from another package. */
139
+ export function isCheckedTag(receiverRoot: string | undefined, bindings: SourceBindings): boolean {
140
+ return !bindings.foreign.has(receiverRoot ?? TAG_NAME)
141
+ }
142
+
143
+ /**
144
+ * Whether `new className(...)` yields a trusted binding: any class imported from
145
+ * this package counts, and the bare `FhirPathEngine` name also counts in files
146
+ * whose imports don't claim it — so import-less snippets stay checkable.
147
+ */
148
+ export function constructsEngine(className: string, bindings: SourceBindings): boolean {
149
+ return bindings.trusted.has(className) || (className === ENGINE_CLASS_NAME && !bindings.foreign.has(className))
150
+ }
151
+
152
+ /** Which import sources count as the real FHIRPath API rather than a foreign module. */
153
+ export interface LocalModuleOptions {
154
+ /** Import-source prefixes that are the FHIRPath API. Defaults to `['fhirpath-ts']`. */
155
+ packages?: readonly string[]
156
+ /**
157
+ * Also treat relative imports (`./`, `../`) as the FHIRPath API. Off by default
158
+ * (a consumer's relative `compile` is not FHIRPath); turn it on so the package
159
+ * can dogfood the rule on its own source, which imports its API relatively.
160
+ */
161
+ localImports?: boolean
162
+ }
163
+
164
+ /**
165
+ * Whether an import specifier's module is foreign (not the FHIRPath API). By
166
+ * default only `fhirpath-ts` (any subpath) is local; `options` widens that to
167
+ * extra package prefixes and/or relative imports.
168
+ */
169
+ export function isForeignModule(moduleSpecifier: string, options: LocalModuleOptions = {}): boolean {
170
+ const packages = options.packages ?? [PACKAGE_PREFIX]
171
+ if (packages.some(prefix => moduleSpecifier.startsWith(prefix))) {
172
+ return false
173
+ }
174
+ if (options.localImports === true && moduleSpecifier.startsWith('.')) {
175
+ return false
176
+ }
177
+ return true
178
+ }
179
+
180
+ /** An extracted expression literal: the AST node (for reporting) and its text. */
181
+ export interface ExpressionEntry<N> {
182
+ node: N
183
+ expression: string
184
+ }
185
+
186
+ /**
187
+ * What a site tells `analyzeSite` about itself, beyond the expression text: the
188
+ * type the expression runs against, and whether it is a DTO member. Both walkers
189
+ * produce this shape so `analyzeSite` cannot be handed two different vocabularies.
190
+ */
191
+ export interface SiteContext {
192
+ /** The type the expression is analyzed against, when the site fixes one. */
193
+ inputType?: string
194
+ /** A DTO member site, whose findings are weighed differently (see `CallSitePolicy.dto`). */
195
+ dto?: true
196
+ }
197
+
198
+ /** One class of a file, as a walker reads its heritage clause. */
199
+ export interface ClassHeritage {
200
+ /** The class's own name, when it has one. */
201
+ name: string | undefined
202
+ /** The fhirType its own `extends defineDto('X')` clause fixes, when it has one. */
203
+ ownRoot: string | undefined
204
+ /** The name of the class it extends, when that clause is a plain identifier. */
205
+ baseName: string | undefined
206
+ }
207
+
208
+ /**
209
+ * Resolves DTO roots through base classes declared in the same file. Duplicate
210
+ * names, cycles, factories, and imported bases remain unresolved because the
211
+ * source cannot prove their root.
212
+ */
213
+ export function dtoRootsOf(classes: readonly ClassHeritage[]): ReadonlyMap<string, string> {
214
+ const byName = new Map<string, ClassHeritage>()
215
+ const ambiguous = new Set<string>()
216
+ for (const cls of classes) {
217
+ if (cls.name === undefined) {
218
+ continue
219
+ }
220
+ if (byName.has(cls.name)) {
221
+ ambiguous.add(cls.name)
222
+ }
223
+ byName.set(cls.name, cls)
224
+ }
225
+ const rootOf = (cls: ClassHeritage, seen: Set<string>): string | undefined => {
226
+ if (cls.ownRoot !== undefined) {
227
+ return cls.ownRoot
228
+ }
229
+ const base = cls.baseName
230
+ if (base === undefined || ambiguous.has(base) || seen.has(base)) {
231
+ return undefined
232
+ }
233
+ const declaration = byName.get(base)
234
+ if (declaration === undefined) {
235
+ return undefined
236
+ }
237
+ seen.add(base)
238
+ return rootOf(declaration, seen)
239
+ }
240
+ const roots = new Map<string, string>()
241
+ for (const [name, cls] of byName) {
242
+ if (ambiguous.has(name)) {
243
+ continue
244
+ }
245
+ const root = rootOf(cls, new Set([name]))
246
+ if (root !== undefined) {
247
+ roots.set(name, root)
248
+ }
249
+ }
250
+ return roots
251
+ }
252
+
253
+ /**
254
+ * The DTO root a class's `@column` fields analyze against: its own
255
+ * `extends defineDto('X')` clause, else whatever its `extends` chain settled on in
256
+ * `dtoRootsOf`. Its own clause wins outright, so a class whose *name* the file
257
+ * declares twice — dropped from the chain, since a wrong root would report valid
258
+ * code — still checks its own columns.
259
+ */
260
+ export function rootOf(heritage: ClassHeritage | undefined, dtoRoots: ReadonlyMap<string, string>): string | undefined {
261
+ if (heritage === undefined || heritage.ownRoot !== undefined) {
262
+ return heritage?.ownRoot
263
+ }
264
+ return heritage.name === undefined ? undefined : dtoRoots.get(heritage.name)
265
+ }
266
+
267
+ /**
268
+ * The context a call site's expressions carry, per its policy. The root is named
269
+ * either by one of the call's own arguments (`fhirpath(expr, 'Patient')`,
270
+ * `defineDto('Condition', …)`) or by the enclosing class's `extends defineDto(…)`
271
+ * clause, which the walker resolves and passes as `classRoot`. Mapping a policy
272
+ * to a context is a decision, so it happens here rather than once per walker —
273
+ * the two drifted while each had its own copy.
274
+ */
275
+ export function siteContext<N>(
276
+ policy: CallSitePolicy,
277
+ argumentAt: (index: number) => N | undefined,
278
+ classRoot: string | undefined,
279
+ ast: ExpressionAst<N>
280
+ ): SiteContext {
281
+ const rootArgument = policy.rootArg === undefined ? undefined : argumentAt(policy.rootArg)
282
+ const inputType =
283
+ rootArgument !== undefined
284
+ ? ast.string(rootArgument)?.expression
285
+ : policy.rootFromClass === true
286
+ ? classRoot
287
+ : undefined
288
+ return {
289
+ ...(policy.dto === true && { dto: true as const }),
290
+ ...(inputType !== undefined && { inputType }),
291
+ }
292
+ }
293
+
294
+ /**
295
+ * The AST accessors a walker provides so shape extraction can be written once.
296
+ * Each returns undefined when the node is not of the asked-for kind, which the
297
+ * extractor treats as "dynamic, skip" — so spreads, shorthands, computed values,
298
+ * and variables all fall out of the same rule in both walkers.
299
+ */
300
+ export interface ExpressionAst<N> {
301
+ /** The node as a string literal entry, or undefined when it is not one. */
302
+ string(node: N): ExpressionEntry<N> | undefined
303
+ /** The node's value as a `true`/`false` literal, or undefined when it is not one. */
304
+ boolean(node: N): boolean | undefined
305
+ /**
306
+ * The plain (non-spread, non-shorthand) properties of an object literal, or
307
+ * undefined when the node is not an object literal. `name` is the property's
308
+ * statically-known key — undefined for computed keys.
309
+ */
310
+ properties(node: N): { name: string | undefined; value: N }[] | undefined
311
+ /** The elements of an array literal, or undefined when the node is not one. */
312
+ elements(node: N): N[] | undefined
313
+ }
314
+
315
+ /**
316
+ * Extract the expression literal(s) an argument holds, per the call site's shape.
317
+ * Column keys are output names, not expressions, so a computed key does not stop
318
+ * its value from being checked; `{ path }` / `{ expression }` lookups match
319
+ * identifier and string-literal keys only.
320
+ */
321
+ export function expressionEntries<N>(argument: N, shape: CallSiteShape, ast: ExpressionAst<N>): ExpressionEntry<N>[] {
322
+ if (shape === 'expression') {
323
+ const entry = ast.string(argument)
324
+ return entry ? [entry] : []
325
+ }
326
+ if (shape === 'columns') {
327
+ // project() columns: { name: 'expr' }, { name: { path: 'expr', ... } }, or
328
+ // the boolean-criteria form { name: { test: 'expr' } }.
329
+ return (ast.properties(argument) ?? []).flatMap(({ value }) => {
330
+ const entry = ast.string(value)
331
+ return entry ? [entry] : [...namedStringEntries(value, 'path', ast), ...namedStringEntries(value, 'test', ast)]
332
+ })
333
+ }
334
+ if (shape === 'dto-vars') {
335
+ // defineDto() options: { vars: { name: 'expr' }, callerEnv: [...] }. Only
336
+ // vars hold expressions.
337
+ const vars = (ast.properties(argument) ?? []).filter(({ name }) => name === 'vars')
338
+ return vars.flatMap(({ value }) =>
339
+ (ast.properties(value) ?? []).flatMap(({ value: expression }) => {
340
+ const entry = ast.string(expression)
341
+ return entry ? [entry] : []
342
+ })
343
+ )
344
+ }
345
+ // checkConstraints() constraints: [{ key, expression: 'expr', ... }].
346
+ return (ast.elements(argument) ?? []).flatMap(element => namedStringEntries(element, 'expression', ast))
347
+ }
348
+
349
+ /** The string-literal values of an object literal's `name` properties. */
350
+ function namedStringEntries<N>(object: N, name: string, ast: ExpressionAst<N>): ExpressionEntry<N>[] {
351
+ return (ast.properties(object) ?? []).flatMap(({ name: key, value }) => {
352
+ if (key !== name) {
353
+ return []
354
+ }
355
+ const entry = ast.string(value)
356
+ return entry ? [entry] : []
357
+ })
358
+ }
359
+
360
+ /**
361
+ * What a DTO column declares as a function: every `@column` field of a
362
+ * registered DTO becomes a zero-argument expression function named by the field
363
+ * (see `withDtos`), so a walker that reads a file's columns can resolve the
364
+ * calls between them. Shaped to be assignable to the analyzer's
365
+ * `DeclaredFunction`.
366
+ */
367
+ export interface DeclaredColumnFunction {
368
+ minArity: 0
369
+ maxArity: 0
370
+ signature?: ColumnFunctionSignature
371
+ }
372
+
373
+ /** One field name of a file, with every class that declares it (see `declaredColumnOverloads`). */
374
+ export type FileColumnFunction = DeclaredColumnFunction | { overloads: readonly DeclaredColumnFunction[] }
375
+
376
+ /**
377
+ * Builds a DTO column's function declaration from literal source options and a
378
+ * visible class root. Unknown roots omit the input claim. Dynamic options omit
379
+ * the result claim.
380
+ */
381
+ export function columnFunctionDeclaration<N>(
382
+ kind: 'column' | 'criteria',
383
+ options: N | undefined,
384
+ ast: ExpressionAst<N>,
385
+ hostType: string | undefined
386
+ ): DeclaredColumnFunction {
387
+ const declaration: DeclaredColumnFunction = { minArity: 0, maxArity: 0 }
388
+ if (kind === 'criteria') {
389
+ // A criteria takes no options object. Its result is a single Boolean
390
+ // whatever the expression returns, because the rule lives on the function.
391
+ return { ...declaration, signature: criteriaSignature(hostType) }
392
+ }
393
+ const signature = columnSignature(columnClaim(options, ast), hostType)
394
+ return signature === undefined ? declaration : { ...declaration, signature }
395
+ }
396
+
397
+ /** Keeps every same-name column declaration as an overload for focus-based analysis. */
398
+ export function declaredColumnOverloads(
399
+ seen: FileColumnFunction | undefined,
400
+ next: DeclaredColumnFunction
401
+ ): FileColumnFunction {
402
+ if (seen === undefined) {
403
+ return next
404
+ }
405
+ return { overloads: [...('overloads' in seen ? seen.overloads : [seen]), next] }
406
+ }
407
+
408
+ /**
409
+ * Reads the type claim a column's options object makes. The answer is an empty
410
+ * claim whenever the source cannot say: no options at all, options that are not
411
+ * an object literal, or a `collection` set from a variable, where a guessed
412
+ * cardinality would be worse than none. All three produce no result claim,
413
+ * which leaves the input type as the only thing the declaration carries.
414
+ */
415
+ function columnClaim<N>(options: N | undefined, ast: ExpressionAst<N>): ColumnTypeClaim & { collection?: boolean } {
416
+ const properties = options === undefined ? undefined : ast.properties(options)
417
+ if (properties === undefined) {
418
+ return {}
419
+ }
420
+ const named = (name: string): N | undefined => properties.find(property => property.name === name)?.value
421
+ const collection = named('collection')
422
+ const isCollection = collection === undefined ? false : ast.boolean(collection)
423
+ if (isCollection === undefined) {
424
+ return {}
425
+ }
426
+ const declaredType = named('type')
427
+ return {
428
+ ...(declaredType !== undefined && { type: ast.string(declaredType)?.expression }),
429
+ ...(named('enum') !== undefined && { enum: true }),
430
+ ...(named('as') !== undefined && { as: true }),
431
+ ...(named('choices') !== undefined && { choices: true }),
432
+ collection: isCollection,
433
+ }
434
+ }
@@ -0,0 +1,23 @@
1
+ // The `fhirpath-ts/analyzer` entry point. Listed name by name rather than
2
+ // Re-export the public analyzer API from one entry point. Its contents should
3
+ // be a decision, not a side effect of adding an export to analyze.ts.
4
+ export {
5
+ type AnalysisDetails,
6
+ analyzeExpression,
7
+ analyzeExpressionDetailed,
8
+ type AnalyzeOptions,
9
+ type AnalyzerDiagnostic,
10
+ analyzeSite,
11
+ type DeclaredFunction,
12
+ type DeclaredVariable,
13
+ type OverloadedDeclaredFunction,
14
+ type SingleDeclaredFunction,
15
+ } from './analyze.ts'
16
+ export {
17
+ type AnalyzedContext,
18
+ type AnalyzedEngine,
19
+ analyzeDto,
20
+ type AnalyzeDtoOptions,
21
+ analyzeEngineDtos,
22
+ type DtoDiagnostic,
23
+ } from './analyze-dto.ts'
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Static ReDoS heuristic for literal regex arguments of the matches() family.
3
+ * A synchronous JS engine cannot time out a running RegExp, so catastrophic
4
+ * backtracking is flagged before it runs instead: the classic exponential
5
+ * shape is an unbounded quantifier applied to a group that itself contains an
6
+ * unbounded quantifier — `(a+)+`, `(\d*)*`, `(x|y+)*`. That is what this
7
+ * detects; overlapping-alternation cases like `(a|ab)+` are out of scope.
8
+ */
9
+ export function hasNestedUnboundedQuantifier(pattern: string): boolean {
10
+ // Each open group tracks whether an unbounded quantifier appeared inside it.
11
+ const groups: boolean[] = []
12
+ let currentHasUnbounded = false
13
+ let index = 0
14
+ while (index < pattern.length) {
15
+ const char = pattern[index] as string
16
+ if (char === '\\') {
17
+ index += 2
18
+ continue
19
+ }
20
+ if (char === '[') {
21
+ // Character classes repeat single characters; quantifiers inside are literals.
22
+ index += 1
23
+ while (index < pattern.length && pattern[index] !== ']') {
24
+ index += pattern[index] === '\\' ? 2 : 1
25
+ }
26
+ index += 1
27
+ continue
28
+ }
29
+ if (char === '(') {
30
+ groups.push(currentHasUnbounded)
31
+ currentHasUnbounded = false
32
+ index += 1
33
+ continue
34
+ }
35
+ if (char === ')') {
36
+ const groupHadUnbounded = currentHasUnbounded
37
+ currentHasUnbounded = groups.pop() ?? false
38
+ index += 1
39
+ const quantifier = readQuantifier(pattern, index)
40
+ if (quantifier !== undefined) {
41
+ if (groupHadUnbounded && quantifier.unbounded) {
42
+ return true
43
+ }
44
+ currentHasUnbounded ||= quantifier.unbounded
45
+ index = quantifier.end
46
+ continue
47
+ }
48
+ currentHasUnbounded ||= groupHadUnbounded
49
+ continue
50
+ }
51
+ const quantifier = readQuantifier(pattern, index)
52
+ if (quantifier !== undefined && index > 0) {
53
+ currentHasUnbounded ||= quantifier.unbounded
54
+ index = quantifier.end
55
+ continue
56
+ }
57
+ index += 1
58
+ }
59
+ return false
60
+ }
61
+
62
+ /** The quantifier at `index` (`*`, `+`, `{m,}`, `{m,n}`, with lazy `?`), or undefined. */
63
+ function readQuantifier(pattern: string, index: number): { unbounded: boolean; end: number } | undefined {
64
+ const char = pattern[index]
65
+ if (char === '*' || char === '+') {
66
+ return { unbounded: true, end: skipLazy(pattern, index + 1) }
67
+ }
68
+ if (char === '?') {
69
+ return { unbounded: false, end: skipLazy(pattern, index + 1) }
70
+ }
71
+ if (char === '{') {
72
+ const match = /^\{(\d+)(,(\d*)?)?\}/.exec(pattern.slice(index))
73
+ if (match === null) {
74
+ return undefined
75
+ }
76
+ // {m} and {m,n} are bounded; {m,} is unbounded.
77
+ const unbounded = match[2] !== undefined && (match[3] === undefined || match[3] === '')
78
+ return { unbounded, end: skipLazy(pattern, index + match[0].length) }
79
+ }
80
+ return undefined
81
+ }
82
+
83
+ function skipLazy(pattern: string, index: number): number {
84
+ return pattern[index] === '?' ? index + 1 : index
85
+ }