@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,211 +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
- import { describe, it, expect } from 'vitest';
10
- import { DataScopeManager } from '../DataScopeManager';
11
-
12
- describe('DataScopeManager', () => {
13
- describe('Scope Registration', () => {
14
- it('should register and retrieve a scope', () => {
15
- const manager = new DataScopeManager();
16
- manager.registerScope('contacts', { data: [{ name: 'Alice' }] });
17
- const scope = manager.getScope('contacts');
18
- expect(scope).toBeDefined();
19
- expect(scope?.data).toEqual([{ name: 'Alice' }]);
20
- });
21
-
22
- it('should return undefined for unregistered scope', () => {
23
- const manager = new DataScopeManager();
24
- expect(manager.getScope('unknown')).toBeUndefined();
25
- });
26
-
27
- it('should remove a scope', () => {
28
- const manager = new DataScopeManager();
29
- manager.registerScope('test', { data: [] });
30
- expect(manager.getScope('test')).toBeDefined();
31
- manager.removeScope('test');
32
- expect(manager.getScope('test')).toBeUndefined();
33
- });
34
-
35
- it('should list scope names', () => {
36
- const manager = new DataScopeManager();
37
- manager.registerScope('a', { data: [] });
38
- manager.registerScope('b', { data: [] });
39
- expect(manager.getScopeNames()).toEqual(['a', 'b']);
40
- });
41
-
42
- it('should clear all scopes', () => {
43
- const manager = new DataScopeManager();
44
- manager.registerScope('a', { data: [] });
45
- manager.registerScope('b', { data: [] });
46
- manager.clear();
47
- expect(manager.getScopeNames()).toEqual([]);
48
- });
49
- });
50
-
51
- describe('Scope Configuration', () => {
52
- it('should register scope with config', () => {
53
- const manager = new DataScopeManager();
54
- manager.registerScopeWithConfig('test', {
55
- data: [1, 2, 3],
56
- readOnly: true,
57
- filters: [{ field: 'status', operator: 'eq', value: 'active' }],
58
- });
59
-
60
- expect(manager.getScope('test')?.data).toEqual([1, 2, 3]);
61
- expect(manager.isReadOnly('test')).toBe(true);
62
- expect(manager.getFilters('test')).toHaveLength(1);
63
- });
64
-
65
- it('should throw when updating read-only scope', () => {
66
- const manager = new DataScopeManager();
67
- manager.registerScopeWithConfig('readonly', { readOnly: true });
68
- expect(() => manager.updateScopeData('readonly', [1])).toThrow('Cannot update read-only scope');
69
- });
70
- });
71
-
72
- describe('Row-Level Filtering', () => {
73
- it('should apply eq filter', () => {
74
- const manager = new DataScopeManager();
75
- manager.registerScope('test', { data: [] });
76
- manager.setFilters('test', [{ field: 'status', operator: 'eq', value: 'active' }]);
77
-
78
- const result = manager.applyFilters('test', [
79
- { id: 1, status: 'active' },
80
- { id: 2, status: 'inactive' },
81
- { id: 3, status: 'active' },
82
- ]);
83
-
84
- expect(result).toHaveLength(2);
85
- expect(result[0].id).toBe(1);
86
- expect(result[1].id).toBe(3);
87
- });
88
-
89
- it('should apply gt filter', () => {
90
- const manager = new DataScopeManager();
91
- manager.registerScope('test', { data: [] });
92
- manager.setFilters('test', [{ field: 'age', operator: 'gt', value: 18 }]);
93
-
94
- const result = manager.applyFilters('test', [
95
- { name: 'A', age: 15 },
96
- { name: 'B', age: 25 },
97
- { name: 'C', age: 18 },
98
- ]);
99
-
100
- expect(result).toHaveLength(1);
101
- expect(result[0].name).toBe('B');
102
- });
103
-
104
- it('should apply in filter', () => {
105
- const manager = new DataScopeManager();
106
- manager.registerScope('test', { data: [] });
107
- manager.setFilters('test', [{ field: 'role', operator: 'in', value: ['admin', 'editor'] }]);
108
-
109
- const result = manager.applyFilters('test', [
110
- { name: 'A', role: 'admin' },
111
- { name: 'B', role: 'viewer' },
112
- { name: 'C', role: 'editor' },
113
- ]);
114
-
115
- expect(result).toHaveLength(2);
116
- });
117
-
118
- it('should apply contains filter', () => {
119
- const manager = new DataScopeManager();
120
- manager.registerScope('test', { data: [] });
121
- manager.setFilters('test', [{ field: 'name', operator: 'contains', value: 'ob' }]);
122
-
123
- const result = manager.applyFilters('test', [
124
- { name: 'Bob' },
125
- { name: 'Alice' },
126
- { name: 'Robert' },
127
- ]);
128
-
129
- expect(result).toHaveLength(2);
130
- });
131
-
132
- it('should apply multiple filters (AND logic)', () => {
133
- const manager = new DataScopeManager();
134
- manager.registerScope('test', { data: [] });
135
- manager.setFilters('test', [
136
- { field: 'status', operator: 'eq', value: 'active' },
137
- { field: 'age', operator: 'gte', value: 18 },
138
- ]);
139
-
140
- const result = manager.applyFilters('test', [
141
- { status: 'active', age: 25 },
142
- { status: 'inactive', age: 25 },
143
- { status: 'active', age: 15 },
144
- ]);
145
-
146
- expect(result).toHaveLength(1);
147
- expect(result[0].age).toBe(25);
148
- });
149
-
150
- it('should return all data when no filters set', () => {
151
- const manager = new DataScopeManager();
152
- manager.registerScope('test', { data: [] });
153
- const data = [{ a: 1 }, { a: 2 }];
154
- expect(manager.applyFilters('test', data)).toEqual(data);
155
- });
156
- });
157
-
158
- describe('Scope Updates', () => {
159
- it('should update scope data', () => {
160
- const manager = new DataScopeManager();
161
- manager.registerScope('test', { data: [] });
162
- manager.updateScopeData('test', [1, 2, 3]);
163
- expect(manager.getScope('test')?.data).toEqual([1, 2, 3]);
164
- });
165
-
166
- it('should update loading state', () => {
167
- const manager = new DataScopeManager();
168
- manager.registerScope('test', { data: [], loading: false });
169
- manager.updateScopeLoading('test', true);
170
- expect(manager.getScope('test')?.loading).toBe(true);
171
- });
172
-
173
- it('should update error state', () => {
174
- const manager = new DataScopeManager();
175
- manager.registerScope('test', { data: [] });
176
- manager.updateScopeError('test', new Error('fail'));
177
- expect(manager.getScope('test')?.error).toBeDefined();
178
- });
179
- });
180
-
181
- describe('Change Listeners', () => {
182
- it('should notify listeners on scope registration', () => {
183
- const manager = new DataScopeManager();
184
- let notified = false;
185
- manager.onScopeChange('test', () => { notified = true; });
186
- manager.registerScope('test', { data: [] });
187
- expect(notified).toBe(true);
188
- });
189
-
190
- it('should notify listeners on data update', () => {
191
- const manager = new DataScopeManager();
192
- manager.registerScope('test', { data: [] });
193
- let newData: any;
194
- manager.onScopeChange('test', (scope) => { newData = scope.data; });
195
- manager.updateScopeData('test', [1, 2]);
196
- expect(newData).toEqual([1, 2]);
197
- });
198
-
199
- it('should allow unsubscribing', () => {
200
- const manager = new DataScopeManager();
201
- manager.registerScope('test', { data: [] });
202
- let count = 0;
203
- const unsub = manager.onScopeChange('test', () => { count++; });
204
- manager.updateScopeData('test', [1]);
205
- expect(count).toBe(1);
206
- unsub();
207
- manager.updateScopeData('test', [2]);
208
- expect(count).toBe(1);
209
- });
210
- });
211
- });
@@ -1,270 +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
- import { describe, it, expect, beforeEach, vi } from 'vitest';
10
- import {
11
- ViewDataProvider,
12
- type DataFetcher,
13
- type ViewDataConfig,
14
- } from '../ViewDataProvider';
15
-
16
- describe('ViewDataProvider', () => {
17
- let provider: ViewDataProvider;
18
-
19
- beforeEach(() => {
20
- provider = new ViewDataProvider();
21
- });
22
-
23
- // ===== Value Provider =====
24
- describe('value provider', () => {
25
- it('resolves static items', async () => {
26
- const config: ViewDataConfig = {
27
- provider: 'value',
28
- items: [
29
- { id: 1, name: 'Alice' },
30
- { id: 2, name: 'Bob' },
31
- ],
32
- };
33
-
34
- const result = await provider.resolve(config);
35
-
36
- expect(result.provider).toBe('value');
37
- expect(result.records).toHaveLength(2);
38
- expect(result.total).toBe(2);
39
- expect(result.loading).toBe(false);
40
- expect(result.error).toBeUndefined();
41
- });
42
-
43
- it('handles empty items', async () => {
44
- const result = await provider.resolve({
45
- provider: 'value',
46
- items: [],
47
- });
48
- expect(result.records).toHaveLength(0);
49
- expect(result.total).toBe(0);
50
- });
51
-
52
- it('handles non-array items gracefully', async () => {
53
- const result = await provider.resolve({
54
- provider: 'value',
55
- items: null as any,
56
- });
57
- expect(result.records).toHaveLength(0);
58
- });
59
- });
60
-
61
- // ===== API Provider =====
62
- describe('api provider', () => {
63
- it('returns error when no fetchUrl implementation', async () => {
64
- const config: ViewDataConfig = {
65
- provider: 'api',
66
- read: { url: 'https://api.example.com/records' },
67
- };
68
-
69
- const result = await provider.resolve(config);
70
- expect(result.error).toContain('No fetchUrl implementation');
71
- });
72
-
73
- it('fetches data from API URL', async () => {
74
- const fetcher: DataFetcher = {
75
- fetchRecords: vi.fn(),
76
- fetchUrl: vi.fn().mockResolvedValue({
77
- records: [{ id: 1 }],
78
- total: 1,
79
- }),
80
- };
81
- provider.setFetcher(fetcher);
82
-
83
- const result = await provider.resolve({
84
- provider: 'api',
85
- read: { url: 'https://api.example.com/data', method: 'GET' },
86
- });
87
-
88
- expect(result.records).toHaveLength(1);
89
- expect(result.total).toBe(1);
90
- expect(fetcher.fetchUrl).toHaveBeenCalledWith(
91
- 'https://api.example.com/data',
92
- {
93
- method: 'GET',
94
- headers: undefined,
95
- },
96
- );
97
- });
98
-
99
- it('handles array response shape', async () => {
100
- const fetcher: DataFetcher = {
101
- fetchRecords: vi.fn(),
102
- fetchUrl: vi.fn().mockResolvedValue([{ id: 1 }, { id: 2 }]),
103
- };
104
- provider.setFetcher(fetcher);
105
-
106
- const result = await provider.resolve({
107
- provider: 'api',
108
- read: { url: 'https://api.example.com/data' },
109
- });
110
-
111
- expect(result.records).toHaveLength(2);
112
- expect(result.total).toBe(2);
113
- });
114
-
115
- it('handles { data: [] } response shape', async () => {
116
- const fetcher: DataFetcher = {
117
- fetchRecords: vi.fn(),
118
- fetchUrl: vi.fn().mockResolvedValue({ data: [{ id: 1 }] }),
119
- };
120
- provider.setFetcher(fetcher);
121
-
122
- const result = await provider.resolve({
123
- provider: 'api',
124
- read: { url: 'https://api.example.com/data' },
125
- });
126
-
127
- expect(result.records).toHaveLength(1);
128
- });
129
-
130
- it('handles API errors gracefully', async () => {
131
- const fetcher: DataFetcher = {
132
- fetchRecords: vi.fn(),
133
- fetchUrl: vi.fn().mockRejectedValue(new Error('Network error')),
134
- };
135
- provider.setFetcher(fetcher);
136
-
137
- const result = await provider.resolve({
138
- provider: 'api',
139
- read: { url: 'https://api.example.com/data' },
140
- });
141
-
142
- expect(result.error).toBe('Network error');
143
- expect(result.records).toHaveLength(0);
144
- });
145
- });
146
-
147
- // ===== Object Provider =====
148
- describe('object provider', () => {
149
- it('returns error when no fetcher configured', async () => {
150
- const result = await provider.resolve({
151
- provider: 'object',
152
- object: 'Account',
153
- });
154
- expect(result.error).toContain('No data fetcher configured');
155
- });
156
-
157
- it('fetches records for object', async () => {
158
- const fetcher: DataFetcher = {
159
- fetchRecords: vi.fn().mockResolvedValue({
160
- records: [{ id: '1', name: 'Acme Corp' }],
161
- total: 1,
162
- }),
163
- };
164
- provider.setFetcher(fetcher);
165
-
166
- const result = await provider.resolve({
167
- provider: 'object',
168
- object: 'Account',
169
- });
170
-
171
- expect(result.records).toHaveLength(1);
172
- expect(result.total).toBe(1);
173
- expect(fetcher.fetchRecords).toHaveBeenCalledWith('Account', undefined);
174
- });
175
-
176
- it('passes filter/sort/limit options', async () => {
177
- const fetcher: DataFetcher = {
178
- fetchRecords: vi.fn().mockResolvedValue({ records: [], total: 0 }),
179
- };
180
- provider.setFetcher(fetcher);
181
-
182
- const options = {
183
- filter: { status: 'active' },
184
- sort: [{ field: 'name', order: 'asc' as const }],
185
- limit: 10,
186
- };
187
-
188
- await provider.resolve(
189
- { provider: 'object', object: 'Account' },
190
- options,
191
- );
192
-
193
- expect(fetcher.fetchRecords).toHaveBeenCalledWith('Account', options);
194
- });
195
-
196
- it('fetches metadata when available', async () => {
197
- const fetcher: DataFetcher = {
198
- fetchRecords: vi.fn().mockResolvedValue({ records: [], total: 0 }),
199
- fetchMetadata: vi.fn().mockResolvedValue({
200
- name: 'Account',
201
- label: 'Accounts',
202
- fields: [{ name: 'name', type: 'string', label: 'Name' }],
203
- }),
204
- };
205
- provider.setFetcher(fetcher);
206
-
207
- const result = await provider.resolve({
208
- provider: 'object',
209
- object: 'Account',
210
- });
211
-
212
- expect(result.metadata).toBeDefined();
213
- expect(result.metadata?.name).toBe('Account');
214
- expect(result.metadata?.fields).toHaveLength(1);
215
- });
216
-
217
- it('handles fetch errors gracefully', async () => {
218
- const fetcher: DataFetcher = {
219
- fetchRecords: vi
220
- .fn()
221
- .mockRejectedValue(new Error('Connection failed')),
222
- };
223
- provider.setFetcher(fetcher);
224
-
225
- const result = await provider.resolve({
226
- provider: 'object',
227
- object: 'Account',
228
- });
229
-
230
- expect(result.error).toBe('Connection failed');
231
- expect(result.records).toHaveLength(0);
232
- });
233
- });
234
-
235
- // ===== Element Data Source =====
236
- describe('resolveElementDataSource', () => {
237
- it('resolves element-level data source', async () => {
238
- const fetcher: DataFetcher = {
239
- fetchRecords: vi
240
- .fn()
241
- .mockResolvedValue({ records: [{ id: '1' }], total: 1 }),
242
- };
243
- provider.setFetcher(fetcher);
244
-
245
- const result = await provider.resolveElementDataSource({
246
- object: 'Contact',
247
- filter: { accountId: '123' },
248
- sort: [{ field: 'name', order: 'asc' }],
249
- limit: 5,
250
- });
251
-
252
- expect(result.records).toHaveLength(1);
253
- expect(fetcher.fetchRecords).toHaveBeenCalledWith('Contact', {
254
- filter: { accountId: '123' },
255
- sort: [{ field: 'name', order: 'asc' }],
256
- limit: 5,
257
- });
258
- });
259
- });
260
-
261
- // ===== Unknown Provider =====
262
- describe('unknown provider', () => {
263
- it('returns error for unknown provider type', async () => {
264
- const result = await provider.resolve({
265
- provider: 'unknown' as any,
266
- } as ViewDataConfig);
267
- expect(result.error).toContain('Unknown data provider');
268
- });
269
- });
270
- });
@@ -1,24 +0,0 @@
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';
17
-
18
- export {
19
- ViewDataProvider,
20
- type ViewDataConfig,
21
- type ElementDataSourceConfig,
22
- type DataFetcher,
23
- type ResolvedData,
24
- } from './ViewDataProvider.js';