fhirpath-ts 0.1.0 → 0.2.1
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/resources-data.d.ts +6 -2
- package/dist/r4/generated/resources-data.d.ts.map +1 -1
- package/dist/r4/generated/resources-data.js +626 -28627
- package/dist/r4/generated/resources-data.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/generated/types-data.d.ts +6 -2
- package/dist/r4/generated/types-data.d.ts.map +1 -1
- package/dist/r4/generated/types-data.js +84 -2204
- package/dist/r4/generated/types-data.js.map +1 -1
- package/dist/r4/index.d.ts +3 -1
- package/dist/r4/index.d.ts.map +1 -1
- package/dist/r4/index.js +10 -4
- package/dist/r4/index.js.map +1 -1
- package/dist/r4/model-data.d.ts +17 -0
- package/dist/r4/model-data.d.ts.map +1 -1
- package/dist/r4/model-data.js +118 -1
- package/dist/r4/model-data.js.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/resources-data.ts +624 -28628
- package/src/r4/generated/type-maps.ts +1557 -0
- package/src/r4/generated/types-data.ts +82 -2205
- package/src/r4/index.ts +11 -5
- package/src/r4/model-data.ts +127 -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
package/src/r4/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FhirPathEngine } from '../api/engine.ts'
|
|
2
2
|
import type { ElementInfo, ModelProvider } from '../model/provider.ts'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import type
|
|
3
|
+
import { R4_RESOURCES_COMPACT } from './generated/resources-data.ts'
|
|
4
|
+
import { R4_DATA_TYPES_COMPACT } from './generated/types-data.ts'
|
|
5
|
+
import { CompactTypeTable, type GeneratedElement, type GeneratedType } from './model-data.ts'
|
|
6
6
|
|
|
7
7
|
// One canonical FHIR-primitive → System map lives in values/typed-value.ts;
|
|
8
8
|
// re-exported here because the model subpath is where consumers look for it.
|
|
@@ -12,8 +12,11 @@ export type { GeneratedElement, GeneratedType } from './model-data.ts'
|
|
|
12
12
|
const NAMESPACE = 'FHIR'
|
|
13
13
|
const PREFIX = `${NAMESPACE}.`
|
|
14
14
|
|
|
15
|
+
const RESOURCES = new CompactTypeTable(R4_RESOURCES_COMPACT)
|
|
16
|
+
const DATA_TYPES = new CompactTypeTable(R4_DATA_TYPES_COMPACT)
|
|
17
|
+
|
|
15
18
|
function lookupType(name: string): GeneratedType | undefined {
|
|
16
|
-
return
|
|
19
|
+
return RESOURCES.get(name) ?? DATA_TYPES.get(name)
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
function localName(canonical: string): string {
|
|
@@ -85,7 +88,10 @@ export const r4Model: ModelProvider = {
|
|
|
85
88
|
}
|
|
86
89
|
current = definition.b
|
|
87
90
|
}
|
|
88
|
-
|
|
91
|
+
// The tables carry each inherited element only on the type that declares
|
|
92
|
+
// it, so the walk yields own elements before inherited ones; sorting gives
|
|
93
|
+
// callers one stable order independent of where an element is declared.
|
|
94
|
+
return names.sort()
|
|
89
95
|
},
|
|
90
96
|
|
|
91
97
|
isSubtypeOf(type: string, base: string): boolean {
|
package/src/r4/model-data.ts
CHANGED
|
@@ -15,3 +15,130 @@ export interface GeneratedType {
|
|
|
15
15
|
b?: string
|
|
16
16
|
e: Record<string, GeneratedElement>
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The generated tables ship as one compact string instead of an object literal:
|
|
21
|
+
* the string is smaller to download and cheaper to decode than a large literal
|
|
22
|
+
* is to parse, and `CompactTypeTable` decodes one type at a time so only the
|
|
23
|
+
* types an application touches are ever materialized.
|
|
24
|
+
*
|
|
25
|
+
* Format, one line per type:
|
|
26
|
+
*
|
|
27
|
+
* Name|Base|element:type1,type2*^<Target1,Target2;element2:type
|
|
28
|
+
*
|
|
29
|
+
* `*` marks a collection (`a: 1`), `^` a choice element (`c: 1`), and `<`
|
|
30
|
+
* starts the Reference target list (`r`). An empty Base segment means the type
|
|
31
|
+
* has no base. Elements identical to the nearest ancestor's entry are omitted
|
|
32
|
+
* by the generator; lookups walk the base chain (see index.ts).
|
|
33
|
+
*/
|
|
34
|
+
const RESERVED_IN_COMPACT = /[|;:,*^<\n\\`$]/
|
|
35
|
+
|
|
36
|
+
function assertCompactSafe(name: string): string {
|
|
37
|
+
if (RESERVED_IN_COMPACT.test(name)) {
|
|
38
|
+
throw new Error(`name contains a character reserved by the compact model encoding: ${name}`)
|
|
39
|
+
}
|
|
40
|
+
return name
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Encodes a generated table into the compact string form (generator-side; see the format above). */
|
|
44
|
+
export function encodeCompactTypes(types: Record<string, GeneratedType>): string {
|
|
45
|
+
const lines: string[] = []
|
|
46
|
+
for (const [name, type] of Object.entries(types)) {
|
|
47
|
+
const elements = Object.entries(type.e).map(([element, info]) => {
|
|
48
|
+
let entry = `${assertCompactSafe(element)}:${info.t.map(assertCompactSafe).join(',')}`
|
|
49
|
+
if (info.a === 1) {
|
|
50
|
+
entry += '*'
|
|
51
|
+
}
|
|
52
|
+
if (info.c === 1) {
|
|
53
|
+
entry += '^'
|
|
54
|
+
}
|
|
55
|
+
if (info.r !== undefined) {
|
|
56
|
+
entry += `<${info.r.map(assertCompactSafe).join(',')}`
|
|
57
|
+
}
|
|
58
|
+
return entry
|
|
59
|
+
})
|
|
60
|
+
lines.push(`${assertCompactSafe(name)}|${assertCompactSafe(type.b ?? '')}|${elements.join(';')}`)
|
|
61
|
+
}
|
|
62
|
+
return lines.join('\n')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Decodes the segment after the second `|` of a line. */
|
|
66
|
+
function decodeTypeLine(line: string): GeneratedType {
|
|
67
|
+
const baseStart = line.indexOf('|') + 1
|
|
68
|
+
const baseEnd = line.indexOf('|', baseStart)
|
|
69
|
+
const base = line.slice(baseStart, baseEnd)
|
|
70
|
+
const e: Record<string, GeneratedElement> = {}
|
|
71
|
+
const body = line.slice(baseEnd + 1)
|
|
72
|
+
if (body !== '') {
|
|
73
|
+
for (const entry of body.split(';')) {
|
|
74
|
+
const colon = entry.indexOf(':')
|
|
75
|
+
const name = entry.slice(0, colon)
|
|
76
|
+
let rest = entry.slice(colon + 1)
|
|
77
|
+
const element: GeneratedElement = { t: [] }
|
|
78
|
+
const targetsStart = rest.indexOf('<')
|
|
79
|
+
if (targetsStart >= 0) {
|
|
80
|
+
element.r = rest.slice(targetsStart + 1).split(',')
|
|
81
|
+
rest = rest.slice(0, targetsStart)
|
|
82
|
+
}
|
|
83
|
+
if (rest.endsWith('^')) {
|
|
84
|
+
element.c = 1
|
|
85
|
+
rest = rest.slice(0, -1)
|
|
86
|
+
}
|
|
87
|
+
if (rest.endsWith('*')) {
|
|
88
|
+
element.a = 1
|
|
89
|
+
rest = rest.slice(0, -1)
|
|
90
|
+
}
|
|
91
|
+
element.t = rest.split(',')
|
|
92
|
+
e[name] = element
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return base === '' ? { e } : { b: base, e }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Eagerly decodes a whole table; for build scripts, not the runtime path. */
|
|
99
|
+
export function decodeCompactTypes(encoded: string): Record<string, GeneratedType> {
|
|
100
|
+
const types: Record<string, GeneratedType> = {}
|
|
101
|
+
for (const line of encoded.split('\n')) {
|
|
102
|
+
types[line.slice(0, line.indexOf('|'))] = decodeTypeLine(line)
|
|
103
|
+
}
|
|
104
|
+
return types
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Lazy view over a compact-encoded table. The line index is built on first
|
|
109
|
+
* lookup and each type is decoded once, on demand, so importing the model costs
|
|
110
|
+
* almost nothing until an expression actually navigates it.
|
|
111
|
+
*/
|
|
112
|
+
export class CompactTypeTable {
|
|
113
|
+
private readonly encoded: string
|
|
114
|
+
private index: Map<string, string> | undefined
|
|
115
|
+
private readonly decoded = new Map<string, GeneratedType>()
|
|
116
|
+
|
|
117
|
+
constructor(encoded: string) {
|
|
118
|
+
this.encoded = encoded
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get(name: string): GeneratedType | undefined {
|
|
122
|
+
const cached = this.decoded.get(name)
|
|
123
|
+
if (cached !== undefined) {
|
|
124
|
+
return cached
|
|
125
|
+
}
|
|
126
|
+
const line = this.lines().get(name)
|
|
127
|
+
if (line === undefined) {
|
|
128
|
+
return undefined
|
|
129
|
+
}
|
|
130
|
+
const type = decodeTypeLine(line)
|
|
131
|
+
this.decoded.set(name, type)
|
|
132
|
+
return type
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private lines(): Map<string, string> {
|
|
136
|
+
if (this.index === undefined) {
|
|
137
|
+
this.index = new Map()
|
|
138
|
+
for (const line of this.encoded.split('\n')) {
|
|
139
|
+
this.index.set(line.slice(0, line.indexOf('|')), line)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return this.index
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -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
|