@object-ui/core 0.3.0 → 0.5.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 (90) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +8 -0
  3. package/dist/actions/ActionRunner.d.ts +40 -0
  4. package/dist/actions/ActionRunner.js +160 -0
  5. package/dist/actions/index.d.ts +8 -0
  6. package/dist/actions/index.js +8 -0
  7. package/dist/adapters/index.d.ts +7 -0
  8. package/dist/adapters/index.js +10 -0
  9. package/dist/builder/schema-builder.d.ts +7 -0
  10. package/dist/builder/schema-builder.js +4 -6
  11. package/dist/evaluator/ExpressionCache.d.ts +101 -0
  12. package/dist/evaluator/ExpressionCache.js +135 -0
  13. package/dist/evaluator/ExpressionContext.d.ts +51 -0
  14. package/dist/evaluator/ExpressionContext.js +110 -0
  15. package/dist/evaluator/ExpressionEvaluator.d.ts +117 -0
  16. package/dist/evaluator/ExpressionEvaluator.js +220 -0
  17. package/dist/evaluator/index.d.ts +10 -0
  18. package/dist/evaluator/index.js +10 -0
  19. package/dist/index.d.ts +17 -4
  20. package/dist/index.js +16 -5
  21. package/dist/query/index.d.ts +6 -0
  22. package/dist/query/index.js +6 -0
  23. package/dist/query/query-ast.d.ts +32 -0
  24. package/dist/query/query-ast.js +268 -0
  25. package/dist/registry/PluginScopeImpl.d.ts +80 -0
  26. package/dist/registry/PluginScopeImpl.js +243 -0
  27. package/dist/registry/PluginSystem.d.ts +66 -0
  28. package/dist/registry/PluginSystem.js +142 -0
  29. package/dist/registry/Registry.d.ts +80 -4
  30. package/dist/registry/Registry.js +119 -7
  31. package/dist/types/index.d.ts +7 -0
  32. package/dist/types/index.js +7 -0
  33. package/dist/utils/filter-converter.d.ts +57 -0
  34. package/dist/utils/filter-converter.js +100 -0
  35. package/dist/validation/index.d.ts +9 -0
  36. package/dist/validation/index.js +9 -0
  37. package/dist/validation/schema-validator.d.ts +7 -0
  38. package/dist/validation/schema-validator.js +4 -6
  39. package/dist/validation/validation-engine.d.ts +70 -0
  40. package/dist/validation/validation-engine.js +363 -0
  41. package/dist/validation/validators/index.d.ts +16 -0
  42. package/dist/validation/validators/index.js +16 -0
  43. package/dist/validation/validators/object-validation-engine.d.ts +118 -0
  44. package/dist/validation/validators/object-validation-engine.js +538 -0
  45. package/package.json +26 -7
  46. package/src/actions/ActionRunner.ts +195 -0
  47. package/src/actions/index.ts +9 -0
  48. package/src/adapters/README.md +180 -0
  49. package/src/adapters/index.ts +10 -0
  50. package/src/builder/schema-builder.ts +8 -0
  51. package/src/evaluator/ExpressionCache.ts +192 -0
  52. package/src/evaluator/ExpressionContext.ts +118 -0
  53. package/src/evaluator/ExpressionEvaluator.ts +267 -0
  54. package/src/evaluator/__tests__/ExpressionCache.test.ts +135 -0
  55. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +101 -0
  56. package/src/evaluator/index.ts +11 -0
  57. package/src/index.test.ts +8 -0
  58. package/src/index.ts +18 -5
  59. package/src/query/__tests__/query-ast.test.ts +211 -0
  60. package/src/query/__tests__/window-functions.test.ts +275 -0
  61. package/src/query/index.ts +7 -0
  62. package/src/query/query-ast.ts +341 -0
  63. package/src/registry/PluginScopeImpl.ts +259 -0
  64. package/src/registry/PluginSystem.ts +161 -0
  65. package/src/registry/Registry.ts +133 -8
  66. package/src/registry/__tests__/PluginSystem.test.ts +226 -0
  67. package/src/registry/__tests__/Registry.test.ts +293 -0
  68. package/src/registry/__tests__/plugin-scope-integration.test.ts +283 -0
  69. package/src/types/index.ts +8 -0
  70. package/src/utils/__tests__/filter-converter.test.ts +118 -0
  71. package/src/utils/filter-converter.ts +133 -0
  72. package/src/validation/__tests__/object-validation-engine.test.ts +567 -0
  73. package/src/validation/__tests__/validation-engine.test.ts +102 -0
  74. package/src/validation/index.ts +10 -0
  75. package/src/validation/schema-validator.ts +8 -0
  76. package/src/validation/validation-engine.ts +461 -0
  77. package/src/validation/validators/index.ts +25 -0
  78. package/src/validation/validators/object-validation-engine.ts +722 -0
  79. package/tsconfig.tsbuildinfo +1 -1
  80. package/vitest.config.ts +2 -0
  81. package/src/builder/schema-builder.d.ts +0 -287
  82. package/src/builder/schema-builder.js +0 -505
  83. package/src/index.d.ts +0 -4
  84. package/src/index.js +0 -7
  85. package/src/registry/Registry.d.ts +0 -49
  86. package/src/registry/Registry.js +0 -36
  87. package/src/types/index.d.ts +0 -12
  88. package/src/types/index.js +0 -1
  89. package/src/validation/schema-validator.d.ts +0 -87
  90. package/src/validation/schema-validator.js +0 -280
@@ -0,0 +1,4 @@
1
+
2
+ > @object-ui/core@0.5.0 build /home/runner/work/objectui/objectui/packages/core
3
+ > tsc
4
+
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @object-ui/core
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Maintenance release - Documentation and build improvements
8
+ - Updated dependencies
9
+ - @object-ui/types@0.3.1
10
+
3
11
  ## 0.3.0
4
12
 
5
13
  ### Minor Changes
@@ -0,0 +1,40 @@
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
+ export interface ActionResult {
9
+ success: boolean;
10
+ data?: any;
11
+ error?: string;
12
+ reload?: boolean;
13
+ close?: boolean;
14
+ redirect?: string;
15
+ }
16
+ export interface ActionContext {
17
+ data?: Record<string, any>;
18
+ record?: any;
19
+ user?: any;
20
+ [key: string]: any;
21
+ }
22
+ export type ActionHandler = (action: any, context: ActionContext) => Promise<ActionResult> | ActionResult;
23
+ export declare class ActionRunner {
24
+ private handlers;
25
+ private evaluator;
26
+ private context;
27
+ constructor(context?: ActionContext);
28
+ registerHandler(actionName: string, handler: ActionHandler): void;
29
+ execute(action: any): Promise<ActionResult>;
30
+ private executeActionSchema;
31
+ /**
32
+ * Execute navigation action
33
+ */
34
+ private executeNavigation;
35
+ private executeAPI;
36
+ private showConfirmation;
37
+ updateContext(newContext: Partial<ActionContext>): void;
38
+ getContext(): ActionContext;
39
+ }
40
+ export declare function executeAction(action: any, context?: ActionContext): Promise<ActionResult>;
@@ -0,0 +1,160 @@
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
+ * @object-ui/core - Action Runner
10
+ *
11
+ * Executes actions defined in ActionSchema and EventHandler.
12
+ */
13
+ import { ExpressionEvaluator } from '../evaluator/ExpressionEvaluator';
14
+ export class ActionRunner {
15
+ constructor(context = {}) {
16
+ Object.defineProperty(this, "handlers", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: new Map()
21
+ });
22
+ Object.defineProperty(this, "evaluator", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: void 0
27
+ });
28
+ Object.defineProperty(this, "context", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: void 0
33
+ });
34
+ this.context = context;
35
+ this.evaluator = new ExpressionEvaluator(context);
36
+ }
37
+ registerHandler(actionName, handler) {
38
+ this.handlers.set(actionName, handler);
39
+ }
40
+ async execute(action) {
41
+ try {
42
+ if (action.condition) {
43
+ const shouldExecute = this.evaluator.evaluateCondition(action.condition);
44
+ if (!shouldExecute) {
45
+ return { success: false, error: 'Action condition not met' };
46
+ }
47
+ }
48
+ if (action.disabled) {
49
+ const isDisabled = typeof action.disabled === 'string'
50
+ ? this.evaluator.evaluateCondition(action.disabled)
51
+ : action.disabled;
52
+ if (isDisabled) {
53
+ return { success: false, error: 'Action is disabled' };
54
+ }
55
+ }
56
+ if (action.type === 'action' || action.actionType) {
57
+ return await this.executeActionSchema(action);
58
+ }
59
+ else if (action.type === 'navigation' || action.navigate) {
60
+ return await this.executeNavigation(action);
61
+ }
62
+ else if (action.type === 'api' || action.api) {
63
+ return await this.executeAPI(action);
64
+ }
65
+ else if (action.onClick) {
66
+ await action.onClick();
67
+ return { success: true };
68
+ }
69
+ return { success: false, error: 'Unknown action type' };
70
+ }
71
+ catch (error) {
72
+ return { success: false, error: error.message };
73
+ }
74
+ }
75
+ async executeActionSchema(action) {
76
+ const result = { success: true };
77
+ if (action.confirmText) {
78
+ const confirmed = await this.showConfirmation(action.confirmText);
79
+ if (!confirmed) {
80
+ return { success: false, error: 'Action cancelled by user' };
81
+ }
82
+ }
83
+ if (action.api) {
84
+ const apiResult = await this.executeAPI(action);
85
+ if (!apiResult.success)
86
+ return apiResult;
87
+ result.data = apiResult.data;
88
+ }
89
+ if (action.onClick) {
90
+ await action.onClick();
91
+ }
92
+ result.reload = action.reload !== false;
93
+ result.close = action.close !== false;
94
+ if (action.redirect) {
95
+ result.redirect = this.evaluator.evaluate(action.redirect);
96
+ }
97
+ return result;
98
+ }
99
+ /**
100
+ * Execute navigation action
101
+ */
102
+ async executeNavigation(action) {
103
+ const nav = action.navigate || action;
104
+ const to = this.evaluator.evaluate(nav.to);
105
+ // Validate URL to prevent javascript: or data: schemes
106
+ const isValidUrl = typeof to === 'string' && (to.startsWith('http://') ||
107
+ to.startsWith('https://') ||
108
+ to.startsWith('/') ||
109
+ to.startsWith('./'));
110
+ if (!isValidUrl) {
111
+ return {
112
+ success: false,
113
+ error: 'Invalid URL scheme. Only http://, https://, and relative URLs are allowed.'
114
+ };
115
+ }
116
+ if (nav.external) {
117
+ window.open(to, '_blank', 'noopener,noreferrer');
118
+ }
119
+ else {
120
+ return { success: true, redirect: to };
121
+ }
122
+ return { success: true };
123
+ }
124
+ async executeAPI(action) {
125
+ const apiConfig = action.api;
126
+ if (typeof apiConfig === 'string') {
127
+ try {
128
+ const response = await fetch(apiConfig, {
129
+ method: action.method || 'POST',
130
+ headers: { 'Content-Type': 'application/json' },
131
+ body: JSON.stringify(this.context.data || {})
132
+ });
133
+ if (!response.ok) {
134
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
135
+ }
136
+ const data = await response.json();
137
+ return { success: true, data };
138
+ }
139
+ catch (error) {
140
+ return { success: false, error: error.message };
141
+ }
142
+ }
143
+ return { success: false, error: 'Complex API configuration not yet implemented' };
144
+ }
145
+ async showConfirmation(message) {
146
+ const evaluatedMessage = this.evaluator.evaluate(message);
147
+ return window.confirm(evaluatedMessage);
148
+ }
149
+ updateContext(newContext) {
150
+ this.context = { ...this.context, ...newContext };
151
+ this.evaluator.updateContext(newContext);
152
+ }
153
+ getContext() {
154
+ return this.context;
155
+ }
156
+ }
157
+ export async function executeAction(action, context = {}) {
158
+ const runner = new ActionRunner(context);
159
+ return await runner.execute(action);
160
+ }
@@ -0,0 +1,8 @@
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
+ export * from './ActionRunner.js';
@@ -0,0 +1,8 @@
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
+ export * from './ActionRunner.js';
@@ -0,0 +1,7 @@
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
+ */
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * ObjectUI
4
+ * Copyright (c) 2024-present ObjectStack Inc.
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
9
+ // export { ObjectStackAdapter, createObjectStackAdapter } from './objectstack-adapter';
10
+ // Adapters have been moved to separate packages (e.g. @object-ui/data-objectstack)
@@ -1,3 +1,10 @@
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
+ */
1
8
  /**
2
9
  * @object-ui/core - Schema Builder
3
10
  *
@@ -1,11 +1,9 @@
1
1
  /**
2
- * @object-ui/core - Schema Builder
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
3
4
  *
4
- * Fluent API for building schemas programmatically.
5
- * Provides type-safe builder functions for common schema patterns.
6
- *
7
- * @module builder
8
- * @packageDocumentation
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
9
7
  */
10
8
  /**
11
9
  * Base builder class
@@ -0,0 +1,101 @@
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
+ * @object-ui/core - Expression Cache
10
+ *
11
+ * Caches compiled expressions to avoid re-parsing on every render.
12
+ * Provides significant performance improvement for frequently evaluated expressions.
13
+ *
14
+ * @module evaluator
15
+ * @packageDocumentation
16
+ */
17
+ /**
18
+ * A compiled expression function that can be executed with context values
19
+ */
20
+ export type CompiledExpression = (...args: any[]) => any;
21
+ /**
22
+ * Expression compilation metadata
23
+ */
24
+ export interface ExpressionMetadata {
25
+ /**
26
+ * The compiled function
27
+ */
28
+ fn: CompiledExpression;
29
+ /**
30
+ * Variable names used in the expression
31
+ */
32
+ varNames: string[];
33
+ /**
34
+ * Original expression string
35
+ */
36
+ expression: string;
37
+ /**
38
+ * Timestamp when the expression was compiled
39
+ */
40
+ compiledAt: number;
41
+ /**
42
+ * Number of times this expression has been used
43
+ */
44
+ hitCount: number;
45
+ }
46
+ /**
47
+ * Cache for compiled expressions
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const cache = new ExpressionCache();
52
+ * const compiled = cache.compile('data.amount > 1000', ['data']);
53
+ * const result = compiled.fn({ amount: 1500 }); // true
54
+ * ```
55
+ */
56
+ export declare class ExpressionCache {
57
+ private cache;
58
+ private maxSize;
59
+ /**
60
+ * Create a new expression cache
61
+ *
62
+ * @param maxSize Maximum number of expressions to cache (default: 1000)
63
+ */
64
+ constructor(maxSize?: number);
65
+ /**
66
+ * Compile an expression or retrieve from cache
67
+ *
68
+ * @param expr The expression to compile
69
+ * @param varNames Variable names available in the context
70
+ * @returns Compiled expression metadata
71
+ */
72
+ compile(expr: string, varNames: string[]): ExpressionMetadata;
73
+ /**
74
+ * Compile an expression into a function
75
+ */
76
+ private compileExpression;
77
+ /**
78
+ * Evict the least frequently used expression from cache
79
+ */
80
+ private evictLFU;
81
+ /**
82
+ * Check if an expression is cached
83
+ */
84
+ has(expr: string, varNames: string[]): boolean;
85
+ /**
86
+ * Clear the cache
87
+ */
88
+ clear(): void;
89
+ /**
90
+ * Get cache statistics
91
+ */
92
+ getStats(): {
93
+ size: number;
94
+ maxSize: number;
95
+ totalHits: number;
96
+ entries: Array<{
97
+ expression: string;
98
+ hitCount: number;
99
+ }>;
100
+ };
101
+ }
@@ -0,0 +1,135 @@
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
+ * Cache for compiled expressions
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const cache = new ExpressionCache();
14
+ * const compiled = cache.compile('data.amount > 1000', ['data']);
15
+ * const result = compiled.fn({ amount: 1500 }); // true
16
+ * ```
17
+ */
18
+ export class ExpressionCache {
19
+ /**
20
+ * Create a new expression cache
21
+ *
22
+ * @param maxSize Maximum number of expressions to cache (default: 1000)
23
+ */
24
+ constructor(maxSize = 1000) {
25
+ Object.defineProperty(this, "cache", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: new Map()
30
+ });
31
+ Object.defineProperty(this, "maxSize", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: void 0
36
+ });
37
+ this.maxSize = maxSize;
38
+ }
39
+ /**
40
+ * Compile an expression or retrieve from cache
41
+ *
42
+ * @param expr The expression to compile
43
+ * @param varNames Variable names available in the context
44
+ * @returns Compiled expression metadata
45
+ */
46
+ compile(expr, varNames) {
47
+ // Create a cache key that includes variable names to ensure correct scoping
48
+ const cacheKey = `${expr}::${varNames.join(',')}`;
49
+ if (this.cache.has(cacheKey)) {
50
+ const metadata = this.cache.get(cacheKey);
51
+ metadata.hitCount++;
52
+ return metadata;
53
+ }
54
+ // Evict least frequently used if cache is full
55
+ if (this.cache.size >= this.maxSize) {
56
+ this.evictLFU();
57
+ }
58
+ // Compile the expression
59
+ const fn = this.compileExpression(expr, varNames);
60
+ const metadata = {
61
+ fn,
62
+ varNames: [...varNames],
63
+ expression: expr,
64
+ compiledAt: Date.now(),
65
+ hitCount: 1,
66
+ };
67
+ this.cache.set(cacheKey, metadata);
68
+ return metadata;
69
+ }
70
+ /**
71
+ * Compile an expression into a function
72
+ */
73
+ compileExpression(expression, varNames) {
74
+ // SECURITY NOTE: Using Function constructor for expression evaluation.
75
+ // This is a controlled use case with:
76
+ // 1. Sanitization check (isDangerous) performed by caller
77
+ // 2. Strict mode enabled ("use strict")
78
+ // 3. Limited scope (only varNames variables available)
79
+ // 4. No access to global objects (process, window, etc.)
80
+ return new Function(...varNames, `"use strict"; return (${expression});`);
81
+ }
82
+ /**
83
+ * Evict the least frequently used expression from cache
84
+ */
85
+ evictLFU() {
86
+ let oldestKey = null;
87
+ let oldestTime = Infinity;
88
+ let lowestHits = Infinity;
89
+ // Find the entry with lowest hit count, or oldest if tied
90
+ for (const [key, metadata] of this.cache.entries()) {
91
+ if (metadata.hitCount < lowestHits ||
92
+ (metadata.hitCount === lowestHits && metadata.compiledAt < oldestTime)) {
93
+ oldestKey = key;
94
+ oldestTime = metadata.compiledAt;
95
+ lowestHits = metadata.hitCount;
96
+ }
97
+ }
98
+ if (oldestKey) {
99
+ this.cache.delete(oldestKey);
100
+ }
101
+ }
102
+ /**
103
+ * Check if an expression is cached
104
+ */
105
+ has(expr, varNames) {
106
+ const cacheKey = `${expr}::${varNames.join(',')}`;
107
+ return this.cache.has(cacheKey);
108
+ }
109
+ /**
110
+ * Clear the cache
111
+ */
112
+ clear() {
113
+ this.cache.clear();
114
+ }
115
+ /**
116
+ * Get cache statistics
117
+ */
118
+ getStats() {
119
+ let totalHits = 0;
120
+ const entries = [];
121
+ for (const metadata of this.cache.values()) {
122
+ totalHits += metadata.hitCount;
123
+ entries.push({
124
+ expression: metadata.expression,
125
+ hitCount: metadata.hitCount,
126
+ });
127
+ }
128
+ return {
129
+ size: this.cache.size,
130
+ maxSize: this.maxSize,
131
+ totalHits,
132
+ entries: entries.sort((a, b) => b.hitCount - a.hitCount),
133
+ };
134
+ }
135
+ }
@@ -0,0 +1,51 @@
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
+ * @object-ui/core - Expression Context
10
+ *
11
+ * Manages variable scope and data context for expression evaluation.
12
+ *
13
+ * @module evaluator
14
+ * @packageDocumentation
15
+ */
16
+ /**
17
+ * Expression context for variable resolution
18
+ */
19
+ export declare class ExpressionContext {
20
+ private scopes;
21
+ constructor(initialData?: Record<string, any>);
22
+ /**
23
+ * Push a new scope onto the context stack
24
+ */
25
+ pushScope(data: Record<string, any>): void;
26
+ /**
27
+ * Pop the current scope from the context stack
28
+ */
29
+ popScope(): void;
30
+ /**
31
+ * Get a variable value from the context
32
+ * Searches from innermost to outermost scope
33
+ */
34
+ get(path: string): any;
35
+ /**
36
+ * Set a variable value in the current scope
37
+ */
38
+ set(name: string, value: any): void;
39
+ /**
40
+ * Check if a variable exists in any scope
41
+ */
42
+ has(name: string): boolean;
43
+ /**
44
+ * Get all variables from all scopes as a flat object
45
+ */
46
+ toObject(): Record<string, any>;
47
+ /**
48
+ * Create a child context with additional data
49
+ */
50
+ createChild(data?: Record<string, any>): ExpressionContext;
51
+ }
@@ -0,0 +1,110 @@
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
+ * @object-ui/core - Expression Context
10
+ *
11
+ * Manages variable scope and data context for expression evaluation.
12
+ *
13
+ * @module evaluator
14
+ * @packageDocumentation
15
+ */
16
+ /**
17
+ * Expression context for variable resolution
18
+ */
19
+ export class ExpressionContext {
20
+ constructor(initialData = {}) {
21
+ Object.defineProperty(this, "scopes", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: []
26
+ });
27
+ this.scopes.push(new Map(Object.entries(initialData)));
28
+ }
29
+ /**
30
+ * Push a new scope onto the context stack
31
+ */
32
+ pushScope(data) {
33
+ this.scopes.push(new Map(Object.entries(data)));
34
+ }
35
+ /**
36
+ * Pop the current scope from the context stack
37
+ */
38
+ popScope() {
39
+ if (this.scopes.length > 1) {
40
+ this.scopes.pop();
41
+ }
42
+ }
43
+ /**
44
+ * Get a variable value from the context
45
+ * Searches from innermost to outermost scope
46
+ */
47
+ get(path) {
48
+ // Split path by dots for nested access
49
+ const parts = path.split('.');
50
+ const varName = parts[0];
51
+ // Search scopes from innermost to outermost
52
+ for (let i = this.scopes.length - 1; i >= 0; i--) {
53
+ if (this.scopes[i].has(varName)) {
54
+ let value = this.scopes[i].get(varName);
55
+ // Navigate nested path
56
+ for (let j = 1; j < parts.length; j++) {
57
+ if (value && typeof value === 'object') {
58
+ value = value[parts[j]];
59
+ }
60
+ else {
61
+ return undefined;
62
+ }
63
+ }
64
+ return value;
65
+ }
66
+ }
67
+ return undefined;
68
+ }
69
+ /**
70
+ * Set a variable value in the current scope
71
+ */
72
+ set(name, value) {
73
+ if (this.scopes.length > 0) {
74
+ this.scopes[this.scopes.length - 1].set(name, value);
75
+ }
76
+ }
77
+ /**
78
+ * Check if a variable exists in any scope
79
+ */
80
+ has(name) {
81
+ const varName = name.split('.')[0];
82
+ for (let i = this.scopes.length - 1; i >= 0; i--) {
83
+ if (this.scopes[i].has(varName)) {
84
+ return true;
85
+ }
86
+ }
87
+ return false;
88
+ }
89
+ /**
90
+ * Get all variables from all scopes as a flat object
91
+ */
92
+ toObject() {
93
+ const result = {};
94
+ // Merge from outermost to innermost (later scopes override earlier ones)
95
+ for (const scope of this.scopes) {
96
+ for (const [key, value] of scope.entries()) {
97
+ result[key] = value;
98
+ }
99
+ }
100
+ return result;
101
+ }
102
+ /**
103
+ * Create a child context with additional data
104
+ */
105
+ createChild(data = {}) {
106
+ const child = new ExpressionContext();
107
+ child.scopes = [...this.scopes, new Map(Object.entries(data))];
108
+ return child;
109
+ }
110
+ }