@object-ui/core 3.1.5 → 3.3.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.
Files changed (110) hide show
  1. package/CHANGELOG.md +18 -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.d.ts +5 -1
  6. package/dist/adapters/ValueDataSource.js +30 -1
  7. package/dist/errors/index.js +2 -3
  8. package/dist/evaluator/ExpressionCache.d.ts +9 -10
  9. package/dist/evaluator/ExpressionCache.js +29 -8
  10. package/dist/evaluator/SafeExpressionParser.d.ts +131 -0
  11. package/dist/evaluator/SafeExpressionParser.js +851 -0
  12. package/dist/evaluator/index.d.ts +1 -0
  13. package/dist/evaluator/index.js +1 -0
  14. package/dist/protocols/DndProtocol.js +2 -14
  15. package/dist/protocols/KeyboardProtocol.js +1 -4
  16. package/dist/protocols/NotificationProtocol.js +3 -13
  17. package/dist/utils/debug.js +2 -1
  18. package/dist/utils/filter-converter.js +25 -5
  19. package/package.json +33 -9
  20. package/.turbo/turbo-build.log +0 -4
  21. package/src/__benchmarks__/core.bench.ts +0 -64
  22. package/src/__tests__/protocols/DndProtocol.test.ts +0 -186
  23. package/src/__tests__/protocols/KeyboardProtocol.test.ts +0 -177
  24. package/src/__tests__/protocols/NotificationProtocol.test.ts +0 -142
  25. package/src/__tests__/protocols/ResponsiveProtocol.test.ts +0 -176
  26. package/src/__tests__/protocols/SharingProtocol.test.ts +0 -188
  27. package/src/actions/ActionEngine.ts +0 -268
  28. package/src/actions/ActionRunner.ts +0 -717
  29. package/src/actions/TransactionManager.ts +0 -521
  30. package/src/actions/UndoManager.ts +0 -215
  31. package/src/actions/__tests__/ActionEngine.test.ts +0 -206
  32. package/src/actions/__tests__/ActionRunner.params.test.ts +0 -134
  33. package/src/actions/__tests__/ActionRunner.test.ts +0 -711
  34. package/src/actions/__tests__/TransactionManager.test.ts +0 -447
  35. package/src/actions/__tests__/UndoManager.test.ts +0 -320
  36. package/src/actions/index.ts +0 -12
  37. package/src/adapters/ApiDataSource.ts +0 -376
  38. package/src/adapters/README.md +0 -180
  39. package/src/adapters/ValueDataSource.ts +0 -438
  40. package/src/adapters/__tests__/ApiDataSource.test.ts +0 -418
  41. package/src/adapters/__tests__/ValueDataSource.test.ts +0 -472
  42. package/src/adapters/__tests__/resolveDataSource.test.ts +0 -144
  43. package/src/adapters/index.ts +0 -15
  44. package/src/adapters/resolveDataSource.ts +0 -79
  45. package/src/builder/__tests__/schema-builder.test.ts +0 -235
  46. package/src/builder/schema-builder.ts +0 -584
  47. package/src/data-scope/DataScopeManager.ts +0 -269
  48. package/src/data-scope/ViewDataProvider.ts +0 -282
  49. package/src/data-scope/__tests__/DataScopeManager.test.ts +0 -211
  50. package/src/data-scope/__tests__/ViewDataProvider.test.ts +0 -270
  51. package/src/data-scope/index.ts +0 -24
  52. package/src/errors/__tests__/errors.test.ts +0 -292
  53. package/src/errors/index.ts +0 -270
  54. package/src/evaluator/ExpressionCache.ts +0 -192
  55. package/src/evaluator/ExpressionContext.ts +0 -118
  56. package/src/evaluator/ExpressionEvaluator.ts +0 -315
  57. package/src/evaluator/FormulaFunctions.ts +0 -398
  58. package/src/evaluator/__tests__/ExpressionCache.test.ts +0 -135
  59. package/src/evaluator/__tests__/ExpressionContext.test.ts +0 -110
  60. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +0 -131
  61. package/src/evaluator/__tests__/FormulaFunctions.test.ts +0 -447
  62. package/src/evaluator/index.ts +0 -12
  63. package/src/index.ts +0 -38
  64. package/src/protocols/DndProtocol.ts +0 -184
  65. package/src/protocols/KeyboardProtocol.ts +0 -185
  66. package/src/protocols/NotificationProtocol.ts +0 -159
  67. package/src/protocols/ResponsiveProtocol.ts +0 -210
  68. package/src/protocols/SharingProtocol.ts +0 -185
  69. package/src/protocols/index.ts +0 -13
  70. package/src/query/__tests__/query-ast.test.ts +0 -211
  71. package/src/query/__tests__/window-functions.test.ts +0 -275
  72. package/src/query/index.ts +0 -7
  73. package/src/query/query-ast.ts +0 -341
  74. package/src/registry/PluginScopeImpl.ts +0 -259
  75. package/src/registry/PluginSystem.ts +0 -206
  76. package/src/registry/Registry.ts +0 -219
  77. package/src/registry/WidgetRegistry.ts +0 -316
  78. package/src/registry/__tests__/PluginSystem.test.ts +0 -309
  79. package/src/registry/__tests__/Registry.test.ts +0 -293
  80. package/src/registry/__tests__/WidgetRegistry.test.ts +0 -321
  81. package/src/registry/__tests__/plugin-scope-integration.test.ts +0 -283
  82. package/src/theme/ThemeEngine.ts +0 -530
  83. package/src/theme/__tests__/ThemeEngine.test.ts +0 -668
  84. package/src/theme/index.ts +0 -24
  85. package/src/types/index.ts +0 -21
  86. package/src/utils/__tests__/debug-collector.test.ts +0 -102
  87. package/src/utils/__tests__/debug.test.ts +0 -134
  88. package/src/utils/__tests__/expand-fields.test.ts +0 -120
  89. package/src/utils/__tests__/extract-records.test.ts +0 -50
  90. package/src/utils/__tests__/filter-converter.test.ts +0 -118
  91. package/src/utils/__tests__/merge-views-into-objects.test.ts +0 -110
  92. package/src/utils/__tests__/normalize-quick-filter.test.ts +0 -123
  93. package/src/utils/debug-collector.ts +0 -100
  94. package/src/utils/debug.ts +0 -147
  95. package/src/utils/expand-fields.ts +0 -76
  96. package/src/utils/extract-records.ts +0 -33
  97. package/src/utils/filter-converter.ts +0 -133
  98. package/src/utils/merge-views-into-objects.ts +0 -36
  99. package/src/utils/normalize-quick-filter.ts +0 -78
  100. package/src/validation/__tests__/object-validation-engine.test.ts +0 -567
  101. package/src/validation/__tests__/schema-validator.test.ts +0 -118
  102. package/src/validation/__tests__/validation-engine.test.ts +0 -102
  103. package/src/validation/index.ts +0 -10
  104. package/src/validation/schema-validator.ts +0 -344
  105. package/src/validation/validation-engine.ts +0 -528
  106. package/src/validation/validators/index.ts +0 -25
  107. package/src/validation/validators/object-validation-engine.ts +0 -722
  108. package/tsconfig.json +0 -15
  109. package/tsconfig.tsbuildinfo +0 -1
  110. package/vitest.config.ts +0 -2
@@ -1,192 +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
- /**
20
- * A compiled expression function that can be executed with context values
21
- */
22
- export type CompiledExpression = (...args: any[]) => any;
23
-
24
- /**
25
- * Expression compilation metadata
26
- */
27
- export interface ExpressionMetadata {
28
- /**
29
- * The compiled function
30
- */
31
- fn: CompiledExpression;
32
-
33
- /**
34
- * Variable names used in the expression
35
- */
36
- varNames: string[];
37
-
38
- /**
39
- * Original expression string
40
- */
41
- expression: string;
42
-
43
- /**
44
- * Timestamp when the expression was compiled
45
- */
46
- compiledAt: number;
47
-
48
- /**
49
- * Number of times this expression has been used
50
- */
51
- hitCount: number;
52
- }
53
-
54
- /**
55
- * Cache for compiled expressions
56
- *
57
- * @example
58
- * ```ts
59
- * const cache = new ExpressionCache();
60
- * const compiled = cache.compile('data.amount > 1000', ['data']);
61
- * const result = compiled.fn({ amount: 1500 }); // true
62
- * ```
63
- */
64
- export class ExpressionCache {
65
- private cache = new Map<string, ExpressionMetadata>();
66
- private maxSize: number;
67
-
68
- /**
69
- * Create a new expression cache
70
- *
71
- * @param maxSize Maximum number of expressions to cache (default: 1000)
72
- */
73
- constructor(maxSize = 1000) {
74
- this.maxSize = maxSize;
75
- }
76
-
77
- /**
78
- * Compile an expression or retrieve from cache
79
- *
80
- * @param expr The expression to compile
81
- * @param varNames Variable names available in the context
82
- * @returns Compiled expression metadata
83
- */
84
- compile(expr: string, varNames: string[]): ExpressionMetadata {
85
- // Create a cache key that includes variable names to ensure correct scoping
86
- const cacheKey = `${expr}::${varNames.join(',')}`;
87
-
88
- if (this.cache.has(cacheKey)) {
89
- const metadata = this.cache.get(cacheKey)!;
90
- metadata.hitCount++;
91
- return metadata;
92
- }
93
-
94
- // Evict least frequently used if cache is full
95
- if (this.cache.size >= this.maxSize) {
96
- this.evictLFU();
97
- }
98
-
99
- // Compile the expression
100
- const fn = this.compileExpression(expr, varNames);
101
-
102
- const metadata: ExpressionMetadata = {
103
- fn,
104
- varNames: [...varNames],
105
- expression: expr,
106
- compiledAt: Date.now(),
107
- hitCount: 1,
108
- };
109
-
110
- this.cache.set(cacheKey, metadata);
111
- return metadata;
112
- }
113
-
114
- /**
115
- * Compile an expression into a function
116
- */
117
- private compileExpression(expression: string, varNames: string[]): CompiledExpression {
118
- // SECURITY NOTE: Using Function constructor for expression evaluation.
119
- // This is a controlled use case with:
120
- // 1. Sanitization check (isDangerous) performed by caller
121
- // 2. Strict mode enabled ("use strict")
122
- // 3. Limited scope (only varNames variables available)
123
- // 4. No access to global objects (process, window, etc.)
124
- return new Function(...varNames, `"use strict"; return (${expression});`) as CompiledExpression;
125
- }
126
-
127
- /**
128
- * Evict the least frequently used expression from cache
129
- */
130
- private evictLFU(): void {
131
- let oldestKey: string | null = null;
132
- let oldestTime = Infinity;
133
- let lowestHits = Infinity;
134
-
135
- // Find the entry with lowest hit count, or oldest if tied
136
- for (const [key, metadata] of this.cache.entries()) {
137
- if (metadata.hitCount < lowestHits ||
138
- (metadata.hitCount === lowestHits && metadata.compiledAt < oldestTime)) {
139
- oldestKey = key;
140
- oldestTime = metadata.compiledAt;
141
- lowestHits = metadata.hitCount;
142
- }
143
- }
144
-
145
- if (oldestKey) {
146
- this.cache.delete(oldestKey);
147
- }
148
- }
149
-
150
- /**
151
- * Check if an expression is cached
152
- */
153
- has(expr: string, varNames: string[]): boolean {
154
- const cacheKey = `${expr}::${varNames.join(',')}`;
155
- return this.cache.has(cacheKey);
156
- }
157
-
158
- /**
159
- * Clear the cache
160
- */
161
- clear(): void {
162
- this.cache.clear();
163
- }
164
-
165
- /**
166
- * Get cache statistics
167
- */
168
- getStats(): {
169
- size: number;
170
- maxSize: number;
171
- totalHits: number;
172
- entries: Array<{ expression: string; hitCount: number }>;
173
- } {
174
- let totalHits = 0;
175
- const entries: Array<{ expression: string; hitCount: number }> = [];
176
-
177
- for (const metadata of this.cache.values()) {
178
- totalHits += metadata.hitCount;
179
- entries.push({
180
- expression: metadata.expression,
181
- hitCount: metadata.hitCount,
182
- });
183
- }
184
-
185
- return {
186
- size: this.cache.size,
187
- maxSize: this.maxSize,
188
- totalHits,
189
- entries: entries.sort((a, b) => b.hitCount - a.hitCount),
190
- };
191
- }
192
- }
@@ -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
- }