@object-ui/core 3.3.0 → 3.3.2

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 (101) hide show
  1. package/CHANGELOG.md +12 -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/registry/Registry.d.ts +47 -0
  7. package/dist/registry/Registry.js +92 -0
  8. package/dist/utils/filter-converter.js +25 -5
  9. package/package.json +32 -8
  10. package/.turbo/turbo-build.log +0 -4
  11. package/src/__benchmarks__/core.bench.ts +0 -64
  12. package/src/__tests__/protocols/DndProtocol.test.ts +0 -186
  13. package/src/__tests__/protocols/KeyboardProtocol.test.ts +0 -177
  14. package/src/__tests__/protocols/NotificationProtocol.test.ts +0 -142
  15. package/src/__tests__/protocols/ResponsiveProtocol.test.ts +0 -176
  16. package/src/__tests__/protocols/SharingProtocol.test.ts +0 -188
  17. package/src/actions/ActionEngine.ts +0 -268
  18. package/src/actions/ActionRunner.ts +0 -717
  19. package/src/actions/TransactionManager.ts +0 -521
  20. package/src/actions/UndoManager.ts +0 -215
  21. package/src/actions/__tests__/ActionEngine.test.ts +0 -206
  22. package/src/actions/__tests__/ActionRunner.params.test.ts +0 -134
  23. package/src/actions/__tests__/ActionRunner.test.ts +0 -711
  24. package/src/actions/__tests__/TransactionManager.test.ts +0 -447
  25. package/src/actions/__tests__/UndoManager.test.ts +0 -320
  26. package/src/actions/index.ts +0 -12
  27. package/src/adapters/ApiDataSource.ts +0 -376
  28. package/src/adapters/README.md +0 -180
  29. package/src/adapters/ValueDataSource.ts +0 -459
  30. package/src/adapters/__tests__/ApiDataSource.test.ts +0 -418
  31. package/src/adapters/__tests__/ValueDataSource.test.ts +0 -571
  32. package/src/adapters/__tests__/resolveDataSource.test.ts +0 -144
  33. package/src/adapters/index.ts +0 -15
  34. package/src/adapters/resolveDataSource.ts +0 -79
  35. package/src/builder/__tests__/schema-builder.test.ts +0 -235
  36. package/src/builder/schema-builder.ts +0 -584
  37. package/src/data-scope/DataScopeManager.ts +0 -269
  38. package/src/data-scope/ViewDataProvider.ts +0 -282
  39. package/src/data-scope/__tests__/DataScopeManager.test.ts +0 -211
  40. package/src/data-scope/__tests__/ViewDataProvider.test.ts +0 -270
  41. package/src/data-scope/index.ts +0 -24
  42. package/src/errors/__tests__/errors.test.ts +0 -292
  43. package/src/errors/index.ts +0 -269
  44. package/src/evaluator/ExpressionCache.ts +0 -206
  45. package/src/evaluator/ExpressionContext.ts +0 -118
  46. package/src/evaluator/ExpressionEvaluator.ts +0 -315
  47. package/src/evaluator/FormulaFunctions.ts +0 -398
  48. package/src/evaluator/SafeExpressionParser.ts +0 -893
  49. package/src/evaluator/__tests__/ExpressionCache.test.ts +0 -135
  50. package/src/evaluator/__tests__/ExpressionContext.test.ts +0 -110
  51. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +0 -558
  52. package/src/evaluator/__tests__/FormulaFunctions.test.ts +0 -447
  53. package/src/evaluator/index.ts +0 -13
  54. package/src/index.ts +0 -38
  55. package/src/protocols/DndProtocol.ts +0 -168
  56. package/src/protocols/KeyboardProtocol.ts +0 -181
  57. package/src/protocols/NotificationProtocol.ts +0 -150
  58. package/src/protocols/ResponsiveProtocol.ts +0 -210
  59. package/src/protocols/SharingProtocol.ts +0 -185
  60. package/src/protocols/index.ts +0 -13
  61. package/src/query/__tests__/query-ast.test.ts +0 -211
  62. package/src/query/__tests__/window-functions.test.ts +0 -275
  63. package/src/query/index.ts +0 -7
  64. package/src/query/query-ast.ts +0 -341
  65. package/src/registry/PluginScopeImpl.ts +0 -259
  66. package/src/registry/PluginSystem.ts +0 -206
  67. package/src/registry/Registry.ts +0 -219
  68. package/src/registry/WidgetRegistry.ts +0 -316
  69. package/src/registry/__tests__/PluginSystem.test.ts +0 -309
  70. package/src/registry/__tests__/Registry.test.ts +0 -293
  71. package/src/registry/__tests__/WidgetRegistry.test.ts +0 -321
  72. package/src/registry/__tests__/plugin-scope-integration.test.ts +0 -283
  73. package/src/theme/ThemeEngine.ts +0 -530
  74. package/src/theme/__tests__/ThemeEngine.test.ts +0 -668
  75. package/src/theme/index.ts +0 -24
  76. package/src/types/index.ts +0 -21
  77. package/src/utils/__tests__/debug-collector.test.ts +0 -102
  78. package/src/utils/__tests__/debug.test.ts +0 -134
  79. package/src/utils/__tests__/expand-fields.test.ts +0 -120
  80. package/src/utils/__tests__/extract-records.test.ts +0 -50
  81. package/src/utils/__tests__/filter-converter.test.ts +0 -118
  82. package/src/utils/__tests__/merge-views-into-objects.test.ts +0 -110
  83. package/src/utils/__tests__/normalize-quick-filter.test.ts +0 -123
  84. package/src/utils/debug-collector.ts +0 -100
  85. package/src/utils/debug.ts +0 -148
  86. package/src/utils/expand-fields.ts +0 -76
  87. package/src/utils/extract-records.ts +0 -33
  88. package/src/utils/filter-converter.ts +0 -133
  89. package/src/utils/merge-views-into-objects.ts +0 -36
  90. package/src/utils/normalize-quick-filter.ts +0 -78
  91. package/src/validation/__tests__/object-validation-engine.test.ts +0 -567
  92. package/src/validation/__tests__/schema-validator.test.ts +0 -118
  93. package/src/validation/__tests__/validation-engine.test.ts +0 -102
  94. package/src/validation/index.ts +0 -10
  95. package/src/validation/schema-validator.ts +0 -344
  96. package/src/validation/validation-engine.ts +0 -528
  97. package/src/validation/validators/index.ts +0 -25
  98. package/src/validation/validators/object-validation-engine.ts +0 -722
  99. package/tsconfig.json +0 -15
  100. package/tsconfig.tsbuildinfo +0 -1
  101. package/vitest.config.ts +0 -2
@@ -1,21 +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 interface SchemaNode {
10
- type: string;
11
- id?: string;
12
- className?: string;
13
- data?: any;
14
- body?: SchemaNode | SchemaNode[];
15
- [key: string]: any;
16
- }
17
-
18
- export interface ComponentRendererProps {
19
- schema: SchemaNode;
20
- [key: string]: any;
21
- }
@@ -1,102 +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, vi, beforeEach } from 'vitest';
10
- import { DebugCollector } from '../debug-collector';
11
-
12
- describe('DebugCollector', () => {
13
- beforeEach(() => {
14
- DebugCollector.resetInstance();
15
- });
16
-
17
- it('should return a singleton instance', () => {
18
- const a = DebugCollector.getInstance();
19
- const b = DebugCollector.getInstance();
20
- expect(a).toBe(b);
21
- });
22
-
23
- it('should collect perf entries', () => {
24
- const collector = DebugCollector.getInstance();
25
- collector.addPerf({ type: 'button', id: 'btn1', durationMs: 5.2, timestamp: Date.now() });
26
- const entries = collector.getEntries('perf');
27
- expect(entries).toHaveLength(1);
28
- expect(entries[0].kind).toBe('perf');
29
- expect((entries[0].data as any).type).toBe('button');
30
- });
31
-
32
- it('should collect expr entries', () => {
33
- const collector = DebugCollector.getInstance();
34
- collector.addExpr({ expression: '${data.x > 1}', result: true, timestamp: Date.now() });
35
- const entries = collector.getEntries('expr');
36
- expect(entries).toHaveLength(1);
37
- expect(entries[0].kind).toBe('expr');
38
- expect((entries[0].data as any).result).toBe(true);
39
- });
40
-
41
- it('should collect event entries', () => {
42
- const collector = DebugCollector.getInstance();
43
- collector.addEvent({ action: 'navigate', payload: { to: '/home' }, timestamp: Date.now() });
44
- const entries = collector.getEntries('event');
45
- expect(entries).toHaveLength(1);
46
- expect(entries[0].kind).toBe('event');
47
- expect((entries[0].data as any).action).toBe('navigate');
48
- });
49
-
50
- it('should return all entries when no kind filter', () => {
51
- const collector = DebugCollector.getInstance();
52
- collector.addPerf({ type: 'text', durationMs: 1, timestamp: Date.now() });
53
- collector.addExpr({ expression: 'a', result: 1, timestamp: Date.now() });
54
- collector.addEvent({ action: 'click', timestamp: Date.now() });
55
- expect(collector.getEntries()).toHaveLength(3);
56
- });
57
-
58
- it('should notify subscribers on new entry', () => {
59
- const collector = DebugCollector.getInstance();
60
- const fn = vi.fn();
61
- collector.subscribe(fn);
62
- collector.addPerf({ type: 'card', durationMs: 2, timestamp: Date.now() });
63
- expect(fn).toHaveBeenCalledTimes(1);
64
- expect(fn).toHaveBeenCalledWith(expect.objectContaining({ kind: 'perf' }));
65
- });
66
-
67
- it('should allow unsubscribe', () => {
68
- const collector = DebugCollector.getInstance();
69
- const fn = vi.fn();
70
- const unsub = collector.subscribe(fn);
71
- unsub();
72
- collector.addPerf({ type: 'x', durationMs: 0, timestamp: Date.now() });
73
- expect(fn).not.toHaveBeenCalled();
74
- });
75
-
76
- it('should clear entries', () => {
77
- const collector = DebugCollector.getInstance();
78
- collector.addPerf({ type: 'x', durationMs: 0, timestamp: Date.now() });
79
- collector.addExpr({ expression: 'a', result: 1, timestamp: Date.now() });
80
- collector.clear();
81
- expect(collector.getEntries()).toHaveLength(0);
82
- });
83
-
84
- it('should cap entries at MAX_ENTRIES', () => {
85
- const collector = DebugCollector.getInstance();
86
- for (let i = 0; i < 250; i++) {
87
- collector.addPerf({ type: `c${i}`, durationMs: i, timestamp: Date.now() });
88
- }
89
- // MAX_ENTRIES is 200
90
- expect(collector.getEntries().length).toBeLessThanOrEqual(200);
91
- });
92
-
93
- it('should swallow subscriber errors gracefully', () => {
94
- const collector = DebugCollector.getInstance();
95
- const badFn = vi.fn(() => { throw new Error('boom'); });
96
- const goodFn = vi.fn();
97
- collector.subscribe(badFn);
98
- collector.subscribe(goodFn);
99
- collector.addPerf({ type: 'x', durationMs: 0, timestamp: Date.now() });
100
- expect(goodFn).toHaveBeenCalledTimes(1);
101
- });
102
- });
@@ -1,134 +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, vi, beforeEach, afterEach } from 'vitest';
10
- import { debugLog, debugTime, debugTimeEnd, parseDebugFlags, isDebugEnabled } from '../debug';
11
-
12
- describe('Debug Utilities', () => {
13
- let consoleSpy: ReturnType<typeof vi.spyOn>;
14
-
15
- beforeEach(() => {
16
- consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
17
- });
18
-
19
- afterEach(() => {
20
- consoleSpy.mockRestore();
21
- (globalThis as any).OBJECTUI_DEBUG = undefined;
22
- });
23
-
24
- describe('debugLog', () => {
25
- it('should not log when debug is disabled', () => {
26
- (globalThis as any).OBJECTUI_DEBUG = false;
27
- debugLog('schema', 'test message');
28
- expect(consoleSpy).not.toHaveBeenCalled();
29
- });
30
-
31
- it('should log when OBJECTUI_DEBUG is true', () => {
32
- (globalThis as any).OBJECTUI_DEBUG = true;
33
- debugLog('schema', 'Resolving component');
34
- expect(consoleSpy).toHaveBeenCalledWith('[ObjectUI Debug][schema] Resolving component');
35
- });
36
-
37
- it('should log with data when provided', () => {
38
- (globalThis as any).OBJECTUI_DEBUG = true;
39
- debugLog('registry', 'Registered', { type: 'Button' });
40
- expect(consoleSpy).toHaveBeenCalledWith(
41
- '[ObjectUI Debug][registry] Registered',
42
- { type: 'Button' }
43
- );
44
- });
45
-
46
- it('should not log when debug is undefined', () => {
47
- debugLog('action', 'test');
48
- expect(consoleSpy).not.toHaveBeenCalled();
49
- });
50
- });
51
-
52
- describe('debugTime / debugTimeEnd', () => {
53
- it('should not log timing when debug is disabled', () => {
54
- (globalThis as any).OBJECTUI_DEBUG = false;
55
- debugTime('test-timer');
56
- debugTimeEnd('test-timer');
57
- expect(consoleSpy).not.toHaveBeenCalled();
58
- });
59
-
60
- it('should measure and log elapsed time when debug is enabled', () => {
61
- (globalThis as any).OBJECTUI_DEBUG = true;
62
- debugTime('render-test');
63
-
64
- // Simulate some delay via a busy loop
65
- const start = performance.now();
66
- while (performance.now() - start < 5) {
67
- // wait ~5ms
68
- }
69
-
70
- debugTimeEnd('render-test');
71
- expect(consoleSpy).toHaveBeenCalledTimes(1);
72
- expect(consoleSpy).toHaveBeenCalledWith(
73
- expect.stringMatching(/^\[ObjectUI Debug\]\[perf\] render-test: \d+\.\d{2}ms$/)
74
- );
75
- });
76
-
77
- it('should not log if debugTimeEnd is called without debugTime', () => {
78
- (globalThis as any).OBJECTUI_DEBUG = true;
79
- debugTimeEnd('nonexistent');
80
- expect(consoleSpy).not.toHaveBeenCalled();
81
- });
82
- });
83
-
84
- describe('parseDebugFlags', () => {
85
- it('should return enabled:false for empty search string', () => {
86
- expect(parseDebugFlags('')).toEqual({ enabled: false });
87
- });
88
-
89
- it('should detect __debug master switch', () => {
90
- expect(parseDebugFlags('?__debug')).toEqual({ enabled: true });
91
- });
92
-
93
- it('should detect individual sub-flags', () => {
94
- expect(parseDebugFlags('?__debug_schema')).toEqual({ enabled: true, schema: true });
95
- expect(parseDebugFlags('?__debug_perf')).toEqual({ enabled: true, perf: true });
96
- expect(parseDebugFlags('?__debug_data')).toEqual({ enabled: true, data: true });
97
- expect(parseDebugFlags('?__debug_expr')).toEqual({ enabled: true, expr: true });
98
- expect(parseDebugFlags('?__debug_events')).toEqual({ enabled: true, events: true });
99
- expect(parseDebugFlags('?__debug_registry')).toEqual({ enabled: true, registry: true });
100
- });
101
-
102
- it('should combine multiple sub-flags', () => {
103
- const flags = parseDebugFlags('?__debug_schema&__debug_perf&__debug_data');
104
- expect(flags).toEqual({ enabled: true, schema: true, perf: true, data: true });
105
- });
106
-
107
- it('should handle unrelated params gracefully', () => {
108
- const flags = parseDebugFlags('?foo=bar&baz=1');
109
- expect(flags).toEqual({ enabled: false });
110
- });
111
-
112
- it('should handle __debug mixed with sub-flags', () => {
113
- const flags = parseDebugFlags('?__debug&__debug_schema');
114
- expect(flags).toEqual({ enabled: true, schema: true });
115
- });
116
- });
117
-
118
- describe('isDebugEnabled', () => {
119
- it('should return true when globalThis.OBJECTUI_DEBUG is true', () => {
120
- (globalThis as any).OBJECTUI_DEBUG = true;
121
- expect(isDebugEnabled()).toBe(true);
122
- });
123
-
124
- it('should return true when globalThis.OBJECTUI_DEBUG is "true"', () => {
125
- (globalThis as any).OBJECTUI_DEBUG = 'true';
126
- expect(isDebugEnabled()).toBe(true);
127
- });
128
-
129
- it('should return false when no debug flag is set', () => {
130
- (globalThis as any).OBJECTUI_DEBUG = undefined;
131
- expect(isDebugEnabled()).toBe(false);
132
- });
133
- });
134
- });
@@ -1,120 +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 { buildExpandFields } from '../expand-fields';
11
-
12
- describe('buildExpandFields', () => {
13
- const sampleFields = {
14
- name: { type: 'text', label: 'Name' },
15
- email: { type: 'email', label: 'Email' },
16
- account: { type: 'lookup', label: 'Account', reference_to: 'accounts' },
17
- parent: { type: 'master_detail', label: 'Parent', reference_to: 'contacts' },
18
- status: { type: 'select', label: 'Status' },
19
- };
20
-
21
- it('should return lookup and master_detail field names', () => {
22
- const result = buildExpandFields(sampleFields);
23
- expect(result).toEqual(['account', 'parent']);
24
- });
25
-
26
- it('should return empty array when no lookup/master_detail fields exist', () => {
27
- const fields = {
28
- name: { type: 'text' },
29
- age: { type: 'number' },
30
- };
31
- expect(buildExpandFields(fields)).toEqual([]);
32
- });
33
-
34
- it('should return empty array for null/undefined schema', () => {
35
- expect(buildExpandFields(null)).toEqual([]);
36
- expect(buildExpandFields(undefined)).toEqual([]);
37
- });
38
-
39
- it('should return empty array for empty fields object', () => {
40
- expect(buildExpandFields({})).toEqual([]);
41
- });
42
-
43
- it('should filter by string columns when provided', () => {
44
- const result = buildExpandFields(sampleFields, ['name', 'account']);
45
- expect(result).toEqual(['account']);
46
- });
47
-
48
- it('should filter by ListColumn objects with field property', () => {
49
- const columns = [
50
- { field: 'name', label: 'Name' },
51
- { field: 'parent', label: 'Parent Contact' },
52
- ];
53
- const result = buildExpandFields(sampleFields, columns);
54
- expect(result).toEqual(['parent']);
55
- });
56
-
57
- it('should support columns with name property', () => {
58
- const columns = [
59
- { name: 'account', label: 'Account' },
60
- ];
61
- const result = buildExpandFields(sampleFields, columns);
62
- expect(result).toEqual(['account']);
63
- });
64
-
65
- it('should support columns with fieldName property', () => {
66
- const columns = [
67
- { fieldName: 'parent', label: 'Parent' },
68
- ];
69
- const result = buildExpandFields(sampleFields, columns);
70
- expect(result).toEqual(['parent']);
71
- });
72
-
73
- it('should return empty array when columns have no lookup fields', () => {
74
- const result = buildExpandFields(sampleFields, ['name', 'email']);
75
- expect(result).toEqual([]);
76
- });
77
-
78
- it('should handle mixed string and object columns', () => {
79
- const columns = [
80
- 'name',
81
- { field: 'account' },
82
- 'parent',
83
- ];
84
- const result = buildExpandFields(sampleFields, columns);
85
- expect(result).toEqual(['account', 'parent']);
86
- });
87
-
88
- it('should return all lookup fields when columns is empty array', () => {
89
- // Empty columns array does not satisfy the length > 0 check,
90
- // so no column restriction is applied → all lookup fields returned
91
- const result = buildExpandFields(sampleFields, []);
92
- expect(result).toEqual(['account', 'parent']);
93
- });
94
-
95
- it('should handle malformed field definitions gracefully', () => {
96
- const fields = {
97
- name: null,
98
- account: { type: 'lookup' },
99
- broken: 'not-an-object',
100
- empty: {},
101
- };
102
- const result = buildExpandFields(fields as any);
103
- expect(result).toEqual(['account']);
104
- });
105
-
106
- it('should handle only lookup fields', () => {
107
- const fields = {
108
- ref1: { type: 'lookup', reference_to: 'obj1' },
109
- ref2: { type: 'lookup', reference_to: 'obj2' },
110
- };
111
- expect(buildExpandFields(fields)).toEqual(['ref1', 'ref2']);
112
- });
113
-
114
- it('should handle only master_detail fields', () => {
115
- const fields = {
116
- detail1: { type: 'master_detail', reference_to: 'obj1' },
117
- };
118
- expect(buildExpandFields(fields)).toEqual(['detail1']);
119
- });
120
- });
@@ -1,50 +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 { extractRecords } from '../extract-records';
11
-
12
- describe('extractRecords', () => {
13
- const sampleData = [{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
14
-
15
- it('should return the array directly when results is an array', () => {
16
- expect(extractRecords(sampleData)).toEqual(sampleData);
17
- });
18
-
19
- it('should extract from results.records', () => {
20
- expect(extractRecords({ records: sampleData })).toEqual(sampleData);
21
- });
22
-
23
- it('should extract from results.data', () => {
24
- expect(extractRecords({ data: sampleData })).toEqual(sampleData);
25
- });
26
-
27
- it('should extract from results.value', () => {
28
- expect(extractRecords({ value: sampleData })).toEqual(sampleData);
29
- });
30
-
31
- it('should return empty array for null/undefined', () => {
32
- expect(extractRecords(null)).toEqual([]);
33
- expect(extractRecords(undefined)).toEqual([]);
34
- });
35
-
36
- it('should return empty array for non-array/non-object', () => {
37
- expect(extractRecords('string')).toEqual([]);
38
- expect(extractRecords(42)).toEqual([]);
39
- });
40
-
41
- it('should return empty array for object without recognized keys', () => {
42
- expect(extractRecords({ total: 100 })).toEqual([]);
43
- });
44
-
45
- it('should prefer records over data and value', () => {
46
- const records = [{ id: 1 }];
47
- const data = [{ id: 2 }];
48
- expect(extractRecords({ records, data })).toEqual(records);
49
- });
50
- });
@@ -1,118 +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, vi } from 'vitest';
10
- import { convertFiltersToAST, convertOperatorToAST, type FilterNode } from '../filter-converter';
11
-
12
- describe('Filter Converter Utilities', () => {
13
- describe('convertOperatorToAST', () => {
14
- it('should convert known operators', () => {
15
- expect(convertOperatorToAST('$eq')).toBe('=');
16
- expect(convertOperatorToAST('$ne')).toBe('!=');
17
- expect(convertOperatorToAST('$gt')).toBe('>');
18
- expect(convertOperatorToAST('$gte')).toBe('>=');
19
- expect(convertOperatorToAST('$lt')).toBe('<');
20
- expect(convertOperatorToAST('$lte')).toBe('<=');
21
- expect(convertOperatorToAST('$in')).toBe('in');
22
- expect(convertOperatorToAST('$nin')).toBe('notin');
23
- expect(convertOperatorToAST('$notin')).toBe('notin');
24
- expect(convertOperatorToAST('$contains')).toBe('contains');
25
- expect(convertOperatorToAST('$startswith')).toBe('startswith');
26
- expect(convertOperatorToAST('$between')).toBe('between');
27
- });
28
-
29
- it('should return null for unknown operators', () => {
30
- expect(convertOperatorToAST('$unknown')).toBe(null);
31
- expect(convertOperatorToAST('$exists')).toBe(null);
32
- });
33
- });
34
-
35
- describe('convertFiltersToAST', () => {
36
- it('should convert simple equality filter', () => {
37
- const result = convertFiltersToAST({ status: 'active' });
38
- expect(result).toEqual(['status', '=', 'active']);
39
- });
40
-
41
- it('should convert single operator filter', () => {
42
- const result = convertFiltersToAST({ age: { $gte: 18 } });
43
- expect(result).toEqual(['age', '>=', 18]);
44
- });
45
-
46
- it('should convert multiple operators on same field', () => {
47
- const result = convertFiltersToAST({ age: { $gte: 18, $lte: 65 } }) as FilterNode;
48
- expect(result[0]).toBe('and');
49
- expect(result.slice(1)).toContainEqual(['age', '>=', 18]);
50
- expect(result.slice(1)).toContainEqual(['age', '<=', 65]);
51
- });
52
-
53
- it('should convert multiple fields with and logic', () => {
54
- const result = convertFiltersToAST({
55
- age: { $gte: 18 },
56
- status: 'active'
57
- }) as FilterNode;
58
- expect(result[0]).toBe('and');
59
- expect(result.slice(1)).toContainEqual(['age', '>=', 18]);
60
- expect(result.slice(1)).toContainEqual(['status', '=', 'active']);
61
- });
62
-
63
- it('should handle $in operator', () => {
64
- const result = convertFiltersToAST({
65
- status: { $in: ['active', 'pending'] }
66
- });
67
- expect(result).toEqual(['status', 'in', ['active', 'pending']]);
68
- });
69
-
70
- it('should handle $nin operator', () => {
71
- const result = convertFiltersToAST({
72
- status: { $nin: ['archived'] }
73
- });
74
- expect(result).toEqual(['status', 'notin', ['archived']]);
75
- });
76
-
77
- it('should warn on $regex operator and convert to contains', () => {
78
- const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
79
-
80
- const result = convertFiltersToAST({
81
- name: { $regex: '^John' }
82
- });
83
-
84
- expect(result).toEqual(['name', 'contains', '^John']);
85
- expect(consoleSpy).toHaveBeenCalledWith(
86
- expect.stringContaining('[ObjectUI] Warning: $regex operator is not fully supported')
87
- );
88
-
89
- consoleSpy.mockRestore();
90
- });
91
-
92
- it('should throw error on unknown operator', () => {
93
- expect(() => {
94
- convertFiltersToAST({ age: { $unknown: 18 } });
95
- }).toThrow('[ObjectUI] Unknown filter operator');
96
- });
97
-
98
- it('should skip null and undefined values', () => {
99
- const result = convertFiltersToAST({
100
- name: 'John',
101
- age: null,
102
- email: undefined
103
- });
104
- expect(result).toEqual(['name', '=', 'John']);
105
- });
106
-
107
- it('should return original filter if empty after filtering', () => {
108
- const result = convertFiltersToAST({
109
- age: null,
110
- email: undefined
111
- });
112
- expect(result).toEqual({
113
- age: null,
114
- email: undefined
115
- });
116
- });
117
- });
118
- });
@@ -1,110 +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 { mergeViewsIntoObjects } from '../merge-views-into-objects';
11
-
12
- describe('mergeViewsIntoObjects', () => {
13
- it('should merge listViews from views into corresponding objects', () => {
14
- const objects = [{ name: 'account', label: 'Account', fields: {} }];
15
- const views = [
16
- {
17
- listViews: {
18
- all_accounts: {
19
- name: 'all_accounts',
20
- label: 'All Accounts',
21
- type: 'grid',
22
- data: { provider: 'object', object: 'account' },
23
- },
24
- },
25
- },
26
- ];
27
-
28
- const result = mergeViewsIntoObjects(objects, views);
29
- expect(result[0].listViews.all_accounts).toBeDefined();
30
- expect(result[0].listViews.all_accounts.label).toBe('All Accounts');
31
- });
32
-
33
- it('should preserve existing listViews on objects', () => {
34
- const objects = [
35
- {
36
- name: 'todo_task',
37
- label: 'Task',
38
- fields: {},
39
- listViews: {
40
- existing: { name: 'existing', label: 'Existing View', type: 'grid', data: { provider: 'object', object: 'todo_task' } },
41
- },
42
- },
43
- ];
44
- const views = [
45
- {
46
- listViews: {
47
- new_view: {
48
- name: 'new_view',
49
- label: 'New View',
50
- type: 'grid',
51
- data: { provider: 'object', object: 'todo_task' },
52
- },
53
- },
54
- },
55
- ];
56
-
57
- const result = mergeViewsIntoObjects(objects, views);
58
- const task = result[0];
59
- expect(task.listViews.existing).toBeDefined();
60
- expect(task.listViews.new_view).toBeDefined();
61
- });
62
-
63
- it('should ignore listViews without data.object', () => {
64
- const objects = [{ name: 'account', label: 'Account', fields: {} }];
65
- const views = [
66
- {
67
- listViews: {
68
- orphan: { name: 'orphan', label: 'Orphan View', type: 'grid' },
69
- },
70
- },
71
- ];
72
-
73
- const result = mergeViewsIntoObjects(objects, views);
74
- expect(result[0].listViews).toBeUndefined();
75
- });
76
-
77
- it('should return objects unchanged when views is empty', () => {
78
- const objects = [{ name: 'account', label: 'Account', fields: {} }];
79
-
80
- const result = mergeViewsIntoObjects(objects, []);
81
- expect(result).toEqual(objects);
82
- });
83
-
84
- it('should ignore views without listViews property', () => {
85
- const objects = [{ name: 'account', label: 'Account', fields: {} }];
86
- const views = [{ someOtherProp: true }];
87
-
88
- const result = mergeViewsIntoObjects(objects, views);
89
- expect(result).toEqual(objects);
90
- });
91
-
92
- it('should merge views from multiple view entries into different objects', () => {
93
- const objects = [
94
- { name: 'account', label: 'Account', fields: {} },
95
- { name: 'contact', label: 'Contact', fields: {} },
96
- ];
97
- const views = [
98
- {
99
- listViews: {
100
- all_accounts: { name: 'all_accounts', label: 'All Accounts', type: 'grid', data: { provider: 'object', object: 'account' } },
101
- all_contacts: { name: 'all_contacts', label: 'All Contacts', type: 'grid', data: { provider: 'object', object: 'contact' } },
102
- },
103
- },
104
- ];
105
-
106
- const result = mergeViewsIntoObjects(objects, views);
107
- expect(result[0].listViews.all_accounts).toBeDefined();
108
- expect(result[1].listViews.all_contacts).toBeDefined();
109
- });
110
- });