@object-ui/core 3.1.5 → 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 (110) hide show
  1. package/CHANGELOG.md +18 -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.d.ts +5 -1
  6. package/dist/adapters/ValueDataSource.js +30 -1
  7. package/dist/errors/index.js +2 -3
  8. package/dist/evaluator/ExpressionCache.d.ts +9 -10
  9. package/dist/evaluator/ExpressionCache.js +29 -8
  10. package/dist/evaluator/SafeExpressionParser.d.ts +131 -0
  11. package/dist/evaluator/SafeExpressionParser.js +851 -0
  12. package/dist/evaluator/index.d.ts +1 -0
  13. package/dist/evaluator/index.js +1 -0
  14. package/dist/protocols/DndProtocol.js +2 -14
  15. package/dist/protocols/KeyboardProtocol.js +1 -4
  16. package/dist/protocols/NotificationProtocol.js +3 -13
  17. package/dist/utils/debug.js +2 -1
  18. package/dist/utils/filter-converter.js +25 -5
  19. package/package.json +33 -9
  20. package/.turbo/turbo-build.log +0 -4
  21. package/src/__benchmarks__/core.bench.ts +0 -64
  22. package/src/__tests__/protocols/DndProtocol.test.ts +0 -186
  23. package/src/__tests__/protocols/KeyboardProtocol.test.ts +0 -177
  24. package/src/__tests__/protocols/NotificationProtocol.test.ts +0 -142
  25. package/src/__tests__/protocols/ResponsiveProtocol.test.ts +0 -176
  26. package/src/__tests__/protocols/SharingProtocol.test.ts +0 -188
  27. package/src/actions/ActionEngine.ts +0 -268
  28. package/src/actions/ActionRunner.ts +0 -717
  29. package/src/actions/TransactionManager.ts +0 -521
  30. package/src/actions/UndoManager.ts +0 -215
  31. package/src/actions/__tests__/ActionEngine.test.ts +0 -206
  32. package/src/actions/__tests__/ActionRunner.params.test.ts +0 -134
  33. package/src/actions/__tests__/ActionRunner.test.ts +0 -711
  34. package/src/actions/__tests__/TransactionManager.test.ts +0 -447
  35. package/src/actions/__tests__/UndoManager.test.ts +0 -320
  36. package/src/actions/index.ts +0 -12
  37. package/src/adapters/ApiDataSource.ts +0 -376
  38. package/src/adapters/README.md +0 -180
  39. package/src/adapters/ValueDataSource.ts +0 -438
  40. package/src/adapters/__tests__/ApiDataSource.test.ts +0 -418
  41. package/src/adapters/__tests__/ValueDataSource.test.ts +0 -472
  42. package/src/adapters/__tests__/resolveDataSource.test.ts +0 -144
  43. package/src/adapters/index.ts +0 -15
  44. package/src/adapters/resolveDataSource.ts +0 -79
  45. package/src/builder/__tests__/schema-builder.test.ts +0 -235
  46. package/src/builder/schema-builder.ts +0 -584
  47. package/src/data-scope/DataScopeManager.ts +0 -269
  48. package/src/data-scope/ViewDataProvider.ts +0 -282
  49. package/src/data-scope/__tests__/DataScopeManager.test.ts +0 -211
  50. package/src/data-scope/__tests__/ViewDataProvider.test.ts +0 -270
  51. package/src/data-scope/index.ts +0 -24
  52. package/src/errors/__tests__/errors.test.ts +0 -292
  53. package/src/errors/index.ts +0 -270
  54. package/src/evaluator/ExpressionCache.ts +0 -192
  55. package/src/evaluator/ExpressionContext.ts +0 -118
  56. package/src/evaluator/ExpressionEvaluator.ts +0 -315
  57. package/src/evaluator/FormulaFunctions.ts +0 -398
  58. package/src/evaluator/__tests__/ExpressionCache.test.ts +0 -135
  59. package/src/evaluator/__tests__/ExpressionContext.test.ts +0 -110
  60. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +0 -131
  61. package/src/evaluator/__tests__/FormulaFunctions.test.ts +0 -447
  62. package/src/evaluator/index.ts +0 -12
  63. package/src/index.ts +0 -38
  64. package/src/protocols/DndProtocol.ts +0 -184
  65. package/src/protocols/KeyboardProtocol.ts +0 -185
  66. package/src/protocols/NotificationProtocol.ts +0 -159
  67. package/src/protocols/ResponsiveProtocol.ts +0 -210
  68. package/src/protocols/SharingProtocol.ts +0 -185
  69. package/src/protocols/index.ts +0 -13
  70. package/src/query/__tests__/query-ast.test.ts +0 -211
  71. package/src/query/__tests__/window-functions.test.ts +0 -275
  72. package/src/query/index.ts +0 -7
  73. package/src/query/query-ast.ts +0 -341
  74. package/src/registry/PluginScopeImpl.ts +0 -259
  75. package/src/registry/PluginSystem.ts +0 -206
  76. package/src/registry/Registry.ts +0 -219
  77. package/src/registry/WidgetRegistry.ts +0 -316
  78. package/src/registry/__tests__/PluginSystem.test.ts +0 -309
  79. package/src/registry/__tests__/Registry.test.ts +0 -293
  80. package/src/registry/__tests__/WidgetRegistry.test.ts +0 -321
  81. package/src/registry/__tests__/plugin-scope-integration.test.ts +0 -283
  82. package/src/theme/ThemeEngine.ts +0 -530
  83. package/src/theme/__tests__/ThemeEngine.test.ts +0 -668
  84. package/src/theme/index.ts +0 -24
  85. package/src/types/index.ts +0 -21
  86. package/src/utils/__tests__/debug-collector.test.ts +0 -102
  87. package/src/utils/__tests__/debug.test.ts +0 -134
  88. package/src/utils/__tests__/expand-fields.test.ts +0 -120
  89. package/src/utils/__tests__/extract-records.test.ts +0 -50
  90. package/src/utils/__tests__/filter-converter.test.ts +0 -118
  91. package/src/utils/__tests__/merge-views-into-objects.test.ts +0 -110
  92. package/src/utils/__tests__/normalize-quick-filter.test.ts +0 -123
  93. package/src/utils/debug-collector.ts +0 -100
  94. package/src/utils/debug.ts +0 -147
  95. package/src/utils/expand-fields.ts +0 -76
  96. package/src/utils/extract-records.ts +0 -33
  97. package/src/utils/filter-converter.ts +0 -133
  98. package/src/utils/merge-views-into-objects.ts +0 -36
  99. package/src/utils/normalize-quick-filter.ts +0 -78
  100. package/src/validation/__tests__/object-validation-engine.test.ts +0 -567
  101. package/src/validation/__tests__/schema-validator.test.ts +0 -118
  102. package/src/validation/__tests__/validation-engine.test.ts +0 -102
  103. package/src/validation/index.ts +0 -10
  104. package/src/validation/schema-validator.ts +0 -344
  105. package/src/validation/validation-engine.ts +0 -528
  106. package/src/validation/validators/index.ts +0 -25
  107. package/src/validation/validators/object-validation-engine.ts +0 -722
  108. package/tsconfig.json +0 -15
  109. package/tsconfig.tsbuildinfo +0 -1
  110. package/vitest.config.ts +0 -2
@@ -1,472 +0,0 @@
1
- /**
2
- * ObjectUI — ValueDataSource Tests
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 { ValueDataSource } from '../ValueDataSource';
11
-
12
- const sampleData = [
13
- { id: '1', name: 'Alice', age: 30, role: 'admin' },
14
- { id: '2', name: 'Bob', age: 25, role: 'user' },
15
- { id: '3', name: 'Charlie', age: 35, role: 'admin' },
16
- { id: '4', name: 'Diana', age: 28, role: 'user' },
17
- { id: '5', name: 'Eve', age: 22, role: 'guest' },
18
- ];
19
-
20
- function createDS() {
21
- return new ValueDataSource({ items: sampleData });
22
- }
23
-
24
- // ---------------------------------------------------------------------------
25
- // find
26
- // ---------------------------------------------------------------------------
27
-
28
- describe('ValueDataSource — find', () => {
29
- it('should return all items with no params', async () => {
30
- const ds = createDS();
31
- const result = await ds.find('users');
32
- expect(result.data).toHaveLength(5);
33
- expect(result.total).toBe(5);
34
- });
35
-
36
- it('should filter by equality', async () => {
37
- const ds = createDS();
38
- const result = await ds.find('users', { $filter: { role: 'admin' } });
39
- expect(result.data).toHaveLength(2);
40
- expect(result.data.every((r: any) => r.role === 'admin')).toBe(true);
41
- expect(result.total).toBe(2);
42
- });
43
-
44
- it('should filter with $gt operator', async () => {
45
- const ds = createDS();
46
- const result = await ds.find('users', { $filter: { age: { $gt: 28 } } });
47
- expect(result.data).toHaveLength(2);
48
- expect(result.data.map((r: any) => r.name).sort()).toEqual(['Alice', 'Charlie']);
49
- });
50
-
51
- it('should filter with $gte operator', async () => {
52
- const ds = createDS();
53
- const result = await ds.find('users', { $filter: { age: { $gte: 28 } } });
54
- expect(result.data).toHaveLength(3);
55
- });
56
-
57
- it('should filter with $lt operator', async () => {
58
- const ds = createDS();
59
- const result = await ds.find('users', { $filter: { age: { $lt: 25 } } });
60
- expect(result.data).toHaveLength(1);
61
- expect(result.data[0].name).toBe('Eve');
62
- });
63
-
64
- it('should filter with $ne operator', async () => {
65
- const ds = createDS();
66
- const result = await ds.find('users', { $filter: { role: { $ne: 'admin' } } });
67
- expect(result.data).toHaveLength(3);
68
- });
69
-
70
- it('should filter with $in operator', async () => {
71
- const ds = createDS();
72
- const result = await ds.find('users', {
73
- $filter: { role: { $in: ['admin', 'guest'] } },
74
- });
75
- expect(result.data).toHaveLength(3);
76
- });
77
-
78
- it('should filter with $contains operator', async () => {
79
- const ds = createDS();
80
- const result = await ds.find('users', {
81
- $filter: { name: { $contains: 'ali' } },
82
- });
83
- expect(result.data).toHaveLength(1); // Alice only ('ali' is not in 'Charlie')
84
- });
85
-
86
- it('should sort ascending by Record format', async () => {
87
- const ds = createDS();
88
- const result = await ds.find('users', { $orderby: { name: 'asc' } });
89
- const names = result.data.map((r: any) => r.name);
90
- expect(names).toEqual(['Alice', 'Bob', 'Charlie', 'Diana', 'Eve']);
91
- });
92
-
93
- it('should sort descending by Record format', async () => {
94
- const ds = createDS();
95
- const result = await ds.find('users', { $orderby: { age: 'desc' } });
96
- expect(result.data[0].name).toBe('Charlie');
97
- expect(result.data[4].name).toBe('Eve');
98
- });
99
-
100
- it('should sort by string array format', async () => {
101
- const ds = createDS();
102
- const result = await ds.find('users', { $orderby: ['-age'] });
103
- expect(result.data[0].name).toBe('Charlie');
104
- });
105
-
106
- it('should sort by object array format', async () => {
107
- const ds = createDS();
108
- const result = await ds.find('users', {
109
- $orderby: [{ field: 'age', order: 'asc' }],
110
- });
111
- expect(result.data[0].name).toBe('Eve');
112
- expect(result.data[4].name).toBe('Charlie');
113
- });
114
-
115
- it('should paginate with $skip and $top', async () => {
116
- const ds = createDS();
117
- const result = await ds.find('users', { $skip: 2, $top: 2 });
118
- expect(result.data).toHaveLength(2);
119
- expect(result.total).toBe(5); // total before pagination
120
- expect(result.hasMore).toBe(true);
121
- });
122
-
123
- it('should return hasMore=false when all items returned', async () => {
124
- const ds = createDS();
125
- const result = await ds.find('users', { $top: 10 });
126
- expect(result.hasMore).toBe(false);
127
- });
128
-
129
- it('should select specific fields', async () => {
130
- const ds = createDS();
131
- const result = await ds.find('users', { $select: ['name', 'age'] });
132
- const first = result.data[0] as any;
133
- expect(first.name).toBeDefined();
134
- expect(first.age).toBeDefined();
135
- expect(first.id).toBeUndefined();
136
- expect(first.role).toBeUndefined();
137
- });
138
-
139
- it('should search across string fields', async () => {
140
- const ds = createDS();
141
- const result = await ds.find('users', { $search: 'bob' });
142
- expect(result.data).toHaveLength(1);
143
- expect(result.data[0].name).toBe('Bob');
144
- });
145
-
146
- it('should combine filter + sort + pagination', async () => {
147
- const ds = createDS();
148
- const result = await ds.find('users', {
149
- $filter: { role: 'user' },
150
- $orderby: { age: 'asc' },
151
- $top: 1,
152
- });
153
- expect(result.data).toHaveLength(1);
154
- expect(result.data[0].name).toBe('Bob'); // youngest user
155
- expect(result.total).toBe(2); // 2 users total
156
- expect(result.hasMore).toBe(true);
157
- });
158
- });
159
-
160
- // ---------------------------------------------------------------------------
161
- // AST-format filter support
162
- // ---------------------------------------------------------------------------
163
-
164
- describe('ValueDataSource — AST filter', () => {
165
- it('should filter with simple AST equality condition', async () => {
166
- const ds = createDS();
167
- const result = await ds.find('users', {
168
- $filter: ['role', '=', 'admin'] as any,
169
- });
170
- expect(result.data).toHaveLength(2);
171
- expect(result.data.every((r: any) => r.role === 'admin')).toBe(true);
172
- });
173
-
174
- it('should filter with AST "in" operator', async () => {
175
- const ds = createDS();
176
- const result = await ds.find('users', {
177
- $filter: ['role', 'in', ['admin', 'guest']] as any,
178
- });
179
- expect(result.data).toHaveLength(3);
180
- });
181
-
182
- it('should filter with AST "and" logical operator', async () => {
183
- const ds = createDS();
184
- const result = await ds.find('users', {
185
- $filter: ['and', ['role', '=', 'admin'], ['age', '>', 30]] as any,
186
- });
187
- expect(result.data).toHaveLength(1);
188
- expect(result.data[0].name).toBe('Charlie');
189
- });
190
-
191
- it('should filter with AST "or" logical operator', async () => {
192
- const ds = createDS();
193
- const result = await ds.find('users', {
194
- $filter: ['or', ['role', '=', 'guest'], ['name', '=', 'Alice']] as any,
195
- });
196
- expect(result.data).toHaveLength(2);
197
- });
198
-
199
- it('should filter with AST "!=" operator', async () => {
200
- const ds = createDS();
201
- const result = await ds.find('users', {
202
- $filter: ['role', '!=', 'admin'] as any,
203
- });
204
- expect(result.data).toHaveLength(3);
205
- });
206
-
207
- it('should filter with AST "not in" operator', async () => {
208
- const ds = createDS();
209
- const result = await ds.find('users', {
210
- $filter: ['role', 'not in', ['admin', 'guest']] as any,
211
- });
212
- expect(result.data).toHaveLength(2);
213
- expect(result.data.every((r: any) => r.role === 'user')).toBe(true);
214
- });
215
-
216
- it('should filter with AST "contains" operator', async () => {
217
- const ds = createDS();
218
- const result = await ds.find('users', {
219
- $filter: ['name', 'contains', 'li'] as any,
220
- });
221
- expect(result.data).toHaveLength(2); // Alice, Charlie
222
- });
223
-
224
- it('should filter with nested AST (and with in operator)', async () => {
225
- const ds = createDS();
226
- const result = await ds.find('users', {
227
- $filter: ['and', ['role', 'in', ['admin', 'user']], ['age', '>=', 28]] as any,
228
- });
229
- expect(result.data).toHaveLength(3); // Alice (30, admin), Charlie (35, admin), Diana (28, user)
230
- });
231
-
232
- it('should return all items with empty AST filter', async () => {
233
- const ds = createDS();
234
- const result = await ds.find('users', { $filter: [] as any });
235
- expect(result.data).toHaveLength(5);
236
- });
237
-
238
- it('should combine AST filter with sort', async () => {
239
- const ds = createDS();
240
- const result = await ds.find('users', {
241
- $filter: ['role', '=', 'admin'] as any,
242
- $orderby: { age: 'asc' },
243
- });
244
- expect(result.data).toHaveLength(2);
245
- expect(result.data[0].name).toBe('Alice');
246
- expect(result.data[1].name).toBe('Charlie');
247
- });
248
- });
249
-
250
- // ---------------------------------------------------------------------------
251
- // findOne
252
- // ---------------------------------------------------------------------------
253
-
254
- describe('ValueDataSource — findOne', () => {
255
- it('should find a record by id', async () => {
256
- const ds = createDS();
257
- const record = await ds.findOne('users', '3');
258
- expect(record).not.toBeNull();
259
- expect((record as any).name).toBe('Charlie');
260
- });
261
-
262
- it('should return null for missing id', async () => {
263
- const ds = createDS();
264
- const record = await ds.findOne('users', '999');
265
- expect(record).toBeNull();
266
- });
267
-
268
- it('should use custom idField', async () => {
269
- const ds = new ValueDataSource({
270
- items: [{ code: 'A', label: 'Alpha' }],
271
- idField: 'code',
272
- });
273
- const record = await ds.findOne('items', 'A');
274
- expect(record).not.toBeNull();
275
- expect((record as any).label).toBe('Alpha');
276
- });
277
-
278
- it('should select fields on findOne', async () => {
279
- const ds = createDS();
280
- const record = await ds.findOne('users', '1', { $select: ['name'] });
281
- expect(record).not.toBeNull();
282
- expect((record as any).name).toBe('Alice');
283
- expect((record as any).age).toBeUndefined();
284
- });
285
- });
286
-
287
- // ---------------------------------------------------------------------------
288
- // create
289
- // ---------------------------------------------------------------------------
290
-
291
- describe('ValueDataSource — create', () => {
292
- it('should add a record to the collection', async () => {
293
- const ds = createDS();
294
- const created = await ds.create('users', { name: 'Frank', age: 40, role: 'user' });
295
- expect((created as any).name).toBe('Frank');
296
- expect(ds.count).toBe(6);
297
- });
298
-
299
- it('should auto-generate an ID if missing', async () => {
300
- const ds = createDS();
301
- const created = await ds.create('users', { name: 'Grace' });
302
- expect((created as any).id).toBeDefined();
303
- expect(String((created as any).id).startsWith('auto_')).toBe(true);
304
- });
305
-
306
- it('should preserve existing ID', async () => {
307
- const ds = createDS();
308
- const created = await ds.create('users', { id: 'custom-id', name: 'Heidi' });
309
- expect((created as any).id).toBe('custom-id');
310
- });
311
- });
312
-
313
- // ---------------------------------------------------------------------------
314
- // update
315
- // ---------------------------------------------------------------------------
316
-
317
- describe('ValueDataSource — update', () => {
318
- it('should update an existing record', async () => {
319
- const ds = createDS();
320
- const updated = await ds.update('users', '1', { age: 31 });
321
- expect((updated as any).age).toBe(31);
322
- expect((updated as any).name).toBe('Alice');
323
- });
324
-
325
- it('should throw for non-existent record', async () => {
326
- const ds = createDS();
327
- await expect(ds.update('users', '999', { age: 50 })).rejects.toThrow(
328
- 'Record with id "999" not found',
329
- );
330
- });
331
-
332
- it('should persist updates in subsequent finds', async () => {
333
- const ds = createDS();
334
- await ds.update('users', '2', { name: 'Bobby' });
335
- const result = await ds.findOne('users', '2');
336
- expect((result as any).name).toBe('Bobby');
337
- });
338
- });
339
-
340
- // ---------------------------------------------------------------------------
341
- // delete
342
- // ---------------------------------------------------------------------------
343
-
344
- describe('ValueDataSource — delete', () => {
345
- it('should remove a record', async () => {
346
- const ds = createDS();
347
- const ok = await ds.delete('users', '1');
348
- expect(ok).toBe(true);
349
- expect(ds.count).toBe(4);
350
- });
351
-
352
- it('should return false for non-existent record', async () => {
353
- const ds = createDS();
354
- const ok = await ds.delete('users', '999');
355
- expect(ok).toBe(false);
356
- expect(ds.count).toBe(5);
357
- });
358
- });
359
-
360
- // ---------------------------------------------------------------------------
361
- // bulk
362
- // ---------------------------------------------------------------------------
363
-
364
- describe('ValueDataSource — bulk', () => {
365
- it('should bulk create records', async () => {
366
- const ds = createDS();
367
- const results = await ds.bulk!('users', 'create', [
368
- { name: 'X', age: 10 },
369
- { name: 'Y', age: 20 },
370
- ]);
371
- expect(results).toHaveLength(2);
372
- expect(ds.count).toBe(7);
373
- });
374
-
375
- it('should bulk delete records', async () => {
376
- const ds = createDS();
377
- await ds.bulk!('users', 'delete', [{ id: '1' }, { id: '2' }]);
378
- expect(ds.count).toBe(3);
379
- });
380
- });
381
-
382
- // ---------------------------------------------------------------------------
383
- // getObjectSchema
384
- // ---------------------------------------------------------------------------
385
-
386
- describe('ValueDataSource — getObjectSchema', () => {
387
- it('should infer schema from first item', async () => {
388
- const ds = createDS();
389
- const schema = await ds.getObjectSchema('users');
390
- expect(schema.name).toBe('users');
391
- expect(schema.fields.id).toBeDefined();
392
- expect(schema.fields.name.type).toBe('string');
393
- expect(schema.fields.age.type).toBe('number');
394
- });
395
-
396
- it('should return empty fields for empty dataset', async () => {
397
- const ds = new ValueDataSource({ items: [] });
398
- const schema = await ds.getObjectSchema('empty');
399
- expect(schema.fields).toEqual({});
400
- });
401
- });
402
-
403
- // ---------------------------------------------------------------------------
404
- // Isolation
405
- // ---------------------------------------------------------------------------
406
-
407
- describe('ValueDataSource — isolation', () => {
408
- it('should not mutate the original items array', async () => {
409
- const original = [{ id: '1', name: 'Test' }];
410
- const ds = new ValueDataSource({ items: original });
411
- await ds.create('x', { name: 'New' });
412
- expect(original).toHaveLength(1); // original untouched
413
- expect(ds.count).toBe(2);
414
- });
415
- });
416
-
417
- // ---------------------------------------------------------------------------
418
- // aggregate
419
- // ---------------------------------------------------------------------------
420
-
421
- describe('ValueDataSource — aggregate', () => {
422
- const aggData = [
423
- { id: '1', category: 'A', amount: 10 },
424
- { id: '2', category: 'A', amount: 20 },
425
- { id: '3', category: 'B', amount: 30 },
426
- { id: '4', category: 'B', amount: 40 },
427
- { id: '5', category: 'B', amount: 50 },
428
- ];
429
-
430
- function createAggDS() {
431
- return new ValueDataSource({ items: aggData });
432
- }
433
-
434
- it('should compute sum aggregation', async () => {
435
- const ds = createAggDS();
436
- const result = await ds.aggregate('items', { field: 'amount', function: 'sum', groupBy: 'category' });
437
- expect(result).toHaveLength(2);
438
- const groupA = result.find((r: any) => r.category === 'A');
439
- const groupB = result.find((r: any) => r.category === 'B');
440
- expect(groupA?.amount).toBe(30);
441
- expect(groupB?.amount).toBe(120);
442
- });
443
-
444
- it('should compute count aggregation', async () => {
445
- const ds = createAggDS();
446
- const result = await ds.aggregate('items', { field: 'amount', function: 'count', groupBy: 'category' });
447
- expect(result).toHaveLength(2);
448
- expect(result.find((r: any) => r.category === 'A')?.amount).toBe(2);
449
- expect(result.find((r: any) => r.category === 'B')?.amount).toBe(3);
450
- });
451
-
452
- it('should compute avg aggregation', async () => {
453
- const ds = createAggDS();
454
- const result = await ds.aggregate('items', { field: 'amount', function: 'avg', groupBy: 'category' });
455
- expect(result.find((r: any) => r.category === 'A')?.amount).toBe(15);
456
- expect(result.find((r: any) => r.category === 'B')?.amount).toBe(40);
457
- });
458
-
459
- it('should compute min aggregation', async () => {
460
- const ds = createAggDS();
461
- const result = await ds.aggregate('items', { field: 'amount', function: 'min', groupBy: 'category' });
462
- expect(result.find((r: any) => r.category === 'A')?.amount).toBe(10);
463
- expect(result.find((r: any) => r.category === 'B')?.amount).toBe(30);
464
- });
465
-
466
- it('should compute max aggregation', async () => {
467
- const ds = createAggDS();
468
- const result = await ds.aggregate('items', { field: 'amount', function: 'max', groupBy: 'category' });
469
- expect(result.find((r: any) => r.category === 'A')?.amount).toBe(20);
470
- expect(result.find((r: any) => r.category === 'B')?.amount).toBe(50);
471
- });
472
- });
@@ -1,144 +0,0 @@
1
- /**
2
- * ObjectUI — resolveDataSource Tests
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 { resolveDataSource } from '../resolveDataSource';
11
- import { ApiDataSource } from '../ApiDataSource';
12
- import { ValueDataSource } from '../ValueDataSource';
13
- import type { DataSource, ViewData } from '@object-ui/types';
14
-
15
- // ---------------------------------------------------------------------------
16
- // Minimal mock DataSource for "object" provider fallback
17
- // ---------------------------------------------------------------------------
18
-
19
- const mockFallback: DataSource<any> = {
20
- find: async () => ({ data: [], total: 0 }),
21
- findOne: async () => null,
22
- create: async (_r, d) => d as any,
23
- update: async (_r, _id, d) => d as any,
24
- delete: async () => true,
25
- getObjectSchema: async (name) => ({ name, fields: {} }),
26
- };
27
-
28
- // ---------------------------------------------------------------------------
29
- // Tests
30
- // ---------------------------------------------------------------------------
31
-
32
- describe('resolveDataSource', () => {
33
- it('should return null when viewData is null and no fallback', () => {
34
- const ds = resolveDataSource(null);
35
- expect(ds).toBeNull();
36
- });
37
-
38
- it('should return fallback when viewData is null', () => {
39
- const ds = resolveDataSource(null, mockFallback);
40
- expect(ds).toBe(mockFallback);
41
- });
42
-
43
- it('should return null when viewData is undefined and no fallback', () => {
44
- const ds = resolveDataSource(undefined);
45
- expect(ds).toBeNull();
46
- });
47
-
48
- // -----------------------------------------------------------------------
49
- // provider: 'object'
50
- // -----------------------------------------------------------------------
51
-
52
- it('should return fallback for provider: "object"', () => {
53
- const viewData: ViewData = { provider: 'object', object: 'contacts' };
54
- const ds = resolveDataSource(viewData, mockFallback);
55
- expect(ds).toBe(mockFallback);
56
- });
57
-
58
- it('should return null for provider: "object" without fallback', () => {
59
- const viewData: ViewData = { provider: 'object', object: 'contacts' };
60
- const ds = resolveDataSource(viewData);
61
- expect(ds).toBeNull();
62
- });
63
-
64
- // -----------------------------------------------------------------------
65
- // provider: 'api'
66
- // -----------------------------------------------------------------------
67
-
68
- it('should create ApiDataSource for provider: "api"', () => {
69
- const viewData: ViewData = {
70
- provider: 'api',
71
- read: { url: '/api/contacts' },
72
- };
73
- const ds = resolveDataSource(viewData);
74
- expect(ds).toBeInstanceOf(ApiDataSource);
75
- });
76
-
77
- it('should create ApiDataSource with read and write configs', () => {
78
- const viewData: ViewData = {
79
- provider: 'api',
80
- read: { url: '/api/contacts', method: 'GET' },
81
- write: { url: '/api/contacts', method: 'POST' },
82
- };
83
- const ds = resolveDataSource(viewData);
84
- expect(ds).toBeInstanceOf(ApiDataSource);
85
- });
86
-
87
- it('should pass adapter options to ApiDataSource', () => {
88
- const viewData: ViewData = {
89
- provider: 'api',
90
- read: { url: '/api/contacts' },
91
- };
92
- const ds = resolveDataSource(viewData, null, {
93
- defaultHeaders: { Authorization: 'Bearer test' },
94
- });
95
- expect(ds).toBeInstanceOf(ApiDataSource);
96
- });
97
-
98
- // -----------------------------------------------------------------------
99
- // provider: 'value'
100
- // -----------------------------------------------------------------------
101
-
102
- it('should create ValueDataSource for provider: "value"', () => {
103
- const viewData: ViewData = {
104
- provider: 'value',
105
- items: [{ id: '1', name: 'Test' }],
106
- };
107
- const ds = resolveDataSource(viewData);
108
- expect(ds).toBeInstanceOf(ValueDataSource);
109
- });
110
-
111
- it('should handle empty items array', () => {
112
- const viewData: ViewData = {
113
- provider: 'value',
114
- items: [],
115
- };
116
- const ds = resolveDataSource(viewData);
117
- expect(ds).toBeInstanceOf(ValueDataSource);
118
- });
119
-
120
- it('should pass idField option to ValueDataSource', () => {
121
- const viewData: ViewData = {
122
- provider: 'value',
123
- items: [{ code: 'A', label: 'Alpha' }],
124
- };
125
- const ds = resolveDataSource(viewData, null, { idField: 'code' });
126
- expect(ds).toBeInstanceOf(ValueDataSource);
127
- });
128
-
129
- // -----------------------------------------------------------------------
130
- // Unknown provider
131
- // -----------------------------------------------------------------------
132
-
133
- it('should return fallback for unknown provider', () => {
134
- const viewData = { provider: 'graphql' } as unknown as ViewData;
135
- const ds = resolveDataSource(viewData, mockFallback);
136
- expect(ds).toBe(mockFallback);
137
- });
138
-
139
- it('should return null for unknown provider without fallback', () => {
140
- const viewData = { provider: 'unknown' } as unknown as ViewData;
141
- const ds = resolveDataSource(viewData);
142
- expect(ds).toBeNull();
143
- });
144
- });
@@ -1,15 +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
- // export { ObjectStackAdapter, createObjectStackAdapter } from './objectstack-adapter';
10
- // ObjectStack adapter has been moved to @object-ui/data-objectstack
11
-
12
- // Generic data source adapters (no external SDK dependencies)
13
- export { ApiDataSource, type ApiDataSourceConfig } from './ApiDataSource.js';
14
- export { ValueDataSource, type ValueDataSourceConfig } from './ValueDataSource.js';
15
- export { resolveDataSource, type ResolveDataSourceOptions } from './resolveDataSource.js';