@object-ui/core 0.3.1 → 2.0.0

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 (118) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +11 -0
  3. package/dist/actions/ActionRunner.d.ts +228 -4
  4. package/dist/actions/ActionRunner.js +397 -45
  5. package/dist/actions/TransactionManager.d.ts +193 -0
  6. package/dist/actions/TransactionManager.js +410 -0
  7. package/dist/actions/index.d.ts +2 -1
  8. package/dist/actions/index.js +2 -1
  9. package/dist/adapters/ApiDataSource.d.ts +69 -0
  10. package/dist/adapters/ApiDataSource.js +293 -0
  11. package/dist/adapters/ValueDataSource.d.ts +55 -0
  12. package/dist/adapters/ValueDataSource.js +287 -0
  13. package/dist/adapters/index.d.ts +3 -0
  14. package/dist/adapters/index.js +5 -2
  15. package/dist/adapters/resolveDataSource.d.ts +40 -0
  16. package/dist/adapters/resolveDataSource.js +59 -0
  17. package/dist/data-scope/DataScopeManager.d.ts +127 -0
  18. package/dist/data-scope/DataScopeManager.js +229 -0
  19. package/dist/data-scope/index.d.ts +10 -0
  20. package/dist/data-scope/index.js +10 -0
  21. package/dist/evaluator/ExpressionCache.d.ts +101 -0
  22. package/dist/evaluator/ExpressionCache.js +135 -0
  23. package/dist/evaluator/ExpressionEvaluator.d.ts +30 -2
  24. package/dist/evaluator/ExpressionEvaluator.js +60 -16
  25. package/dist/evaluator/FormulaFunctions.d.ts +58 -0
  26. package/dist/evaluator/FormulaFunctions.js +350 -0
  27. package/dist/evaluator/index.d.ts +4 -2
  28. package/dist/evaluator/index.js +4 -2
  29. package/dist/index.d.ts +14 -7
  30. package/dist/index.js +13 -9
  31. package/dist/query/index.d.ts +6 -0
  32. package/dist/query/index.js +6 -0
  33. package/dist/query/query-ast.d.ts +32 -0
  34. package/dist/query/query-ast.js +268 -0
  35. package/dist/registry/PluginScopeImpl.d.ts +80 -0
  36. package/dist/registry/PluginScopeImpl.js +243 -0
  37. package/dist/registry/PluginSystem.d.ts +66 -0
  38. package/dist/registry/PluginSystem.js +142 -0
  39. package/dist/registry/Registry.d.ts +83 -4
  40. package/dist/registry/Registry.js +113 -7
  41. package/dist/registry/WidgetRegistry.d.ts +120 -0
  42. package/dist/registry/WidgetRegistry.js +275 -0
  43. package/dist/theme/ThemeEngine.d.ts +82 -0
  44. package/dist/theme/ThemeEngine.js +400 -0
  45. package/dist/theme/index.d.ts +8 -0
  46. package/dist/theme/index.js +8 -0
  47. package/dist/validation/index.d.ts +9 -0
  48. package/dist/validation/index.js +9 -0
  49. package/dist/validation/validation-engine.d.ts +88 -0
  50. package/dist/validation/validation-engine.js +428 -0
  51. package/dist/validation/validators/index.d.ts +16 -0
  52. package/dist/validation/validators/index.js +16 -0
  53. package/dist/validation/validators/object-validation-engine.d.ts +118 -0
  54. package/dist/validation/validators/object-validation-engine.js +538 -0
  55. package/package.json +14 -5
  56. package/src/actions/ActionRunner.ts +577 -55
  57. package/src/actions/TransactionManager.ts +521 -0
  58. package/src/actions/__tests__/ActionRunner.params.test.ts +134 -0
  59. package/src/actions/__tests__/ActionRunner.test.ts +711 -0
  60. package/src/actions/__tests__/TransactionManager.test.ts +447 -0
  61. package/src/actions/index.ts +2 -1
  62. package/src/adapters/ApiDataSource.ts +349 -0
  63. package/src/adapters/ValueDataSource.ts +332 -0
  64. package/src/adapters/__tests__/ApiDataSource.test.ts +418 -0
  65. package/src/adapters/__tests__/ValueDataSource.test.ts +325 -0
  66. package/src/adapters/__tests__/resolveDataSource.test.ts +144 -0
  67. package/src/adapters/index.ts +6 -1
  68. package/src/adapters/resolveDataSource.ts +79 -0
  69. package/src/builder/__tests__/schema-builder.test.ts +235 -0
  70. package/src/data-scope/DataScopeManager.ts +269 -0
  71. package/src/data-scope/__tests__/DataScopeManager.test.ts +211 -0
  72. package/src/data-scope/index.ts +16 -0
  73. package/src/evaluator/ExpressionCache.ts +192 -0
  74. package/src/evaluator/ExpressionEvaluator.ts +61 -16
  75. package/src/evaluator/FormulaFunctions.ts +398 -0
  76. package/src/evaluator/__tests__/ExpressionCache.test.ts +135 -0
  77. package/src/evaluator/__tests__/ExpressionContext.test.ts +110 -0
  78. package/src/evaluator/__tests__/FormulaFunctions.test.ts +447 -0
  79. package/src/evaluator/index.ts +4 -2
  80. package/src/index.ts +14 -10
  81. package/src/query/__tests__/query-ast.test.ts +211 -0
  82. package/src/query/__tests__/window-functions.test.ts +275 -0
  83. package/src/query/index.ts +7 -0
  84. package/src/query/query-ast.ts +341 -0
  85. package/src/registry/PluginScopeImpl.ts +259 -0
  86. package/src/registry/PluginSystem.ts +161 -0
  87. package/src/registry/Registry.ts +136 -8
  88. package/src/registry/WidgetRegistry.ts +316 -0
  89. package/src/registry/__tests__/PluginSystem.test.ts +226 -0
  90. package/src/registry/__tests__/Registry.test.ts +293 -0
  91. package/src/registry/__tests__/WidgetRegistry.test.ts +321 -0
  92. package/src/registry/__tests__/plugin-scope-integration.test.ts +283 -0
  93. package/src/theme/ThemeEngine.ts +452 -0
  94. package/src/theme/__tests__/ThemeEngine.test.ts +606 -0
  95. package/src/theme/index.ts +22 -0
  96. package/src/validation/__tests__/object-validation-engine.test.ts +567 -0
  97. package/src/validation/__tests__/schema-validator.test.ts +118 -0
  98. package/src/validation/__tests__/validation-engine.test.ts +102 -0
  99. package/src/validation/index.ts +10 -0
  100. package/src/validation/validation-engine.ts +520 -0
  101. package/src/validation/validators/index.ts +25 -0
  102. package/src/validation/validators/object-validation-engine.ts +722 -0
  103. package/tsconfig.tsbuildinfo +1 -1
  104. package/vitest.config.ts +2 -0
  105. package/src/adapters/index.d.ts +0 -8
  106. package/src/adapters/index.js +0 -10
  107. package/src/builder/schema-builder.d.ts +0 -294
  108. package/src/builder/schema-builder.js +0 -503
  109. package/src/index.d.ts +0 -13
  110. package/src/index.js +0 -16
  111. package/src/registry/Registry.d.ts +0 -56
  112. package/src/registry/Registry.js +0 -43
  113. package/src/types/index.d.ts +0 -19
  114. package/src/types/index.js +0 -8
  115. package/src/utils/filter-converter.d.ts +0 -57
  116. package/src/utils/filter-converter.js +0 -100
  117. package/src/validation/schema-validator.d.ts +0 -94
  118. package/src/validation/schema-validator.js +0 -278
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @object-ui/core - DataScope Module
3
+ *
4
+ * Runtime data scope management for row-level security and
5
+ * reactive data state within the UI component tree.
6
+ *
7
+ * @module data-scope
8
+ * @packageDocumentation
9
+ */
10
+
11
+ export {
12
+ DataScopeManager,
13
+ defaultDataScopeManager,
14
+ type RowLevelFilter,
15
+ type DataScopeConfig,
16
+ } from './DataScopeManager.js';
@@ -0,0 +1,192 @@
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
+ }
@@ -16,7 +16,9 @@
16
16
  * @packageDocumentation
17
17
  */
18
18
 
19
- import { ExpressionContext } from './ExpressionContext';
19
+ import { ExpressionContext } from './ExpressionContext.js';
20
+ import { ExpressionCache } from './ExpressionCache.js';
21
+ import { FormulaFunctions } from './FormulaFunctions.js';
20
22
 
21
23
  /**
22
24
  * Options for expression evaluation
@@ -45,13 +47,23 @@ export interface EvaluationOptions {
45
47
  */
46
48
  export class ExpressionEvaluator {
47
49
  private context: ExpressionContext;
50
+ private cache: ExpressionCache;
51
+ private formulas: FormulaFunctions;
48
52
 
49
- constructor(context?: ExpressionContext | Record<string, any>) {
53
+ constructor(
54
+ context?: ExpressionContext | Record<string, any>,
55
+ cache?: ExpressionCache,
56
+ formulas?: FormulaFunctions,
57
+ ) {
50
58
  if (context instanceof ExpressionContext) {
51
59
  this.context = context;
52
60
  } else {
53
61
  this.context = new ExpressionContext(context || {});
54
62
  }
63
+
64
+ // Use provided cache or create a new one
65
+ this.cache = cache || new ExpressionCache();
66
+ this.formulas = formulas || new FormulaFunctions();
55
67
  }
56
68
 
57
69
  /**
@@ -134,21 +146,19 @@ export class ExpressionEvaluator {
134
146
  // Create a safe evaluation function
135
147
  const contextObj = this.context.toObject();
136
148
 
149
+ // Inject formula functions into the evaluation context
150
+ const formulaObj = this.formulas.toObject();
151
+ const mergedContext = { ...formulaObj, ...contextObj };
152
+
137
153
  // Build safe function with context variables
138
- const varNames = Object.keys(contextObj);
139
- const varValues = Object.values(contextObj);
154
+ const varNames = Object.keys(mergedContext);
155
+ const varValues = Object.values(mergedContext);
140
156
 
141
- // SECURITY NOTE: Using Function constructor for expression evaluation.
142
- // This is a controlled use case with:
143
- // 1. Sanitization check (isDangerous) blocks dangerous patterns
144
- // 2. Strict mode enabled ("use strict")
145
- // 3. Limited scope (only contextObj variables available)
146
- // 4. No access to global objects (process, window, etc.)
147
- // For production use, consider: expr-eval, safe-eval, or a custom parser
148
- const fn = new Function(...varNames, `"use strict"; return (${expression});`);
157
+ // Use cached compilation
158
+ const compiled = this.cache.compile(expression, varNames);
149
159
 
150
160
  // Execute with context values
151
- return fn(...varValues);
161
+ return compiled.fn(...varValues);
152
162
  } catch (error) {
153
163
  throw new Error(`Failed to evaluate expression "${expression}": ${(error as Error).message}`);
154
164
  }
@@ -220,10 +230,45 @@ export class ExpressionEvaluator {
220
230
  * Create a new evaluator with additional context data
221
231
  */
222
232
  withContext(data: Record<string, any>): ExpressionEvaluator {
223
- return new ExpressionEvaluator(this.context.createChild(data));
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);
224
263
  }
225
264
  }
226
265
 
266
+ /**
267
+ * Shared global cache and formulas for convenience functions
268
+ */
269
+ const globalCache = new ExpressionCache();
270
+ const globalFormulas = new FormulaFunctions();
271
+
227
272
  /**
228
273
  * Convenience function to quickly evaluate an expression
229
274
  */
@@ -232,7 +277,7 @@ export function evaluateExpression(
232
277
  context: Record<string, any> = {},
233
278
  options: EvaluationOptions = {}
234
279
  ): any {
235
- const evaluator = new ExpressionEvaluator(context);
280
+ const evaluator = new ExpressionEvaluator(context, globalCache, globalFormulas);
236
281
  return evaluator.evaluate(expression, options);
237
282
  }
238
283
 
@@ -243,6 +288,6 @@ export function evaluateCondition(
243
288
  condition: string | boolean | undefined,
244
289
  context: Record<string, any> = {}
245
290
  ): boolean {
246
- const evaluator = new ExpressionEvaluator(context);
291
+ const evaluator = new ExpressionEvaluator(context, globalCache, globalFormulas);
247
292
  return evaluator.evaluateCondition(condition);
248
293
  }