@object-ui/core 3.3.0 → 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 (99) hide show
  1. package/CHANGELOG.md +6 -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/utils/filter-converter.js +25 -5
  7. package/package.json +32 -8
  8. package/.turbo/turbo-build.log +0 -4
  9. package/src/__benchmarks__/core.bench.ts +0 -64
  10. package/src/__tests__/protocols/DndProtocol.test.ts +0 -186
  11. package/src/__tests__/protocols/KeyboardProtocol.test.ts +0 -177
  12. package/src/__tests__/protocols/NotificationProtocol.test.ts +0 -142
  13. package/src/__tests__/protocols/ResponsiveProtocol.test.ts +0 -176
  14. package/src/__tests__/protocols/SharingProtocol.test.ts +0 -188
  15. package/src/actions/ActionEngine.ts +0 -268
  16. package/src/actions/ActionRunner.ts +0 -717
  17. package/src/actions/TransactionManager.ts +0 -521
  18. package/src/actions/UndoManager.ts +0 -215
  19. package/src/actions/__tests__/ActionEngine.test.ts +0 -206
  20. package/src/actions/__tests__/ActionRunner.params.test.ts +0 -134
  21. package/src/actions/__tests__/ActionRunner.test.ts +0 -711
  22. package/src/actions/__tests__/TransactionManager.test.ts +0 -447
  23. package/src/actions/__tests__/UndoManager.test.ts +0 -320
  24. package/src/actions/index.ts +0 -12
  25. package/src/adapters/ApiDataSource.ts +0 -376
  26. package/src/adapters/README.md +0 -180
  27. package/src/adapters/ValueDataSource.ts +0 -459
  28. package/src/adapters/__tests__/ApiDataSource.test.ts +0 -418
  29. package/src/adapters/__tests__/ValueDataSource.test.ts +0 -571
  30. package/src/adapters/__tests__/resolveDataSource.test.ts +0 -144
  31. package/src/adapters/index.ts +0 -15
  32. package/src/adapters/resolveDataSource.ts +0 -79
  33. package/src/builder/__tests__/schema-builder.test.ts +0 -235
  34. package/src/builder/schema-builder.ts +0 -584
  35. package/src/data-scope/DataScopeManager.ts +0 -269
  36. package/src/data-scope/ViewDataProvider.ts +0 -282
  37. package/src/data-scope/__tests__/DataScopeManager.test.ts +0 -211
  38. package/src/data-scope/__tests__/ViewDataProvider.test.ts +0 -270
  39. package/src/data-scope/index.ts +0 -24
  40. package/src/errors/__tests__/errors.test.ts +0 -292
  41. package/src/errors/index.ts +0 -269
  42. package/src/evaluator/ExpressionCache.ts +0 -206
  43. package/src/evaluator/ExpressionContext.ts +0 -118
  44. package/src/evaluator/ExpressionEvaluator.ts +0 -315
  45. package/src/evaluator/FormulaFunctions.ts +0 -398
  46. package/src/evaluator/SafeExpressionParser.ts +0 -893
  47. package/src/evaluator/__tests__/ExpressionCache.test.ts +0 -135
  48. package/src/evaluator/__tests__/ExpressionContext.test.ts +0 -110
  49. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +0 -558
  50. package/src/evaluator/__tests__/FormulaFunctions.test.ts +0 -447
  51. package/src/evaluator/index.ts +0 -13
  52. package/src/index.ts +0 -38
  53. package/src/protocols/DndProtocol.ts +0 -168
  54. package/src/protocols/KeyboardProtocol.ts +0 -181
  55. package/src/protocols/NotificationProtocol.ts +0 -150
  56. package/src/protocols/ResponsiveProtocol.ts +0 -210
  57. package/src/protocols/SharingProtocol.ts +0 -185
  58. package/src/protocols/index.ts +0 -13
  59. package/src/query/__tests__/query-ast.test.ts +0 -211
  60. package/src/query/__tests__/window-functions.test.ts +0 -275
  61. package/src/query/index.ts +0 -7
  62. package/src/query/query-ast.ts +0 -341
  63. package/src/registry/PluginScopeImpl.ts +0 -259
  64. package/src/registry/PluginSystem.ts +0 -206
  65. package/src/registry/Registry.ts +0 -219
  66. package/src/registry/WidgetRegistry.ts +0 -316
  67. package/src/registry/__tests__/PluginSystem.test.ts +0 -309
  68. package/src/registry/__tests__/Registry.test.ts +0 -293
  69. package/src/registry/__tests__/WidgetRegistry.test.ts +0 -321
  70. package/src/registry/__tests__/plugin-scope-integration.test.ts +0 -283
  71. package/src/theme/ThemeEngine.ts +0 -530
  72. package/src/theme/__tests__/ThemeEngine.test.ts +0 -668
  73. package/src/theme/index.ts +0 -24
  74. package/src/types/index.ts +0 -21
  75. package/src/utils/__tests__/debug-collector.test.ts +0 -102
  76. package/src/utils/__tests__/debug.test.ts +0 -134
  77. package/src/utils/__tests__/expand-fields.test.ts +0 -120
  78. package/src/utils/__tests__/extract-records.test.ts +0 -50
  79. package/src/utils/__tests__/filter-converter.test.ts +0 -118
  80. package/src/utils/__tests__/merge-views-into-objects.test.ts +0 -110
  81. package/src/utils/__tests__/normalize-quick-filter.test.ts +0 -123
  82. package/src/utils/debug-collector.ts +0 -100
  83. package/src/utils/debug.ts +0 -148
  84. package/src/utils/expand-fields.ts +0 -76
  85. package/src/utils/extract-records.ts +0 -33
  86. package/src/utils/filter-converter.ts +0 -133
  87. package/src/utils/merge-views-into-objects.ts +0 -36
  88. package/src/utils/normalize-quick-filter.ts +0 -78
  89. package/src/validation/__tests__/object-validation-engine.test.ts +0 -567
  90. package/src/validation/__tests__/schema-validator.test.ts +0 -118
  91. package/src/validation/__tests__/validation-engine.test.ts +0 -102
  92. package/src/validation/index.ts +0 -10
  93. package/src/validation/schema-validator.ts +0 -344
  94. package/src/validation/validation-engine.ts +0 -528
  95. package/src/validation/validators/index.ts +0 -25
  96. package/src/validation/validators/object-validation-engine.ts +0 -722
  97. package/tsconfig.json +0 -15
  98. package/tsconfig.tsbuildinfo +0 -1
  99. 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
- }