@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,341 +0,0 @@
1
- /**
2
- * ObjectUI - Query AST Builder
3
- * Phase 3.3: QuerySchema AST implementation
4
- * ObjectStack Spec v2.0.1: Window functions support
5
- */
6
-
7
- import type {
8
- QueryAST,
9
- QuerySchema,
10
- SelectNode,
11
- FromNode,
12
- WhereNode,
13
- JoinNode,
14
- GroupByNode,
15
- OrderByNode,
16
- LimitNode,
17
- OffsetNode,
18
- AggregateNode,
19
- WindowNode,
20
- WindowFunction,
21
- WindowFrame,
22
- WindowConfig,
23
- FieldNode,
24
- LiteralNode,
25
- OperatorNode,
26
- LogicalOperator,
27
- AdvancedFilterSchema,
28
- AdvancedFilterCondition,
29
- QuerySortConfig,
30
- JoinConfig,
31
- AggregationConfig,
32
- } from '@object-ui/types';
33
-
34
- /**
35
- * Query AST Builder - Converts QuerySchema to AST
36
- */
37
- export class QueryASTBuilder {
38
- build(query: QuerySchema): QueryAST {
39
- const ast: QueryAST = {
40
- select: this.buildSelect(query),
41
- from: this.buildFrom(query),
42
- };
43
-
44
- if (query.filter) {
45
- ast.where = this.buildWhere(query.filter);
46
- }
47
-
48
- if (query.joins && query.joins.length > 0) {
49
- ast.joins = query.joins.map(join => this.buildJoin(join));
50
- }
51
-
52
- if (query.group_by && query.group_by.length > 0) {
53
- ast.group_by = this.buildGroupBy(query.group_by);
54
- }
55
-
56
- if (query.sort && query.sort.length > 0) {
57
- ast.order_by = this.buildOrderBy(query.sort);
58
- }
59
-
60
- if (query.limit !== undefined) {
61
- ast.limit = this.buildLimit(query.limit);
62
- }
63
-
64
- if (query.offset !== undefined) {
65
- ast.offset = this.buildOffset(query.offset);
66
- }
67
-
68
- return ast;
69
- }
70
-
71
- private buildSelect(query: QuerySchema): SelectNode {
72
- const fields: (FieldNode | AggregateNode | WindowNode)[] = [];
73
-
74
- if (query.fields && query.fields.length > 0) {
75
- fields.push(...query.fields.map(field => this.buildField(field)));
76
- } else if (!query.aggregations || query.aggregations.length === 0) {
77
- // Only add '*' if there are no aggregations
78
- fields.push(this.buildField('*'));
79
- }
80
-
81
- if (query.aggregations && query.aggregations.length > 0) {
82
- fields.push(...query.aggregations.map(agg => this.buildAggregation(agg)));
83
- }
84
-
85
- // Add window functions (ObjectStack Spec v2.0.1)
86
- if (query.windows && query.windows.length > 0) {
87
- fields.push(...query.windows.map(win => this.buildWindow(win)));
88
- }
89
-
90
- return {
91
- type: 'select',
92
- fields,
93
- distinct: false,
94
- };
95
- }
96
-
97
- private buildFrom(query: QuerySchema): FromNode {
98
- return {
99
- type: 'from',
100
- table: query.object,
101
- };
102
- }
103
-
104
- private buildWhere(filter: AdvancedFilterSchema): WhereNode {
105
- return {
106
- type: 'where',
107
- condition: this.buildFilterCondition(filter),
108
- };
109
- }
110
-
111
- private buildFilterCondition(filter: AdvancedFilterSchema): OperatorNode {
112
- const operator = filter.operator || 'and';
113
- const operands: (OperatorNode | FieldNode | LiteralNode)[] = [];
114
-
115
- if (filter.conditions && filter.conditions.length > 0) {
116
- operands.push(...filter.conditions.map(cond => this.buildCondition(cond)));
117
- }
118
-
119
- if (filter.groups && filter.groups.length > 0) {
120
- operands.push(...filter.groups.map(group => this.buildFilterCondition(group)));
121
- }
122
-
123
- return {
124
- type: 'operator',
125
- operator: operator as LogicalOperator,
126
- operands,
127
- };
128
- }
129
-
130
- private buildCondition(condition: AdvancedFilterCondition): OperatorNode {
131
- const field = this.buildField(condition.field);
132
-
133
- // Map filter operators to comparison operators
134
- const operatorMap: Record<string, string> = {
135
- 'equals': '=',
136
- 'not_equals': '!=',
137
- 'greater_than': '>',
138
- 'greater_than_or_equal': '>=',
139
- 'less_than': '<',
140
- 'less_than_or_equal': '<=',
141
- 'contains': 'contains',
142
- 'not_contains': 'contains',
143
- 'starts_with': 'starts_with',
144
- 'ends_with': 'ends_with',
145
- 'like': 'like',
146
- 'ilike': 'ilike',
147
- 'in': 'in',
148
- 'not_in': 'not_in',
149
- 'is_null': 'is_null',
150
- 'is_not_null': 'is_not_null',
151
- 'between': 'between',
152
- };
153
-
154
- const operator = operatorMap[condition.operator] || '=';
155
-
156
- // Handle special operators
157
- if (operator === 'between' && condition.values && condition.values.length === 2) {
158
- return {
159
- type: 'operator',
160
- operator: 'between' as any,
161
- operands: [
162
- field,
163
- this.buildLiteral(condition.values[0]),
164
- this.buildLiteral(condition.values[1])
165
- ],
166
- };
167
- }
168
-
169
- if ((operator === 'in' || operator === 'not_in') && condition.values) {
170
- return {
171
- type: 'operator',
172
- operator: operator as any,
173
- operands: [field, ...condition.values.map(v => this.buildLiteral(v))],
174
- };
175
- }
176
-
177
- if (operator === 'is_null' || operator === 'is_not_null') {
178
- return {
179
- type: 'operator',
180
- operator: operator as any,
181
- operands: [field],
182
- };
183
- }
184
-
185
- // Standard binary operator
186
- const value = this.buildLiteral(condition.value);
187
- return {
188
- type: 'operator',
189
- operator: operator as any,
190
- operands: [field, value],
191
- };
192
- }
193
-
194
- private buildJoin(join: JoinConfig): JoinNode {
195
- const onCondition: OperatorNode = {
196
- type: 'operator',
197
- operator: '=',
198
- operands: [
199
- this.buildField(join.on.local_field),
200
- this.buildField(join.on.foreign_field, join.alias || join.object),
201
- ],
202
- };
203
-
204
- return {
205
- type: 'join',
206
- join_type: join.type,
207
- table: join.object,
208
- alias: join.alias,
209
- on: onCondition,
210
- };
211
- }
212
-
213
- private buildGroupBy(fields: string[]): GroupByNode {
214
- return {
215
- type: 'group_by',
216
- fields: fields.map(field => this.buildField(field)),
217
- };
218
- }
219
-
220
- private buildOrderBy(sorts: QuerySortConfig[]): OrderByNode {
221
- return {
222
- type: 'order_by',
223
- fields: sorts.map(sort => ({
224
- field: this.buildField(sort.field),
225
- direction: sort.order,
226
- })),
227
- };
228
- }
229
-
230
- private buildLimit(limit: number): LimitNode {
231
- return {
232
- type: 'limit',
233
- value: limit,
234
- };
235
- }
236
-
237
- private buildOffset(offset: number): OffsetNode {
238
- return {
239
- type: 'offset',
240
- value: offset,
241
- };
242
- }
243
-
244
- private buildField(field: string, table?: string): FieldNode {
245
- const parts = field.split('.');
246
-
247
- if (parts.length === 2) {
248
- return {
249
- type: 'field',
250
- table: parts[0],
251
- name: parts[1],
252
- };
253
- }
254
-
255
- return {
256
- type: 'field',
257
- table,
258
- name: field,
259
- };
260
- }
261
-
262
- private buildLiteral(value: any): LiteralNode {
263
- let dataType: 'string' | 'number' | 'boolean' | 'date' | 'null' = 'string';
264
-
265
- if (value === null || value === undefined) {
266
- dataType = 'null';
267
- } else if (typeof value === 'number') {
268
- dataType = 'number';
269
- } else if (typeof value === 'boolean') {
270
- dataType = 'boolean';
271
- } else if (value instanceof Date) {
272
- dataType = 'date';
273
- }
274
-
275
- return {
276
- type: 'literal',
277
- value,
278
- data_type: dataType,
279
- };
280
- }
281
-
282
- private buildAggregation(agg: AggregationConfig): AggregateNode {
283
- return {
284
- type: 'aggregate',
285
- function: agg.function,
286
- field: agg.field ? this.buildField(agg.field) : undefined,
287
- alias: agg.alias,
288
- distinct: agg.distinct,
289
- };
290
- }
291
-
292
- /**
293
- * Build window function node (ObjectStack Spec v2.0.1)
294
- */
295
- private buildWindow(config: WindowConfig): WindowNode {
296
- const node: WindowNode = {
297
- type: 'window',
298
- function: config.function,
299
- alias: config.alias,
300
- };
301
-
302
- if (config.field) {
303
- node.field = this.buildField(config.field);
304
- }
305
-
306
- if (config.partitionBy && config.partitionBy.length > 0) {
307
- node.partitionBy = config.partitionBy.map(field => this.buildField(field));
308
- }
309
-
310
- if (config.orderBy && config.orderBy.length > 0) {
311
- node.orderBy = config.orderBy.map(sort => ({
312
- field: this.buildField(sort.field),
313
- direction: sort.direction,
314
- }));
315
- }
316
-
317
- if (config.frame) {
318
- node.frame = config.frame;
319
- }
320
-
321
- if (config.offset !== undefined) {
322
- node.offset = config.offset;
323
- }
324
-
325
- if (config.defaultValue !== undefined) {
326
- node.defaultValue = this.buildLiteral(config.defaultValue);
327
- }
328
-
329
- return node;
330
- }
331
-
332
- optimize(ast: QueryAST): QueryAST {
333
- return ast;
334
- }
335
- }
336
-
337
- export const defaultQueryASTBuilder = new QueryASTBuilder();
338
-
339
- export function buildQueryAST(query: QuerySchema): QueryAST {
340
- return defaultQueryASTBuilder.build(query);
341
- }
@@ -1,259 +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 - Plugin Scope Implementation
11
- *
12
- * Section 3.3: Implementation of scoped plugin system to prevent conflicts.
13
- * Provides isolated component registration, state management, and event bus.
14
- *
15
- * @module plugin-scope-impl
16
- * @packageDocumentation
17
- */
18
-
19
- import type {
20
- PluginScope,
21
- PluginScopeConfig,
22
- PluginEventHandler
23
- } from '@object-ui/types';
24
- import type { Registry, ComponentMeta as RegistryComponentMeta } from './Registry.js';
25
-
26
- /**
27
- * Event Bus for scoped plugin events
28
- */
29
- class EventBus {
30
- private listeners = new Map<string, Set<PluginEventHandler>>();
31
-
32
- on(event: string, handler: PluginEventHandler): () => void {
33
- if (!this.listeners.has(event)) {
34
- this.listeners.set(event, new Set());
35
- }
36
- this.listeners.get(event)!.add(handler);
37
-
38
- // Return unsubscribe function
39
- return () => {
40
- this.listeners.get(event)?.delete(handler);
41
- if (this.listeners.get(event)?.size === 0) {
42
- this.listeners.delete(event);
43
- }
44
- };
45
- }
46
-
47
- emit(event: string, data?: any): void {
48
- const handlers = this.listeners.get(event);
49
- if (handlers) {
50
- handlers.forEach(handler => {
51
- try {
52
- handler(data);
53
- } catch (error) {
54
- console.error(`Error in event handler for "${event}":`, error);
55
- }
56
- });
57
- }
58
- }
59
-
60
- cleanup(): void {
61
- this.listeners.clear();
62
- }
63
- }
64
-
65
- /**
66
- * Global event bus for cross-plugin communication
67
- */
68
- const globalEventBus = new EventBus();
69
-
70
- /**
71
- * Plugin Scope Implementation
72
- *
73
- * Provides isolated access to registry, state, and events for each plugin.
74
- */
75
- export class PluginScopeImpl implements PluginScope {
76
- public readonly name: string;
77
- public readonly version: string;
78
-
79
- private registry: Registry;
80
- private state = new Map<string, any>();
81
- private eventBus = new EventBus();
82
- private config: Required<PluginScopeConfig>;
83
-
84
- constructor(
85
- name: string,
86
- version: string,
87
- registry: Registry,
88
- config?: PluginScopeConfig
89
- ) {
90
- this.name = name;
91
- this.version = version;
92
- this.registry = registry;
93
- this.config = {
94
- enableStateIsolation: config?.enableStateIsolation ?? true,
95
- enableEventIsolation: config?.enableEventIsolation ?? true,
96
- allowGlobalEvents: config?.allowGlobalEvents ?? true,
97
- maxStateSize: config?.maxStateSize ?? 5 * 1024 * 1024, // 5MB
98
- };
99
- }
100
-
101
- /**
102
- * Register a component in the scoped namespace
103
- */
104
- registerComponent(type: string, component: any, meta?: any): void {
105
- // Components are registered as "pluginName:type"
106
- const registryMeta: RegistryComponentMeta = {
107
- ...meta,
108
- namespace: this.name,
109
- };
110
- this.registry.register(type, component, registryMeta);
111
- }
112
-
113
- /**
114
- * Get a component from the scoped namespace
115
- */
116
- getComponent(type: string): any | undefined {
117
- // First try scoped lookup
118
- const scoped = this.registry.get(`${this.name}:${type}`);
119
- if (scoped) {
120
- return scoped;
121
- }
122
-
123
- // Fall back to global lookup
124
- return this.registry.get(type);
125
- }
126
-
127
- /**
128
- * Scoped state management
129
- */
130
- useState<T>(key: string, initialValue: T): [T, (value: T | ((prev: T) => T)) => void] {
131
- if (!this.config.enableStateIsolation) {
132
- throw new Error('State isolation is disabled for this plugin');
133
- }
134
-
135
- // Initialize state if not present
136
- if (!this.state.has(key)) {
137
- this.setState(key, initialValue);
138
- }
139
-
140
- const currentValue = this.getState<T>(key) ?? initialValue;
141
-
142
- const setValue = (value: T | ((prev: T) => T)) => {
143
- const newValue = typeof value === 'function'
144
- ? (value as (prev: T) => T)(this.getState<T>(key) ?? initialValue)
145
- : value;
146
- this.setState(key, newValue);
147
- };
148
-
149
- return [currentValue, setValue];
150
- }
151
-
152
- /**
153
- * Get scoped state value
154
- */
155
- getState<T>(key: string): T | undefined {
156
- return this.state.get(key);
157
- }
158
-
159
- /**
160
- * Set scoped state value
161
- */
162
- setState<T>(key: string, value: T): void {
163
- if (!this.config.enableStateIsolation) {
164
- throw new Error('State isolation is disabled for this plugin');
165
- }
166
-
167
- // Check state size limit
168
- const stateSize = this.estimateStateSize();
169
- const valueSize = this.estimateValueSize(value);
170
-
171
- if (stateSize + valueSize > this.config.maxStateSize) {
172
- throw new Error(
173
- `Plugin "${this.name}" exceeded maximum state size of ${this.config.maxStateSize} bytes`
174
- );
175
- }
176
-
177
- this.state.set(key, value);
178
- }
179
-
180
- /**
181
- * Subscribe to scoped events
182
- */
183
- on(event: string, handler: PluginEventHandler): () => void {
184
- if (!this.config.enableEventIsolation) {
185
- // If isolation is disabled, use global event bus
186
- return this.onGlobal(event, handler);
187
- }
188
-
189
- // Scoped event: prefix with plugin name
190
- const scopedEvent = `${this.name}:${event}`;
191
- return this.eventBus.on(scopedEvent, handler);
192
- }
193
-
194
- /**
195
- * Emit a scoped event
196
- */
197
- emit(event: string, data?: any): void {
198
- if (!this.config.enableEventIsolation) {
199
- // If isolation is disabled, emit globally
200
- this.emitGlobal(event, data);
201
- return;
202
- }
203
-
204
- // Scoped event: prefix with plugin name
205
- const scopedEvent = `${this.name}:${event}`;
206
- this.eventBus.emit(scopedEvent, data);
207
- }
208
-
209
- /**
210
- * Emit a global event
211
- */
212
- emitGlobal(event: string, data?: any): void {
213
- if (!this.config.allowGlobalEvents) {
214
- throw new Error('Global events are disabled for this plugin');
215
- }
216
- globalEventBus.emit(event, data);
217
- }
218
-
219
- /**
220
- * Subscribe to global events
221
- */
222
- onGlobal(event: string, handler: PluginEventHandler): () => void {
223
- if (!this.config.allowGlobalEvents) {
224
- throw new Error('Global events are disabled for this plugin');
225
- }
226
- return globalEventBus.on(event, handler);
227
- }
228
-
229
- /**
230
- * Clean up all plugin resources
231
- */
232
- cleanup(): void {
233
- this.state.clear();
234
- this.eventBus.cleanup();
235
- }
236
-
237
- /**
238
- * Estimate total state size in bytes
239
- */
240
- private estimateStateSize(): number {
241
- let size = 0;
242
- for (const value of this.state.values()) {
243
- size += this.estimateValueSize(value);
244
- }
245
- return size;
246
- }
247
-
248
- /**
249
- * Estimate size of a value in bytes
250
- */
251
- private estimateValueSize(value: any): number {
252
- try {
253
- return JSON.stringify(value).length * 2; // UTF-16 encoding
254
- } catch {
255
- // If not serializable, use rough estimate
256
- return 1024; // 1KB default
257
- }
258
- }
259
- }