@object-ui/core 3.3.0 → 3.3.2

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 (101) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +20 -1
  3. package/dist/actions/ActionRunner.d.ts +9 -0
  4. package/dist/actions/ActionRunner.js +41 -4
  5. package/dist/adapters/ValueDataSource.js +3 -1
  6. package/dist/registry/Registry.d.ts +47 -0
  7. package/dist/registry/Registry.js +92 -0
  8. package/dist/utils/filter-converter.js +25 -5
  9. package/package.json +32 -8
  10. package/.turbo/turbo-build.log +0 -4
  11. package/src/__benchmarks__/core.bench.ts +0 -64
  12. package/src/__tests__/protocols/DndProtocol.test.ts +0 -186
  13. package/src/__tests__/protocols/KeyboardProtocol.test.ts +0 -177
  14. package/src/__tests__/protocols/NotificationProtocol.test.ts +0 -142
  15. package/src/__tests__/protocols/ResponsiveProtocol.test.ts +0 -176
  16. package/src/__tests__/protocols/SharingProtocol.test.ts +0 -188
  17. package/src/actions/ActionEngine.ts +0 -268
  18. package/src/actions/ActionRunner.ts +0 -717
  19. package/src/actions/TransactionManager.ts +0 -521
  20. package/src/actions/UndoManager.ts +0 -215
  21. package/src/actions/__tests__/ActionEngine.test.ts +0 -206
  22. package/src/actions/__tests__/ActionRunner.params.test.ts +0 -134
  23. package/src/actions/__tests__/ActionRunner.test.ts +0 -711
  24. package/src/actions/__tests__/TransactionManager.test.ts +0 -447
  25. package/src/actions/__tests__/UndoManager.test.ts +0 -320
  26. package/src/actions/index.ts +0 -12
  27. package/src/adapters/ApiDataSource.ts +0 -376
  28. package/src/adapters/README.md +0 -180
  29. package/src/adapters/ValueDataSource.ts +0 -459
  30. package/src/adapters/__tests__/ApiDataSource.test.ts +0 -418
  31. package/src/adapters/__tests__/ValueDataSource.test.ts +0 -571
  32. package/src/adapters/__tests__/resolveDataSource.test.ts +0 -144
  33. package/src/adapters/index.ts +0 -15
  34. package/src/adapters/resolveDataSource.ts +0 -79
  35. package/src/builder/__tests__/schema-builder.test.ts +0 -235
  36. package/src/builder/schema-builder.ts +0 -584
  37. package/src/data-scope/DataScopeManager.ts +0 -269
  38. package/src/data-scope/ViewDataProvider.ts +0 -282
  39. package/src/data-scope/__tests__/DataScopeManager.test.ts +0 -211
  40. package/src/data-scope/__tests__/ViewDataProvider.test.ts +0 -270
  41. package/src/data-scope/index.ts +0 -24
  42. package/src/errors/__tests__/errors.test.ts +0 -292
  43. package/src/errors/index.ts +0 -269
  44. package/src/evaluator/ExpressionCache.ts +0 -206
  45. package/src/evaluator/ExpressionContext.ts +0 -118
  46. package/src/evaluator/ExpressionEvaluator.ts +0 -315
  47. package/src/evaluator/FormulaFunctions.ts +0 -398
  48. package/src/evaluator/SafeExpressionParser.ts +0 -893
  49. package/src/evaluator/__tests__/ExpressionCache.test.ts +0 -135
  50. package/src/evaluator/__tests__/ExpressionContext.test.ts +0 -110
  51. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +0 -558
  52. package/src/evaluator/__tests__/FormulaFunctions.test.ts +0 -447
  53. package/src/evaluator/index.ts +0 -13
  54. package/src/index.ts +0 -38
  55. package/src/protocols/DndProtocol.ts +0 -168
  56. package/src/protocols/KeyboardProtocol.ts +0 -181
  57. package/src/protocols/NotificationProtocol.ts +0 -150
  58. package/src/protocols/ResponsiveProtocol.ts +0 -210
  59. package/src/protocols/SharingProtocol.ts +0 -185
  60. package/src/protocols/index.ts +0 -13
  61. package/src/query/__tests__/query-ast.test.ts +0 -211
  62. package/src/query/__tests__/window-functions.test.ts +0 -275
  63. package/src/query/index.ts +0 -7
  64. package/src/query/query-ast.ts +0 -341
  65. package/src/registry/PluginScopeImpl.ts +0 -259
  66. package/src/registry/PluginSystem.ts +0 -206
  67. package/src/registry/Registry.ts +0 -219
  68. package/src/registry/WidgetRegistry.ts +0 -316
  69. package/src/registry/__tests__/PluginSystem.test.ts +0 -309
  70. package/src/registry/__tests__/Registry.test.ts +0 -293
  71. package/src/registry/__tests__/WidgetRegistry.test.ts +0 -321
  72. package/src/registry/__tests__/plugin-scope-integration.test.ts +0 -283
  73. package/src/theme/ThemeEngine.ts +0 -530
  74. package/src/theme/__tests__/ThemeEngine.test.ts +0 -668
  75. package/src/theme/index.ts +0 -24
  76. package/src/types/index.ts +0 -21
  77. package/src/utils/__tests__/debug-collector.test.ts +0 -102
  78. package/src/utils/__tests__/debug.test.ts +0 -134
  79. package/src/utils/__tests__/expand-fields.test.ts +0 -120
  80. package/src/utils/__tests__/extract-records.test.ts +0 -50
  81. package/src/utils/__tests__/filter-converter.test.ts +0 -118
  82. package/src/utils/__tests__/merge-views-into-objects.test.ts +0 -110
  83. package/src/utils/__tests__/normalize-quick-filter.test.ts +0 -123
  84. package/src/utils/debug-collector.ts +0 -100
  85. package/src/utils/debug.ts +0 -148
  86. package/src/utils/expand-fields.ts +0 -76
  87. package/src/utils/extract-records.ts +0 -33
  88. package/src/utils/filter-converter.ts +0 -133
  89. package/src/utils/merge-views-into-objects.ts +0 -36
  90. package/src/utils/normalize-quick-filter.ts +0 -78
  91. package/src/validation/__tests__/object-validation-engine.test.ts +0 -567
  92. package/src/validation/__tests__/schema-validator.test.ts +0 -118
  93. package/src/validation/__tests__/validation-engine.test.ts +0 -102
  94. package/src/validation/index.ts +0 -10
  95. package/src/validation/schema-validator.ts +0 -344
  96. package/src/validation/validation-engine.ts +0 -528
  97. package/src/validation/validators/index.ts +0 -25
  98. package/src/validation/validators/object-validation-engine.ts +0 -722
  99. package/tsconfig.json +0 -15
  100. package/tsconfig.tsbuildinfo +0 -1
  101. package/vitest.config.ts +0 -2
@@ -1,206 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- /**
10
- * @object-ui/core - Expression Cache
11
- *
12
- * Caches compiled expressions to avoid re-parsing on every render.
13
- * Provides significant performance improvement for frequently evaluated expressions.
14
- *
15
- * @module evaluator
16
- * @packageDocumentation
17
- */
18
-
19
- import { SafeExpressionParser } from './SafeExpressionParser.js';
20
-
21
- /**
22
- * A compiled expression function that can be executed with context values
23
- */
24
- export type CompiledExpression = (...args: any[]) => any;
25
-
26
- /**
27
- * Expression compilation metadata
28
- */
29
- export interface ExpressionMetadata {
30
- /**
31
- * The compiled function
32
- */
33
- fn: CompiledExpression;
34
-
35
- /**
36
- * Variable names used in the expression
37
- */
38
- varNames: string[];
39
-
40
- /**
41
- * Original expression string
42
- */
43
- expression: string;
44
-
45
- /**
46
- * Timestamp when the expression was compiled
47
- */
48
- compiledAt: number;
49
-
50
- /**
51
- * Number of times this expression has been used
52
- */
53
- hitCount: number;
54
- }
55
-
56
- /**
57
- * Cache for compiled expressions
58
- *
59
- * @example
60
- * ```ts
61
- * const cache = new ExpressionCache();
62
- * const compiled = cache.compile('data.amount > 1000', ['data']);
63
- * const result = compiled.fn({ amount: 1500 }); // true
64
- * ```
65
- */
66
- export class ExpressionCache {
67
- private cache = new Map<string, ExpressionMetadata>();
68
- private maxSize: number;
69
-
70
- /**
71
- * Create a new expression cache
72
- *
73
- * @param maxSize Maximum number of expressions to cache (default: 1000)
74
- */
75
- constructor(maxSize = 1000) {
76
- this.maxSize = maxSize;
77
- }
78
-
79
- /**
80
- * Compile an expression or retrieve from cache
81
- *
82
- * @param expr The expression to compile
83
- * @param varNames Variable names available in the context
84
- * @returns Compiled expression metadata
85
- */
86
- compile(expr: string, varNames: string[]): ExpressionMetadata {
87
- // Create a cache key that includes variable names to ensure correct scoping
88
- const cacheKey = `${expr}::${varNames.join(',')}`;
89
-
90
- if (this.cache.has(cacheKey)) {
91
- const metadata = this.cache.get(cacheKey)!;
92
- metadata.hitCount++;
93
- return metadata;
94
- }
95
-
96
- // Evict least frequently used if cache is full
97
- if (this.cache.size >= this.maxSize) {
98
- this.evictLFU();
99
- }
100
-
101
- // Compile the expression
102
- const fn = this.compileExpression(expr, varNames);
103
-
104
- const metadata: ExpressionMetadata = {
105
- fn,
106
- varNames: [...varNames],
107
- expression: expr,
108
- compiledAt: Date.now(),
109
- hitCount: 1,
110
- };
111
-
112
- this.cache.set(cacheKey, metadata);
113
- return metadata;
114
- }
115
-
116
- /**
117
- * Compile an expression into a CSP-safe callable function.
118
- *
119
- * Uses `SafeExpressionParser` — a recursive-descent interpreter — instead of
120
- * `new Function()` so that the expression engine works under strict
121
- * Content Security Policy headers that forbid `'unsafe-eval'`.
122
- *
123
- * A single parser instance is created per compiled expression and reused
124
- * across all invocations of the returned closure (`evaluate()` resets all
125
- * internal state on every call), avoiding repeated allocations on hot paths.
126
- */
127
- private compileExpression(expression: string, varNames: string[]): CompiledExpression {
128
- // One parser per compiled expression — reused across hot-path calls.
129
- const parser = new SafeExpressionParser();
130
-
131
- return (...args: unknown[]) => {
132
- // Reconstruct the named variable context from positional arguments.
133
- const context: Record<string, unknown> = {};
134
- for (let i = 0; i < varNames.length; i++) {
135
- context[varNames[i]] = args[i];
136
- }
137
- return parser.evaluate(expression, context);
138
- };
139
- }
140
-
141
- /**
142
- * Evict the least frequently used expression from cache
143
- */
144
- private evictLFU(): void {
145
- let oldestKey: string | null = null;
146
- let oldestTime = Infinity;
147
- let lowestHits = Infinity;
148
-
149
- // Find the entry with lowest hit count, or oldest if tied
150
- for (const [key, metadata] of this.cache.entries()) {
151
- if (metadata.hitCount < lowestHits ||
152
- (metadata.hitCount === lowestHits && metadata.compiledAt < oldestTime)) {
153
- oldestKey = key;
154
- oldestTime = metadata.compiledAt;
155
- lowestHits = metadata.hitCount;
156
- }
157
- }
158
-
159
- if (oldestKey) {
160
- this.cache.delete(oldestKey);
161
- }
162
- }
163
-
164
- /**
165
- * Check if an expression is cached
166
- */
167
- has(expr: string, varNames: string[]): boolean {
168
- const cacheKey = `${expr}::${varNames.join(',')}`;
169
- return this.cache.has(cacheKey);
170
- }
171
-
172
- /**
173
- * Clear the cache
174
- */
175
- clear(): void {
176
- this.cache.clear();
177
- }
178
-
179
- /**
180
- * Get cache statistics
181
- */
182
- getStats(): {
183
- size: number;
184
- maxSize: number;
185
- totalHits: number;
186
- entries: Array<{ expression: string; hitCount: number }>;
187
- } {
188
- let totalHits = 0;
189
- const entries: Array<{ expression: string; hitCount: number }> = [];
190
-
191
- for (const metadata of this.cache.values()) {
192
- totalHits += metadata.hitCount;
193
- entries.push({
194
- expression: metadata.expression,
195
- hitCount: metadata.hitCount,
196
- });
197
- }
198
-
199
- return {
200
- size: this.cache.size,
201
- maxSize: this.maxSize,
202
- totalHits,
203
- entries: entries.sort((a, b) => b.hitCount - a.hitCount),
204
- };
205
- }
206
- }
@@ -1,118 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- /**
10
- * @object-ui/core - Expression Context
11
- *
12
- * Manages variable scope and data context for expression evaluation.
13
- *
14
- * @module evaluator
15
- * @packageDocumentation
16
- */
17
-
18
- /**
19
- * Expression context for variable resolution
20
- */
21
- export class ExpressionContext {
22
- private scopes: Map<string, any>[] = [];
23
-
24
- constructor(initialData: Record<string, any> = {}) {
25
- this.scopes.push(new Map(Object.entries(initialData)));
26
- }
27
-
28
- /**
29
- * Push a new scope onto the context stack
30
- */
31
- pushScope(data: Record<string, any>): void {
32
- this.scopes.push(new Map(Object.entries(data)));
33
- }
34
-
35
- /**
36
- * Pop the current scope from the context stack
37
- */
38
- popScope(): void {
39
- if (this.scopes.length > 1) {
40
- this.scopes.pop();
41
- }
42
- }
43
-
44
- /**
45
- * Get a variable value from the context
46
- * Searches from innermost to outermost scope
47
- */
48
- get(path: string): any {
49
- // Split path by dots for nested access
50
- const parts = path.split('.');
51
- const varName = parts[0];
52
-
53
- // Search scopes from innermost to outermost
54
- for (let i = this.scopes.length - 1; i >= 0; i--) {
55
- if (this.scopes[i].has(varName)) {
56
- let value = this.scopes[i].get(varName);
57
-
58
- // Navigate nested path
59
- for (let j = 1; j < parts.length; j++) {
60
- if (value && typeof value === 'object') {
61
- value = value[parts[j]];
62
- } else {
63
- return undefined;
64
- }
65
- }
66
-
67
- return value;
68
- }
69
- }
70
-
71
- return undefined;
72
- }
73
-
74
- /**
75
- * Set a variable value in the current scope
76
- */
77
- set(name: string, value: any): void {
78
- if (this.scopes.length > 0) {
79
- this.scopes[this.scopes.length - 1].set(name, value);
80
- }
81
- }
82
-
83
- /**
84
- * Check if a variable exists in any scope
85
- */
86
- has(name: string): boolean {
87
- const varName = name.split('.')[0];
88
- for (let i = this.scopes.length - 1; i >= 0; i--) {
89
- if (this.scopes[i].has(varName)) {
90
- return true;
91
- }
92
- }
93
- return false;
94
- }
95
-
96
- /**
97
- * Get all variables from all scopes as a flat object
98
- */
99
- toObject(): Record<string, any> {
100
- const result: Record<string, any> = {};
101
- // Merge from outermost to innermost (later scopes override earlier ones)
102
- for (const scope of this.scopes) {
103
- for (const [key, value] of scope.entries()) {
104
- result[key] = value;
105
- }
106
- }
107
- return result;
108
- }
109
-
110
- /**
111
- * Create a child context with additional data
112
- */
113
- createChild(data: Record<string, any> = {}): ExpressionContext {
114
- const child = new ExpressionContext();
115
- child.scopes = [...this.scopes, new Map(Object.entries(data))];
116
- return child;
117
- }
118
- }
@@ -1,315 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- /**
10
- * @object-ui/core - Expression Evaluator
11
- *
12
- * Evaluates template string expressions like ${data.amount > 1000} for dynamic UI behavior.
13
- * Supports variable substitution, comparison operators, and basic JavaScript expressions.
14
- *
15
- * @module evaluator
16
- * @packageDocumentation
17
- */
18
-
19
- import { ExpressionContext } from './ExpressionContext.js';
20
- import { ExpressionCache } from './ExpressionCache.js';
21
- import { FormulaFunctions } from './FormulaFunctions.js';
22
-
23
- /**
24
- * Options for expression evaluation
25
- */
26
- export interface EvaluationOptions {
27
- /**
28
- * Default value to return if evaluation fails
29
- */
30
- defaultValue?: any;
31
-
32
- /**
33
- * Whether to throw errors on evaluation failure
34
- * @default false
35
- */
36
- throwOnError?: boolean;
37
-
38
- /**
39
- * Whether to sanitize the expression before evaluation
40
- * @default true
41
- */
42
- sanitize?: boolean;
43
- }
44
-
45
- /**
46
- * Expression evaluator for dynamic UI expressions
47
- */
48
- export class ExpressionEvaluator {
49
- private context: ExpressionContext;
50
- private cache: ExpressionCache;
51
- private formulas: FormulaFunctions;
52
-
53
- constructor(
54
- context?: ExpressionContext | Record<string, any>,
55
- cache?: ExpressionCache,
56
- formulas?: FormulaFunctions,
57
- ) {
58
- if (context instanceof ExpressionContext) {
59
- this.context = context;
60
- } else {
61
- this.context = new ExpressionContext(context || {});
62
- }
63
-
64
- // Use provided cache or create a new one
65
- this.cache = cache || new ExpressionCache();
66
- this.formulas = formulas || new FormulaFunctions();
67
- }
68
-
69
- /**
70
- * Evaluate a string that may contain template expressions like ${...}
71
- *
72
- * @example
73
- * ```ts
74
- * const evaluator = new ExpressionEvaluator({ data: { amount: 1500 } });
75
- * evaluator.evaluate('${data.amount > 1000}'); // Returns: true
76
- * evaluator.evaluate('Amount is ${data.amount}'); // Returns: "Amount is 1500"
77
- * ```
78
- */
79
- evaluate(expression: string | boolean | number | null | undefined, options: EvaluationOptions = {}): any {
80
- // Handle non-string primitives
81
- if (typeof expression !== 'string') {
82
- return expression;
83
- }
84
-
85
- const { defaultValue, throwOnError = false, sanitize = true } = options;
86
-
87
- try {
88
- // Check if string contains template expressions
89
- const hasTemplates = expression.includes('${');
90
-
91
- if (!hasTemplates) {
92
- // No templates, return as-is
93
- return expression;
94
- }
95
-
96
- // Special case: if the entire string is a single template expression, return the value directly
97
- const singleTemplateMatch = expression.match(/^\$\{([^}]+)\}$/);
98
- if (singleTemplateMatch) {
99
- return this.evaluateExpression(singleTemplateMatch[1].trim(), { sanitize });
100
- }
101
-
102
- // Replace all ${...} expressions in a string with multiple parts
103
- return expression.replace(/\$\{([^}]+)\}/g, (match, expr) => {
104
- try {
105
- const result = this.evaluateExpression(expr.trim(), { sanitize });
106
- return String(result ?? '');
107
- } catch (error) {
108
- if (throwOnError) {
109
- throw error;
110
- }
111
- console.warn(`Expression evaluation failed for: ${expr}`, error);
112
- return match; // Return original if evaluation fails
113
- }
114
- });
115
- } catch (error) {
116
- if (throwOnError) {
117
- throw error;
118
- }
119
- console.warn(`Failed to evaluate expression: ${expression}`, error);
120
- return defaultValue ?? expression;
121
- }
122
- }
123
-
124
- /**
125
- * Evaluate a single expression (without ${} wrapper)
126
- *
127
- * @example
128
- * ```ts
129
- * evaluator.evaluateExpression('data.amount > 1000'); // Returns: true
130
- * evaluator.evaluateExpression('data.user.name'); // Returns: "John"
131
- * ```
132
- */
133
- evaluateExpression(expression: string, options: { sanitize?: boolean } = {}): any {
134
- const { sanitize = true } = options;
135
-
136
- if (!expression || expression.trim() === '') {
137
- return undefined;
138
- }
139
-
140
- // Sanitize expression to prevent dangerous code execution
141
- if (sanitize && this.isDangerous(expression)) {
142
- throw new Error(`Potentially dangerous expression detected: ${expression}`);
143
- }
144
-
145
- try {
146
- // Create a safe evaluation function
147
- const contextObj = this.context.toObject();
148
-
149
- // Inject formula functions into the evaluation context
150
- const formulaObj = this.formulas.toObject();
151
- const mergedContext = { ...formulaObj, ...contextObj };
152
-
153
- // Build safe function with context variables
154
- const varNames = Object.keys(mergedContext);
155
- const varValues = Object.values(mergedContext);
156
-
157
- // Use cached compilation
158
- const compiled = this.cache.compile(expression, varNames);
159
-
160
- // Execute with context values
161
- return compiled.fn(...varValues);
162
- } catch (error) {
163
- throw new Error(`Failed to evaluate expression "${expression}": ${(error as Error).message}`);
164
- }
165
- }
166
-
167
- /**
168
- * Check if expression contains potentially dangerous code
169
- */
170
- private isDangerous(expression: string): boolean {
171
- const dangerousPatterns = [
172
- /eval\s*\(/i,
173
- /Function\s*\(/i,
174
- /setTimeout\s*\(/i,
175
- /setInterval\s*\(/i,
176
- /import\s*\(/i,
177
- /require\s*\(/i,
178
- /process\./i,
179
- /global\./i,
180
- /window\./i,
181
- /document\./i,
182
- /__proto__/i,
183
- /constructor\s*\(/i,
184
- /prototype\./i,
185
- ];
186
-
187
- return dangerousPatterns.some(pattern => pattern.test(expression));
188
- }
189
-
190
- /**
191
- * Evaluate a conditional expression and return boolean
192
- *
193
- * @example
194
- * ```ts
195
- * evaluator.evaluateCondition('${data.age >= 18}'); // Returns: true/false
196
- * ```
197
- */
198
- evaluateCondition(condition: string | boolean | undefined, options: EvaluationOptions = {}): boolean {
199
- if (typeof condition === 'boolean') {
200
- return condition;
201
- }
202
-
203
- if (!condition) {
204
- return true; // Default to visible/enabled if no condition
205
- }
206
-
207
- const result = this.evaluate(condition, options);
208
-
209
- // Convert result to boolean
210
- return Boolean(result);
211
- }
212
-
213
- /**
214
- * Update the context with new data
215
- */
216
- updateContext(data: Record<string, any>): void {
217
- Object.entries(data).forEach(([key, value]) => {
218
- this.context.set(key, value);
219
- });
220
- }
221
-
222
- /**
223
- * Get the current context
224
- */
225
- getContext(): ExpressionContext {
226
- return this.context;
227
- }
228
-
229
- /**
230
- * Create a new evaluator with additional context data
231
- */
232
- withContext(data: Record<string, any>): ExpressionEvaluator {
233
- // Share the cache and formulas with the new evaluator for maximum efficiency
234
- return new ExpressionEvaluator(this.context.createChild(data), this.cache, this.formulas);
235
- }
236
-
237
- /**
238
- * Get cache statistics (useful for debugging and optimization)
239
- */
240
- getCacheStats() {
241
- return this.cache.getStats();
242
- }
243
-
244
- /**
245
- * Clear the expression cache
246
- */
247
- clearCache(): void {
248
- this.cache.clear();
249
- }
250
-
251
- /**
252
- * Get the formula functions registry
253
- */
254
- getFormulas(): FormulaFunctions {
255
- return this.formulas;
256
- }
257
-
258
- /**
259
- * Register a custom formula function
260
- */
261
- registerFunction(name: string, fn: (...args: any[]) => any): void {
262
- this.formulas.register(name, fn);
263
- }
264
- }
265
-
266
- /**
267
- * Shared global cache and formulas for convenience functions
268
- */
269
- const globalCache = new ExpressionCache();
270
- const globalFormulas = new FormulaFunctions();
271
-
272
- /**
273
- * Convenience function to quickly evaluate an expression
274
- */
275
- export function evaluateExpression(
276
- expression: string | boolean | number | null | undefined,
277
- context: Record<string, any> = {},
278
- options: EvaluationOptions = {}
279
- ): any {
280
- const evaluator = new ExpressionEvaluator(context, globalCache, globalFormulas);
281
- return evaluator.evaluate(expression, options);
282
- }
283
-
284
- /**
285
- * Convenience function to evaluate a condition
286
- */
287
- export function evaluateCondition(
288
- condition: string | boolean | undefined,
289
- context: Record<string, any> = {}
290
- ): boolean {
291
- const evaluator = new ExpressionEvaluator(context, globalCache, globalFormulas);
292
- return evaluator.evaluateCondition(condition);
293
- }
294
-
295
- /**
296
- * Convenience function to evaluate a plain condition string against a data record.
297
- * Supports both template expressions (e.g., '${data.amount > 1000}') and
298
- * plain expressions (e.g., "status == 'overdue'").
299
- * Record fields are available both directly (status) and namespaced (data.status).
300
- */
301
- export function evaluatePlainCondition(
302
- condition: string,
303
- record: Record<string, any>
304
- ): boolean {
305
- const evaluator = new ExpressionEvaluator({ ...record, data: record }, globalCache, globalFormulas);
306
- try {
307
- const isTemplate = /\$\{/.test(condition);
308
- const result = isTemplate
309
- ? evaluator.evaluate(condition, { throwOnError: true })
310
- : evaluator.evaluateExpression(condition);
311
- return result === true;
312
- } catch {
313
- return false;
314
- }
315
- }