@atomic-ehr/fhirpath 0.0.1-canary.0c6931e.20250727185306
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 +473 -0
- package/dist/index.d.ts +462 -0
- package/dist/index.js +10307 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
- package/src/analyzer/analyzer.ts +499 -0
- package/src/analyzer/model-provider.ts +244 -0
- package/src/analyzer/schemas/index.ts +2 -0
- package/src/analyzer/schemas/types.ts +40 -0
- package/src/analyzer/types.ts +142 -0
- package/src/api/builder.ts +157 -0
- package/src/api/errors.ts +145 -0
- package/src/api/expression.ts +156 -0
- package/src/api/index.ts +122 -0
- package/src/api/inspect.ts +99 -0
- package/src/api/registry.ts +128 -0
- package/src/api/types.ts +210 -0
- package/src/compiler/compiler.ts +546 -0
- package/src/compiler/index.ts +2 -0
- package/src/compiler/prototype-context-adapter.ts +99 -0
- package/src/compiler/types.ts +24 -0
- package/src/index.ts +107 -0
- package/src/interpreter/README.md +78 -0
- package/src/interpreter/interpreter.ts +475 -0
- package/src/interpreter/types.ts +108 -0
- package/src/lexer/char-tables.ts +37 -0
- package/src/lexer/errors.ts +31 -0
- package/src/lexer/index.ts +5 -0
- package/src/lexer/lexer.ts +745 -0
- package/src/lexer/token.ts +104 -0
- package/src/lexer2/index.md +232 -0
- package/src/lexer2/index.perf.test.ts +68 -0
- package/src/lexer2/index.test.ts +549 -0
- package/src/lexer2/index.ts +1251 -0
- package/src/lexer2/notes.md +173 -0
- package/src/lexer2/optimization-summary.md +718 -0
- package/src/parser/ast-factory.ts +220 -0
- package/src/parser/ast.ts +144 -0
- package/src/parser/collection-parser.ts +89 -0
- package/src/parser/diagnostic-messages.ts +216 -0
- package/src/parser/diagnostics.ts +85 -0
- package/src/parser/error-reporter.ts +230 -0
- package/src/parser/index.ts +3 -0
- package/src/parser/literal-parser.ts +103 -0
- package/src/parser/parse-error.ts +16 -0
- package/src/parser/parser-error-factory.ts +141 -0
- package/src/parser/parser-state.ts +134 -0
- package/src/parser/parser.ts +1272 -0
- package/src/parser/pprint.ts +169 -0
- package/src/parser/precedence-manager.ts +64 -0
- package/src/parser/source-mapper.ts +248 -0
- package/src/parser/special-constructs.ts +142 -0
- package/src/parser/token-navigator.ts +110 -0
- package/src/parser/types.ts +60 -0
- package/src/parser2/index.md +177 -0
- package/src/parser2/index.perf.test.ts +184 -0
- package/src/parser2/index.test.ts +305 -0
- package/src/parser2/index.ts +578 -0
- package/src/parser2/optimization-summary.md +176 -0
- package/src/registry/default-analyzers.ts +257 -0
- package/src/registry/default-compilers.ts +31 -0
- package/src/registry/index.ts +96 -0
- package/src/registry/operations/arithmetic.ts +506 -0
- package/src/registry/operations/collection.ts +425 -0
- package/src/registry/operations/comparison.ts +432 -0
- package/src/registry/operations/existence.ts +703 -0
- package/src/registry/operations/filtering.ts +358 -0
- package/src/registry/operations/literals.ts +341 -0
- package/src/registry/operations/logical.ts +439 -0
- package/src/registry/operations/math.ts +128 -0
- package/src/registry/operations/membership.ts +132 -0
- package/src/registry/operations/navigation.ts +52 -0
- package/src/registry/operations/string.ts +507 -0
- package/src/registry/operations/subsetting.ts +174 -0
- package/src/registry/operations/type-checking.ts +162 -0
- package/src/registry/operations/type-conversion.ts +404 -0
- package/src/registry/operations/type-operators.ts +308 -0
- package/src/registry/operations/utility.ts +644 -0
- package/src/registry/registry.ts +146 -0
- package/src/registry/types.ts +161 -0
- package/src/registry/utils/evaluation-helpers.ts +93 -0
- package/src/registry/utils/index.ts +3 -0
- package/src/registry/utils/type-system.ts +173 -0
- package/src/runtime/context.ts +158 -0
- package/src/runtime/debug-context.ts +135 -0
package/README.md
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# @atomic-ehr/fhirpath
|
|
2
|
+
|
|
3
|
+
A TypeScript implementation of FHIRPath, the path-based navigation and extraction language for FHIR (Fast Healthcare Interoperability Resources).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @atomic-ehr/fhirpath
|
|
9
|
+
# or
|
|
10
|
+
bun add @atomic-ehr/fhirpath
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import fhirpath from '@atomic-ehr/fhirpath';
|
|
17
|
+
|
|
18
|
+
const patient = {
|
|
19
|
+
name: [
|
|
20
|
+
{ given: ['John', 'James'], family: 'Doe' },
|
|
21
|
+
{ given: ['Johnny'], family: 'Doe' }
|
|
22
|
+
],
|
|
23
|
+
birthDate: '1990-01-01'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Simple evaluation
|
|
27
|
+
const givenNames = fhirpath.evaluate('name.given', patient);
|
|
28
|
+
console.log(givenNames); // ['John', 'James', 'Johnny']
|
|
29
|
+
|
|
30
|
+
// With filtering
|
|
31
|
+
const officialName = fhirpath.evaluate('name.where(use = \'official\').given', patient);
|
|
32
|
+
|
|
33
|
+
// Arithmetic
|
|
34
|
+
const age = fhirpath.evaluate('today().year() - birthDate.toDateTime().year()', patient);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API Reference
|
|
38
|
+
|
|
39
|
+
### Core Functions
|
|
40
|
+
|
|
41
|
+
#### `parse(expression: string, options?: ParserOptions): ParseResult`
|
|
42
|
+
|
|
43
|
+
Parses a FHIRPath expression string into an AST (Abstract Syntax Tree) with optional parser features.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Basic parsing (default - collects diagnostics)
|
|
47
|
+
const result = fhirpath.parse('Patient.name.given');
|
|
48
|
+
console.log(result.ast); // The parsed AST
|
|
49
|
+
console.log(result.diagnostics); // Array of any syntax issues
|
|
50
|
+
console.log(result.hasErrors); // Boolean indicating if there are errors
|
|
51
|
+
|
|
52
|
+
// Parse with error on first syntax error (fastest)
|
|
53
|
+
const ast = fhirpath.parseForEvaluation('Patient.name.given');
|
|
54
|
+
// Throws immediately on syntax errors - best for production use
|
|
55
|
+
|
|
56
|
+
// Parse with advanced features for development tools
|
|
57
|
+
const result = fhirpath.parse('Patient..name', {
|
|
58
|
+
errorRecovery: true, // Continue parsing after errors
|
|
59
|
+
trackRanges: true // Track source location for each AST node
|
|
60
|
+
});
|
|
61
|
+
console.log(result.isPartial); // true - indicates incomplete AST due to errors
|
|
62
|
+
console.log(result.ranges); // Map of AST nodes to source locations
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Parser Options:**
|
|
66
|
+
- `throwOnError?: boolean` - Throw on first error instead of collecting diagnostics (performance mode)
|
|
67
|
+
- `trackRanges?: boolean` - Enable source range tracking for each AST node (useful for IDEs)
|
|
68
|
+
- `errorRecovery?: boolean` - Enable error recovery to continue parsing after errors (useful for IDEs)
|
|
69
|
+
- `maxErrors?: number` - Maximum number of errors to collect before stopping
|
|
70
|
+
|
|
71
|
+
#### `evaluate(expression: string | FHIRPathExpression, input?: any, context?: EvaluationContext): any[]`
|
|
72
|
+
|
|
73
|
+
Evaluates a FHIRPath expression against input data.
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// Evaluate with string expression
|
|
77
|
+
const names = fhirpath.evaluate('name.family', patient);
|
|
78
|
+
|
|
79
|
+
// Evaluate with parsed expression
|
|
80
|
+
const expr = fhirpath.parse('name.family');
|
|
81
|
+
const names = fhirpath.evaluate(expr, patient);
|
|
82
|
+
|
|
83
|
+
// With context variables
|
|
84
|
+
const result = fhirpath.evaluate('%myVar + 5', null, {
|
|
85
|
+
variables: { myVar: 10 }
|
|
86
|
+
}); // [15]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `compile(expression: string | FHIRPathExpression, options?: CompileOptions): CompiledExpression`
|
|
90
|
+
|
|
91
|
+
Compiles an expression into an optimized JavaScript function for better performance.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const compiled = fhirpath.compile('name.given');
|
|
95
|
+
|
|
96
|
+
// Use compiled function multiple times
|
|
97
|
+
const names1 = compiled(patient1);
|
|
98
|
+
const names2 = compiled(patient2);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### `analyze(expression: string | FHIRPathExpression, options?: AnalyzeOptions): AnalysisResult`
|
|
102
|
+
|
|
103
|
+
Performs static type analysis on an expression.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const analysis = fhirpath.analyze('name.given');
|
|
107
|
+
console.log(analysis.type); // Type information
|
|
108
|
+
console.log(analysis.errors); // Any type errors
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### `inspect(expression: string | FHIRPathExpression, input?: any, context?: EvaluationContext, options?: InspectOptions): InspectResult`
|
|
112
|
+
|
|
113
|
+
Evaluates an expression while capturing rich debugging information including traces, execution time, and AST.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Basic usage - capture trace output
|
|
117
|
+
const result = fhirpath.inspect(
|
|
118
|
+
'name.trace("names").given.trace("given names")',
|
|
119
|
+
patient
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
console.log(result.result); // ['John', 'James', 'Johnny']
|
|
123
|
+
console.log(result.traces); // Array of trace entries
|
|
124
|
+
console.log(result.executionTime); // Time in milliseconds
|
|
125
|
+
console.log(result.ast); // Parsed AST
|
|
126
|
+
|
|
127
|
+
// Access trace information
|
|
128
|
+
result.traces.forEach(trace => {
|
|
129
|
+
console.log(`${trace.name}: ${JSON.stringify(trace.values)}`);
|
|
130
|
+
console.log(` at ${trace.timestamp}ms, depth: ${trace.depth}`);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// With options
|
|
134
|
+
const detailedResult = fhirpath.inspect(
|
|
135
|
+
'Patient.name.where(use = "official")',
|
|
136
|
+
bundle,
|
|
137
|
+
undefined,
|
|
138
|
+
{
|
|
139
|
+
maxTraces: 100, // Limit number of traces collected
|
|
140
|
+
recordSteps: true // Future: enable step-by-step recording
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// Error handling
|
|
145
|
+
const errorResult = fhirpath.inspect('invalid.expression()');
|
|
146
|
+
if (errorResult.errors) {
|
|
147
|
+
console.log('Errors:', errorResult.errors);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The `InspectResult` contains:
|
|
152
|
+
- `result`: The evaluation result (same as `evaluate()`)
|
|
153
|
+
- `expression`: The original expression string
|
|
154
|
+
- `ast`: The parsed Abstract Syntax Tree
|
|
155
|
+
- `executionTime`: Total execution time in milliseconds
|
|
156
|
+
- `traces`: Array of trace entries from `trace()` calls
|
|
157
|
+
- `errors`: Any errors encountered during evaluation
|
|
158
|
+
- `warnings`: Any warnings (optional)
|
|
159
|
+
- `evaluationSteps`: Step-by-step evaluation details (when enabled)
|
|
160
|
+
|
|
161
|
+
### Convenience Functions
|
|
162
|
+
|
|
163
|
+
#### `parseForEvaluation(expression: string): ASTNode`
|
|
164
|
+
|
|
165
|
+
Parses an expression and returns just the AST, throwing on any syntax errors. This is the most performant option for production use.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
try {
|
|
169
|
+
const ast = fhirpath.parseForEvaluation('Patient.name.given');
|
|
170
|
+
// Use ast for evaluation
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error('Parse error:', error.message);
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `validate(expression: string): { valid: boolean; diagnostics: ParseDiagnostic[] }`
|
|
177
|
+
|
|
178
|
+
Validates a FHIRPath expression syntax without throwing errors.
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
const validation = fhirpath.validate('Patient..name');
|
|
182
|
+
if (!validation.valid) {
|
|
183
|
+
console.log('Syntax errors:', validation.diagnostics);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Registry API
|
|
188
|
+
|
|
189
|
+
The registry provides introspection capabilities for available operations.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// List all available functions
|
|
193
|
+
const functions = fhirpath.registry.listFunctions();
|
|
194
|
+
console.log(functions.map(f => f.name)); // ['where', 'select', 'first', ...]
|
|
195
|
+
|
|
196
|
+
// Check if operation exists
|
|
197
|
+
fhirpath.registry.hasFunction('where'); // true
|
|
198
|
+
fhirpath.registry.hasOperator('+'); // true
|
|
199
|
+
|
|
200
|
+
// Get operation details
|
|
201
|
+
const whereInfo = fhirpath.registry.getOperationInfo('where');
|
|
202
|
+
console.log(whereInfo.syntax.notation); // "where(expression)"
|
|
203
|
+
|
|
204
|
+
// Validate custom function names
|
|
205
|
+
fhirpath.registry.canRegisterFunction('myFunc'); // true
|
|
206
|
+
fhirpath.registry.canRegisterFunction('where'); // false (built-in)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Builder Pattern
|
|
210
|
+
|
|
211
|
+
For advanced configurations, use the builder pattern:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { FHIRPath } from '@atomic-ehr/fhirpath';
|
|
215
|
+
|
|
216
|
+
const fp = FHIRPath.builder()
|
|
217
|
+
// Add custom functions
|
|
218
|
+
.withCustomFunction('double', (context, input) => {
|
|
219
|
+
return input.map(x => x * 2);
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// Set default variables
|
|
223
|
+
.withVariable('defaultStatus', 'active')
|
|
224
|
+
|
|
225
|
+
// Add model provider for type information
|
|
226
|
+
.withModelProvider({
|
|
227
|
+
resolveType: (typeName) => { /* ... */ },
|
|
228
|
+
getTypeHierarchy: (typeName) => { /* ... */ },
|
|
229
|
+
getProperties: (typeName) => { /* ... */ }
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
.build();
|
|
233
|
+
|
|
234
|
+
// Use the configured instance
|
|
235
|
+
const result = fp.evaluate('value.double()', { value: [5] }); // [10]
|
|
236
|
+
const status = fp.evaluate('%defaultStatus'); // ['active']
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Custom Functions
|
|
240
|
+
|
|
241
|
+
Custom functions extend FHIRPath with domain-specific operations:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const fp = FHIRPath.builder()
|
|
245
|
+
.withCustomFunction('age', (context, input) => {
|
|
246
|
+
// Calculate age from birthDate
|
|
247
|
+
return input.map(birthDate => {
|
|
248
|
+
const today = new Date();
|
|
249
|
+
const birth = new Date(birthDate);
|
|
250
|
+
let age = today.getFullYear() - birth.getFullYear();
|
|
251
|
+
const monthDiff = today.getMonth() - birth.getMonth();
|
|
252
|
+
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
|
|
253
|
+
age--;
|
|
254
|
+
}
|
|
255
|
+
return age;
|
|
256
|
+
});
|
|
257
|
+
})
|
|
258
|
+
.withCustomFunction('fullName', (context, input) => {
|
|
259
|
+
return input.map(name => {
|
|
260
|
+
if (name && typeof name === 'object') {
|
|
261
|
+
const given = Array.isArray(name.given) ? name.given.join(' ') : '';
|
|
262
|
+
const family = name.family || '';
|
|
263
|
+
return `${given} ${family}`.trim();
|
|
264
|
+
}
|
|
265
|
+
return '';
|
|
266
|
+
});
|
|
267
|
+
})
|
|
268
|
+
.build();
|
|
269
|
+
|
|
270
|
+
// Use custom functions
|
|
271
|
+
const age = fp.evaluate('birthDate.age()', patient);
|
|
272
|
+
const fullNames = fp.evaluate('name.fullName()', patient);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Error Handling
|
|
276
|
+
|
|
277
|
+
All API functions throw `FHIRPathError` for invalid expressions or runtime errors:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { FHIRPathError, ErrorCode } from '@atomic-ehr/fhirpath';
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
fhirpath.parse('invalid..expression');
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (error instanceof FHIRPathError) {
|
|
286
|
+
console.error(`Error: ${error.message}`);
|
|
287
|
+
console.error(`Code: ${error.code}`);
|
|
288
|
+
console.error(`Location: Line ${error.location?.line}, Column ${error.location?.column}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Type Definitions
|
|
294
|
+
|
|
295
|
+
The library is fully typed with TypeScript:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import type {
|
|
299
|
+
FHIRPathExpression,
|
|
300
|
+
CompiledExpression,
|
|
301
|
+
EvaluationContext,
|
|
302
|
+
ModelProvider,
|
|
303
|
+
CustomFunction,
|
|
304
|
+
OperationInfo,
|
|
305
|
+
AnalysisResult,
|
|
306
|
+
InspectResult,
|
|
307
|
+
InspectOptions,
|
|
308
|
+
// Parser types
|
|
309
|
+
ParserOptions,
|
|
310
|
+
ParseResult,
|
|
311
|
+
ParseDiagnostic,
|
|
312
|
+
DiagnosticSeverity,
|
|
313
|
+
TextRange,
|
|
314
|
+
ASTNode
|
|
315
|
+
} from '@atomic-ehr/fhirpath';
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Common Use Cases
|
|
319
|
+
|
|
320
|
+
### Working with FHIR Resources
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
const bundle = {
|
|
324
|
+
resourceType: 'Bundle',
|
|
325
|
+
entry: [
|
|
326
|
+
{ resource: { resourceType: 'Patient', id: '1', active: true } },
|
|
327
|
+
{ resource: { resourceType: 'Patient', id: '2', active: false } },
|
|
328
|
+
{ resource: { resourceType: 'Observation', status: 'final' } }
|
|
329
|
+
]
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// Get all patients
|
|
333
|
+
const patients = fhirpath.evaluate(
|
|
334
|
+
'entry.resource.where(resourceType = \'Patient\')',
|
|
335
|
+
bundle
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// Get active patients
|
|
339
|
+
const activePatients = fhirpath.evaluate(
|
|
340
|
+
'entry.resource.where(resourceType = \'Patient\' and active = true)',
|
|
341
|
+
bundle
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
// Count resources by type
|
|
345
|
+
const patientCount = fhirpath.evaluate(
|
|
346
|
+
'entry.resource.where(resourceType = \'Patient\').count()',
|
|
347
|
+
bundle
|
|
348
|
+
); // [2]
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Complex Filtering
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
const observations = [
|
|
355
|
+
{ code: { coding: [{ system: 'loinc', code: '1234' }] }, value: 140 },
|
|
356
|
+
{ code: { coding: [{ system: 'loinc', code: '1234' }] }, value: 120 },
|
|
357
|
+
{ code: { coding: [{ system: 'loinc', code: '5678' }] }, value: 98.6 }
|
|
358
|
+
];
|
|
359
|
+
|
|
360
|
+
// Find high blood pressure readings
|
|
361
|
+
const highBP = fhirpath.evaluate(
|
|
362
|
+
'where(code.coding.exists(system = \'loinc\' and code = \'1234\') and value > 130)',
|
|
363
|
+
observations
|
|
364
|
+
);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Date Manipulation
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
const patient = {
|
|
371
|
+
birthDate: '1990-05-15'
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
// Check if patient is adult (>= 18 years)
|
|
375
|
+
const isAdult = fhirpath.evaluate(
|
|
376
|
+
'today() - birthDate.toDateTime() >= 18 years',
|
|
377
|
+
patient
|
|
378
|
+
);
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Debugging Expressions
|
|
382
|
+
|
|
383
|
+
Use the `inspect()` function to debug complex FHIRPath expressions:
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
const bundle = {
|
|
387
|
+
entry: [
|
|
388
|
+
{ resource: { resourceType: 'Patient', name: [{ given: ['John'] }] } },
|
|
389
|
+
{ resource: { resourceType: 'Patient', name: [{ given: ['Jane'] }] } }
|
|
390
|
+
]
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// Debug a complex expression with traces
|
|
394
|
+
const result = fhirpath.inspect(
|
|
395
|
+
`entry.resource
|
|
396
|
+
.trace('all resources')
|
|
397
|
+
.where(resourceType = 'Patient')
|
|
398
|
+
.trace('patients only')
|
|
399
|
+
.name.given
|
|
400
|
+
.trace('all given names')`,
|
|
401
|
+
bundle
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
// Analyze the execution
|
|
405
|
+
console.log('Result:', result.result);
|
|
406
|
+
console.log('Execution time:', result.executionTime + 'ms');
|
|
407
|
+
console.log('\nTrace output:');
|
|
408
|
+
result.traces.forEach(trace => {
|
|
409
|
+
console.log(`- ${trace.name}: ${trace.values.length} items`);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Output:
|
|
413
|
+
// Result: ['John', 'Jane']
|
|
414
|
+
// Execution time: 0.523ms
|
|
415
|
+
//
|
|
416
|
+
// Trace output:
|
|
417
|
+
// - all resources: 2 items
|
|
418
|
+
// - patients only: 2 items
|
|
419
|
+
// - all given names: 2 items
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Performance Tips
|
|
423
|
+
|
|
424
|
+
1. **Use Fast Parsing for Production**: When parsing expressions in production, use `parseForEvaluation()` or enable `throwOnError`:
|
|
425
|
+
```typescript
|
|
426
|
+
// Fastest - throws on first error
|
|
427
|
+
const ast = fhirpath.parseForEvaluation('name.given');
|
|
428
|
+
|
|
429
|
+
// Or use throwOnError option
|
|
430
|
+
const result = fhirpath.parse('name.given', { throwOnError: true });
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
2. **Parse Once, Evaluate Many**: Parse expressions once and reuse the parsed AST:
|
|
434
|
+
```typescript
|
|
435
|
+
const expr = fhirpath.parse('name.given');
|
|
436
|
+
for (const patient of patients) {
|
|
437
|
+
const names = fhirpath.evaluate(expr, patient);
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
3. **Use Compiled Functions**: For expressions evaluated frequently, use compilation:
|
|
442
|
+
```typescript
|
|
443
|
+
const getName = fhirpath.compile('name.given');
|
|
444
|
+
const results = patients.map(p => getName(p));
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
4. **Disable Unnecessary Parser Features**: Only enable parser features you need:
|
|
448
|
+
```typescript
|
|
449
|
+
// For development tools (IDEs, linters)
|
|
450
|
+
const result = fhirpath.parse(expr, {
|
|
451
|
+
errorRecovery: true, // Only if you need partial ASTs
|
|
452
|
+
trackRanges: true // Only if you need source mapping
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// For production (fastest)
|
|
456
|
+
const ast = fhirpath.parseForEvaluation(expr);
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
5. **Builder Instance**: Create a configured instance once and reuse:
|
|
460
|
+
```typescript
|
|
461
|
+
const fp = FHIRPath.builder()
|
|
462
|
+
.withCustomFunction('myFunc', /* ... */)
|
|
463
|
+
.build();
|
|
464
|
+
// Use fp instance throughout your application
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Contributing
|
|
468
|
+
|
|
469
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
|
|
470
|
+
|
|
471
|
+
## License
|
|
472
|
+
|
|
473
|
+
MIT
|