@atomic-ehr/fhirpath 0.0.1-canary.35b105d.20250724165800
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 +307 -0
- package/dist/index.d.ts +225 -0
- package/dist/index.js +8185 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
- package/src/analyzer/analyzer.ts +486 -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 +148 -0
- package/src/api/errors.ts +134 -0
- package/src/api/expression.ts +152 -0
- package/src/api/index.ts +57 -0
- package/src/api/registry.ts +128 -0
- package/src/api/types.ts +154 -0
- package/src/compiler/compiler.ts +579 -0
- package/src/compiler/index.ts +2 -0
- package/src/compiler/prototype-context-adapter.ts +99 -0
- package/src/compiler/types.ts +23 -0
- package/src/index.ts +52 -0
- package/src/interpreter/README.md +78 -0
- package/src/interpreter/interpreter.ts +485 -0
- package/src/interpreter/types.ts +110 -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/parser/ast.ts +123 -0
- package/src/parser/index.ts +3 -0
- package/src/parser/parser.ts +701 -0
- package/src/parser/pprint.ts +169 -0
- package/src/registry/default-analyzers.ts +257 -0
- package/src/registry/default-compilers.ts +31 -0
- package/src/registry/index.ts +93 -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 +402 -0
- package/src/registry/operations/math.ts +128 -0
- package/src/registry/operations/membership.ts +132 -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 +307 -0
- package/src/registry/operations/utility.ts +542 -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 +179 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { ASTNode } from '../parser/ast';
|
|
2
|
+
import type {
|
|
3
|
+
FHIRPathExpression as IFHIRPathExpression,
|
|
4
|
+
CompiledExpression,
|
|
5
|
+
EvaluationContext,
|
|
6
|
+
CompileOptions,
|
|
7
|
+
AnalyzeOptions,
|
|
8
|
+
AnalysisResult
|
|
9
|
+
} from './types';
|
|
10
|
+
import { Interpreter } from '../interpreter/interpreter';
|
|
11
|
+
import { Compiler } from '../compiler/compiler';
|
|
12
|
+
import { analyzeFHIRPath } from '../analyzer/analyzer';
|
|
13
|
+
import { RuntimeContextManager } from '../runtime/context';
|
|
14
|
+
import { pprint } from '../parser/pprint';
|
|
15
|
+
|
|
16
|
+
export class FHIRPathExpression implements IFHIRPathExpression {
|
|
17
|
+
constructor(
|
|
18
|
+
public readonly ast: ASTNode,
|
|
19
|
+
private readonly originalExpression: string
|
|
20
|
+
) {}
|
|
21
|
+
|
|
22
|
+
evaluate(input?: any, context?: EvaluationContext): any[] {
|
|
23
|
+
const interpreter = new Interpreter();
|
|
24
|
+
const inputArray = input === undefined ? [] : Array.isArray(input) ? input : [input];
|
|
25
|
+
const ctx = this.createContext(context, inputArray);
|
|
26
|
+
|
|
27
|
+
const result = interpreter.evaluate(this.ast, inputArray, ctx);
|
|
28
|
+
|
|
29
|
+
return result.value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
compile(options?: CompileOptions): CompiledExpression {
|
|
33
|
+
const compiler = new Compiler();
|
|
34
|
+
const compiled = compiler.compile(this.ast);
|
|
35
|
+
|
|
36
|
+
// Create the compiled function
|
|
37
|
+
const fn = (input?: any, context?: EvaluationContext): any[] => {
|
|
38
|
+
const inputArray = input === undefined ? [] : Array.isArray(input) ? input : [input];
|
|
39
|
+
const ctx = this.createContext(context, inputArray);
|
|
40
|
+
|
|
41
|
+
return compiled.fn({
|
|
42
|
+
input: inputArray,
|
|
43
|
+
focus: inputArray,
|
|
44
|
+
env: ctx.env || {}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Add source property
|
|
49
|
+
Object.defineProperty(fn, 'source', {
|
|
50
|
+
value: options?.sourceMap ? this.originalExpression : this.toString(),
|
|
51
|
+
writable: false,
|
|
52
|
+
enumerable: true
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return fn as CompiledExpression;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
analyze(options?: AnalyzeOptions): AnalysisResult {
|
|
59
|
+
try {
|
|
60
|
+
// Create analyzer-compatible model provider
|
|
61
|
+
const analyzerModelProvider = options?.modelProvider ? {
|
|
62
|
+
resolveType: options.modelProvider.resolveType.bind(options.modelProvider),
|
|
63
|
+
getPropertyType: (type: any, propName: string) => {
|
|
64
|
+
const props = options.modelProvider!.getProperties(options.modelProvider!.getTypeName ? options.modelProvider!.getTypeName(type) : String(type));
|
|
65
|
+
const prop = props.find(p => p.name === propName);
|
|
66
|
+
if (!prop) return undefined;
|
|
67
|
+
return {
|
|
68
|
+
type: options.modelProvider!.resolveType(prop.type),
|
|
69
|
+
isSingleton: !prop.isCollection
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
isAssignable: (from: any, to: any) => {
|
|
73
|
+
const hierarchy = options.modelProvider!.getTypeHierarchy(options.modelProvider!.getTypeName ? options.modelProvider!.getTypeName(from) : String(from));
|
|
74
|
+
const toName = options.modelProvider!.getTypeName ? options.modelProvider!.getTypeName(to) : String(to);
|
|
75
|
+
return hierarchy.includes(toName);
|
|
76
|
+
},
|
|
77
|
+
getTypeName: (type: any) => String(type)
|
|
78
|
+
} : {
|
|
79
|
+
resolveType: () => undefined,
|
|
80
|
+
getPropertyType: () => undefined,
|
|
81
|
+
isAssignable: () => false,
|
|
82
|
+
getTypeName: () => 'unknown'
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const result = analyzeFHIRPath(this.ast, analyzerModelProvider);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
type: result.resultType || { kind: 'unknown' },
|
|
89
|
+
isSingleton: result.resultIsSingleton || false,
|
|
90
|
+
errors: result.diagnostics.filter(d => d.severity === 'error').map(d => ({
|
|
91
|
+
message: d.message,
|
|
92
|
+
location: d.position ? {
|
|
93
|
+
line: d.position.line,
|
|
94
|
+
column: d.position.column,
|
|
95
|
+
offset: d.position.offset,
|
|
96
|
+
length: 1 // We don't have length info in Position
|
|
97
|
+
} : undefined,
|
|
98
|
+
code: 'ANALYSIS_ERROR'
|
|
99
|
+
})),
|
|
100
|
+
warnings: result.diagnostics.filter(d => d.severity === 'warning').map(d => ({
|
|
101
|
+
message: d.message,
|
|
102
|
+
location: d.position ? {
|
|
103
|
+
line: d.position.line,
|
|
104
|
+
column: d.position.column,
|
|
105
|
+
offset: d.position.offset,
|
|
106
|
+
length: 1
|
|
107
|
+
} : undefined,
|
|
108
|
+
code: 'ANALYSIS_WARNING'
|
|
109
|
+
}))
|
|
110
|
+
};
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
type: { kind: 'unknown' },
|
|
114
|
+
isSingleton: false,
|
|
115
|
+
errors: [{
|
|
116
|
+
message: error instanceof Error ? error.message : String(error),
|
|
117
|
+
code: 'ANALYSIS_ERROR'
|
|
118
|
+
}],
|
|
119
|
+
warnings: []
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
toString(): string {
|
|
125
|
+
return pprint(this.ast);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private createContext(evalContext?: EvaluationContext, input: any[] = []) {
|
|
129
|
+
let ctx = RuntimeContextManager.create(input);
|
|
130
|
+
|
|
131
|
+
if (evalContext?.variables) {
|
|
132
|
+
for (const [name, value] of Object.entries(evalContext.variables)) {
|
|
133
|
+
ctx = RuntimeContextManager.setVariable(ctx, name, Array.isArray(value) ? value : [value]);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (evalContext?.environment) {
|
|
138
|
+
// Environment variables would need to be handled through a different mechanism
|
|
139
|
+
// as the context.env only supports specific FHIRPath environment variables
|
|
140
|
+
// TODO: Consider how to handle custom environment variables
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Store custom functions in context for interpreter to access
|
|
144
|
+
if (evalContext?.customFunctions) {
|
|
145
|
+
// TODO: Custom functions need to be implemented through the registry
|
|
146
|
+
// For now, we'll store them in env
|
|
147
|
+
(ctx as any).customFunctions = evalContext.customFunctions;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return ctx;
|
|
151
|
+
}
|
|
152
|
+
}
|
package/src/api/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FHIRPathExpression,
|
|
3
|
+
CompiledExpression,
|
|
4
|
+
EvaluationContext,
|
|
5
|
+
CompileOptions,
|
|
6
|
+
AnalyzeOptions,
|
|
7
|
+
AnalysisResult
|
|
8
|
+
} from './types';
|
|
9
|
+
import { FHIRPathParser } from '../parser/parser';
|
|
10
|
+
import { FHIRPathExpression as Expression } from './expression';
|
|
11
|
+
import { FHIRPathError, parseError } from './errors';
|
|
12
|
+
import { publicRegistry } from './registry';
|
|
13
|
+
|
|
14
|
+
// Parse expression into AST
|
|
15
|
+
export function parse(expression: string): FHIRPathExpression {
|
|
16
|
+
try {
|
|
17
|
+
const parser = new FHIRPathParser(expression);
|
|
18
|
+
const ast = parser.parse();
|
|
19
|
+
return new Expression(ast, expression);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (error instanceof Error) {
|
|
22
|
+
throw parseError(error.message, undefined, expression);
|
|
23
|
+
}
|
|
24
|
+
throw parseError(String(error), undefined, expression);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Evaluate expression directly
|
|
29
|
+
export function evaluate(
|
|
30
|
+
expression: string | FHIRPathExpression,
|
|
31
|
+
input?: any,
|
|
32
|
+
context?: EvaluationContext
|
|
33
|
+
): any[] {
|
|
34
|
+
const expr = typeof expression === 'string' ? parse(expression) : expression;
|
|
35
|
+
return expr.evaluate(input, context);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Compile to optimized function
|
|
39
|
+
export function compile(
|
|
40
|
+
expression: string | FHIRPathExpression,
|
|
41
|
+
options?: CompileOptions
|
|
42
|
+
): CompiledExpression {
|
|
43
|
+
const expr = typeof expression === 'string' ? parse(expression) : expression;
|
|
44
|
+
return expr.compile(options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Analyze expression for validation
|
|
48
|
+
export function analyze(
|
|
49
|
+
expression: string | FHIRPathExpression,
|
|
50
|
+
options?: AnalyzeOptions
|
|
51
|
+
): AnalysisResult {
|
|
52
|
+
const expr = typeof expression === 'string' ? parse(expression) : expression;
|
|
53
|
+
return expr.analyze(options);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Default registry instance
|
|
57
|
+
export const registry = publicRegistry;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RegistryAPI,
|
|
3
|
+
OperationMetadata,
|
|
4
|
+
OperationInfo
|
|
5
|
+
} from './types';
|
|
6
|
+
import { Registry } from '../registry/registry';
|
|
7
|
+
import type { Operation } from '../registry/types';
|
|
8
|
+
|
|
9
|
+
export class PublicRegistryAPI implements RegistryAPI {
|
|
10
|
+
listFunctions(): OperationMetadata[] {
|
|
11
|
+
return Registry.getAllFunctions().map(op => this.toMetadata(op));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
listOperators(): OperationMetadata[] {
|
|
15
|
+
const operators = [
|
|
16
|
+
...Registry.getOperatorsByForm('infix'),
|
|
17
|
+
...Registry.getOperatorsByForm('prefix'),
|
|
18
|
+
...Registry.getOperatorsByForm('postfix')
|
|
19
|
+
];
|
|
20
|
+
return operators.map(op => this.toMetadata(op));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
listAllOperations(): OperationMetadata[] {
|
|
24
|
+
return Registry.getAllOperations().map(op => this.toMetadata(op));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
hasOperation(name: string): boolean {
|
|
28
|
+
return Registry.get(name) !== undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
hasFunction(name: string): boolean {
|
|
32
|
+
const op = Registry.get(name);
|
|
33
|
+
return op !== undefined && op.kind === 'function';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
hasOperator(symbol: string): boolean {
|
|
37
|
+
const op = Registry.get(symbol);
|
|
38
|
+
return op !== undefined && op.kind === 'operator';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getOperationInfo(name: string): OperationInfo | undefined {
|
|
42
|
+
const op = Registry.get(name);
|
|
43
|
+
if (!op) return undefined;
|
|
44
|
+
|
|
45
|
+
return this.toOperationInfo(op);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
canRegisterFunction(name: string): boolean {
|
|
49
|
+
// Check if name is valid and not already taken
|
|
50
|
+
if (!name || typeof name !== 'string') return false;
|
|
51
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) return false;
|
|
52
|
+
|
|
53
|
+
// Check if already exists as built-in
|
|
54
|
+
return !this.hasOperation(name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private toMetadata(op: Operation): OperationMetadata {
|
|
58
|
+
return {
|
|
59
|
+
name: op.name,
|
|
60
|
+
kind: op.kind,
|
|
61
|
+
syntax: {
|
|
62
|
+
notation: op.syntax.notation
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private toOperationInfo(op: Operation): OperationInfo {
|
|
68
|
+
const info: OperationInfo = {
|
|
69
|
+
...this.toMetadata(op),
|
|
70
|
+
signature: {}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Add input signature for functions
|
|
74
|
+
if (op.kind === 'function' && op.signature.input) {
|
|
75
|
+
info.signature.input = {
|
|
76
|
+
types: this.extractTypes(op.signature.input.types),
|
|
77
|
+
cardinality: op.signature.input.cardinality
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Add parameters
|
|
82
|
+
if ('parameters' in op.signature && op.signature.parameters && op.signature.parameters.length > 0) {
|
|
83
|
+
info.signature.parameters = op.signature.parameters.map((param: any) => ({
|
|
84
|
+
name: param.name,
|
|
85
|
+
types: this.extractTypes(param.types),
|
|
86
|
+
cardinality: param.cardinality,
|
|
87
|
+
optional: param.optional
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Add output signature
|
|
92
|
+
if (op.signature.output) {
|
|
93
|
+
info.signature.output = {
|
|
94
|
+
type: typeof op.signature.output.type === 'string'
|
|
95
|
+
? op.signature.output.type
|
|
96
|
+
: 'dynamic',
|
|
97
|
+
cardinality: typeof op.signature.output.cardinality === 'string'
|
|
98
|
+
? (op.signature.output.cardinality === 'all-singleton' ? 'singleton' : op.signature.output.cardinality as any)
|
|
99
|
+
: undefined
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Add description if available (for now, operations don't have description in the type)
|
|
104
|
+
// TODO: Add description to Operation type if needed
|
|
105
|
+
|
|
106
|
+
// Add examples if available (for now, operations don't have examples in the type)
|
|
107
|
+
// TODO: Add examples to Operation type if needed
|
|
108
|
+
|
|
109
|
+
return info;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private extractTypes(constraint?: any): string[] | undefined {
|
|
113
|
+
if (!constraint) return undefined;
|
|
114
|
+
|
|
115
|
+
if (constraint.kind === 'primitive' || constraint.kind === 'class') {
|
|
116
|
+
return constraint.types;
|
|
117
|
+
} else if (constraint.kind === 'union' && constraint.types) {
|
|
118
|
+
return constraint.types;
|
|
119
|
+
} else if (constraint.kind === 'any') {
|
|
120
|
+
return ['any'];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Singleton instance
|
|
128
|
+
export const publicRegistry = new PublicRegistryAPI();
|
package/src/api/types.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { ASTNode } from '../parser/ast';
|
|
2
|
+
import type { TypeRef } from '../analyzer/types';
|
|
3
|
+
import type { Context } from '../interpreter/types';
|
|
4
|
+
|
|
5
|
+
// Core expression interface
|
|
6
|
+
export interface FHIRPathExpression {
|
|
7
|
+
readonly ast: ASTNode;
|
|
8
|
+
evaluate(input?: any, context?: EvaluationContext): any[];
|
|
9
|
+
compile(options?: CompileOptions): CompiledExpression;
|
|
10
|
+
analyze(options?: AnalyzeOptions): AnalysisResult;
|
|
11
|
+
toString(): string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Compiled expression function
|
|
15
|
+
export interface CompiledExpression {
|
|
16
|
+
(input?: any, context?: EvaluationContext): any[];
|
|
17
|
+
readonly source: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Evaluation context for expressions
|
|
21
|
+
export interface EvaluationContext {
|
|
22
|
+
variables?: Record<string, any>;
|
|
23
|
+
environment?: Record<string, any>;
|
|
24
|
+
modelProvider?: ModelProvider;
|
|
25
|
+
customFunctions?: CustomFunctionMap;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Custom function definition
|
|
29
|
+
export type CustomFunction = (
|
|
30
|
+
context: Context,
|
|
31
|
+
input: any[],
|
|
32
|
+
...args: any[]
|
|
33
|
+
) => any[];
|
|
34
|
+
|
|
35
|
+
export type CustomFunctionMap = Record<string, CustomFunction>;
|
|
36
|
+
|
|
37
|
+
// Model provider interface
|
|
38
|
+
export interface ModelProvider {
|
|
39
|
+
resolveType(typeName: string): TypeRef | undefined;
|
|
40
|
+
getTypeHierarchy(typeName: string): string[];
|
|
41
|
+
getProperties(typeName: string): PropertyDefinition[];
|
|
42
|
+
getTypeName?(type: TypeRef): string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface PropertyDefinition {
|
|
46
|
+
name: string;
|
|
47
|
+
type: string;
|
|
48
|
+
isCollection: boolean;
|
|
49
|
+
isRequired: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Compilation options
|
|
53
|
+
export interface CompileOptions {
|
|
54
|
+
optimize?: boolean;
|
|
55
|
+
sourceMap?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Analysis options
|
|
59
|
+
export interface AnalyzeOptions {
|
|
60
|
+
modelProvider?: ModelProvider;
|
|
61
|
+
strict?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Analysis result
|
|
65
|
+
export interface AnalysisResult {
|
|
66
|
+
type: TypeRef;
|
|
67
|
+
isSingleton: boolean;
|
|
68
|
+
errors: AnalysisError[];
|
|
69
|
+
warnings: AnalysisWarning[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface AnalysisError {
|
|
73
|
+
message: string;
|
|
74
|
+
location?: Location;
|
|
75
|
+
code: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface AnalysisWarning {
|
|
79
|
+
message: string;
|
|
80
|
+
location?: Location;
|
|
81
|
+
code: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface Location {
|
|
85
|
+
line: number;
|
|
86
|
+
column: number;
|
|
87
|
+
offset: number;
|
|
88
|
+
length: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Registry API types
|
|
92
|
+
export interface RegistryAPI {
|
|
93
|
+
// List operations by type
|
|
94
|
+
listFunctions(): OperationMetadata[];
|
|
95
|
+
listOperators(): OperationMetadata[];
|
|
96
|
+
listAllOperations(): OperationMetadata[];
|
|
97
|
+
|
|
98
|
+
// Check existence
|
|
99
|
+
hasOperation(name: string): boolean;
|
|
100
|
+
hasFunction(name: string): boolean;
|
|
101
|
+
hasOperator(symbol: string): boolean;
|
|
102
|
+
|
|
103
|
+
// Get operation metadata (read-only view)
|
|
104
|
+
getOperationInfo(name: string): OperationInfo | undefined;
|
|
105
|
+
|
|
106
|
+
// Extension validation
|
|
107
|
+
canRegisterFunction(name: string): boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Simplified metadata for public consumption
|
|
111
|
+
export interface OperationMetadata {
|
|
112
|
+
name: string;
|
|
113
|
+
kind: 'function' | 'operator' | 'literal';
|
|
114
|
+
syntax: {
|
|
115
|
+
notation: string; // e.g., "a + b", "substring(start, length)"
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface OperationInfo extends OperationMetadata {
|
|
120
|
+
signature: {
|
|
121
|
+
input?: {
|
|
122
|
+
types?: string[];
|
|
123
|
+
cardinality?: 'singleton' | 'collection' | 'any';
|
|
124
|
+
};
|
|
125
|
+
parameters?: Array<{
|
|
126
|
+
name: string;
|
|
127
|
+
types?: string[];
|
|
128
|
+
cardinality?: 'singleton' | 'collection' | 'any';
|
|
129
|
+
optional?: boolean;
|
|
130
|
+
}>;
|
|
131
|
+
output?: {
|
|
132
|
+
type?: string | 'dynamic';
|
|
133
|
+
cardinality?: 'singleton' | 'collection' | 'preserve-input';
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
description?: string;
|
|
137
|
+
examples?: string[];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Builder interfaces
|
|
141
|
+
export interface FHIRPathBuilder {
|
|
142
|
+
withModelProvider(provider: ModelProvider): this;
|
|
143
|
+
withCustomFunction(name: string, fn: CustomFunction): this;
|
|
144
|
+
withVariable(name: string, value: any): this;
|
|
145
|
+
build(): FHIRPathAPI;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface FHIRPathAPI {
|
|
149
|
+
parse(expression: string): FHIRPathExpression;
|
|
150
|
+
evaluate(expression: string | FHIRPathExpression, input?: any): any[];
|
|
151
|
+
compile(expression: string | FHIRPathExpression): CompiledExpression;
|
|
152
|
+
analyze(expression: string | FHIRPathExpression): AnalysisResult;
|
|
153
|
+
registry: RegistryAPI;
|
|
154
|
+
}
|