@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.
Files changed (57) hide show
  1. package/README.md +307 -0
  2. package/dist/index.d.ts +225 -0
  3. package/dist/index.js +8185 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +51 -0
  6. package/src/analyzer/analyzer.ts +486 -0
  7. package/src/analyzer/model-provider.ts +244 -0
  8. package/src/analyzer/schemas/index.ts +2 -0
  9. package/src/analyzer/schemas/types.ts +40 -0
  10. package/src/analyzer/types.ts +142 -0
  11. package/src/api/builder.ts +148 -0
  12. package/src/api/errors.ts +134 -0
  13. package/src/api/expression.ts +152 -0
  14. package/src/api/index.ts +57 -0
  15. package/src/api/registry.ts +128 -0
  16. package/src/api/types.ts +154 -0
  17. package/src/compiler/compiler.ts +579 -0
  18. package/src/compiler/index.ts +2 -0
  19. package/src/compiler/prototype-context-adapter.ts +99 -0
  20. package/src/compiler/types.ts +23 -0
  21. package/src/index.ts +52 -0
  22. package/src/interpreter/README.md +78 -0
  23. package/src/interpreter/interpreter.ts +485 -0
  24. package/src/interpreter/types.ts +110 -0
  25. package/src/lexer/char-tables.ts +37 -0
  26. package/src/lexer/errors.ts +31 -0
  27. package/src/lexer/index.ts +5 -0
  28. package/src/lexer/lexer.ts +745 -0
  29. package/src/lexer/token.ts +104 -0
  30. package/src/parser/ast.ts +123 -0
  31. package/src/parser/index.ts +3 -0
  32. package/src/parser/parser.ts +701 -0
  33. package/src/parser/pprint.ts +169 -0
  34. package/src/registry/default-analyzers.ts +257 -0
  35. package/src/registry/default-compilers.ts +31 -0
  36. package/src/registry/index.ts +93 -0
  37. package/src/registry/operations/arithmetic.ts +506 -0
  38. package/src/registry/operations/collection.ts +425 -0
  39. package/src/registry/operations/comparison.ts +432 -0
  40. package/src/registry/operations/existence.ts +703 -0
  41. package/src/registry/operations/filtering.ts +358 -0
  42. package/src/registry/operations/literals.ts +341 -0
  43. package/src/registry/operations/logical.ts +402 -0
  44. package/src/registry/operations/math.ts +128 -0
  45. package/src/registry/operations/membership.ts +132 -0
  46. package/src/registry/operations/string.ts +507 -0
  47. package/src/registry/operations/subsetting.ts +174 -0
  48. package/src/registry/operations/type-checking.ts +162 -0
  49. package/src/registry/operations/type-conversion.ts +404 -0
  50. package/src/registry/operations/type-operators.ts +307 -0
  51. package/src/registry/operations/utility.ts +542 -0
  52. package/src/registry/registry.ts +146 -0
  53. package/src/registry/types.ts +161 -0
  54. package/src/registry/utils/evaluation-helpers.ts +93 -0
  55. package/src/registry/utils/index.ts +3 -0
  56. package/src/registry/utils/type-system.ts +173 -0
  57. package/src/runtime/context.ts +179 -0
@@ -0,0 +1,169 @@
1
+ import type {
2
+ ASTNode,
3
+ IdentifierNode,
4
+ TypeOrIdentifierNode,
5
+ VariableNode,
6
+ TypeReferenceNode,
7
+ BinaryNode,
8
+ UnaryNode,
9
+ UnionNode,
10
+ FunctionNode,
11
+ CollectionNode,
12
+ MembershipTestNode,
13
+ TypeCastNode,
14
+ IndexNode,
15
+ LiteralNode
16
+ } from './ast';
17
+ import { NodeType } from './ast';
18
+ import { TokenType } from '../lexer/token';
19
+
20
+ /**
21
+ * Pretty print an AST node as an S-expression (without position information)
22
+ */
23
+ export function pprint(node: ASTNode, multiline: boolean = false, indent: number = 0): string {
24
+ const spaces = multiline ? ' '.repeat(indent) : '';
25
+ const nl = multiline ? '\n' : '';
26
+ const childIndent = indent + 2;
27
+
28
+ switch (node.type) {
29
+ case NodeType.Literal:
30
+ const lit = node as LiteralNode;
31
+ return `${spaces}(${pprintLiteralValue(lit)}:${lit.valueType})`;
32
+
33
+ case NodeType.Identifier:
34
+ return `${spaces}(${(node as IdentifierNode).name}:id)`;
35
+
36
+ case NodeType.TypeOrIdentifier:
37
+ return `${spaces}(${(node as TypeOrIdentifierNode).name}:type-or-id)`;
38
+
39
+ case NodeType.Variable:
40
+ const varNode = node as VariableNode;
41
+ const varName = varNode.name.startsWith('$') ? varNode.name : `%${varNode.name}`;
42
+ return `${spaces}(${varName}:var)`;
43
+
44
+ case NodeType.TypeReference:
45
+ return `${spaces}(${(node as TypeReferenceNode).typeName}:type-ref)`;
46
+
47
+ case NodeType.Binary:
48
+ const binary = node as BinaryNode;
49
+ const op = tokenToOp(binary.operator);
50
+ if (multiline) {
51
+ return `${spaces}(${op}${nl}${pprint(binary.left, multiline, childIndent)}${nl}${pprint(binary.right, multiline, childIndent)})`;
52
+ } else {
53
+ return `(${op} ${pprint(binary.left)} ${pprint(binary.right)})`;
54
+ }
55
+
56
+ case NodeType.Unary:
57
+ const unary = node as UnaryNode;
58
+ const unaryOp = tokenToOp(unary.operator);
59
+ if (multiline) {
60
+ return `${spaces}(${unaryOp}:unary${nl}${pprint(unary.operand, multiline, childIndent)})`;
61
+ } else {
62
+ return `(${unaryOp}:unary ${pprint(unary.operand)})`;
63
+ }
64
+
65
+ case NodeType.Union:
66
+ const union = node as UnionNode;
67
+ if (multiline) {
68
+ const operands = union.operands.map(op => pprint(op, multiline, childIndent)).join(nl);
69
+ return `${spaces}(|${nl}${operands})`;
70
+ } else {
71
+ return `(| ${union.operands.map(op => pprint(op)).join(' ')})`;
72
+ }
73
+
74
+ case NodeType.Function:
75
+ const func = node as FunctionNode;
76
+ const funcName = (func.name as IdentifierNode).name;
77
+ if (multiline && func.arguments.length > 0) {
78
+ const args = func.arguments.map(arg => pprint(arg, multiline, childIndent)).join(nl);
79
+ return `${spaces}(${funcName}:fn${nl}${args})`;
80
+ } else {
81
+ const args = func.arguments.map(arg => pprint(arg)).join(' ');
82
+ return args ? `${spaces}(${funcName}:fn ${args})` : `${spaces}(${funcName}:fn)`;
83
+ }
84
+
85
+ case NodeType.Collection:
86
+ const coll = node as CollectionNode;
87
+ if (multiline && coll.elements.length > 0) {
88
+ const elements = coll.elements.map(el => pprint(el, multiline, childIndent)).join(nl);
89
+ return `${spaces}({}:collection${nl}${elements})`;
90
+ } else {
91
+ const elements = coll.elements.map(el => pprint(el)).join(' ');
92
+ return elements ? `${spaces}({}:collection ${elements})` : `${spaces}({}:collection)`;
93
+ }
94
+
95
+ case NodeType.MembershipTest:
96
+ const memberTest = node as MembershipTestNode;
97
+ if (multiline) {
98
+ return `${spaces}(is${nl}${pprint(memberTest.expression, multiline, childIndent)}${nl}${' '.repeat(childIndent)}${memberTest.targetType})`;
99
+ } else {
100
+ return `(is ${pprint(memberTest.expression)} ${memberTest.targetType})`;
101
+ }
102
+
103
+ case NodeType.TypeCast:
104
+ const typeCast = node as TypeCastNode;
105
+ if (multiline) {
106
+ return `${spaces}(as${nl}${pprint(typeCast.expression, multiline, childIndent)}${nl}${' '.repeat(childIndent)}${typeCast.targetType})`;
107
+ } else {
108
+ return `(as ${pprint(typeCast.expression)} ${typeCast.targetType})`;
109
+ }
110
+
111
+ case NodeType.Index:
112
+ const index = node as IndexNode;
113
+ if (multiline) {
114
+ return `${spaces}([]${nl}${pprint(index.expression, multiline, childIndent)}${nl}${pprint(index.index, multiline, childIndent)})`;
115
+ } else {
116
+ return `([] ${pprint(index.expression)} ${pprint(index.index)})`;
117
+ }
118
+
119
+ default:
120
+ return `${spaces}(unknown:${node.type})`;
121
+ }
122
+ }
123
+
124
+ function pprintLiteralValue(node: LiteralNode): string {
125
+ const value = node.value;
126
+ const valueType = node.valueType;
127
+
128
+ switch (valueType) {
129
+ case 'string':
130
+ return `'${value}'`;
131
+ case 'number':
132
+ return String(value);
133
+ case 'boolean':
134
+ return String(value);
135
+ case 'null':
136
+ return '{}';
137
+ case 'date':
138
+ case 'time':
139
+ case 'datetime':
140
+ return `@${value}`;
141
+ default:
142
+ return String(value);
143
+ }
144
+ }
145
+
146
+ function tokenToOp(token: TokenType): string {
147
+ switch (token) {
148
+ case TokenType.PLUS: return '+';
149
+ case TokenType.MINUS: return '-';
150
+ case TokenType.STAR: return '*';
151
+ case TokenType.DIV: return 'div';
152
+ case TokenType.MOD: return 'mod';
153
+ case TokenType.GT: return '>';
154
+ case TokenType.LT: return '<';
155
+ case TokenType.GTE: return '>=';
156
+ case TokenType.LTE: return '<=';
157
+ case TokenType.EQ: return '=';
158
+ case TokenType.NEQ: return '!=';
159
+ case TokenType.AND: return 'and';
160
+ case TokenType.OR: return 'or';
161
+ case TokenType.XOR: return 'xor';
162
+ case TokenType.IMPLIES: return 'implies';
163
+ case TokenType.NOT: return 'not';
164
+ case TokenType.DOT: return '.';
165
+ case TokenType.CONTAINS: return 'contains';
166
+ case TokenType.IN: return 'in';
167
+ default: return token;
168
+ }
169
+ }
@@ -0,0 +1,257 @@
1
+ import type {
2
+ Analyzer,
3
+ TypeInfo,
4
+ TypeRef,
5
+ Operator,
6
+ Function,
7
+ Literal,
8
+ TypeConstraint,
9
+ TypeInferenceRule,
10
+ CardinalityInferenceRule
11
+ } from './types';
12
+ import type { ModelProvider } from '../analyzer/types';
13
+
14
+ // Helper to check if type matches constraint
15
+ export function matchesConstraint(type: TypeRef, constraint: TypeConstraint): boolean {
16
+ if (constraint.kind === 'any') return true;
17
+
18
+ if (!constraint.types) return true;
19
+
20
+ // For now, simple type name matching
21
+ // TODO: Handle inheritance and complex types
22
+ let typeName = typeof type === 'string' ? type : (type as any).type || (type as any).name || 'unknown';
23
+
24
+ // Normalize case - constraint types are capitalized, but actual types might be lowercase
25
+ const normalizedTypeName = typeName.charAt(0).toUpperCase() + typeName.slice(1);
26
+
27
+ return constraint.types.includes(normalizedTypeName) || constraint.types.includes(typeName);
28
+ }
29
+
30
+ // Helper to format constraint for error messages
31
+ export function formatConstraint(constraint: TypeConstraint): string {
32
+ if (constraint.kind === 'any') return 'any type';
33
+ if (!constraint.types || constraint.types.length === 0) return 'any type';
34
+ if (constraint.types.length === 1) return constraint.types[0]!;
35
+ return constraint.types.join(' or ');
36
+ }
37
+
38
+ // Helper to resolve type inference rule
39
+ export function resolveTypeInferenceRule(
40
+ rule: TypeInferenceRule,
41
+ input: TypeRef,
42
+ args: TypeRef[],
43
+ analyzer: Analyzer
44
+ ): TypeRef {
45
+ if (typeof rule === 'string') {
46
+ if (rule === 'preserve-input') return input;
47
+ if (rule === 'promote-numeric') {
48
+ // Check if we have numeric types
49
+ const types = [input, ...args].map(t => {
50
+ const rawType = typeof t === 'string' ? t : (t as any).type || (t as any).name || 'unknown';
51
+ // Normalize to uppercase first letter
52
+ return rawType.charAt(0).toUpperCase() + rawType.slice(1);
53
+ });
54
+ if (types.includes('Decimal')) return analyzer.resolveType('Decimal');
55
+ if (types.includes('Integer')) return analyzer.resolveType('Integer');
56
+ return input; // fallback
57
+ }
58
+ return analyzer.resolveType(rule); // Fixed type name - resolve through analyzer
59
+ }
60
+
61
+ if (typeof rule === 'function') {
62
+ // TODO: We need to pass ModelProvider to the rule function
63
+ // For now, just return input
64
+ return input;
65
+ }
66
+
67
+ return input; // fallback
68
+ }
69
+
70
+ // Helper to resolve cardinality inference rule
71
+ export function resolveCardinalityInferenceRule(
72
+ rule: CardinalityInferenceRule,
73
+ inputIsSingleton: boolean,
74
+ argsAreSingleton: boolean[]
75
+ ): boolean {
76
+ if (rule === 'singleton') return true;
77
+ if (rule === 'collection') return false;
78
+ if (rule === 'preserve-input') return inputIsSingleton;
79
+ if (rule === 'all-singleton') return inputIsSingleton && argsAreSingleton.every(s => s);
80
+
81
+ if (typeof rule === 'function') {
82
+ return rule(inputIsSingleton, argsAreSingleton);
83
+ }
84
+
85
+ return inputIsSingleton; // fallback
86
+ }
87
+
88
+ // Default analyzer for operators
89
+ export function defaultOperatorAnalyze(
90
+ this: Operator,
91
+ analyzer: Analyzer,
92
+ input: TypeInfo,
93
+ args: TypeInfo[]
94
+ ): TypeInfo {
95
+ const { signature } = this;
96
+
97
+ // Validate parameter count
98
+ const expectedParams = signature.parameters.length;
99
+ if (args.length !== expectedParams) {
100
+ analyzer.error(`Operator '${this.name}' expects ${expectedParams} parameter(s) but got ${args.length}`);
101
+ }
102
+
103
+ // Validate parameter types and cardinality
104
+ signature.parameters.forEach((param, i) => {
105
+ const arg = args[i];
106
+ if (!arg) return;
107
+
108
+ // Check type constraint
109
+ if (param.types && !matchesConstraint(arg.type, param.types)) {
110
+ const argTypeName = typeof arg.type === 'string'
111
+ ? arg.type
112
+ : (arg.type as any).type || (arg.type as any).name || 'unknown';
113
+ analyzer.error(
114
+ `Operator '${this.name}' parameter '${param.name}' expects ${formatConstraint(param.types)} but got ${argTypeName}`
115
+ );
116
+ }
117
+
118
+ // Check cardinality constraint
119
+ if (param.cardinality) {
120
+ if (param.cardinality === 'singleton' && !arg.isSingleton) {
121
+ analyzer.error(
122
+ `Operator '${this.name}' parameter '${param.name}' requires singleton value but got collection`
123
+ );
124
+ } else if (param.cardinality === 'collection' && arg.isSingleton) {
125
+ analyzer.error(
126
+ `Operator '${this.name}' parameter '${param.name}' requires collection but got singleton`
127
+ );
128
+ }
129
+ // 'any' accepts both
130
+ }
131
+ });
132
+
133
+ // Determine output type
134
+ const outputType = resolveTypeInferenceRule(
135
+ signature.output.type,
136
+ input.type,
137
+ args.map(a => a.type),
138
+ analyzer
139
+ );
140
+
141
+ // Determine output cardinality
142
+ const isSingleton = resolveCardinalityInferenceRule(
143
+ signature.output.cardinality,
144
+ input.isSingleton,
145
+ args.map(a => a.isSingleton)
146
+ );
147
+
148
+ return { type: outputType, isSingleton };
149
+ }
150
+
151
+ // Default analyzer for functions
152
+ export function defaultFunctionAnalyze(
153
+ this: Function,
154
+ analyzer: Analyzer,
155
+ input: TypeInfo,
156
+ args: TypeInfo[]
157
+ ): TypeInfo {
158
+ const { signature } = this;
159
+
160
+ // Check input constraints if specified
161
+ if (signature.input) {
162
+ if (signature.input.types && !matchesConstraint(input.type, signature.input.types)) {
163
+ const inputTypeName = typeof input.type === 'string'
164
+ ? input.type
165
+ : (input.type as any).type || (input.type as any).name || 'unknown';
166
+ analyzer.error(
167
+ `Function '${this.name}' expects input of type ${formatConstraint(signature.input.types)} but got ${inputTypeName}`
168
+ );
169
+ }
170
+
171
+ if (signature.input.cardinality) {
172
+ if (signature.input.cardinality === 'singleton' && !input.isSingleton) {
173
+ analyzer.error(`Function '${this.name}' requires singleton input but got collection`);
174
+ } else if (signature.input.cardinality === 'collection' && input.isSingleton) {
175
+ analyzer.error(`Function '${this.name}' requires collection input but got singleton`);
176
+ }
177
+ }
178
+ }
179
+
180
+ // Validate parameters
181
+ const requiredParams = signature.parameters.filter(p => !p.optional);
182
+ if (args.length < requiredParams.length) {
183
+ analyzer.error(
184
+ `Function '${this.name}' requires at least ${requiredParams.length} parameter(s) but got ${args.length}`
185
+ );
186
+ }
187
+
188
+ if (args.length > signature.parameters.length) {
189
+ analyzer.error(
190
+ `Function '${this.name}' expects at most ${signature.parameters.length} parameter(s) but got ${args.length}`
191
+ );
192
+ }
193
+
194
+ // Check each parameter
195
+ signature.parameters.forEach((param, i) => {
196
+ const arg = args[i];
197
+ if (!arg && !param.optional) {
198
+ analyzer.error(`Function '${this.name}' parameter '${param.name}' is required`);
199
+ return;
200
+ }
201
+ if (!arg) return; // Optional parameter not provided
202
+
203
+ // Check type constraint
204
+ if (param.types && !matchesConstraint(arg.type, param.types)) {
205
+ const argTypeName = typeof arg.type === 'string'
206
+ ? arg.type
207
+ : (arg.type as any).type || (arg.type as any).name || 'unknown';
208
+ analyzer.error(
209
+ `Function '${this.name}' parameter '${param.name}' expects ${formatConstraint(param.types)} but got ${argTypeName}`
210
+ );
211
+ }
212
+
213
+ // Check cardinality constraint
214
+ if (param.cardinality) {
215
+ if (param.cardinality === 'singleton' && !arg.isSingleton) {
216
+ analyzer.error(
217
+ `Function '${this.name}' parameter '${param.name}' requires singleton value but got collection`
218
+ );
219
+ } else if (param.cardinality === 'collection' && arg.isSingleton) {
220
+ analyzer.error(
221
+ `Function '${this.name}' parameter '${param.name}' requires collection but got singleton`
222
+ );
223
+ }
224
+ }
225
+ });
226
+
227
+ // Determine output type
228
+ const outputType = resolveTypeInferenceRule(
229
+ signature.output.type,
230
+ input.type,
231
+ args.map(a => a.type),
232
+ analyzer
233
+ );
234
+
235
+ // Determine output cardinality
236
+ const isSingleton = resolveCardinalityInferenceRule(
237
+ signature.output.cardinality,
238
+ input.isSingleton,
239
+ args.map(a => a.isSingleton)
240
+ );
241
+
242
+ return { type: outputType, isSingleton };
243
+ }
244
+
245
+ // Default analyzer for literals
246
+ export function defaultLiteralAnalyze(
247
+ this: Literal,
248
+ analyzer: Analyzer,
249
+ input: TypeInfo,
250
+ args: TypeInfo[]
251
+ ): TypeInfo {
252
+ // Literals have fixed output type and cardinality
253
+ return {
254
+ type: analyzer.resolveType(this.signature.output.type),
255
+ isSingleton: true
256
+ };
257
+ }
@@ -0,0 +1,31 @@
1
+ import type { Compiler, CompiledExpression } from './types';
2
+
3
+ export function defaultFunctionCompile(
4
+ compiler: Compiler,
5
+ input: CompiledExpression,
6
+ args: CompiledExpression[]
7
+ ): CompiledExpression {
8
+ // Default implementation returns a simple pass-through
9
+ return {
10
+ fn: (ctx) => {
11
+ throw new Error('Function compile not implemented');
12
+ },
13
+ type: compiler.resolveType('Any'),
14
+ isSingleton: false
15
+ };
16
+ }
17
+
18
+ export function defaultOperatorCompile(
19
+ compiler: Compiler,
20
+ left: CompiledExpression,
21
+ right: CompiledExpression[]
22
+ ): CompiledExpression {
23
+ // Default implementation returns a simple pass-through
24
+ return {
25
+ fn: (ctx) => {
26
+ throw new Error('Operator compile not implemented');
27
+ },
28
+ type: compiler.resolveType('Any'),
29
+ isSingleton: false
30
+ };
31
+ }
@@ -0,0 +1,93 @@
1
+ import { Registry } from './registry';
2
+ import { arithmeticOperators } from './operations/arithmetic';
3
+ import { logicalOperators } from './operations/logical';
4
+ import { comparisonOperators } from './operations/comparison';
5
+ import { membershipOperators } from './operations/membership';
6
+ import { typeOperators } from './operations/type-operators';
7
+ import { literals } from './operations/literals';
8
+ import { existenceFunctions } from './operations/existence';
9
+ import {
10
+ aggregateFunction,
11
+ childrenFunction,
12
+ descendantsFunction,
13
+ iifFunction,
14
+ defineVariableFunction,
15
+ traceFunction,
16
+ checkFunction
17
+ } from './operations/utility';
18
+ import { typeFunction, isFunction, asFunction } from './operations/type-checking';
19
+ import { absFunction, roundFunction, sqrtFunction } from './operations/math';
20
+ import { whereFunction, selectFunction, ofTypeFunction, repeatFunction } from './operations/filtering';
21
+ import {
22
+ containsFunction, lengthFunction, substringFunction, startsWithFunction,
23
+ endsWithFunction, upperFunction, lowerFunction, indexOfFunction,
24
+ replaceFunction, splitFunction, joinFunction, trimFunction, toCharsFunction
25
+ } from './operations/string';
26
+ import {
27
+ toStringFunction, toIntegerFunction, toDecimalFunction,
28
+ toBooleanFunction, toQuantityFunction
29
+ } from './operations/type-conversion';
30
+ import { tailFunction, skipFunction, takeFunction } from './operations/subsetting';
31
+ import { unionFunction, combineFunction, intersectFunction, excludeFunction, collectionOperators } from './operations/collection';
32
+
33
+ // Export types
34
+ export * from './types';
35
+ export { Registry } from './registry';
36
+ export * from './default-analyzers';
37
+
38
+ // Register all operations on module load
39
+ [
40
+ ...arithmeticOperators,
41
+ ...logicalOperators,
42
+ ...comparisonOperators,
43
+ ...membershipOperators,
44
+ ...typeOperators,
45
+ ...collectionOperators,
46
+ ...literals,
47
+ ...existenceFunctions,
48
+ aggregateFunction,
49
+ childrenFunction,
50
+ descendantsFunction,
51
+ iifFunction,
52
+ defineVariableFunction,
53
+ traceFunction,
54
+ checkFunction,
55
+ typeFunction,
56
+ isFunction,
57
+ asFunction,
58
+ absFunction,
59
+ roundFunction,
60
+ sqrtFunction,
61
+ whereFunction,
62
+ selectFunction,
63
+ ofTypeFunction,
64
+ repeatFunction,
65
+ containsFunction,
66
+ lengthFunction,
67
+ substringFunction,
68
+ startsWithFunction,
69
+ endsWithFunction,
70
+ upperFunction,
71
+ lowerFunction,
72
+ indexOfFunction,
73
+ replaceFunction,
74
+ splitFunction,
75
+ joinFunction,
76
+ trimFunction,
77
+ toCharsFunction,
78
+ toStringFunction,
79
+ toIntegerFunction,
80
+ toDecimalFunction,
81
+ toBooleanFunction,
82
+ toQuantityFunction,
83
+ tailFunction,
84
+ skipFunction,
85
+ takeFunction,
86
+ unionFunction,
87
+ combineFunction,
88
+ intersectFunction,
89
+ excludeFunction
90
+ ].forEach(op => Registry.register(op));
91
+
92
+ // Export default registry instance
93
+ export default Registry;