fhirpath-ts 0.0.0-bootstrap.0 → 0.2.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/README.md +50 -18
- package/dist/analyzer/analyze-dto.d.ts +4 -0
- package/dist/analyzer/analyze-dto.d.ts.map +1 -1
- package/dist/analyzer/analyze-dto.js +12 -14
- package/dist/analyzer/analyze-dto.js.map +1 -1
- package/dist/analyzer/analyze.d.ts +22 -6
- package/dist/analyzer/analyze.d.ts.map +1 -1
- package/dist/analyzer/analyze.js +242 -98
- package/dist/analyzer/analyze.js.map +1 -1
- package/dist/analyzer/declarations.d.ts +26 -0
- package/dist/analyzer/declarations.d.ts.map +1 -0
- package/dist/analyzer/declarations.js +68 -0
- package/dist/analyzer/declarations.js.map +1 -0
- package/dist/analyzer/operator-rules.d.ts +127 -0
- package/dist/analyzer/operator-rules.d.ts.map +1 -0
- package/dist/analyzer/operator-rules.js +70 -0
- package/dist/analyzer/operator-rules.js.map +1 -0
- package/dist/analyzer/signatures.d.ts +1097 -10
- package/dist/analyzer/signatures.d.ts.map +1 -1
- package/dist/analyzer/signatures.js +70 -29
- package/dist/analyzer/signatures.js.map +1 -1
- package/dist/api/compile.d.ts +32 -8
- package/dist/api/compile.d.ts.map +1 -1
- package/dist/api/compile.js +2 -0
- package/dist/api/compile.js.map +1 -1
- package/dist/api/engine.d.ts +42 -16
- package/dist/api/engine.d.ts.map +1 -1
- package/dist/api/engine.js +2 -1
- package/dist/api/engine.js.map +1 -1
- package/dist/api/project.d.ts +8 -8
- package/dist/api/project.d.ts.map +1 -1
- package/dist/api/project.js +31 -19
- package/dist/api/project.js.map +1 -1
- package/dist/api/strict.d.ts +6 -0
- package/dist/api/strict.d.ts.map +1 -0
- package/dist/api/strict.js +71 -0
- package/dist/api/strict.js.map +1 -0
- package/dist/api/tagged.d.ts +4 -4
- package/dist/api/tagged.d.ts.map +1 -1
- package/dist/api/tagged.js.map +1 -1
- package/dist/cli/dto-check.js +7 -3
- package/dist/cli/dto-check.js.map +1 -1
- package/dist/engine/context.d.ts +3 -1
- package/dist/engine/context.d.ts.map +1 -1
- package/dist/engine/context.js +5 -1
- package/dist/engine/context.js.map +1 -1
- package/dist/engine/navigation.d.ts.map +1 -1
- package/dist/engine/navigation.js +7 -48
- package/dist/engine/navigation.js.map +1 -1
- package/dist/engine/type-matching.d.ts.map +1 -1
- package/dist/engine/type-matching.js +6 -16
- package/dist/engine/type-matching.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +102 -73
- package/dist/parser/parser.js.map +1 -1
- package/dist/parser/precedence.d.ts +368 -4
- package/dist/parser/precedence.d.ts.map +1 -1
- package/dist/parser/precedence.js +146 -32
- package/dist/parser/precedence.js.map +1 -1
- package/dist/r4/generated/type-maps.d.ts +1367 -0
- package/dist/r4/generated/type-maps.d.ts.map +1 -1
- package/dist/r4/index.d.ts +3 -1
- package/dist/r4/index.d.ts.map +1 -1
- package/dist/typed/context-maps.d.ts +23 -0
- package/dist/typed/context-maps.d.ts.map +1 -0
- package/dist/typed/context-maps.js +2 -0
- package/dist/typed/context-maps.js.map +1 -0
- package/dist/typed/infer.d.ts +99 -229
- package/dist/typed/infer.d.ts.map +1 -1
- package/dist/typed/infer.js +1 -84
- package/dist/typed/infer.js.map +1 -1
- package/dist/typed/inference-limits.d.ts +7 -0
- package/dist/typed/inference-limits.d.ts.map +1 -0
- package/dist/typed/inference-limits.js +6 -0
- package/dist/typed/inference-limits.js.map +1 -0
- package/dist/typed/metadata-compact.d.ts +189 -0
- package/dist/typed/metadata-compact.d.ts.map +1 -0
- package/dist/typed/metadata-compact.js +3 -0
- package/dist/typed/metadata-compact.js.map +1 -0
- package/dist/typed/parser.d.ts +515 -0
- package/dist/typed/parser.d.ts.map +1 -0
- package/dist/typed/parser.js +2 -0
- package/dist/typed/parser.js.map +1 -0
- package/dist/values/type-compat.d.ts +8 -0
- package/dist/values/type-compat.d.ts.map +1 -1
- package/dist/values/type-compat.js +44 -1
- package/dist/values/type-compat.js.map +1 -1
- package/package.json +20 -1
- package/src/analyzer/analyze-dto.ts +21 -17
- package/src/analyzer/analyze.ts +335 -109
- package/src/analyzer/declarations.ts +120 -0
- package/src/analyzer/operator-rules.ts +88 -0
- package/src/analyzer/signatures.ts +99 -41
- package/src/api/compile.ts +66 -11
- package/src/api/engine.ts +97 -33
- package/src/api/project.ts +54 -30
- package/src/api/strict.ts +112 -0
- package/src/api/tagged.ts +5 -5
- package/src/cli/dto-check.ts +7 -3
- package/src/engine/context.ts +7 -2
- package/src/engine/navigation.ts +7 -52
- package/src/engine/type-matching.ts +11 -17
- package/src/index.ts +25 -2
- package/src/parser/parser.ts +106 -74
- package/src/parser/precedence.ts +250 -33
- package/src/r4/generated/type-maps.ts +1557 -0
- package/src/typed/context-maps.ts +41 -0
- package/src/typed/infer.ts +157 -425
- package/src/typed/inference-limits.ts +9 -0
- package/src/typed/metadata-compact.ts +307 -0
- package/src/typed/parser.ts +2277 -0
- package/src/values/type-compat.ts +48 -1
- package/dist/typed/perf-fixture.types.d.ts +0 -136
- package/dist/typed/perf-fixture.types.d.ts.map +0 -1
- package/dist/typed/perf-fixture.types.js +0 -2
- package/dist/typed/perf-fixture.types.js.map +0 -1
- package/src/typed/perf-fixture.types.ts +0 -143
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** An empty host context that contributes no known keys. */
|
|
2
|
+
export type EmptyContextMap = Record<never, never>
|
|
3
|
+
|
|
4
|
+
/** Preserve host values for best-effort type inference without exposing them as declarations. */
|
|
5
|
+
export type HostValueDeclarations<Values> =
|
|
6
|
+
Values extends Readonly<Record<PropertyKey, unknown>>
|
|
7
|
+
? { readonly [Name in keyof Values]: { readonly __value: Values[Name] } }
|
|
8
|
+
: EmptyContextMap
|
|
9
|
+
|
|
10
|
+
/** Environment names are stored without the optional leading `%`. */
|
|
11
|
+
export type BareContextName<Name extends PropertyKey> = Name extends string
|
|
12
|
+
? Name extends `%${infer Bare}`
|
|
13
|
+
? Bare
|
|
14
|
+
: Name
|
|
15
|
+
: Name
|
|
16
|
+
|
|
17
|
+
/** Normalize both accepted environment-key spellings into one type-level map. */
|
|
18
|
+
export type NormalizeContextMap<Map> =
|
|
19
|
+
Map extends Readonly<Record<PropertyKey, unknown>>
|
|
20
|
+
? { readonly [Name in keyof Map as BareContextName<Name>]: Map[Name] }
|
|
21
|
+
: EmptyContextMap
|
|
22
|
+
|
|
23
|
+
/** Merge normalized maps by name, with the overlay taking precedence. */
|
|
24
|
+
export type MergeContextMaps<Base, Overlay> = Omit<NormalizeContextMap<Base>, keyof NormalizeContextMap<Overlay>> &
|
|
25
|
+
NormalizeContextMap<Overlay>
|
|
26
|
+
|
|
27
|
+
/** Infer undeclared host values while letting explicit declarations replace them. */
|
|
28
|
+
export type InferredHostValueDeclarations<Values, Declarations> =
|
|
29
|
+
keyof NormalizeContextMap<Values> extends keyof NormalizeContextMap<Declarations>
|
|
30
|
+
? NormalizeContextMap<Declarations>
|
|
31
|
+
: MergeContextMaps<HostValueDeclarations<Values>, Declarations>
|
|
32
|
+
|
|
33
|
+
/** Read an optional context field without distributing undefined into its map. */
|
|
34
|
+
export type ContextProperty<Context, Name extends PropertyKey> = Name extends keyof Context
|
|
35
|
+
? Exclude<Context[Name], undefined>
|
|
36
|
+
: EmptyContextMap
|
|
37
|
+
|
|
38
|
+
/** Look up one normalized name, returning never when it is undeclared. */
|
|
39
|
+
export type LookupContextMap<Map, Name extends string> = Name extends keyof NormalizeContextMap<Map>
|
|
40
|
+
? NormalizeContextMap<Map>[Name]
|
|
41
|
+
: never
|
package/src/typed/infer.ts
CHANGED
|
@@ -1,433 +1,165 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* expression runs against. Lives here, with the rest of the inference vocabulary,
|
|
14
|
-
* so every layer above spells it one way.
|
|
15
|
-
*/
|
|
1
|
+
import type { R4Resources, R4TypeOf } from '../r4/generated/type-maps.ts'
|
|
2
|
+
import type {
|
|
3
|
+
BareContextName,
|
|
4
|
+
ContextProperty,
|
|
5
|
+
EmptyContextMap,
|
|
6
|
+
InferredHostValueDeclarations,
|
|
7
|
+
MergeContextMaps,
|
|
8
|
+
NormalizeContextMap,
|
|
9
|
+
} from './context-maps.ts'
|
|
10
|
+
import type { InferTypeExpression } from './parser.ts'
|
|
11
|
+
|
|
12
|
+
/** A type name known by the generated R4 model. */
|
|
16
13
|
export type FhirTypeName = keyof R4TypeOf & string
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
type
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
convertsToString: 'boolean',
|
|
158
|
-
convertsToDate: 'boolean',
|
|
159
|
-
convertsToDateTime: 'boolean',
|
|
160
|
-
convertsToTime: 'boolean',
|
|
161
|
-
convertsToQuantity: 'boolean',
|
|
162
|
-
toInteger: 'integer',
|
|
163
|
-
toDecimal: 'decimal',
|
|
164
|
-
toString: 'string',
|
|
165
|
-
toDate: 'date',
|
|
166
|
-
toDateTime: 'dateTime',
|
|
167
|
-
toTime: 'time',
|
|
168
|
-
toQuantity: 'Quantity',
|
|
169
|
-
toChars: 'string',
|
|
170
|
-
join: 'string',
|
|
171
|
-
trim: 'string',
|
|
172
|
-
upper: 'string',
|
|
173
|
-
lower: 'string',
|
|
174
|
-
replace: 'string',
|
|
175
|
-
replaceMatches: 'string',
|
|
176
|
-
substring: 'string',
|
|
177
|
-
encode: 'string',
|
|
178
|
-
decode: 'string',
|
|
179
|
-
escape: 'string',
|
|
180
|
-
unescape: 'string',
|
|
181
|
-
split: 'string',
|
|
182
|
-
matches: 'boolean',
|
|
183
|
-
matchesFull: 'boolean',
|
|
184
|
-
startsWith: 'boolean',
|
|
185
|
-
endsWith: 'boolean',
|
|
186
|
-
contains: 'boolean',
|
|
187
|
-
subsetOf: 'boolean',
|
|
188
|
-
supersetOf: 'boolean',
|
|
189
|
-
isDistinct: 'boolean',
|
|
190
|
-
allTrue: 'boolean',
|
|
191
|
-
anyTrue: 'boolean',
|
|
192
|
-
allFalse: 'boolean',
|
|
193
|
-
anyFalse: 'boolean',
|
|
194
|
-
all: 'boolean',
|
|
195
|
-
indexOf: 'integer',
|
|
196
|
-
lastIndexOf: 'integer',
|
|
197
|
-
ceiling: 'integer',
|
|
198
|
-
floor: 'integer',
|
|
199
|
-
truncate: 'integer',
|
|
200
|
-
round: 'decimal',
|
|
201
|
-
sqrt: 'decimal',
|
|
202
|
-
exp: 'decimal',
|
|
203
|
-
ln: 'decimal',
|
|
204
|
-
log: 'decimal',
|
|
205
|
-
} as const satisfies Record<string, FhirTypeName>
|
|
206
|
-
|
|
207
|
-
type FixedReturns = typeof FIXED_RETURNS
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Functions that yield a subset or reordering of their input, so the input's
|
|
211
|
-
* type carries through. Cross-checked against the analyzer the same way as
|
|
212
|
-
* FIXED_RETURNS (their signatures preserve the input's types). `abs` is
|
|
213
|
-
* excluded deliberately: it preserves the numeric kind at runtime, but the
|
|
214
|
-
* analyzer declares its result unknown, and the table must not outrun it.
|
|
215
|
-
*/
|
|
216
|
-
export const IDENTITY_RETURNS = [
|
|
217
|
-
'where',
|
|
218
|
-
'first',
|
|
219
|
-
'last',
|
|
220
|
-
'single',
|
|
221
|
-
'distinct',
|
|
222
|
-
'tail',
|
|
223
|
-
'skip',
|
|
224
|
-
'take',
|
|
225
|
-
'exclude',
|
|
226
|
-
'intersect',
|
|
227
|
-
'trace',
|
|
228
|
-
] as const
|
|
229
|
-
|
|
230
|
-
type IdentityFn = (typeof IDENTITY_RETURNS)[number]
|
|
231
|
-
|
|
232
|
-
/** One call segment, dispatched on the function name. */
|
|
233
|
-
type Call<S extends string, Fn extends string, Arg extends string> = Fn extends IdentityFn
|
|
234
|
-
? S
|
|
235
|
-
: Fn extends 'ofType' | 'as'
|
|
236
|
-
? Arg extends FhirTypeName
|
|
237
|
-
? Arg
|
|
238
|
-
: 'opaque'
|
|
239
|
-
: Fn extends keyof FixedReturns
|
|
240
|
-
? FixedReturns[Fn]
|
|
241
|
-
: 'opaque'
|
|
242
|
-
|
|
243
|
-
/** One `.`-separated segment: a call the subset knows, an indexer, or an element. */
|
|
244
|
-
type Step<S extends string, Seg extends string> = [S] extends ['opaque']
|
|
245
|
-
? 'opaque'
|
|
246
|
-
: Seg extends `${infer Fn}(${infer Arg})`
|
|
247
|
-
? Fn extends 'select'
|
|
248
|
-
? // A select argument is a sub-expression, so parse it before inference.
|
|
249
|
-
ParseExpr<Arg, S>
|
|
250
|
-
: CleanArg<Arg> extends true
|
|
251
|
-
? Call<S, Fn, Arg>
|
|
252
|
-
: 'opaque'
|
|
253
|
-
: Seg extends `${infer N}[${infer I}]`
|
|
254
|
-
? I extends `${string}]${string}` | `${string}[${string}`
|
|
255
|
-
? // A ']' or '[' inside the index means this "one indexer" spans an
|
|
256
|
-
// operator (`family[0] | active[0]`): outside the subset.
|
|
257
|
-
'opaque'
|
|
258
|
-
: Navigate<S, N>
|
|
259
|
-
: Navigate<S, Seg>
|
|
260
|
-
|
|
261
|
-
/** Walk the remaining `.`-separated segments. */
|
|
262
|
-
type ParseSegments<Expr extends string, S extends string> = Expr extends ''
|
|
263
|
-
? S
|
|
264
|
-
: Expr extends `${infer Head}.${infer Rest}`
|
|
265
|
-
? Head extends `${string}(` | `${string}(${string}`
|
|
266
|
-
? // A '.' inside parentheses split the segment: rejoin before stepping.
|
|
267
|
-
StepAcrossParen<Expr, S>
|
|
268
|
-
: ParseSegments<Rest, Step<S, Head>>
|
|
269
|
-
: Step<S, Expr>
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* A segment whose parentheses contain dots (e.g. `select(name.given)`) runs
|
|
273
|
-
* to the `)` that completes its argument; the `.` after that starts the next
|
|
274
|
-
* segment. Scanned `).` by `).`: a candidate close inside a string literal,
|
|
275
|
-
* or with parens still open, is not the close — CompleteFragment decides.
|
|
276
|
-
* With no valid `).` split, the whole rest is one segment ending at its
|
|
277
|
-
* final `)`.
|
|
278
|
-
*/
|
|
279
|
-
type StepAcrossParen<Expr extends string, S extends string> = ScanSegmentEnd<Expr, '', S>
|
|
280
|
-
|
|
281
|
-
type ScanSegmentEnd<
|
|
282
|
-
Expr extends string,
|
|
283
|
-
Acc extends string,
|
|
284
|
-
S extends string,
|
|
285
|
-
> = Expr extends `${infer Head}).${infer Rest}`
|
|
286
|
-
? SegmentComplete<`${Acc}${Head}`> extends true
|
|
287
|
-
? ParseSegments<Rest, Step<S, `${Acc}${Head})`>>
|
|
288
|
-
: ScanSegmentEnd<Rest, `${Acc}${Head}).`, S>
|
|
289
|
-
: WholeParenSegment<`${Acc}${Expr}`, S>
|
|
290
|
-
|
|
291
|
-
/** Whether `Body)` is one complete call segment: its argument closes exactly there. */
|
|
292
|
-
type SegmentComplete<Body extends string> = `${Body})` extends `${infer _Fn}(${infer Arg})`
|
|
293
|
-
? CompleteFragment<Arg>
|
|
294
|
-
: false
|
|
295
|
-
|
|
296
|
-
/** The rest of the expression is a single paren segment ending at its final `)`. */
|
|
297
|
-
type WholeParenSegment<Expr extends string, S extends string> = Expr extends `${infer Head})${''}`
|
|
298
|
-
? Step<S, `${Head})`>
|
|
299
|
-
: 'opaque'
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* One expression in a context state `S`: a `|`-union of terms, or a single
|
|
303
|
-
* term. Plain dotted paths take two cheap checks: one skips the union scanner,
|
|
304
|
-
* and the next skips trimming and group or `%var` handling.
|
|
305
|
-
*/
|
|
306
|
-
type ParseExpr<Expr extends string, S extends string> = Expr extends `${string}|${string}` | `(${string}` | `%${string}`
|
|
307
|
-
? ParseUnion<Expr, S>
|
|
308
|
-
: Expr extends `${infer Root}.${infer Rest}`
|
|
309
|
-
? Root extends keyof R4Resources & string
|
|
310
|
-
? ParseSegments<Rest, Root>
|
|
311
|
-
: ParseSegments<Expr, S>
|
|
312
|
-
: Expr extends keyof R4Resources & string
|
|
313
|
-
? Expr
|
|
314
|
-
: ParseSegments<Expr, S>
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Split off the leftmost top-level `|`. A candidate fragment whose quotes or
|
|
318
|
-
* parens are cut mid-way (the `|` sat inside a literal or a group) is not a
|
|
319
|
-
* split point: the fragment absorbs the `|` and the scan continues. With no
|
|
320
|
-
* top-level `|` at all, the whole expression is one term — its own parse is
|
|
321
|
-
* the validation, so this branch needs no separate completeness check.
|
|
322
|
-
*/
|
|
323
|
-
type SplitUnion<Expr extends string, Acc extends string = ''> = Expr extends `${infer L}|${infer R}`
|
|
324
|
-
? CompleteFragment<`${Acc}${L}`> extends true
|
|
325
|
-
? { term: `${Acc}${L}`; rest: R }
|
|
326
|
-
: SplitUnion<R, `${Acc}${L}|`>
|
|
327
|
-
: { whole: `${Acc}${Expr}` }
|
|
328
|
-
|
|
329
|
-
/** Fold the split terms into one union state. */
|
|
330
|
-
type ParseUnion<Expr extends string, S extends string> =
|
|
331
|
-
SplitUnion<Expr> extends { term: infer T extends string; rest: infer R extends string }
|
|
332
|
-
? UnionStates<ParseTerm<Trim<T>, S>, ParseUnion<R, S>>
|
|
333
|
-
: SplitUnion<Expr> extends { whole: infer W extends string }
|
|
334
|
-
? ParseTerm<Trim<W>, S>
|
|
335
|
-
: 'opaque'
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* The union of two term states. If EITHER side failed to parse, the whole
|
|
339
|
-
* union is 'opaque' — a known name must never absorb a failed parse. A broad
|
|
340
|
-
* member needs no special case: `string` swallows the other names, and
|
|
341
|
-
* unknowable-joined-with-anything is unknowable.
|
|
342
|
-
*/
|
|
343
|
-
type UnionStates<A extends string, B extends string> = [A] extends ['opaque']
|
|
344
|
-
? 'opaque'
|
|
345
|
-
: [B] extends ['opaque']
|
|
346
|
-
? 'opaque'
|
|
347
|
-
: A | B
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* One union term (or a whole single-term expression): a parenthesized group,
|
|
351
|
-
* a `%var` root, a resource-rooted path, or a path relative to the context
|
|
352
|
-
* state `S`. The top level passes S = 'opaque', so relative terms degrade
|
|
353
|
-
* there; select() passes its input state, so its sub-paths resolve.
|
|
354
|
-
*/
|
|
355
|
-
type ParseTerm<Term extends string, S extends string> = Term extends `(${infer Body}`
|
|
356
|
-
? ExtractGroup<Body, '', S>
|
|
357
|
-
: Term extends `%${infer Var}`
|
|
358
|
-
? ParseVarTerm<Var>
|
|
359
|
-
: Term extends `${infer Root}.${infer Rest}`
|
|
360
|
-
? Root extends keyof R4Resources & string
|
|
361
|
-
? ParseSegments<Rest, Root>
|
|
362
|
-
: ParseSegments<Term, S>
|
|
363
|
-
: Term extends keyof R4Resources & string
|
|
364
|
-
? Term
|
|
365
|
-
: ParseSegments<Term, S>
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* A `%var` root enters the broad state: the variable's value is unknowable
|
|
369
|
-
* here, and broad is exactly "unknowable" — plain navigation stays
|
|
370
|
-
* `unknown[]`, while a fixed-return call (`%rowIndex.toString()`) keeps its
|
|
371
|
-
* input-independent type. The name must look like a name: a glued operator
|
|
372
|
-
* (`%a = b`) would make the runtime evaluate a comparison, not a variable.
|
|
373
|
-
*/
|
|
374
|
-
type ParseVarTerm<Var extends string> = Var extends `${infer Name}.${infer Rest}`
|
|
375
|
-
? GluedName<Name> extends true
|
|
376
|
-
? 'opaque'
|
|
377
|
-
: ParseSegments<Rest, string>
|
|
378
|
-
: GluedName<Var> extends true
|
|
379
|
-
? 'opaque'
|
|
380
|
-
: string
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Find the `)` closing a group's opening paren: scan `)` by `)`, and the
|
|
384
|
-
* first one whose interior is a complete fragment closes the group. What
|
|
385
|
-
* follows must be nothing or a `.`-chain — anything else (`(A | B) = (C)`)
|
|
386
|
-
* is a glued operator.
|
|
387
|
-
*/
|
|
388
|
-
type ExtractGroup<Body extends string, Acc extends string, S extends string> = Body extends `${infer L})${infer R}`
|
|
389
|
-
? CompleteFragment<`${Acc}${L}`> extends true
|
|
390
|
-
? GroupTail<ParseExpr<`${Acc}${L}`, S>, R>
|
|
391
|
-
: ExtractGroup<R, `${Acc}${L})`, S>
|
|
392
|
-
: 'opaque'
|
|
393
|
-
|
|
394
|
-
/** Continue after a group: `(…)` ends the term, `(…).rest` walks on from the group's state. */
|
|
395
|
-
type GroupTail<G extends string, Tail extends string> = Tail extends ''
|
|
396
|
-
? G
|
|
397
|
-
: Tail extends `.${infer Rest}`
|
|
398
|
-
? ParseSegments<Rest, G>
|
|
14
|
+
export type EmptyFhirpathTypeContext = EmptyContextMap
|
|
15
|
+
|
|
16
|
+
/** A host declaration that the type-level evaluator can use without reading a runtime value. */
|
|
17
|
+
export interface FhirpathTypeDeclaration<
|
|
18
|
+
Type extends FhirTypeName = FhirTypeName,
|
|
19
|
+
Collection extends boolean = boolean,
|
|
20
|
+
> {
|
|
21
|
+
/** One candidate type, or every candidate a value may hold. */
|
|
22
|
+
type: Type | readonly Type[]
|
|
23
|
+
/** Omitted means at most one item; true means the value may contain many. */
|
|
24
|
+
collection?: Collection
|
|
25
|
+
/** Resource targets when `type` includes Reference. */
|
|
26
|
+
targets?: FhirTypeName | readonly FhirTypeName[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type FhirpathTypeDeclarations = Readonly<Record<string, FhirpathTypeDeclaration>>
|
|
30
|
+
|
|
31
|
+
/** The static fields of a native, expression-defined, or overloaded custom function. */
|
|
32
|
+
export type FhirpathFunctionDeclaration =
|
|
33
|
+
| {
|
|
34
|
+
readonly signature?: {
|
|
35
|
+
readonly input?: { readonly types?: readonly string[] }
|
|
36
|
+
readonly args?: readonly string[]
|
|
37
|
+
readonly result?: { readonly types?: readonly string[]; readonly single?: boolean }
|
|
38
|
+
}
|
|
39
|
+
readonly expression?: string | { readonly source: string }
|
|
40
|
+
readonly criteria?: boolean
|
|
41
|
+
readonly env?: Readonly<Record<string, unknown>>
|
|
42
|
+
readonly envTypes?: FhirpathTypeDeclarations
|
|
43
|
+
}
|
|
44
|
+
| { readonly overloads: readonly FhirpathFunctionDeclaration[] }
|
|
45
|
+
|
|
46
|
+
/** Type information supplied by a host around a literal FHIRPath expression. */
|
|
47
|
+
export interface FhirpathTypeContext {
|
|
48
|
+
env?: FhirpathTypeDeclarations
|
|
49
|
+
vars?: FhirpathTypeDeclarations
|
|
50
|
+
/** The same declarations accepted by `EvaluateOptions.functions`. */
|
|
51
|
+
functions?: Readonly<Record<string, FhirpathFunctionDeclaration>>
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The inferred result of evaluating a literal FHIRPath expression. */
|
|
55
|
+
export type FhirpathResult<
|
|
56
|
+
Expression extends string,
|
|
57
|
+
Context extends FhirpathTypeContext = EmptyFhirpathTypeContext,
|
|
58
|
+
> = FhirpathResultIn<Expression, 'opaque', Context>
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The inferred result with an explicit FHIR input type. Non-literal,
|
|
62
|
+
* malformed, over-budget, or unsupported expressions safely become
|
|
63
|
+
* `unknown[]`.
|
|
64
|
+
*/
|
|
65
|
+
export type FhirpathResultIn<
|
|
66
|
+
Expression extends string,
|
|
67
|
+
Input extends string,
|
|
68
|
+
Context extends FhirpathTypeContext = EmptyFhirpathTypeContext,
|
|
69
|
+
> = FhirpathResultForContext<Expression, Input, Context>
|
|
70
|
+
|
|
71
|
+
/** Internal inference entry point for contexts assembled from generic API options. */
|
|
72
|
+
export type FhirpathResultForContext<
|
|
73
|
+
Expression extends string,
|
|
74
|
+
Input extends string,
|
|
75
|
+
Context extends object = EmptyFhirpathTypeContext,
|
|
76
|
+
> = string extends Expression ? unknown[] : InferTypeExpression<Expression, Input, Context>
|
|
77
|
+
|
|
78
|
+
/** Merge contexts by normalized name. The later context wins, matching per-call runtime options. */
|
|
79
|
+
export type MergeFhirpathTypeContexts<Base extends object, Overlay extends object> = {
|
|
80
|
+
env: MergeContextMaps<ContextProperty<Base, 'env'>, ContextProperty<Overlay, 'env'>>
|
|
81
|
+
vars: MergeContextMaps<ContextProperty<Base, 'vars'>, ContextProperty<Overlay, 'vars'>>
|
|
82
|
+
functions: MergeContextMaps<ContextProperty<Base, 'functions'>, ContextProperty<Overlay, 'functions'>>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type LiteralVarDeclaration<Value> = Value extends string
|
|
86
|
+
? string extends Value
|
|
87
|
+
? FhirpathTypeDeclaration
|
|
88
|
+
: FhirpathTypeDeclaration & { readonly __expression: Value }
|
|
89
|
+
: Value extends { readonly source: infer Source extends string }
|
|
90
|
+
? string extends Source
|
|
91
|
+
? FhirpathTypeDeclaration
|
|
92
|
+
: FhirpathTypeDeclaration & { readonly __expression: Source }
|
|
93
|
+
: FhirpathTypeDeclaration
|
|
94
|
+
|
|
95
|
+
type LiteralVarDeclarations<Values> =
|
|
96
|
+
Values extends Readonly<Record<PropertyKey, unknown>>
|
|
97
|
+
? { readonly [Name in keyof Values]: LiteralVarDeclaration<Values[Name]> }
|
|
98
|
+
: EmptyFhirpathTypeContext
|
|
99
|
+
|
|
100
|
+
type OptionEnvironmentContext<Options> = Options extends { readonly env?: infer Env }
|
|
101
|
+
? InferredHostValueDeclarations<Exclude<Env, undefined>, ContextProperty<Options, 'envTypes'>>
|
|
102
|
+
: NormalizeContextMap<ContextProperty<Options, 'envTypes'>>
|
|
103
|
+
|
|
104
|
+
/** The inference context retained from one literal engine or per-call options object. */
|
|
105
|
+
export type FhirpathTypeContextOf<Options> = {
|
|
106
|
+
env: OptionEnvironmentContext<Options>
|
|
107
|
+
vars: MergeContextMaps<LiteralVarDeclarations<ContextProperty<Options, 'vars'>>, ContextProperty<Options, 'varTypes'>>
|
|
108
|
+
functions: NormalizeContextMap<ContextProperty<Options, 'functions'>>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type DeclarationElement<Declaration> = Declaration extends { readonly type: infer Type }
|
|
112
|
+
? Type extends readonly FhirTypeName[]
|
|
113
|
+
? R4TypeOf[Type[number]]
|
|
114
|
+
: Type extends FhirTypeName
|
|
115
|
+
? R4TypeOf[Type]
|
|
116
|
+
: unknown
|
|
117
|
+
: unknown
|
|
118
|
+
|
|
119
|
+
type DeclaredHostValue<Declaration> = Declaration extends { readonly collection: true }
|
|
120
|
+
? DeclarationElement<Declaration> | readonly DeclarationElement<Declaration>[] | undefined
|
|
121
|
+
: DeclarationElement<Declaration> | readonly [] | readonly [DeclarationElement<Declaration>] | undefined
|
|
122
|
+
|
|
123
|
+
type ConstrainedDeclaredValues<Values, Declarations> =
|
|
124
|
+
Values extends Readonly<Record<PropertyKey, unknown>>
|
|
125
|
+
? {
|
|
126
|
+
[Name in keyof Values]: LookupNormalizedDeclaration<Declarations, Name> extends infer Declaration
|
|
127
|
+
? [Declaration] extends [never]
|
|
128
|
+
? Values[Name]
|
|
129
|
+
: Values[Name] extends DeclaredHostValue<Declaration>
|
|
130
|
+
? Values[Name]
|
|
131
|
+
: never
|
|
132
|
+
: Values[Name]
|
|
133
|
+
}
|
|
134
|
+
: Values
|
|
135
|
+
|
|
136
|
+
type LookupNormalizedDeclaration<Declarations, Name extends PropertyKey> =
|
|
137
|
+
BareContextName<Name> extends keyof NormalizeContextMap<Declarations>
|
|
138
|
+
? NormalizeContextMap<Declarations>[BareContextName<Name>]
|
|
139
|
+
: never
|
|
140
|
+
|
|
141
|
+
/** Cross-check declarations and values when both remain visible in one literal options object. */
|
|
142
|
+
export type CheckedFhirpathOptionValues<Options> = Options extends {
|
|
143
|
+
readonly env: infer Env
|
|
144
|
+
readonly envTypes: infer EnvTypes
|
|
145
|
+
}
|
|
146
|
+
? { readonly env: ConstrainedDeclaredValues<Env, EnvTypes> }
|
|
147
|
+
: unknown
|
|
148
|
+
|
|
149
|
+
/** A model root known from a resource-shaped input; ambiguous and structural inputs stay opaque. */
|
|
150
|
+
export type FhirpathRootOf<Input> = Input extends readonly (infer Item)[]
|
|
151
|
+
? FhirpathRootOf<Item>
|
|
152
|
+
: Input extends { readonly resourceType: infer Root extends FhirTypeName }
|
|
153
|
+
? Root
|
|
399
154
|
: 'opaque'
|
|
400
155
|
|
|
401
|
-
/** The
|
|
402
|
-
type
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* The inferred result of evaluating `Expr` against its root resource.
|
|
406
|
-
* `string` (a non-literal expression) and anything outside the subset give
|
|
407
|
-
* `unknown[]`. The context is 'opaque': terms must be resource-rooted,
|
|
408
|
-
* `%var`-rooted, or parenthesized groups of those — a relative term
|
|
409
|
-
* (`id | …`) has no root to resolve against here and degrades.
|
|
410
|
-
*/
|
|
411
|
-
export type FhirpathResult<Expr extends string> = FhirpathResultIn<Expr, 'opaque'>
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* The inferred result of evaluating `Expr` with `Input` as the context type —
|
|
415
|
-
* what a DTO's `fhirType` gives its columns, so their paths can stay relative
|
|
416
|
-
* (`clinicalStatus.coding.first().code`). A resource-rooted term still resolves
|
|
417
|
-
* against its own root, and a type name outside the model degrades like any
|
|
418
|
-
* other out-of-subset expression.
|
|
419
|
-
*/
|
|
420
|
-
export type FhirpathResultIn<Expr extends string, Input extends string> = string extends Expr
|
|
421
|
-
? unknown[]
|
|
422
|
-
: ResultOf<ParseExpr<Expr, Input>>
|
|
423
|
-
|
|
424
|
-
/** The expected input resource for `Expr` (`Patient.name` wants a Patient). */
|
|
425
|
-
export type FhirpathInput<Expr extends string> = string extends Expr
|
|
156
|
+
/** The expected input resource for a resource-rooted literal expression. */
|
|
157
|
+
export type FhirpathInput<Expression extends string> = string extends Expression
|
|
426
158
|
? unknown
|
|
427
|
-
:
|
|
159
|
+
: Expression extends `${infer Root}.${string}`
|
|
428
160
|
? Root extends keyof R4Resources
|
|
429
161
|
? R4Resources[Root]
|
|
430
162
|
: unknown
|
|
431
|
-
:
|
|
432
|
-
? R4Resources[
|
|
163
|
+
: Expression extends keyof R4Resources
|
|
164
|
+
? R4Resources[Expression]
|
|
433
165
|
: unknown
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* v8 ignore file -- build-time constants consumed by the type parser and generation scripts */
|
|
2
|
+
/** Maximum tokens the type-level scanner emits for one literal expression. */
|
|
3
|
+
export const INFERENCE_TOKEN_LIMIT = 64
|
|
4
|
+
|
|
5
|
+
/** Maximum source characters the type-level scanner visits for one literal expression. */
|
|
6
|
+
export const INFERENCE_SOURCE_STEP_LIMIT = 256
|
|
7
|
+
|
|
8
|
+
export type InferenceTokenLimit = typeof INFERENCE_TOKEN_LIMIT
|
|
9
|
+
export type InferenceSourceStepLimit = typeof INFERENCE_SOURCE_STEP_LIMIT
|