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.
- package/LICENSE +201 -0
- package/README.md +391 -0
- package/THIRD-PARTY-NOTICES.md +296 -0
- package/dist/analyzer/analyze-dto.d.ts +45 -0
- package/dist/analyzer/analyze-dto.d.ts.map +1 -0
- package/dist/analyzer/analyze-dto.js +147 -0
- package/dist/analyzer/analyze-dto.js.map +1 -0
- package/dist/analyzer/analyze.d.ts +98 -0
- package/dist/analyzer/analyze.d.ts.map +1 -0
- package/dist/analyzer/analyze.js +925 -0
- package/dist/analyzer/analyze.js.map +1 -0
- package/dist/analyzer/expression-policy.d.ts +210 -0
- package/dist/analyzer/expression-policy.d.ts.map +1 -0
- package/dist/analyzer/expression-policy.js +254 -0
- package/dist/analyzer/expression-policy.js.map +1 -0
- package/dist/analyzer/index.d.ts +3 -0
- package/dist/analyzer/index.d.ts.map +1 -0
- package/dist/analyzer/index.js +6 -0
- package/dist/analyzer/index.js.map +1 -0
- package/dist/analyzer/regex-safety.d.ts +10 -0
- package/dist/analyzer/regex-safety.d.ts.map +1 -0
- package/dist/analyzer/regex-safety.js +84 -0
- package/dist/analyzer/regex-safety.js.map +1 -0
- package/dist/analyzer/signatures.d.ts +72 -0
- package/dist/analyzer/signatures.d.ts.map +1 -0
- package/dist/analyzer/signatures.js +195 -0
- package/dist/analyzer/signatures.js.map +1 -0
- package/dist/api/bundle.d.ts +29 -0
- package/dist/api/bundle.d.ts.map +1 -0
- package/dist/api/bundle.js +108 -0
- package/dist/api/bundle.js.map +1 -0
- package/dist/api/cache.d.ts +10 -0
- package/dist/api/cache.d.ts.map +1 -0
- package/dist/api/cache.js +38 -0
- package/dist/api/cache.js.map +1 -0
- package/dist/api/column-signature.d.ts +53 -0
- package/dist/api/column-signature.d.ts.map +1 -0
- package/dist/api/column-signature.js +43 -0
- package/dist/api/column-signature.js.map +1 -0
- package/dist/api/compile.d.ts +120 -0
- package/dist/api/compile.d.ts.map +1 -0
- package/dist/api/compile.js +167 -0
- package/dist/api/compile.js.map +1 -0
- package/dist/api/constraints.d.ts +48 -0
- package/dist/api/constraints.d.ts.map +1 -0
- package/dist/api/constraints.js +52 -0
- package/dist/api/constraints.js.map +1 -0
- package/dist/api/dto.d.ts +134 -0
- package/dist/api/dto.d.ts.map +1 -0
- package/dist/api/dto.js +295 -0
- package/dist/api/dto.js.map +1 -0
- package/dist/api/engine.d.ts +136 -0
- package/dist/api/engine.d.ts.map +1 -0
- package/dist/api/engine.js +183 -0
- package/dist/api/engine.js.map +1 -0
- package/dist/api/evaluate.d.ts +7 -0
- package/dist/api/evaluate.d.ts.map +1 -0
- package/dist/api/evaluate.js +10 -0
- package/dist/api/evaluate.js.map +1 -0
- package/dist/api/project.d.ts +89 -0
- package/dist/api/project.d.ts.map +1 -0
- package/dist/api/project.js +150 -0
- package/dist/api/project.js.map +1 -0
- package/dist/api/tagged.d.ts +14 -0
- package/dist/api/tagged.d.ts.map +1 -0
- package/dist/api/tagged.js +13 -0
- package/dist/api/tagged.js.map +1 -0
- package/dist/cli/dto-check.d.ts +25 -0
- package/dist/cli/dto-check.d.ts.map +1 -0
- package/dist/cli/dto-check.js +77 -0
- package/dist/cli/dto-check.js.map +1 -0
- package/dist/cli/fhirpath-check.d.ts +3 -0
- package/dist/cli/fhirpath-check.d.ts.map +1 -0
- package/dist/cli/fhirpath-check.js +149 -0
- package/dist/cli/fhirpath-check.js.map +1 -0
- package/dist/cli/ts-loader.d.mts +3 -0
- package/dist/cli/ts-loader.d.mts.map +1 -0
- package/dist/cli/ts-loader.mjs +48 -0
- package/dist/cli/ts-loader.mjs.map +1 -0
- package/dist/engine/context.d.ts +173 -0
- package/dist/engine/context.d.ts.map +1 -0
- package/dist/engine/context.js +118 -0
- package/dist/engine/context.js.map +1 -0
- package/dist/engine/evaluator.d.ts +7 -0
- package/dist/engine/evaluator.d.ts.map +1 -0
- package/dist/engine/evaluator.js +196 -0
- package/dist/engine/evaluator.js.map +1 -0
- package/dist/engine/navigation.d.ts +11 -0
- package/dist/engine/navigation.d.ts.map +1 -0
- package/dist/engine/navigation.js +102 -0
- package/dist/engine/navigation.js.map +1 -0
- package/dist/engine/operators/collections.d.ts +12 -0
- package/dist/engine/operators/collections.d.ts.map +1 -0
- package/dist/engine/operators/collections.js +31 -0
- package/dist/engine/operators/collections.js.map +1 -0
- package/dist/engine/operators/comparison.d.ts +13 -0
- package/dist/engine/operators/comparison.d.ts.map +1 -0
- package/dist/engine/operators/comparison.js +77 -0
- package/dist/engine/operators/comparison.js.map +1 -0
- package/dist/engine/operators/equality.d.ts +19 -0
- package/dist/engine/operators/equality.d.ts.map +1 -0
- package/dist/engine/operators/equality.js +203 -0
- package/dist/engine/operators/equality.js.map +1 -0
- package/dist/engine/operators/index.d.ts +18 -0
- package/dist/engine/operators/index.d.ts.map +1 -0
- package/dist/engine/operators/index.js +30 -0
- package/dist/engine/operators/index.js.map +1 -0
- package/dist/engine/operators/logic.d.ts +8 -0
- package/dist/engine/operators/logic.d.ts.map +1 -0
- package/dist/engine/operators/logic.js +38 -0
- package/dist/engine/operators/logic.js.map +1 -0
- package/dist/engine/operators/math.d.ts +13 -0
- package/dist/engine/operators/math.d.ts.map +1 -0
- package/dist/engine/operators/math.js +198 -0
- package/dist/engine/operators/math.js.map +1 -0
- package/dist/engine/operators/strings.d.ts +8 -0
- package/dist/engine/operators/strings.d.ts.map +1 -0
- package/dist/engine/operators/strings.js +22 -0
- package/dist/engine/operators/strings.js.map +1 -0
- package/dist/engine/operators/types.d.ts +3 -0
- package/dist/engine/operators/types.d.ts.map +1 -0
- package/dist/engine/operators/types.js +15 -0
- package/dist/engine/operators/types.js.map +1 -0
- package/dist/engine/type-matching.d.ts +20 -0
- package/dist/engine/type-matching.d.ts.map +1 -0
- package/dist/engine/type-matching.js +122 -0
- package/dist/engine/type-matching.js.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/eslint/index.d.ts +12 -0
- package/dist/eslint/index.d.ts.map +1 -0
- package/dist/eslint/index.js +316 -0
- package/dist/eslint/index.js.map +1 -0
- package/dist/fhir/html-checks.d.ts +7 -0
- package/dist/fhir/html-checks.d.ts.map +1 -0
- package/dist/fhir/html-checks.js +256 -0
- package/dist/fhir/html-checks.js.map +1 -0
- package/dist/fhir/model-navigation.d.ts +11 -0
- package/dist/fhir/model-navigation.d.ts.map +1 -0
- package/dist/fhir/model-navigation.js +142 -0
- package/dist/fhir/model-navigation.js.map +1 -0
- package/dist/functions/aggregate.d.ts +2 -0
- package/dist/functions/aggregate.d.ts.map +1 -0
- package/dist/functions/aggregate.js +127 -0
- package/dist/functions/aggregate.js.map +1 -0
- package/dist/functions/boundary.d.ts +2 -0
- package/dist/functions/boundary.d.ts.map +1 -0
- package/dist/functions/boundary.js +189 -0
- package/dist/functions/boundary.js.map +1 -0
- package/dist/functions/combining.d.ts +2 -0
- package/dist/functions/combining.d.ts.map +1 -0
- package/dist/functions/combining.js +13 -0
- package/dist/functions/combining.js.map +1 -0
- package/dist/functions/control.d.ts +2 -0
- package/dist/functions/control.d.ts.map +1 -0
- package/dist/functions/control.js +25 -0
- package/dist/functions/control.js.map +1 -0
- package/dist/functions/conversion.d.ts +3 -0
- package/dist/functions/conversion.d.ts.map +1 -0
- package/dist/functions/conversion.js +316 -0
- package/dist/functions/conversion.js.map +1 -0
- package/dist/functions/date-components.d.ts +2 -0
- package/dist/functions/date-components.d.ts.map +1 -0
- package/dist/functions/date-components.js +81 -0
- package/dist/functions/date-components.js.map +1 -0
- package/dist/functions/existence.d.ts +4 -0
- package/dist/functions/existence.d.ts.map +1 -0
- package/dist/functions/existence.js +97 -0
- package/dist/functions/existence.js.map +1 -0
- package/dist/functions/fhir.d.ts +2 -0
- package/dist/functions/fhir.d.ts.map +1 -0
- package/dist/functions/fhir.js +222 -0
- package/dist/functions/fhir.js.map +1 -0
- package/dist/functions/filtering.d.ts +2 -0
- package/dist/functions/filtering.d.ts.map +1 -0
- package/dist/functions/filtering.js +112 -0
- package/dist/functions/filtering.js.map +1 -0
- package/dist/functions/install.d.ts +18 -0
- package/dist/functions/install.d.ts.map +1 -0
- package/dist/functions/install.js +20 -0
- package/dist/functions/install.js.map +1 -0
- package/dist/functions/iteration.d.ts +7 -0
- package/dist/functions/iteration.d.ts.map +1 -0
- package/dist/functions/iteration.js +9 -0
- package/dist/functions/iteration.js.map +1 -0
- package/dist/functions/math.d.ts +2 -0
- package/dist/functions/math.d.ts.map +1 -0
- package/dist/functions/math.js +152 -0
- package/dist/functions/math.js.map +1 -0
- package/dist/functions/reflection.d.ts +2 -0
- package/dist/functions/reflection.d.ts.map +1 -0
- package/dist/functions/reflection.js +23 -0
- package/dist/functions/reflection.js.map +1 -0
- package/dist/functions/registry.d.ts +28 -0
- package/dist/functions/registry.d.ts.map +1 -0
- package/dist/functions/registry.js +46 -0
- package/dist/functions/registry.js.map +1 -0
- package/dist/functions/sort.d.ts +2 -0
- package/dist/functions/sort.d.ts.map +1 -0
- package/dist/functions/sort.js +48 -0
- package/dist/functions/sort.js.map +1 -0
- package/dist/functions/string.d.ts +2 -0
- package/dist/functions/string.d.ts.map +1 -0
- package/dist/functions/string.js +353 -0
- package/dist/functions/string.js.map +1 -0
- package/dist/functions/subsetting.d.ts +2 -0
- package/dist/functions/subsetting.d.ts.map +1 -0
- package/dist/functions/subsetting.js +72 -0
- package/dist/functions/subsetting.js.map +1 -0
- package/dist/functions/tree.d.ts +8 -0
- package/dist/functions/tree.d.ts.map +1 -0
- package/dist/functions/tree.js +109 -0
- package/dist/functions/tree.js.map +1 -0
- package/dist/functions/type-specifier.d.ts +8 -0
- package/dist/functions/type-specifier.d.ts.map +1 -0
- package/dist/functions/type-specifier.js +27 -0
- package/dist/functions/type-specifier.js.map +1 -0
- package/dist/functions/utility.d.ts +2 -0
- package/dist/functions/utility.d.ts.map +1 -0
- package/dist/functions/utility.js +76 -0
- package/dist/functions/utility.js.map +1 -0
- package/dist/functions/variables.d.ts +2 -0
- package/dist/functions/variables.d.ts.map +1 -0
- package/dist/functions/variables.js +35 -0
- package/dist/functions/variables.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/lexer/lexer.d.ts +4 -0
- package/dist/lexer/lexer.d.ts.map +1 -0
- package/dist/lexer/lexer.js +318 -0
- package/dist/lexer/lexer.js.map +1 -0
- package/dist/lexer/tokens.d.ts +23 -0
- package/dist/lexer/tokens.d.ts.map +1 -0
- package/dist/lexer/tokens.js +40 -0
- package/dist/lexer/tokens.js.map +1 -0
- package/dist/model/provider.d.ts +36 -0
- package/dist/model/provider.d.ts.map +1 -0
- package/dist/model/provider.js +2 -0
- package/dist/model/provider.js.map +1 -0
- package/dist/parser/ast.d.ts +104 -0
- package/dist/parser/ast.d.ts.map +1 -0
- package/dist/parser/ast.js +2 -0
- package/dist/parser/ast.js.map +1 -0
- package/dist/parser/parser.d.ts +4 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/parser/parser.js +299 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/precedence.d.ts +24 -0
- package/dist/parser/precedence.d.ts.map +1 -0
- package/dist/parser/precedence.js +52 -0
- package/dist/parser/precedence.js.map +1 -0
- package/dist/parser/printer.d.ts +4 -0
- package/dist/parser/printer.d.ts.map +1 -0
- package/dist/parser/printer.js +112 -0
- package/dist/parser/printer.js.map +1 -0
- package/dist/r4/generated/resources-data.d.ts +3 -0
- package/dist/r4/generated/resources-data.d.ts.map +1 -0
- package/dist/r4/generated/resources-data.js +28628 -0
- package/dist/r4/generated/resources-data.js.map +1 -0
- package/dist/r4/generated/type-maps.d.ts +43153 -0
- package/dist/r4/generated/type-maps.d.ts.map +1 -0
- package/dist/r4/generated/type-maps.js +4 -0
- package/dist/r4/generated/type-maps.js.map +1 -0
- package/dist/r4/generated/types-data.d.ts +3 -0
- package/dist/r4/generated/types-data.d.ts.map +1 -0
- package/dist/r4/generated/types-data.js +2205 -0
- package/dist/r4/generated/types-data.js.map +1 -0
- package/dist/r4/index.d.ts +9 -0
- package/dist/r4/index.d.ts.map +1 -0
- package/dist/r4/index.js +94 -0
- package/dist/r4/index.js.map +1 -0
- package/dist/r4/model-data.d.ts +17 -0
- package/dist/r4/model-data.d.ts.map +1 -0
- package/dist/r4/model-data.js +2 -0
- package/dist/r4/model-data.js.map +1 -0
- package/dist/sites/index.d.ts +31 -0
- package/dist/sites/index.d.ts.map +1 -0
- package/dist/sites/index.js +205 -0
- package/dist/sites/index.js.map +1 -0
- package/dist/typed/infer.d.ts +234 -0
- package/dist/typed/infer.d.ts.map +1 -0
- package/dist/typed/infer.js +85 -0
- package/dist/typed/infer.js.map +1 -0
- package/dist/typed/perf-fixture.types.d.ts +136 -0
- package/dist/typed/perf-fixture.types.d.ts.map +1 -0
- package/dist/typed/perf-fixture.types.js +2 -0
- package/dist/typed/perf-fixture.types.js.map +1 -0
- package/dist/values/collection.d.ts +24 -0
- package/dist/values/collection.d.ts.map +1 -0
- package/dist/values/collection.js +51 -0
- package/dist/values/collection.js.map +1 -0
- package/dist/values/datetime.d.ts +54 -0
- package/dist/values/datetime.d.ts.map +1 -0
- package/dist/values/datetime.js +226 -0
- package/dist/values/datetime.js.map +1 -0
- package/dist/values/decimal.d.ts +48 -0
- package/dist/values/decimal.d.ts.map +1 -0
- package/dist/values/decimal.js +188 -0
- package/dist/values/decimal.js.map +1 -0
- package/dist/values/numeric.d.ts +24 -0
- package/dist/values/numeric.d.ts.map +1 -0
- package/dist/values/numeric.js +53 -0
- package/dist/values/numeric.js.map +1 -0
- package/dist/values/quantity.d.ts +49 -0
- package/dist/values/quantity.d.ts.map +1 -0
- package/dist/values/quantity.js +297 -0
- package/dist/values/quantity.js.map +1 -0
- package/dist/values/temporal-arithmetic.d.ts +17 -0
- package/dist/values/temporal-arithmetic.d.ts.map +1 -0
- package/dist/values/temporal-arithmetic.js +183 -0
- package/dist/values/temporal-arithmetic.js.map +1 -0
- package/dist/values/temporal-compare.d.ts +9 -0
- package/dist/values/temporal-compare.d.ts.map +1 -0
- package/dist/values/temporal-compare.js +59 -0
- package/dist/values/temporal-compare.js.map +1 -0
- package/dist/values/type-compat.d.ts +47 -0
- package/dist/values/type-compat.d.ts.map +1 -0
- package/dist/values/type-compat.js +100 -0
- package/dist/values/type-compat.js.map +1 -0
- package/dist/values/typed-value.d.ts +48 -0
- package/dist/values/typed-value.d.ts.map +1 -0
- package/dist/values/typed-value.js +117 -0
- package/dist/values/typed-value.js.map +1 -0
- package/dist/values/ucum.d.ts +14 -0
- package/dist/values/ucum.d.ts.map +1 -0
- package/dist/values/ucum.js +146 -0
- package/dist/values/ucum.js.map +1 -0
- package/package.json +137 -0
- package/src/analyzer/analyze-dto.ts +195 -0
- package/src/analyzer/analyze.ts +1181 -0
- package/src/analyzer/expression-policy.ts +434 -0
- package/src/analyzer/index.ts +23 -0
- package/src/analyzer/regex-safety.ts +85 -0
- package/src/analyzer/signatures.ts +260 -0
- package/src/api/bundle.ts +132 -0
- package/src/api/cache.ts +40 -0
- package/src/api/column-signature.ts +71 -0
- package/src/api/compile.ts +308 -0
- package/src/api/constraints.ts +104 -0
- package/src/api/dto.ts +445 -0
- package/src/api/engine.ts +328 -0
- package/src/api/evaluate.ts +11 -0
- package/src/api/project.ts +255 -0
- package/src/api/tagged.ts +35 -0
- package/src/cli/dto-check.ts +94 -0
- package/src/cli/fhirpath-check.ts +168 -0
- package/src/cli/ts-loader.mjs +49 -0
- package/src/engine/context.ts +282 -0
- package/src/engine/evaluator.ts +246 -0
- package/src/engine/navigation.ts +106 -0
- package/src/engine/operators/collections.ts +34 -0
- package/src/engine/operators/comparison.ts +82 -0
- package/src/engine/operators/equality.ts +232 -0
- package/src/engine/operators/index.ts +65 -0
- package/src/engine/operators/logic.ts +53 -0
- package/src/engine/operators/math.ts +225 -0
- package/src/engine/operators/strings.ts +25 -0
- package/src/engine/operators/types.ts +16 -0
- package/src/engine/type-matching.ts +148 -0
- package/src/errors.ts +33 -0
- package/src/eslint/index.ts +388 -0
- package/src/fhir/html-checks.ts +266 -0
- package/src/fhir/model-navigation.ts +148 -0
- package/src/functions/aggregate.ts +136 -0
- package/src/functions/boundary.ts +209 -0
- package/src/functions/combining.ts +15 -0
- package/src/functions/control.ts +25 -0
- package/src/functions/conversion.ts +349 -0
- package/src/functions/date-components.ts +88 -0
- package/src/functions/existence.ts +107 -0
- package/src/functions/fhir.ts +247 -0
- package/src/functions/filtering.ts +120 -0
- package/src/functions/install.ts +19 -0
- package/src/functions/iteration.ts +21 -0
- package/src/functions/math.ts +167 -0
- package/src/functions/reflection.ts +24 -0
- package/src/functions/registry.ts +72 -0
- package/src/functions/sort.ts +68 -0
- package/src/functions/string.ts +413 -0
- package/src/functions/subsetting.ts +89 -0
- package/src/functions/tree.ts +112 -0
- package/src/functions/type-specifier.ts +29 -0
- package/src/functions/utility.ts +82 -0
- package/src/functions/variables.ts +36 -0
- package/src/index.ts +30 -0
- package/src/lexer/lexer.ts +341 -0
- package/src/lexer/tokens.ts +69 -0
- package/src/model/provider.ts +36 -0
- package/src/parser/ast.ts +164 -0
- package/src/parser/parser.ts +321 -0
- package/src/parser/precedence.ts +52 -0
- package/src/parser/printer.ts +114 -0
- package/src/r4/generated/resources-data.ts +28631 -0
- package/src/r4/generated/type-maps.ts +22145 -0
- package/src/r4/generated/types-data.ts +2208 -0
- package/src/r4/index.ts +107 -0
- package/src/r4/model-data.ts +17 -0
- package/src/sites/index.ts +285 -0
- package/src/typed/infer.ts +433 -0
- package/src/typed/perf-fixture.types.ts +143 -0
- package/src/values/collection.ts +57 -0
- package/src/values/datetime.ts +285 -0
- package/src/values/decimal.ts +213 -0
- package/src/values/numeric.ts +63 -0
- package/src/values/quantity.ts +358 -0
- package/src/values/temporal-arithmetic.ts +231 -0
- package/src/values/temporal-compare.ts +65 -0
- package/src/values/type-compat.ts +133 -0
- package/src/values/typed-value.ts +151 -0
- package/src/values/ucum.ts +181 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { ValueKind } from '../values/type-compat.ts'
|
|
2
|
+
|
|
3
|
+
interface StaticStateLike {
|
|
4
|
+
types: string[] | undefined
|
|
5
|
+
/** True: at most one item. False: may hold several. Undefined: cardinality unknown. */
|
|
6
|
+
single: boolean | undefined
|
|
7
|
+
/** Canonical resource types a Reference state may point to — resolve()'s result. */
|
|
8
|
+
targets?: string[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Cardinality of a value combined from two parts: single only when both are. */
|
|
12
|
+
export function singleAnd(a: boolean | undefined, b: boolean | undefined): boolean | undefined {
|
|
13
|
+
if (a === false || b === false) {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
return a === true && b === true ? true : undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Describes whether an argument is a lambda, condition, sort key, type name, or eager value. */
|
|
20
|
+
export type ArgSpec = 'expression' | 'condition' | 'sort-key' | 'type-name' | 'any' | ValueKind
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The argument shapes a host function can declare. Host functions always
|
|
24
|
+
* receive eagerly evaluated values (HostFunction.fn), so only the eager specs
|
|
25
|
+
* are offered — declaring a lambda spec like `expression` would make the
|
|
26
|
+
* analyzer check semantics the runtime does not implement.
|
|
27
|
+
*/
|
|
28
|
+
export type ValueArgSpec = 'any' | ValueKind
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Accepted focus kind, cardinality, and model types. DTO functions may set
|
|
32
|
+
* `types`; built-ins must not because specification functions accept many types.
|
|
33
|
+
*/
|
|
34
|
+
export interface InputSpec {
|
|
35
|
+
kind?: ValueKind
|
|
36
|
+
singleton?: boolean
|
|
37
|
+
/** Canonical or local model type names ('CodeableConcept', 'System.String'). */
|
|
38
|
+
types?: string[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The declared analyzer signature of a host-supplied function (HAPI's
|
|
43
|
+
* checkFunction): what input it accepts, how its arguments are treated, and
|
|
44
|
+
* what it returns. Result `types` use model or System names ('Patient',
|
|
45
|
+
* 'System.String'); omitting them keeps the result an unknown region.
|
|
46
|
+
*/
|
|
47
|
+
export interface CustomFunctionSignature {
|
|
48
|
+
input?: InputSpec
|
|
49
|
+
args?: ValueArgSpec[]
|
|
50
|
+
result?: { types?: string[]; single?: boolean }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface FunctionSignature {
|
|
54
|
+
input?: InputSpec
|
|
55
|
+
args?: ArgSpec[]
|
|
56
|
+
/**
|
|
57
|
+
* Result state from the input state and the analyzed argument states
|
|
58
|
+
* (undefined for `type-name` positions and missing optional arguments).
|
|
59
|
+
*/
|
|
60
|
+
result: (input: StaticStateLike, args: readonly (StaticStateLike | undefined)[]) => StaticStateLike
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const BOOLEAN = (): StaticStateLike => ({ types: ['System.Boolean'], single: true })
|
|
64
|
+
const INTEGER = (): StaticStateLike => ({ types: ['System.Integer'], single: true })
|
|
65
|
+
const STRING = (): StaticStateLike => ({ types: ['System.String'], single: true })
|
|
66
|
+
const DECIMAL = (): StaticStateLike => ({ types: ['System.Decimal'], single: true })
|
|
67
|
+
const UNKNOWN = (): StaticStateLike => ({ types: undefined, single: undefined })
|
|
68
|
+
const SAME = (input: StaticStateLike): StaticStateLike => input
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The input's candidate types and reference targets at a different cardinality
|
|
72
|
+
* — the composition every selection/projection result goes through, so target
|
|
73
|
+
* metadata survives by construction instead of by per-function special cases.
|
|
74
|
+
*/
|
|
75
|
+
export function withSingle(input: StaticStateLike, single: boolean | undefined): StaticStateLike {
|
|
76
|
+
return input.targets === undefined
|
|
77
|
+
? { types: input.types, single }
|
|
78
|
+
: { types: input.types, single, targets: input.targets }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const ITEM = (input: StaticStateLike): StaticStateLike => withSingle(input, true)
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The union of several alternative states (iif branches, coalesce arguments,
|
|
85
|
+
* merged collections): all candidate types, single only when every alternative
|
|
86
|
+
* is, and reference targets preserved when every non-empty alternative
|
|
87
|
+
* declares them (a statically empty side contributes nothing).
|
|
88
|
+
*/
|
|
89
|
+
export function unionStates(states: (StaticStateLike | undefined)[]): StaticStateLike {
|
|
90
|
+
const present = states.filter((state): state is StaticStateLike => state !== undefined)
|
|
91
|
+
const single = present.length > 0 ? present.map(state => state.single).reduce(singleAnd, true) : undefined
|
|
92
|
+
const contributing = present.filter(state => state.types === undefined || state.types.length > 0)
|
|
93
|
+
const targets =
|
|
94
|
+
contributing.length > 0 && contributing.every(state => state.targets !== undefined)
|
|
95
|
+
? [...new Set(contributing.flatMap(state => state.targets as string[]))]
|
|
96
|
+
: undefined
|
|
97
|
+
const types =
|
|
98
|
+
present.length === 0 || present.some(state => state.types === undefined)
|
|
99
|
+
? undefined
|
|
100
|
+
: [...new Set(present.flatMap(state => state.types as string[]))]
|
|
101
|
+
return targets === undefined ? { types, single } : { types, single, targets }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const STRING_FN: FunctionSignature = { input: { kind: 'String', singleton: true }, args: ['String'], result: STRING }
|
|
105
|
+
const MATH_FN: FunctionSignature = { input: { kind: 'Numeric', singleton: true }, result: DECIMAL }
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* What the analyzer knows about each function. Functions missing here still get
|
|
109
|
+
* arity checks from the runtime registry; their results become unknown.
|
|
110
|
+
*/
|
|
111
|
+
export const FUNCTION_SIGNATURES: Readonly<Record<string, FunctionSignature>> = {
|
|
112
|
+
empty: { result: BOOLEAN },
|
|
113
|
+
exists: { args: ['expression'], result: BOOLEAN },
|
|
114
|
+
all: { args: ['expression'], result: BOOLEAN },
|
|
115
|
+
allTrue: { input: { kind: 'Boolean' }, result: BOOLEAN },
|
|
116
|
+
anyTrue: { input: { kind: 'Boolean' }, result: BOOLEAN },
|
|
117
|
+
allFalse: { input: { kind: 'Boolean' }, result: BOOLEAN },
|
|
118
|
+
anyFalse: { input: { kind: 'Boolean' }, result: BOOLEAN },
|
|
119
|
+
count: { result: INTEGER },
|
|
120
|
+
distinct: { result: SAME },
|
|
121
|
+
isDistinct: { result: BOOLEAN },
|
|
122
|
+
subsetOf: { args: ['any'], result: BOOLEAN },
|
|
123
|
+
supersetOf: { args: ['any'], result: BOOLEAN },
|
|
124
|
+
// Filters cannot grow their input, so cardinality is preserved: filtering a
|
|
125
|
+
// single item yields at most one item.
|
|
126
|
+
where: { args: ['expression'], result: SAME },
|
|
127
|
+
select: {
|
|
128
|
+
args: ['expression'],
|
|
129
|
+
// The projection's analyzed state, collection-ized: single only when both
|
|
130
|
+
// the input and the projection body are single.
|
|
131
|
+
result: (input, args) => withSingle(args[0] ?? UNKNOWN(), singleAnd(input.single, args[0]?.single)),
|
|
132
|
+
},
|
|
133
|
+
repeat: { args: ['expression'], result: UNKNOWN },
|
|
134
|
+
// ofType/as results narrow to the named type; the analyzer computes that with
|
|
135
|
+
// the model (walkCall), so their table results are never consulted.
|
|
136
|
+
ofType: { args: ['type-name'], result: UNKNOWN },
|
|
137
|
+
is: { input: { singleton: true }, args: ['type-name'], result: BOOLEAN },
|
|
138
|
+
as: { input: { singleton: true }, args: ['type-name'], result: UNKNOWN },
|
|
139
|
+
single: { result: ITEM },
|
|
140
|
+
first: { result: ITEM },
|
|
141
|
+
last: { result: ITEM },
|
|
142
|
+
tail: { result: SAME },
|
|
143
|
+
skip: { args: ['Numeric'], result: SAME },
|
|
144
|
+
take: { args: ['Numeric'], result: SAME },
|
|
145
|
+
intersect: { args: ['any'], result: SAME },
|
|
146
|
+
exclude: { args: ['any'], result: SAME },
|
|
147
|
+
union: { args: ['any'], result: (input, args) => withSingle(unionStates([input, args[0]]), false) },
|
|
148
|
+
combine: { args: ['any'], result: (input, args) => withSingle(unionStates([input, args[0]]), false) },
|
|
149
|
+
iif: {
|
|
150
|
+
args: ['condition', 'expression', 'expression'],
|
|
151
|
+
// The union of the branch states; a missing else-branch contributes empty.
|
|
152
|
+
result: (_input, args) => unionStates([args[1], args[2] ?? { types: [], single: true }]),
|
|
153
|
+
},
|
|
154
|
+
// not() takes anything a Boolean test accepts (0/1, single items), so no kind pin.
|
|
155
|
+
not: { input: { singleton: true }, result: BOOLEAN },
|
|
156
|
+
trace: { args: ['String', 'expression'], result: SAME },
|
|
157
|
+
children: { result: UNKNOWN },
|
|
158
|
+
descendants: { result: UNKNOWN },
|
|
159
|
+
// A reference resolves to its declared target types (Reference.targetProfile,
|
|
160
|
+
// HAPI's TypeDetails.targets); an unconstrained reference stays unknown.
|
|
161
|
+
resolve: { result: input => ({ types: input.targets, single: input.single }) },
|
|
162
|
+
extension: { args: ['String'], result: UNKNOWN },
|
|
163
|
+
hasValue: { input: { singleton: true }, result: BOOLEAN },
|
|
164
|
+
getValue: { input: { singleton: true }, result: UNKNOWN },
|
|
165
|
+
htmlChecks: { input: { singleton: true }, result: BOOLEAN },
|
|
166
|
+
comparable: { input: { kind: 'Quantity', singleton: true }, args: ['Quantity'], result: BOOLEAN },
|
|
167
|
+
conformsTo: { input: { singleton: true }, args: ['String'], result: BOOLEAN },
|
|
168
|
+
|
|
169
|
+
length: { input: { kind: 'String', singleton: true }, result: INTEGER },
|
|
170
|
+
indexOf: { ...STRING_FN, result: INTEGER },
|
|
171
|
+
lastIndexOf: { ...STRING_FN, result: INTEGER },
|
|
172
|
+
substring: { input: { kind: 'String', singleton: true }, args: ['Numeric', 'Numeric'], result: STRING },
|
|
173
|
+
startsWith: { ...STRING_FN, result: BOOLEAN },
|
|
174
|
+
endsWith: { ...STRING_FN, result: BOOLEAN },
|
|
175
|
+
contains: { ...STRING_FN, result: BOOLEAN },
|
|
176
|
+
upper: { input: { kind: 'String', singleton: true }, result: STRING },
|
|
177
|
+
lower: { input: { kind: 'String', singleton: true }, result: STRING },
|
|
178
|
+
replace: { input: { kind: 'String', singleton: true }, args: ['String', 'String'], result: STRING },
|
|
179
|
+
matches: { ...STRING_FN, result: BOOLEAN },
|
|
180
|
+
matchesFull: { ...STRING_FN, result: BOOLEAN },
|
|
181
|
+
replaceMatches: { input: { kind: 'String', singleton: true }, args: ['String', 'String'], result: STRING },
|
|
182
|
+
toChars: {
|
|
183
|
+
input: { kind: 'String', singleton: true },
|
|
184
|
+
result: () => ({ types: ['System.String'], single: false }),
|
|
185
|
+
},
|
|
186
|
+
trim: { input: { kind: 'String', singleton: true }, result: STRING },
|
|
187
|
+
split: {
|
|
188
|
+
input: { kind: 'String', singleton: true },
|
|
189
|
+
args: ['String'],
|
|
190
|
+
result: () => ({ types: ['System.String'], single: false }),
|
|
191
|
+
},
|
|
192
|
+
join: { input: { kind: 'String' }, args: ['String'], result: STRING },
|
|
193
|
+
encode: STRING_FN,
|
|
194
|
+
decode: STRING_FN,
|
|
195
|
+
escape: STRING_FN,
|
|
196
|
+
unescape: STRING_FN,
|
|
197
|
+
|
|
198
|
+
abs: { input: { singleton: true }, result: UNKNOWN },
|
|
199
|
+
ceiling: { input: { kind: 'Numeric', singleton: true }, result: INTEGER },
|
|
200
|
+
floor: { input: { kind: 'Numeric', singleton: true }, result: INTEGER },
|
|
201
|
+
truncate: { input: { kind: 'Numeric', singleton: true }, result: INTEGER },
|
|
202
|
+
round: { input: { kind: 'Numeric', singleton: true }, args: ['Numeric'], result: DECIMAL },
|
|
203
|
+
exp: MATH_FN,
|
|
204
|
+
ln: MATH_FN,
|
|
205
|
+
sqrt: MATH_FN,
|
|
206
|
+
log: { input: { kind: 'Numeric', singleton: true }, args: ['Numeric'], result: DECIMAL },
|
|
207
|
+
power: { input: { kind: 'Numeric', singleton: true }, args: ['Numeric'], result: UNKNOWN },
|
|
208
|
+
aggregate: { args: ['expression', 'any'], result: UNKNOWN },
|
|
209
|
+
sum: { input: { kind: 'Numeric' }, result: UNKNOWN },
|
|
210
|
+
min: { input: { kind: 'Numeric' }, result: UNKNOWN },
|
|
211
|
+
max: { input: { kind: 'Numeric' }, result: UNKNOWN },
|
|
212
|
+
avg: { input: { kind: 'Numeric' }, result: DECIMAL },
|
|
213
|
+
sort: { args: ['sort-key'], result: SAME },
|
|
214
|
+
|
|
215
|
+
toBoolean: { input: { singleton: true }, result: BOOLEAN },
|
|
216
|
+
toInteger: { input: { singleton: true }, result: INTEGER },
|
|
217
|
+
toLong: { input: { singleton: true }, result: () => ({ types: ['System.Long'], single: true }) },
|
|
218
|
+
toDecimal: { input: { singleton: true }, result: DECIMAL },
|
|
219
|
+
toString: { input: { singleton: true }, result: STRING },
|
|
220
|
+
toDate: { input: { singleton: true }, result: () => ({ types: ['System.Date'], single: true }) },
|
|
221
|
+
toDateTime: { input: { singleton: true }, result: () => ({ types: ['System.DateTime'], single: true }) },
|
|
222
|
+
toTime: { input: { singleton: true }, result: () => ({ types: ['System.Time'], single: true }) },
|
|
223
|
+
toQuantity: {
|
|
224
|
+
input: { singleton: true },
|
|
225
|
+
args: ['String'],
|
|
226
|
+
result: () => ({ types: ['System.Quantity'], single: true }),
|
|
227
|
+
},
|
|
228
|
+
convertsToBoolean: { input: { singleton: true }, result: BOOLEAN },
|
|
229
|
+
convertsToInteger: { input: { singleton: true }, result: BOOLEAN },
|
|
230
|
+
convertsToLong: { input: { singleton: true }, result: BOOLEAN },
|
|
231
|
+
convertsToDecimal: { input: { singleton: true }, result: BOOLEAN },
|
|
232
|
+
convertsToString: { input: { singleton: true }, result: BOOLEAN },
|
|
233
|
+
convertsToDate: { input: { singleton: true }, result: BOOLEAN },
|
|
234
|
+
convertsToDateTime: { input: { singleton: true }, result: BOOLEAN },
|
|
235
|
+
convertsToTime: { input: { singleton: true }, result: BOOLEAN },
|
|
236
|
+
convertsToQuantity: { input: { singleton: true }, args: ['String'], result: BOOLEAN },
|
|
237
|
+
|
|
238
|
+
now: { result: () => ({ types: ['System.DateTime'], single: true }) },
|
|
239
|
+
today: { result: () => ({ types: ['System.Date'], single: true }) },
|
|
240
|
+
timeOfDay: { result: () => ({ types: ['System.Time'], single: true }) },
|
|
241
|
+
yearOf: { input: { kind: 'Temporal', singleton: true }, result: INTEGER },
|
|
242
|
+
monthOf: { input: { kind: 'Temporal', singleton: true }, result: INTEGER },
|
|
243
|
+
dayOf: { input: { kind: 'Temporal', singleton: true }, result: INTEGER },
|
|
244
|
+
hourOf: { input: { kind: 'Temporal', singleton: true }, result: INTEGER },
|
|
245
|
+
minuteOf: { input: { kind: 'Temporal', singleton: true }, result: INTEGER },
|
|
246
|
+
secondOf: { input: { kind: 'Temporal', singleton: true }, result: INTEGER },
|
|
247
|
+
millisecondOf: { input: { kind: 'Temporal', singleton: true }, result: INTEGER },
|
|
248
|
+
timezoneOffsetOf: { input: { kind: 'Temporal', singleton: true }, result: DECIMAL },
|
|
249
|
+
dateOf: { input: { kind: 'Temporal', singleton: true }, result: () => ({ types: ['System.Date'], single: true }) },
|
|
250
|
+
timeOf: { input: { kind: 'Temporal', singleton: true }, result: () => ({ types: ['System.Time'], single: true }) },
|
|
251
|
+
lowBoundary: { input: { singleton: true }, args: ['Numeric'], result: UNKNOWN },
|
|
252
|
+
highBoundary: { input: { singleton: true }, args: ['Numeric'], result: UNKNOWN },
|
|
253
|
+
precision: { input: { singleton: true }, result: INTEGER },
|
|
254
|
+
defineVariable: { args: ['String', 'expression'], result: SAME },
|
|
255
|
+
// Variadic: the analyzer repeats the last arg spec for every position, so one
|
|
256
|
+
// 'expression' entry covers all of coalesce's arguments. The result is the
|
|
257
|
+
// first non-empty argument, hence the union of all of them.
|
|
258
|
+
coalesce: { args: ['expression'], result: (_input, args) => unionStates([...args]) },
|
|
259
|
+
type: { result: UNKNOWN },
|
|
260
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { FhirPathRuntimeError } from '../errors.ts'
|
|
2
|
+
import type { ModelProvider } from '../model/provider.ts'
|
|
3
|
+
import type { AstNode } from '../parser/ast.ts'
|
|
4
|
+
|
|
5
|
+
/** The subset of a FHIR Bundle the engine needs in order to unwrap it. */
|
|
6
|
+
export interface BundleLike {
|
|
7
|
+
resourceType: 'Bundle'
|
|
8
|
+
entry?: { resource?: unknown }[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isBundle(value: unknown): value is BundleLike {
|
|
12
|
+
return typeof value === 'object' && value !== null && (value as { resourceType?: unknown }).resourceType === 'Bundle'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** One unit of work for the per-resource engine methods (`filter`, `project`, `checkConstraints`). */
|
|
16
|
+
export interface Subject {
|
|
17
|
+
value: unknown
|
|
18
|
+
/** Position in the input array or in `Bundle.entry`; absent for a single resource. */
|
|
19
|
+
index?: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The per-resource view of an engine input: array items, a Bundle's entry
|
|
24
|
+
* resources (original entry positions kept, entries without a resource
|
|
25
|
+
* skipped), or the value itself.
|
|
26
|
+
*/
|
|
27
|
+
export function toSubjects(input: unknown): Subject[] {
|
|
28
|
+
if (Array.isArray(input)) {
|
|
29
|
+
return input.map((value, index) => ({ value, index }))
|
|
30
|
+
}
|
|
31
|
+
if (isBundle(input)) {
|
|
32
|
+
return (Array.isArray(input.entry) ? input.entry : [])
|
|
33
|
+
.map((entry, index) => ({ value: entry.resource, index }))
|
|
34
|
+
.filter(subject => subject.value != null)
|
|
35
|
+
}
|
|
36
|
+
return [{ value: input }]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const rootIdentifiersCache = new WeakMap<AstNode, ReadonlySet<string>>()
|
|
40
|
+
|
|
41
|
+
/** Identifiers in root (path-head) position — the names that resolve against the input. */
|
|
42
|
+
function rootIdentifiers(ast: AstNode): ReadonlySet<string> {
|
|
43
|
+
let heads = rootIdentifiersCache.get(ast)
|
|
44
|
+
if (!heads) {
|
|
45
|
+
const collected = new Set<string>()
|
|
46
|
+
collectRootIdentifiers(ast, collected)
|
|
47
|
+
rootIdentifiersCache.set(ast, collected)
|
|
48
|
+
heads = collected
|
|
49
|
+
}
|
|
50
|
+
return heads
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function collectRootIdentifiers(node: AstNode, heads: Set<string>): void {
|
|
54
|
+
switch (node.kind) {
|
|
55
|
+
case 'identifier':
|
|
56
|
+
heads.add(node.name)
|
|
57
|
+
return
|
|
58
|
+
case 'dot':
|
|
59
|
+
// Only the head of a dotted path is a root; the right side is an element name,
|
|
60
|
+
// and arguments of dotted-in functions resolve against $this, not the input.
|
|
61
|
+
collectRootIdentifiers(node.left, heads)
|
|
62
|
+
return
|
|
63
|
+
case 'indexer':
|
|
64
|
+
collectRootIdentifiers(node.target, heads)
|
|
65
|
+
collectRootIdentifiers(node.index, heads)
|
|
66
|
+
return
|
|
67
|
+
case 'call':
|
|
68
|
+
for (const arg of node.args) {
|
|
69
|
+
collectRootIdentifiers(arg, heads)
|
|
70
|
+
}
|
|
71
|
+
return
|
|
72
|
+
case 'unary':
|
|
73
|
+
collectRootIdentifiers(node.operand, heads)
|
|
74
|
+
return
|
|
75
|
+
case 'binary':
|
|
76
|
+
collectRootIdentifiers(node.left, heads)
|
|
77
|
+
collectRootIdentifiers(node.right, heads)
|
|
78
|
+
return
|
|
79
|
+
case 'typeOp':
|
|
80
|
+
collectRootIdentifiers(node.operand, heads)
|
|
81
|
+
return
|
|
82
|
+
default:
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Bundle element names (R4/R5 plus the Resource base), the fallback when no model is bound. */
|
|
88
|
+
const BUNDLE_ELEMENTS = new Set([
|
|
89
|
+
'id',
|
|
90
|
+
'meta',
|
|
91
|
+
'implicitRules',
|
|
92
|
+
'language',
|
|
93
|
+
'identifier',
|
|
94
|
+
'type',
|
|
95
|
+
'timestamp',
|
|
96
|
+
'total',
|
|
97
|
+
'link',
|
|
98
|
+
'entry',
|
|
99
|
+
'signature',
|
|
100
|
+
'issues',
|
|
101
|
+
])
|
|
102
|
+
|
|
103
|
+
function isBundleElement(name: string, model: ModelProvider | undefined): boolean {
|
|
104
|
+
const bundleType = model?.resolveType('Bundle')
|
|
105
|
+
if (model && bundleType !== undefined) {
|
|
106
|
+
return model.getElement(bundleType, name) !== undefined
|
|
107
|
+
}
|
|
108
|
+
return BUNDLE_ELEMENTS.has(name)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Bundle in, entry resources out — unless the expression addresses the Bundle
|
|
113
|
+
* itself (a `Bundle` root). An expression whose root is a bare Bundle element
|
|
114
|
+
* (`entry.count()`, `type`) could mean either and throws instead of guessing.
|
|
115
|
+
*/
|
|
116
|
+
export function normalizeInput(input: unknown, ast: AstNode, model: ModelProvider | undefined): unknown {
|
|
117
|
+
if (!isBundle(input)) {
|
|
118
|
+
return input
|
|
119
|
+
}
|
|
120
|
+
const heads = rootIdentifiers(ast)
|
|
121
|
+
if (heads.has('Bundle')) {
|
|
122
|
+
return input
|
|
123
|
+
}
|
|
124
|
+
for (const head of heads) {
|
|
125
|
+
if (isBundleElement(head, model)) {
|
|
126
|
+
throw new FhirPathRuntimeError(
|
|
127
|
+
`Ambiguous expression for a Bundle input: '${head}' is a Bundle element, but a bare Bundle evaluates against its entry resources. Start the expression at Bundle to address the bundle, or wrap the input in an array to treat it as one resource.`
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return toSubjects(input).map(subject => subject.value)
|
|
132
|
+
}
|
package/src/api/cache.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Small LRU built on Map insertion order; get() refreshes recency. */
|
|
2
|
+
export class LruCache<V> {
|
|
3
|
+
private readonly capacity: number
|
|
4
|
+
private readonly entries = new Map<string, V>()
|
|
5
|
+
|
|
6
|
+
constructor(capacity: number) {
|
|
7
|
+
if (!Number.isInteger(capacity) || capacity < 0) {
|
|
8
|
+
throw new RangeError(`LruCache capacity must be a non-negative integer, got ${capacity}`)
|
|
9
|
+
}
|
|
10
|
+
this.capacity = capacity
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get(key: string): V | undefined {
|
|
14
|
+
const value = this.entries.get(key)
|
|
15
|
+
if (value !== undefined) {
|
|
16
|
+
this.entries.delete(key)
|
|
17
|
+
this.entries.set(key, value)
|
|
18
|
+
}
|
|
19
|
+
return value
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set(key: string, value: V): void {
|
|
23
|
+
if (this.capacity === 0) {
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
if (this.entries.has(key)) {
|
|
27
|
+
this.entries.delete(key)
|
|
28
|
+
} else if (this.entries.size >= this.capacity) {
|
|
29
|
+
const oldest = this.entries.keys().next().value
|
|
30
|
+
if (oldest !== undefined) {
|
|
31
|
+
this.entries.delete(oldest)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
this.entries.set(key, value)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get size(): number {
|
|
38
|
+
return this.entries.size
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared column signature rules for DTO registration, source walkers, and
|
|
3
|
+
* `analyzeDto`. This file accepts simple option shapes so it has no runtime or
|
|
4
|
+
* AST dependency.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** The column options this rule reads; `ColumnOptions` and an AST-derived record both satisfy it. */
|
|
8
|
+
export interface ColumnTypeClaim {
|
|
9
|
+
/** The declared FHIR type (`ColumnOptions.type`). */
|
|
10
|
+
type?: string | undefined
|
|
11
|
+
/** Present when the column narrows to an `enum` of codes. */
|
|
12
|
+
enum?: unknown
|
|
13
|
+
/** Present when `as` reshapes values outside FHIRPath. */
|
|
14
|
+
as?: unknown
|
|
15
|
+
/** Present when `choices` decodes values outside FHIRPath. */
|
|
16
|
+
choices?: unknown
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The type a column's values carry, or undefined when it claims nothing:
|
|
21
|
+
* `as`/`choices` hand values to JavaScript, so the expression's own result is all
|
|
22
|
+
* that can be said about them, while an `enum` only narrows codes that are
|
|
23
|
+
* Strings either way.
|
|
24
|
+
*/
|
|
25
|
+
export function columnResultType(column: ColumnTypeClaim): string | undefined {
|
|
26
|
+
if (column.as !== undefined || column.choices !== undefined) {
|
|
27
|
+
return undefined
|
|
28
|
+
}
|
|
29
|
+
return column.type ?? (column.enum !== undefined ? 'System.String' : undefined)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* What a registered column declares as a function. `input` is the type the
|
|
34
|
+
* column was written for, which is the DTO class's `fhirType`. `result` is what
|
|
35
|
+
* its expression returns. A column may declare either, both, or neither. The
|
|
36
|
+
* shape is assignable to `CustomFunctionSignature`.
|
|
37
|
+
*/
|
|
38
|
+
export interface ColumnFunctionSignature {
|
|
39
|
+
input?: { types: string[] }
|
|
40
|
+
result?: { types: string[]; single: boolean }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns the function signature contributed by a column. `hostType` is the DTO
|
|
45
|
+
* type. A source walker may omit it when the class root is not visible.
|
|
46
|
+
*/
|
|
47
|
+
export function columnSignature(
|
|
48
|
+
column: ColumnTypeClaim & { collection?: boolean },
|
|
49
|
+
hostType?: string
|
|
50
|
+
): ColumnFunctionSignature | undefined {
|
|
51
|
+
const type = columnResultType(column)
|
|
52
|
+
const input = hostType === undefined ? undefined : { types: [hostType] }
|
|
53
|
+
const result = type === undefined ? undefined : { types: [type], single: column.collection !== true }
|
|
54
|
+
if (input === undefined && result === undefined) {
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
return { ...(input !== undefined && { input }), ...(result !== undefined && { result }) }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The signature a `@criteria` contributes. Its expression goes through the
|
|
62
|
+
* criteria rule (see `criteriaBoolean`), so the function returns a single
|
|
63
|
+
* Boolean whatever the expression returns. This is a function rather than a
|
|
64
|
+
* shared constant, so one caller cannot change what the next caller reads.
|
|
65
|
+
*/
|
|
66
|
+
export function criteriaSignature(hostType?: string): ColumnFunctionSignature {
|
|
67
|
+
return {
|
|
68
|
+
...(hostType !== undefined && { input: { types: [hostType] } }),
|
|
69
|
+
result: { types: ['System.Boolean'], single: true },
|
|
70
|
+
}
|
|
71
|
+
}
|