@object-ui/core 0.5.0 → 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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/actions/ActionRunner.d.ts +228 -4
- package/dist/actions/ActionRunner.js +397 -45
- package/dist/actions/TransactionManager.d.ts +193 -0
- package/dist/actions/TransactionManager.js +410 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/adapters/ApiDataSource.d.ts +69 -0
- package/dist/adapters/ApiDataSource.js +293 -0
- package/dist/adapters/ValueDataSource.d.ts +55 -0
- package/dist/adapters/ValueDataSource.js +287 -0
- package/dist/adapters/index.d.ts +3 -0
- package/dist/adapters/index.js +5 -2
- package/dist/adapters/resolveDataSource.d.ts +40 -0
- package/dist/adapters/resolveDataSource.js +59 -0
- package/dist/data-scope/DataScopeManager.d.ts +127 -0
- package/dist/data-scope/DataScopeManager.js +229 -0
- package/dist/data-scope/index.d.ts +10 -0
- package/dist/data-scope/index.js +10 -0
- package/dist/evaluator/ExpressionEvaluator.d.ts +11 -1
- package/dist/evaluator/ExpressionEvaluator.js +32 -8
- package/dist/evaluator/FormulaFunctions.d.ts +58 -0
- package/dist/evaluator/FormulaFunctions.js +350 -0
- package/dist/evaluator/index.d.ts +1 -0
- package/dist/evaluator/index.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -2
- package/dist/query/query-ast.d.ts +2 -2
- package/dist/query/query-ast.js +3 -3
- package/dist/registry/Registry.d.ts +10 -0
- package/dist/registry/Registry.js +2 -1
- package/dist/registry/WidgetRegistry.d.ts +120 -0
- package/dist/registry/WidgetRegistry.js +275 -0
- package/dist/theme/ThemeEngine.d.ts +82 -0
- package/dist/theme/ThemeEngine.js +400 -0
- package/dist/theme/index.d.ts +8 -0
- package/dist/theme/index.js +8 -0
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/validation-engine.d.ts +19 -1
- package/dist/validation/validation-engine.js +67 -2
- package/dist/validation/validators/index.d.ts +1 -1
- package/dist/validation/validators/index.js +1 -1
- package/dist/validation/validators/object-validation-engine.d.ts +2 -2
- package/dist/validation/validators/object-validation-engine.js +1 -1
- package/package.json +4 -3
- package/src/actions/ActionRunner.ts +577 -55
- package/src/actions/TransactionManager.ts +521 -0
- package/src/actions/__tests__/ActionRunner.params.test.ts +134 -0
- package/src/actions/__tests__/ActionRunner.test.ts +711 -0
- package/src/actions/__tests__/TransactionManager.test.ts +447 -0
- package/src/actions/index.ts +1 -0
- package/src/adapters/ApiDataSource.ts +349 -0
- package/src/adapters/ValueDataSource.ts +332 -0
- package/src/adapters/__tests__/ApiDataSource.test.ts +418 -0
- package/src/adapters/__tests__/ValueDataSource.test.ts +325 -0
- package/src/adapters/__tests__/resolveDataSource.test.ts +144 -0
- package/src/adapters/index.ts +6 -1
- package/src/adapters/resolveDataSource.ts +79 -0
- package/src/builder/__tests__/schema-builder.test.ts +235 -0
- package/src/data-scope/DataScopeManager.ts +269 -0
- package/src/data-scope/__tests__/DataScopeManager.test.ts +211 -0
- package/src/data-scope/index.ts +16 -0
- package/src/evaluator/ExpressionEvaluator.ts +34 -8
- package/src/evaluator/FormulaFunctions.ts +398 -0
- package/src/evaluator/__tests__/ExpressionContext.test.ts +110 -0
- package/src/evaluator/__tests__/FormulaFunctions.test.ts +447 -0
- package/src/evaluator/index.ts +1 -0
- package/src/index.ts +4 -3
- package/src/query/__tests__/window-functions.test.ts +1 -1
- package/src/query/query-ast.ts +3 -3
- package/src/registry/Registry.ts +12 -1
- package/src/registry/WidgetRegistry.ts +316 -0
- package/src/registry/__tests__/WidgetRegistry.test.ts +321 -0
- package/src/theme/ThemeEngine.ts +452 -0
- package/src/theme/__tests__/ThemeEngine.test.ts +606 -0
- package/src/theme/index.ts +22 -0
- package/src/validation/__tests__/object-validation-engine.test.ts +1 -1
- package/src/validation/__tests__/schema-validator.test.ts +118 -0
- package/src/validation/index.ts +1 -1
- package/src/validation/validation-engine.ts +61 -2
- package/src/validation/validators/index.ts +1 -1
- package/src/validation/validators/object-validation-engine.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI — resolveDataSource
|
|
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
|
+
* Factory function to create the right DataSource from a ViewData config.
|
|
9
|
+
*/
|
|
10
|
+
import { ApiDataSource } from './ApiDataSource.js';
|
|
11
|
+
import { ValueDataSource } from './ValueDataSource.js';
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a ViewData configuration into a concrete DataSource instance.
|
|
14
|
+
*
|
|
15
|
+
* - `provider: 'object'` → returns `fallback` (the context DataSource — typically ObjectStackAdapter)
|
|
16
|
+
* - `provider: 'api'` → returns a new `ApiDataSource`
|
|
17
|
+
* - `provider: 'value'` → returns a new `ValueDataSource`
|
|
18
|
+
*
|
|
19
|
+
* @param viewData - The ViewData configuration from the schema
|
|
20
|
+
* @param fallback - The default DataSource from context (for `provider: 'object'`)
|
|
21
|
+
* @param options - Additional options for adapter construction
|
|
22
|
+
* @returns A DataSource instance, or null if neither viewData nor fallback is available
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const ds = resolveDataSource(
|
|
27
|
+
* { provider: 'api', read: { url: '/api/users' } },
|
|
28
|
+
* contextDataSource,
|
|
29
|
+
* );
|
|
30
|
+
* const result = await ds.find('users');
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function resolveDataSource(viewData, fallback, options) {
|
|
34
|
+
if (!viewData) {
|
|
35
|
+
return fallback ?? null;
|
|
36
|
+
}
|
|
37
|
+
switch (viewData.provider) {
|
|
38
|
+
case 'object':
|
|
39
|
+
// Delegate to the context DataSource (ObjectStackAdapter, etc.)
|
|
40
|
+
return fallback ?? null;
|
|
41
|
+
case 'api': {
|
|
42
|
+
const config = {
|
|
43
|
+
read: viewData.read,
|
|
44
|
+
write: viewData.write,
|
|
45
|
+
fetch: options?.fetch,
|
|
46
|
+
defaultHeaders: options?.defaultHeaders,
|
|
47
|
+
};
|
|
48
|
+
return new ApiDataSource(config);
|
|
49
|
+
}
|
|
50
|
+
case 'value':
|
|
51
|
+
return new ValueDataSource({
|
|
52
|
+
items: (viewData.items ?? []),
|
|
53
|
+
idField: options?.idField,
|
|
54
|
+
});
|
|
55
|
+
default:
|
|
56
|
+
// Unknown provider — fall back to context
|
|
57
|
+
return fallback ?? null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
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 - DataScope Manager
|
|
10
|
+
*
|
|
11
|
+
* Runtime implementation of the DataContext interface for managing
|
|
12
|
+
* named data scopes. Provides row-level data access control and
|
|
13
|
+
* reactive data state management within the UI component tree.
|
|
14
|
+
*
|
|
15
|
+
* @module data-scope
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
import type { DataScope, DataContext, DataSource } from '@object-ui/types';
|
|
19
|
+
/**
|
|
20
|
+
* Row-level filter for restricting data access within a scope
|
|
21
|
+
*/
|
|
22
|
+
export interface RowLevelFilter {
|
|
23
|
+
/** Field to filter on */
|
|
24
|
+
field: string;
|
|
25
|
+
/** Filter operator */
|
|
26
|
+
operator: 'eq' | 'ne' | 'gt' | 'lt' | 'gte' | 'lte' | 'in' | 'nin' | 'contains';
|
|
27
|
+
/** Filter value */
|
|
28
|
+
value: any;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Configuration for creating a data scope
|
|
32
|
+
*/
|
|
33
|
+
export interface DataScopeConfig {
|
|
34
|
+
/** Data source instance */
|
|
35
|
+
dataSource?: DataSource;
|
|
36
|
+
/** Initial data */
|
|
37
|
+
data?: any;
|
|
38
|
+
/** Row-level filters to apply */
|
|
39
|
+
filters?: RowLevelFilter[];
|
|
40
|
+
/** Whether this scope is read-only */
|
|
41
|
+
readOnly?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* DataScopeManager — Runtime implementation of DataContext.
|
|
45
|
+
*
|
|
46
|
+
* Manages named data scopes for the component tree, providing:
|
|
47
|
+
* - Scope registration and lookup
|
|
48
|
+
* - Row-level security via filters
|
|
49
|
+
* - Data state management (data, loading, error)
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const manager = new DataScopeManager();
|
|
54
|
+
* manager.registerScope('contacts', {
|
|
55
|
+
* dataSource: myDataSource,
|
|
56
|
+
* data: [],
|
|
57
|
+
* });
|
|
58
|
+
* const scope = manager.getScope('contacts');
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare class DataScopeManager implements DataContext {
|
|
62
|
+
scopes: Record<string, DataScope>;
|
|
63
|
+
private filters;
|
|
64
|
+
private readOnlyScopes;
|
|
65
|
+
private listeners;
|
|
66
|
+
/**
|
|
67
|
+
* Register a data scope
|
|
68
|
+
*/
|
|
69
|
+
registerScope(name: string, scope: DataScope): void;
|
|
70
|
+
/**
|
|
71
|
+
* Register a data scope with configuration
|
|
72
|
+
*/
|
|
73
|
+
registerScopeWithConfig(name: string, config: DataScopeConfig): void;
|
|
74
|
+
/**
|
|
75
|
+
* Get a data scope by name
|
|
76
|
+
*/
|
|
77
|
+
getScope(name: string): DataScope | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Remove a data scope
|
|
80
|
+
*/
|
|
81
|
+
removeScope(name: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Check if a scope is read-only
|
|
84
|
+
*/
|
|
85
|
+
isReadOnly(name: string): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Get row-level filters for a scope
|
|
88
|
+
*/
|
|
89
|
+
getFilters(name: string): RowLevelFilter[];
|
|
90
|
+
/**
|
|
91
|
+
* Set row-level filters for a scope
|
|
92
|
+
*/
|
|
93
|
+
setFilters(name: string, filters: RowLevelFilter[]): void;
|
|
94
|
+
/**
|
|
95
|
+
* Apply row-level filters to a dataset
|
|
96
|
+
*/
|
|
97
|
+
applyFilters(name: string, data: any[]): any[];
|
|
98
|
+
/**
|
|
99
|
+
* Update data in a scope
|
|
100
|
+
*/
|
|
101
|
+
updateScopeData(name: string, data: any): void;
|
|
102
|
+
/**
|
|
103
|
+
* Update loading state for a scope
|
|
104
|
+
*/
|
|
105
|
+
updateScopeLoading(name: string, loading: boolean): void;
|
|
106
|
+
/**
|
|
107
|
+
* Update error state for a scope
|
|
108
|
+
*/
|
|
109
|
+
updateScopeError(name: string, error: Error | string | null): void;
|
|
110
|
+
/**
|
|
111
|
+
* Subscribe to scope changes
|
|
112
|
+
*/
|
|
113
|
+
onScopeChange(name: string, listener: (scope: DataScope) => void): () => void;
|
|
114
|
+
/**
|
|
115
|
+
* Get all registered scope names
|
|
116
|
+
*/
|
|
117
|
+
getScopeNames(): string[];
|
|
118
|
+
/**
|
|
119
|
+
* Clear all scopes
|
|
120
|
+
*/
|
|
121
|
+
clear(): void;
|
|
122
|
+
private notifyListeners;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Default DataScopeManager instance
|
|
126
|
+
*/
|
|
127
|
+
export declare const defaultDataScopeManager: DataScopeManager;
|
|
@@ -0,0 +1,229 @@
|
|
|
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
|
+
* DataScopeManager — Runtime implementation of DataContext.
|
|
10
|
+
*
|
|
11
|
+
* Manages named data scopes for the component tree, providing:
|
|
12
|
+
* - Scope registration and lookup
|
|
13
|
+
* - Row-level security via filters
|
|
14
|
+
* - Data state management (data, loading, error)
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const manager = new DataScopeManager();
|
|
19
|
+
* manager.registerScope('contacts', {
|
|
20
|
+
* dataSource: myDataSource,
|
|
21
|
+
* data: [],
|
|
22
|
+
* });
|
|
23
|
+
* const scope = manager.getScope('contacts');
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class DataScopeManager {
|
|
27
|
+
constructor() {
|
|
28
|
+
Object.defineProperty(this, "scopes", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
writable: true,
|
|
32
|
+
value: {}
|
|
33
|
+
});
|
|
34
|
+
Object.defineProperty(this, "filters", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
writable: true,
|
|
38
|
+
value: {}
|
|
39
|
+
});
|
|
40
|
+
Object.defineProperty(this, "readOnlyScopes", {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
writable: true,
|
|
44
|
+
value: new Set()
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(this, "listeners", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
configurable: true,
|
|
49
|
+
writable: true,
|
|
50
|
+
value: new Map()
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Register a data scope
|
|
55
|
+
*/
|
|
56
|
+
registerScope(name, scope) {
|
|
57
|
+
this.scopes[name] = scope;
|
|
58
|
+
this.notifyListeners(name, scope);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Register a data scope with configuration
|
|
62
|
+
*/
|
|
63
|
+
registerScopeWithConfig(name, config) {
|
|
64
|
+
const scope = {
|
|
65
|
+
dataSource: config.dataSource,
|
|
66
|
+
data: config.data,
|
|
67
|
+
loading: false,
|
|
68
|
+
error: null,
|
|
69
|
+
};
|
|
70
|
+
if (config.filters) {
|
|
71
|
+
this.filters[name] = config.filters;
|
|
72
|
+
}
|
|
73
|
+
if (config.readOnly) {
|
|
74
|
+
this.readOnlyScopes.add(name);
|
|
75
|
+
}
|
|
76
|
+
this.scopes[name] = scope;
|
|
77
|
+
this.notifyListeners(name, scope);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get a data scope by name
|
|
81
|
+
*/
|
|
82
|
+
getScope(name) {
|
|
83
|
+
return this.scopes[name];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Remove a data scope
|
|
87
|
+
*/
|
|
88
|
+
removeScope(name) {
|
|
89
|
+
delete this.scopes[name];
|
|
90
|
+
delete this.filters[name];
|
|
91
|
+
this.readOnlyScopes.delete(name);
|
|
92
|
+
this.listeners.delete(name);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if a scope is read-only
|
|
96
|
+
*/
|
|
97
|
+
isReadOnly(name) {
|
|
98
|
+
return this.readOnlyScopes.has(name);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get row-level filters for a scope
|
|
102
|
+
*/
|
|
103
|
+
getFilters(name) {
|
|
104
|
+
return this.filters[name] || [];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Set row-level filters for a scope
|
|
108
|
+
*/
|
|
109
|
+
setFilters(name, filters) {
|
|
110
|
+
this.filters[name] = filters;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Apply row-level filters to a dataset
|
|
114
|
+
*/
|
|
115
|
+
applyFilters(name, data) {
|
|
116
|
+
const scopeFilters = this.filters[name];
|
|
117
|
+
if (!scopeFilters || scopeFilters.length === 0) {
|
|
118
|
+
return data;
|
|
119
|
+
}
|
|
120
|
+
return data.filter(row => {
|
|
121
|
+
return scopeFilters.every(filter => {
|
|
122
|
+
const fieldValue = row[filter.field];
|
|
123
|
+
return evaluateFilter(fieldValue, filter.operator, filter.value);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Update data in a scope
|
|
129
|
+
*/
|
|
130
|
+
updateScopeData(name, data) {
|
|
131
|
+
const scope = this.scopes[name];
|
|
132
|
+
if (!scope)
|
|
133
|
+
return;
|
|
134
|
+
if (this.readOnlyScopes.has(name)) {
|
|
135
|
+
throw new Error(`Cannot update read-only scope: ${name}`);
|
|
136
|
+
}
|
|
137
|
+
scope.data = data;
|
|
138
|
+
this.notifyListeners(name, scope);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Update loading state for a scope
|
|
142
|
+
*/
|
|
143
|
+
updateScopeLoading(name, loading) {
|
|
144
|
+
const scope = this.scopes[name];
|
|
145
|
+
if (!scope)
|
|
146
|
+
return;
|
|
147
|
+
scope.loading = loading;
|
|
148
|
+
this.notifyListeners(name, scope);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Update error state for a scope
|
|
152
|
+
*/
|
|
153
|
+
updateScopeError(name, error) {
|
|
154
|
+
const scope = this.scopes[name];
|
|
155
|
+
if (!scope)
|
|
156
|
+
return;
|
|
157
|
+
scope.error = error;
|
|
158
|
+
this.notifyListeners(name, scope);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Subscribe to scope changes
|
|
162
|
+
*/
|
|
163
|
+
onScopeChange(name, listener) {
|
|
164
|
+
if (!this.listeners.has(name)) {
|
|
165
|
+
this.listeners.set(name, []);
|
|
166
|
+
}
|
|
167
|
+
this.listeners.get(name).push(listener);
|
|
168
|
+
return () => {
|
|
169
|
+
const arr = this.listeners.get(name);
|
|
170
|
+
if (arr) {
|
|
171
|
+
const idx = arr.indexOf(listener);
|
|
172
|
+
if (idx >= 0)
|
|
173
|
+
arr.splice(idx, 1);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get all registered scope names
|
|
179
|
+
*/
|
|
180
|
+
getScopeNames() {
|
|
181
|
+
return Object.keys(this.scopes);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Clear all scopes
|
|
185
|
+
*/
|
|
186
|
+
clear() {
|
|
187
|
+
this.scopes = {};
|
|
188
|
+
this.filters = {};
|
|
189
|
+
this.readOnlyScopes.clear();
|
|
190
|
+
this.listeners.clear();
|
|
191
|
+
}
|
|
192
|
+
notifyListeners(name, scope) {
|
|
193
|
+
const arr = this.listeners.get(name);
|
|
194
|
+
if (arr) {
|
|
195
|
+
arr.forEach(listener => listener(scope));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Evaluate a single filter condition against a field value
|
|
201
|
+
*/
|
|
202
|
+
function evaluateFilter(fieldValue, operator, filterValue) {
|
|
203
|
+
switch (operator) {
|
|
204
|
+
case 'eq':
|
|
205
|
+
return fieldValue === filterValue;
|
|
206
|
+
case 'ne':
|
|
207
|
+
return fieldValue !== filterValue;
|
|
208
|
+
case 'gt':
|
|
209
|
+
return fieldValue > filterValue;
|
|
210
|
+
case 'lt':
|
|
211
|
+
return fieldValue < filterValue;
|
|
212
|
+
case 'gte':
|
|
213
|
+
return fieldValue >= filterValue;
|
|
214
|
+
case 'lte':
|
|
215
|
+
return fieldValue <= filterValue;
|
|
216
|
+
case 'in':
|
|
217
|
+
return Array.isArray(filterValue) && filterValue.includes(fieldValue);
|
|
218
|
+
case 'nin':
|
|
219
|
+
return Array.isArray(filterValue) && !filterValue.includes(fieldValue);
|
|
220
|
+
case 'contains':
|
|
221
|
+
return typeof fieldValue === 'string' && fieldValue.includes(String(filterValue));
|
|
222
|
+
default:
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Default DataScopeManager instance
|
|
228
|
+
*/
|
|
229
|
+
export const defaultDataScopeManager = new DataScopeManager();
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export { DataScopeManager, defaultDataScopeManager, type RowLevelFilter, type DataScopeConfig, } from './DataScopeManager.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export { DataScopeManager, defaultDataScopeManager, } from './DataScopeManager.js';
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { ExpressionContext } from './ExpressionContext.js';
|
|
18
18
|
import { ExpressionCache } from './ExpressionCache.js';
|
|
19
|
+
import { FormulaFunctions } from './FormulaFunctions.js';
|
|
19
20
|
/**
|
|
20
21
|
* Options for expression evaluation
|
|
21
22
|
*/
|
|
@@ -41,7 +42,8 @@ export interface EvaluationOptions {
|
|
|
41
42
|
export declare class ExpressionEvaluator {
|
|
42
43
|
private context;
|
|
43
44
|
private cache;
|
|
44
|
-
|
|
45
|
+
private formulas;
|
|
46
|
+
constructor(context?: ExpressionContext | Record<string, any>, cache?: ExpressionCache, formulas?: FormulaFunctions);
|
|
45
47
|
/**
|
|
46
48
|
* Evaluate a string that may contain template expressions like ${...}
|
|
47
49
|
*
|
|
@@ -106,6 +108,14 @@ export declare class ExpressionEvaluator {
|
|
|
106
108
|
* Clear the expression cache
|
|
107
109
|
*/
|
|
108
110
|
clearCache(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Get the formula functions registry
|
|
113
|
+
*/
|
|
114
|
+
getFormulas(): FormulaFunctions;
|
|
115
|
+
/**
|
|
116
|
+
* Register a custom formula function
|
|
117
|
+
*/
|
|
118
|
+
registerFunction(name: string, fn: (...args: any[]) => any): void;
|
|
109
119
|
}
|
|
110
120
|
/**
|
|
111
121
|
* Convenience function to quickly evaluate an expression
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { ExpressionContext } from './ExpressionContext.js';
|
|
18
18
|
import { ExpressionCache } from './ExpressionCache.js';
|
|
19
|
+
import { FormulaFunctions } from './FormulaFunctions.js';
|
|
19
20
|
/**
|
|
20
21
|
* Expression evaluator for dynamic UI expressions
|
|
21
22
|
*/
|
|
22
23
|
export class ExpressionEvaluator {
|
|
23
|
-
constructor(context, cache) {
|
|
24
|
+
constructor(context, cache, formulas) {
|
|
24
25
|
Object.defineProperty(this, "context", {
|
|
25
26
|
enumerable: true,
|
|
26
27
|
configurable: true,
|
|
@@ -33,6 +34,12 @@ export class ExpressionEvaluator {
|
|
|
33
34
|
writable: true,
|
|
34
35
|
value: void 0
|
|
35
36
|
});
|
|
37
|
+
Object.defineProperty(this, "formulas", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: void 0
|
|
42
|
+
});
|
|
36
43
|
if (context instanceof ExpressionContext) {
|
|
37
44
|
this.context = context;
|
|
38
45
|
}
|
|
@@ -41,6 +48,7 @@ export class ExpressionEvaluator {
|
|
|
41
48
|
}
|
|
42
49
|
// Use provided cache or create a new one
|
|
43
50
|
this.cache = cache || new ExpressionCache();
|
|
51
|
+
this.formulas = formulas || new FormulaFunctions();
|
|
44
52
|
}
|
|
45
53
|
/**
|
|
46
54
|
* Evaluate a string that may contain template expressions like ${...}
|
|
@@ -114,9 +122,12 @@ export class ExpressionEvaluator {
|
|
|
114
122
|
try {
|
|
115
123
|
// Create a safe evaluation function
|
|
116
124
|
const contextObj = this.context.toObject();
|
|
125
|
+
// Inject formula functions into the evaluation context
|
|
126
|
+
const formulaObj = this.formulas.toObject();
|
|
127
|
+
const mergedContext = { ...formulaObj, ...contextObj };
|
|
117
128
|
// Build safe function with context variables
|
|
118
|
-
const varNames = Object.keys(
|
|
119
|
-
const varValues = Object.values(
|
|
129
|
+
const varNames = Object.keys(mergedContext);
|
|
130
|
+
const varValues = Object.values(mergedContext);
|
|
120
131
|
// Use cached compilation
|
|
121
132
|
const compiled = this.cache.compile(expression, varNames);
|
|
122
133
|
// Execute with context values
|
|
@@ -184,8 +195,8 @@ export class ExpressionEvaluator {
|
|
|
184
195
|
* Create a new evaluator with additional context data
|
|
185
196
|
*/
|
|
186
197
|
withContext(data) {
|
|
187
|
-
// Share the cache with the new evaluator for maximum efficiency
|
|
188
|
-
return new ExpressionEvaluator(this.context.createChild(data), this.cache);
|
|
198
|
+
// Share the cache and formulas with the new evaluator for maximum efficiency
|
|
199
|
+
return new ExpressionEvaluator(this.context.createChild(data), this.cache, this.formulas);
|
|
189
200
|
}
|
|
190
201
|
/**
|
|
191
202
|
* Get cache statistics (useful for debugging and optimization)
|
|
@@ -199,22 +210,35 @@ export class ExpressionEvaluator {
|
|
|
199
210
|
clearCache() {
|
|
200
211
|
this.cache.clear();
|
|
201
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Get the formula functions registry
|
|
215
|
+
*/
|
|
216
|
+
getFormulas() {
|
|
217
|
+
return this.formulas;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Register a custom formula function
|
|
221
|
+
*/
|
|
222
|
+
registerFunction(name, fn) {
|
|
223
|
+
this.formulas.register(name, fn);
|
|
224
|
+
}
|
|
202
225
|
}
|
|
203
226
|
/**
|
|
204
|
-
* Shared global cache for convenience functions
|
|
227
|
+
* Shared global cache and formulas for convenience functions
|
|
205
228
|
*/
|
|
206
229
|
const globalCache = new ExpressionCache();
|
|
230
|
+
const globalFormulas = new FormulaFunctions();
|
|
207
231
|
/**
|
|
208
232
|
* Convenience function to quickly evaluate an expression
|
|
209
233
|
*/
|
|
210
234
|
export function evaluateExpression(expression, context = {}, options = {}) {
|
|
211
|
-
const evaluator = new ExpressionEvaluator(context, globalCache);
|
|
235
|
+
const evaluator = new ExpressionEvaluator(context, globalCache, globalFormulas);
|
|
212
236
|
return evaluator.evaluate(expression, options);
|
|
213
237
|
}
|
|
214
238
|
/**
|
|
215
239
|
* Convenience function to evaluate a condition
|
|
216
240
|
*/
|
|
217
241
|
export function evaluateCondition(condition, context = {}) {
|
|
218
|
-
const evaluator = new ExpressionEvaluator(context, globalCache);
|
|
242
|
+
const evaluator = new ExpressionEvaluator(context, globalCache, globalFormulas);
|
|
219
243
|
return evaluator.evaluateCondition(condition);
|
|
220
244
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 - Formula Functions
|
|
10
|
+
*
|
|
11
|
+
* Built-in formula functions for the expression engine.
|
|
12
|
+
* Provides aggregation, date, logic, and string functions
|
|
13
|
+
* compatible with low-code platform expression evaluation.
|
|
14
|
+
*
|
|
15
|
+
* @module evaluator
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* A formula function that can be registered with the expression evaluator
|
|
20
|
+
*/
|
|
21
|
+
export type FormulaFunction = (...args: any[]) => any;
|
|
22
|
+
/**
|
|
23
|
+
* Registry of built-in formula functions
|
|
24
|
+
*/
|
|
25
|
+
export declare class FormulaFunctions {
|
|
26
|
+
private functions;
|
|
27
|
+
constructor();
|
|
28
|
+
/**
|
|
29
|
+
* Register a custom formula function
|
|
30
|
+
*/
|
|
31
|
+
register(name: string, fn: FormulaFunction): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get a formula function by name
|
|
34
|
+
*/
|
|
35
|
+
get(name: string): FormulaFunction | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a function is registered
|
|
38
|
+
*/
|
|
39
|
+
has(name: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Get all registered function names
|
|
42
|
+
*/
|
|
43
|
+
getNames(): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Get all functions as a plain object (for injection into expression context)
|
|
46
|
+
*/
|
|
47
|
+
toObject(): Record<string, FormulaFunction>;
|
|
48
|
+
/**
|
|
49
|
+
* Register all default built-in functions
|
|
50
|
+
*/
|
|
51
|
+
private registerDefaults;
|
|
52
|
+
private registerAggregationFunctions;
|
|
53
|
+
private registerDateFunctions;
|
|
54
|
+
private registerLogicFunctions;
|
|
55
|
+
private registerStringFunctions;
|
|
56
|
+
private registerStringSearchFunctions;
|
|
57
|
+
private registerStatisticalFunctions;
|
|
58
|
+
}
|