@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,447 +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 { FormulaFunctions } from '../FormulaFunctions';
11
- import { ExpressionEvaluator, evaluateExpression as evalExprFn } from '../ExpressionEvaluator';
12
-
13
- describe('FormulaFunctions', () => {
14
- describe('Registry', () => {
15
- it('should register and retrieve custom functions', () => {
16
- const formulas = new FormulaFunctions();
17
- formulas.register('DOUBLE', (x: number) => x * 2);
18
- expect(formulas.has('DOUBLE')).toBe(true);
19
- expect(formulas.get('DOUBLE')!(5)).toBe(10);
20
- });
21
-
22
- it('should be case-insensitive', () => {
23
- const formulas = new FormulaFunctions();
24
- expect(formulas.has('sum')).toBe(true);
25
- expect(formulas.has('SUM')).toBe(true);
26
- expect(formulas.has('Sum')).toBe(true);
27
- });
28
-
29
- it('should list all registered function names', () => {
30
- const formulas = new FormulaFunctions();
31
- const names = formulas.getNames();
32
- expect(names).toContain('SUM');
33
- expect(names).toContain('AVG');
34
- expect(names).toContain('IF');
35
- expect(names).toContain('TODAY');
36
- expect(names).toContain('CONCAT');
37
- });
38
-
39
- it('should export functions as an object', () => {
40
- const formulas = new FormulaFunctions();
41
- const obj = formulas.toObject();
42
- expect(typeof obj.SUM).toBe('function');
43
- expect(typeof obj.IF).toBe('function');
44
- });
45
- });
46
-
47
- describe('Aggregation Functions', () => {
48
- it('SUM should sum numeric values', () => {
49
- const formulas = new FormulaFunctions();
50
- const SUM = formulas.get('SUM')!;
51
- expect(SUM(1, 2, 3)).toBe(6);
52
- expect(SUM(10, 20)).toBe(30);
53
- });
54
-
55
- it('SUM should handle arrays', () => {
56
- const formulas = new FormulaFunctions();
57
- const SUM = formulas.get('SUM')!;
58
- expect(SUM([1, 2, 3])).toBe(6);
59
- expect(SUM([10], [20])).toBe(30);
60
- });
61
-
62
- it('SUM should handle empty args', () => {
63
- const formulas = new FormulaFunctions();
64
- const SUM = formulas.get('SUM')!;
65
- expect(SUM()).toBe(0);
66
- });
67
-
68
- it('AVG should calculate average', () => {
69
- const formulas = new FormulaFunctions();
70
- const AVG = formulas.get('AVG')!;
71
- expect(AVG(10, 20, 30)).toBe(20);
72
- expect(AVG([2, 4, 6])).toBe(4);
73
- });
74
-
75
- it('AVG should return 0 for empty args', () => {
76
- const formulas = new FormulaFunctions();
77
- const AVG = formulas.get('AVG')!;
78
- expect(AVG()).toBe(0);
79
- });
80
-
81
- it('COUNT should count non-null values', () => {
82
- const formulas = new FormulaFunctions();
83
- const COUNT = formulas.get('COUNT')!;
84
- expect(COUNT(1, 2, 3)).toBe(3);
85
- expect(COUNT(1, null, undefined, 4)).toBe(2);
86
- expect(COUNT([1, null, 3])).toBe(2);
87
- });
88
-
89
- it('MIN should return minimum value', () => {
90
- const formulas = new FormulaFunctions();
91
- const MIN = formulas.get('MIN')!;
92
- expect(MIN(5, 3, 8, 1)).toBe(1);
93
- expect(MIN([10, 2, 7])).toBe(2);
94
- });
95
-
96
- it('MIN should return 0 for empty args', () => {
97
- const formulas = new FormulaFunctions();
98
- const MIN = formulas.get('MIN')!;
99
- expect(MIN()).toBe(0);
100
- });
101
-
102
- it('MAX should return maximum value', () => {
103
- const formulas = new FormulaFunctions();
104
- const MAX = formulas.get('MAX')!;
105
- expect(MAX(5, 3, 8, 1)).toBe(8);
106
- expect(MAX([10, 2, 7])).toBe(10);
107
- });
108
-
109
- it('MAX should return 0 for empty args', () => {
110
- const formulas = new FormulaFunctions();
111
- const MAX = formulas.get('MAX')!;
112
- expect(MAX()).toBe(0);
113
- });
114
- });
115
-
116
- describe('Date Functions', () => {
117
- it('TODAY should return current date string', () => {
118
- const formulas = new FormulaFunctions();
119
- const TODAY = formulas.get('TODAY')!;
120
- const result = TODAY();
121
- // Should be YYYY-MM-DD format
122
- expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
123
- });
124
-
125
- it('NOW should return ISO timestamp', () => {
126
- const formulas = new FormulaFunctions();
127
- const NOW = formulas.get('NOW')!;
128
- const result = NOW();
129
- // Should be a valid ISO 8601 date
130
- expect(new Date(result).toISOString()).toBe(result);
131
- });
132
-
133
- it('DATEADD should add days', () => {
134
- const formulas = new FormulaFunctions();
135
- const DATEADD = formulas.get('DATEADD')!;
136
- const result = DATEADD('2025-01-01T00:00:00.000Z', 5, 'days');
137
- expect(new Date(result).getUTCDate()).toBe(6);
138
- });
139
-
140
- it('DATEADD should add months', () => {
141
- const formulas = new FormulaFunctions();
142
- const DATEADD = formulas.get('DATEADD')!;
143
- const result = DATEADD('2025-01-15T00:00:00.000Z', 2, 'months');
144
- expect(new Date(result).getUTCMonth()).toBe(2); // March (0-indexed)
145
- });
146
-
147
- it('DATEADD should add years', () => {
148
- const formulas = new FormulaFunctions();
149
- const DATEADD = formulas.get('DATEADD')!;
150
- const result = DATEADD('2025-06-15T00:00:00.000Z', 3, 'years');
151
- expect(new Date(result).getUTCFullYear()).toBe(2028);
152
- });
153
-
154
- it('DATEADD should throw for invalid date', () => {
155
- const formulas = new FormulaFunctions();
156
- const DATEADD = formulas.get('DATEADD')!;
157
- expect(() => DATEADD('invalid', 1, 'days')).toThrow('Invalid date');
158
- });
159
-
160
- it('DATEADD should throw for unsupported unit', () => {
161
- const formulas = new FormulaFunctions();
162
- const DATEADD = formulas.get('DATEADD')!;
163
- expect(() => DATEADD('2025-01-01', 1, 'weeks')).toThrow('Unsupported unit');
164
- });
165
-
166
- it('DATEDIFF should calculate day difference', () => {
167
- const formulas = new FormulaFunctions();
168
- const DATEDIFF = formulas.get('DATEDIFF')!;
169
- expect(DATEDIFF('2025-01-01', '2025-01-11', 'days')).toBe(10);
170
- });
171
-
172
- it('DATEDIFF should calculate month difference', () => {
173
- const formulas = new FormulaFunctions();
174
- const DATEDIFF = formulas.get('DATEDIFF')!;
175
- expect(DATEDIFF('2025-01-15', '2025-04-15', 'months')).toBe(3);
176
- });
177
-
178
- it('DATEDIFF should calculate year difference', () => {
179
- const formulas = new FormulaFunctions();
180
- const DATEDIFF = formulas.get('DATEDIFF')!;
181
- expect(DATEDIFF('2020-06-01', '2025-06-01', 'years')).toBe(5);
182
- });
183
-
184
- it('DATEDIFF should throw for invalid date', () => {
185
- const formulas = new FormulaFunctions();
186
- const DATEDIFF = formulas.get('DATEDIFF')!;
187
- expect(() => DATEDIFF('invalid', '2025-01-01', 'days')).toThrow('Invalid date');
188
- });
189
- });
190
-
191
- describe('Logic Functions', () => {
192
- it('IF should return trueValue when condition is truthy', () => {
193
- const formulas = new FormulaFunctions();
194
- const IF = formulas.get('IF')!;
195
- expect(IF(true, 'yes', 'no')).toBe('yes');
196
- expect(IF(1, 'yes', 'no')).toBe('yes');
197
- });
198
-
199
- it('IF should return falseValue when condition is falsy', () => {
200
- const formulas = new FormulaFunctions();
201
- const IF = formulas.get('IF')!;
202
- expect(IF(false, 'yes', 'no')).toBe('no');
203
- expect(IF(0, 'yes', 'no')).toBe('no');
204
- expect(IF(null, 'yes', 'no')).toBe('no');
205
- });
206
-
207
- it('AND should return true only when all args are truthy', () => {
208
- const formulas = new FormulaFunctions();
209
- const AND = formulas.get('AND')!;
210
- expect(AND(true, true, true)).toBe(true);
211
- expect(AND(true, false, true)).toBe(false);
212
- expect(AND(1, 'hello', true)).toBe(true);
213
- });
214
-
215
- it('OR should return true when any arg is truthy', () => {
216
- const formulas = new FormulaFunctions();
217
- const OR = formulas.get('OR')!;
218
- expect(OR(false, false, true)).toBe(true);
219
- expect(OR(false, false, false)).toBe(false);
220
- expect(OR(0, '', null)).toBe(false);
221
- });
222
-
223
- it('NOT should negate a value', () => {
224
- const formulas = new FormulaFunctions();
225
- const NOT = formulas.get('NOT')!;
226
- expect(NOT(true)).toBe(false);
227
- expect(NOT(false)).toBe(true);
228
- expect(NOT(0)).toBe(true);
229
- expect(NOT(1)).toBe(false);
230
- });
231
-
232
- it('SWITCH should match cases and return result', () => {
233
- const formulas = new FormulaFunctions();
234
- const SWITCH = formulas.get('SWITCH')!;
235
- expect(SWITCH('a', 'a', 1, 'b', 2, 'c', 3)).toBe(1);
236
- expect(SWITCH('b', 'a', 1, 'b', 2, 'c', 3)).toBe(2);
237
- });
238
-
239
- it('SWITCH should return default when no match', () => {
240
- const formulas = new FormulaFunctions();
241
- const SWITCH = formulas.get('SWITCH')!;
242
- expect(SWITCH('x', 'a', 1, 'b', 2, 'default')).toBe('default');
243
- });
244
-
245
- it('SWITCH should return undefined when no match and no default', () => {
246
- const formulas = new FormulaFunctions();
247
- const SWITCH = formulas.get('SWITCH')!;
248
- expect(SWITCH('x', 'a', 1, 'b', 2)).toBeUndefined();
249
- });
250
- });
251
-
252
- describe('String Functions', () => {
253
- it('CONCAT should join strings', () => {
254
- const formulas = new FormulaFunctions();
255
- const CONCAT = formulas.get('CONCAT')!;
256
- expect(CONCAT('Hello', ' ', 'World')).toBe('Hello World');
257
- expect(CONCAT('a', 'b', 'c')).toBe('abc');
258
- });
259
-
260
- it('CONCAT should handle null/undefined', () => {
261
- const formulas = new FormulaFunctions();
262
- const CONCAT = formulas.get('CONCAT')!;
263
- expect(CONCAT('Hello', null, 'World')).toBe('HelloWorld');
264
- });
265
-
266
- it('LEFT should return leftmost characters', () => {
267
- const formulas = new FormulaFunctions();
268
- const LEFT = formulas.get('LEFT')!;
269
- expect(LEFT('Hello World', 5)).toBe('Hello');
270
- expect(LEFT('Hi', 10)).toBe('Hi');
271
- });
272
-
273
- it('RIGHT should return rightmost characters', () => {
274
- const formulas = new FormulaFunctions();
275
- const RIGHT = formulas.get('RIGHT')!;
276
- expect(RIGHT('Hello World', 5)).toBe('World');
277
- expect(RIGHT('Hi', 10)).toBe('Hi');
278
- });
279
-
280
- it('TRIM should remove whitespace', () => {
281
- const formulas = new FormulaFunctions();
282
- const TRIM = formulas.get('TRIM')!;
283
- expect(TRIM(' hello ')).toBe('hello');
284
- expect(TRIM(' test ')).toBe('test');
285
- });
286
-
287
- it('UPPER should convert to uppercase', () => {
288
- const formulas = new FormulaFunctions();
289
- const UPPER = formulas.get('UPPER')!;
290
- expect(UPPER('hello')).toBe('HELLO');
291
- });
292
-
293
- it('LOWER should convert to lowercase', () => {
294
- const formulas = new FormulaFunctions();
295
- const LOWER = formulas.get('LOWER')!;
296
- expect(LOWER('HELLO')).toBe('hello');
297
- });
298
- });
299
-
300
- describe('String Search Functions', () => {
301
- const formulas = new FormulaFunctions();
302
-
303
- it('should find substring position with FIND', () => {
304
- const FIND = formulas.get('FIND')!;
305
- expect(FIND('world', 'hello world')).toBe(6);
306
- expect(FIND('xyz', 'hello world')).toBe(-1);
307
- });
308
-
309
- it('should find substring from start position', () => {
310
- const FIND = formulas.get('FIND')!;
311
- expect(FIND('l', 'hello world', 4)).toBe(9);
312
- });
313
-
314
- it('should replace all occurrences with REPLACE', () => {
315
- const REPLACE = formulas.get('REPLACE')!;
316
- expect(REPLACE('hello world', 'o', '0')).toBe('hell0 w0rld');
317
- });
318
-
319
- it('should extract substring with SUBSTRING', () => {
320
- const SUBSTRING = formulas.get('SUBSTRING')!;
321
- expect(SUBSTRING('hello world', 6)).toBe('world');
322
- expect(SUBSTRING('hello world', 0, 5)).toBe('hello');
323
- });
324
-
325
- it('should test regex pattern with REGEX', () => {
326
- const REGEX = formulas.get('REGEX')!;
327
- expect(REGEX('hello123', '\\d+')).toBe(true);
328
- expect(REGEX('hello', '\\d+')).toBe(false);
329
- expect(REGEX('Hello', '^hello$', 'i')).toBe(true);
330
- });
331
-
332
- it('should get string length with LEN', () => {
333
- const LEN = formulas.get('LEN')!;
334
- expect(LEN('hello')).toBe(5);
335
- expect(LEN('')).toBe(0);
336
- });
337
- });
338
-
339
- describe('Statistical Functions', () => {
340
- const formulas = new FormulaFunctions();
341
-
342
- it('should calculate MEDIAN for odd count', () => {
343
- const MEDIAN = formulas.get('MEDIAN')!;
344
- expect(MEDIAN(3, 1, 2)).toBe(2);
345
- });
346
-
347
- it('should calculate MEDIAN for even count', () => {
348
- const MEDIAN = formulas.get('MEDIAN')!;
349
- expect(MEDIAN(1, 2, 3, 4)).toBe(2.5);
350
- });
351
-
352
- it('should return 0 for empty MEDIAN', () => {
353
- const MEDIAN = formulas.get('MEDIAN')!;
354
- expect(MEDIAN()).toBe(0);
355
- });
356
-
357
- it('should calculate STDEV', () => {
358
- const STDEV = formulas.get('STDEV')!;
359
- const result = STDEV(2, 4, 4, 4, 5, 5, 7, 9);
360
- // Sample standard deviation (Bessel's correction, divides by n-1)
361
- expect(result).toBeCloseTo(2.138, 2);
362
- });
363
-
364
- it('should return 0 for STDEV with less than 2 values', () => {
365
- const STDEV = formulas.get('STDEV')!;
366
- expect(STDEV(5)).toBe(0);
367
- expect(STDEV()).toBe(0);
368
- });
369
-
370
- it('should calculate VARIANCE', () => {
371
- const VARIANCE = formulas.get('VARIANCE')!;
372
- const result = VARIANCE(2, 4, 4, 4, 5, 5, 7, 9);
373
- // Sample variance (Bessel's correction, divides by n-1)
374
- expect(result).toBeCloseTo(4.571, 2);
375
- });
376
-
377
- it('should calculate PERCENTILE', () => {
378
- const PERCENTILE = formulas.get('PERCENTILE')!;
379
- expect(PERCENTILE(0, 1, 2, 3, 4, 5)).toBe(1);
380
- expect(PERCENTILE(100, 1, 2, 3, 4, 5)).toBe(5);
381
- expect(PERCENTILE(50, 1, 2, 3, 4, 5)).toBe(3);
382
- });
383
-
384
- it('should handle PERCENTILE with empty values', () => {
385
- const PERCENTILE = formulas.get('PERCENTILE')!;
386
- expect(PERCENTILE(50)).toBe(0);
387
- });
388
- });
389
-
390
- describe('DATEFORMAT Function', () => {
391
- const formulas = new FormulaFunctions();
392
-
393
- it('should format date with DATEFORMAT', () => {
394
- const DATEFORMAT = formulas.get('DATEFORMAT')!;
395
- const result = DATEFORMAT('2026-02-10T00:00:00.000Z', 'YYYY-MM-DD');
396
- expect(result).toBe('2026-02-10');
397
- });
398
-
399
- it('should throw for invalid date in DATEFORMAT', () => {
400
- const DATEFORMAT = formulas.get('DATEFORMAT')!;
401
- expect(() => DATEFORMAT('invalid', 'YYYY')).toThrow('DATEFORMAT: Invalid date');
402
- });
403
- });
404
- });
405
-
406
- describe('ExpressionEvaluator with Formula Functions', () => {
407
- it('should use SUM in expressions', () => {
408
- const evaluator = new ExpressionEvaluator({ values: [10, 20, 30] });
409
- expect(evaluator.evaluateExpression('SUM(values)')).toBe(60);
410
- });
411
-
412
- it('should use AVG in expressions', () => {
413
- const evaluator = new ExpressionEvaluator({ values: [10, 20, 30] });
414
- expect(evaluator.evaluateExpression('AVG(values)')).toBe(20);
415
- });
416
-
417
- it('should use IF in expressions', () => {
418
- const evaluator = new ExpressionEvaluator({ data: { age: 25 } });
419
- expect(evaluator.evaluateExpression('IF(data.age >= 18, "adult", "minor")')).toBe('adult');
420
- });
421
-
422
- it('should use CONCAT in template expressions', () => {
423
- const evaluator = new ExpressionEvaluator({ first: 'John', last: 'Doe' });
424
- expect(evaluator.evaluate('${CONCAT(first, " ", last)}')).toBe('John Doe');
425
- });
426
-
427
- it('should use UPPER in expressions', () => {
428
- const evaluator = new ExpressionEvaluator({ name: 'hello' });
429
- expect(evaluator.evaluateExpression('UPPER(name)')).toBe('HELLO');
430
- });
431
-
432
- it('should support nested formula calls', () => {
433
- const evaluator = new ExpressionEvaluator({ items: [10, 20, 30] });
434
- expect(evaluator.evaluateExpression('IF(SUM(items) > 50, "high", "low")')).toBe('high');
435
- });
436
-
437
- it('should support registering custom functions', () => {
438
- const evaluator = new ExpressionEvaluator({ x: 5 });
439
- evaluator.registerFunction('DOUBLE', (n: number) => n * 2);
440
- expect(evaluator.evaluateExpression('DOUBLE(x)')).toBe(10);
441
- });
442
-
443
- it('should use convenience function with formulas', () => {
444
- expect(evalExprFn('${SUM(1, 2, 3)}')).toBe(6);
445
- expect(evalExprFn('${IF(true, "yes", "no")}')).toBe('yes');
446
- });
447
- });
@@ -1,13 +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 * from './ExpressionContext.js';
10
- export * from './ExpressionEvaluator.js';
11
- export * from './ExpressionCache.js';
12
- export * from './FormulaFunctions.js';
13
- export * from './SafeExpressionParser.js';
package/src/index.ts DELETED
@@ -1,38 +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 type { SchemaNode, ComponentRendererProps } from './types/index.js';
10
- export * from './registry/Registry.js';
11
- export * from './registry/PluginSystem.js';
12
- export * from './registry/PluginScopeImpl.js';
13
- export * from './registry/WidgetRegistry.js';
14
- export * from './validation/index.js';
15
- export * from './builder/schema-builder.js';
16
- export * from './utils/filter-converter.js';
17
- export * from './utils/normalize-quick-filter.js';
18
- export * from './utils/extract-records.js';
19
- export * from './utils/expand-fields.js';
20
- export * from './evaluator/index.js';
21
- export * from './actions/index.js';
22
- export * from './query/index.js';
23
- export * from './adapters/index.js';
24
- export * from './theme/index.js';
25
- export * from './data-scope/index.js';
26
- export * from './errors/index.js';
27
- export * from './utils/debug.js';
28
- export * from './utils/debug-collector.js';
29
- export * from './utils/merge-views-into-objects.js';
30
- export * from './protocols/index.js';
31
-
32
- /**
33
- * @deprecated Import `composeStacks` from `@objectstack/spec` instead.
34
- *
35
- * This re-export is kept only for backward compatibility and will be removed
36
- * in the next major version of `@object-ui/core`.
37
- */
38
- export { composeStacks } from '@objectstack/spec';
@@ -1,168 +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 - DnD Protocol Bridge
11
- *
12
- * Converts spec-aligned DnD configuration schemas into runtime-usable
13
- * component props and CSS constraint styles for drag-and-drop interactions.
14
- *
15
- * @module protocols/DndProtocol
16
- * @packageDocumentation
17
- */
18
-
19
- import type { DndConfig, DragItem, DropZone, DragConstraint } from '@object-ui/types';
20
-
21
- // ============================================================================
22
- // Resolved Types
23
- // ============================================================================
24
-
25
- /** Fully resolved DnD configuration with all defaults applied. */
26
- export interface ResolvedDndConfig {
27
- enabled: boolean;
28
- sortable: boolean;
29
- autoScroll: boolean;
30
- touchDelay: number;
31
- dragItem?: DragItem;
32
- dropZone?: DropZone;
33
- }
34
-
35
- /** Component props for a draggable element. */
36
- export interface DragItemProps {
37
- draggable: boolean;
38
- 'aria-roledescription': string;
39
- 'aria-label'?: string;
40
- 'aria-describedby'?: string;
41
- role: string;
42
- 'data-drag-type': string;
43
- 'data-drag-handle': string;
44
- 'data-drag-disabled': string;
45
- }
46
-
47
- /** Component props for a droppable area. */
48
- export interface DropZoneProps {
49
- 'aria-dropeffect': string;
50
- 'aria-label'?: string;
51
- 'aria-describedby'?: string;
52
- role: string;
53
- 'data-drop-accept': string;
54
- 'data-drop-max-items'?: number;
55
- 'data-drop-highlight': string;
56
- }
57
-
58
- /** CSS constraint styles for drag movement. */
59
- export interface DragConstraintStyles {
60
- position: string;
61
- touchAction: string;
62
- [key: string]: string | number | undefined;
63
- }
64
-
65
- // ============================================================================
66
- // DnD Config Resolution
67
- // ============================================================================
68
-
69
- /**
70
- * Resolve a DnD configuration by applying spec defaults to missing fields.
71
- *
72
- * @param config - Partial DnD configuration from the spec
73
- * @returns Fully resolved DnD configuration
74
- */
75
- export function resolveDndConfig(config: DndConfig): ResolvedDndConfig {
76
- return {
77
- enabled: config.enabled ?? false,
78
- sortable: config.sortable ?? false,
79
- autoScroll: config.autoScroll ?? true,
80
- touchDelay: config.touchDelay ?? 200,
81
- dragItem: config.dragItem,
82
- dropZone: config.dropZone,
83
- };
84
- }
85
-
86
- // ============================================================================
87
- // Drag Item Props
88
- // ============================================================================
89
-
90
- /**
91
- * Convert a spec DragItem to component props suitable for a draggable element.
92
- * Produces `draggable`, ARIA attributes, and data attributes for DnD libraries.
93
- *
94
- * @param item - DragItem configuration from the spec
95
- * @returns Component props object for a draggable element
96
- */
97
- export function createDragItemProps(item: DragItem): DragItemProps {
98
- return {
99
- draggable: !(item.disabled ?? false),
100
- 'aria-roledescription': 'draggable',
101
- 'aria-label': item.ariaLabel ?? item.label,
102
- 'aria-describedby': item.ariaDescribedBy,
103
- role: item.role ?? 'listitem',
104
- 'data-drag-type': item.type,
105
- 'data-drag-handle': item.handle ?? 'element',
106
- 'data-drag-disabled': String(item.disabled ?? false),
107
- };
108
- }
109
-
110
- // ============================================================================
111
- // Drop Zone Props
112
- // ============================================================================
113
-
114
- /**
115
- * Convert a spec DropZone to component props suitable for a droppable area.
116
- * Produces ARIA attributes and data attributes for DnD libraries.
117
- *
118
- * @param zone - DropZone configuration from the spec
119
- * @returns Component props object for a droppable area
120
- */
121
- export function createDropZoneProps(zone: DropZone): DropZoneProps {
122
- return {
123
- 'aria-dropeffect': zone.dropEffect ?? 'move',
124
- 'aria-label': zone.ariaLabel ?? zone.label,
125
- 'aria-describedby': zone.ariaDescribedBy,
126
- role: zone.role ?? 'list',
127
- 'data-drop-accept': zone.accept.join(','),
128
- 'data-drop-max-items': zone.maxItems,
129
- 'data-drop-highlight': String(zone.highlightOnDragOver ?? true),
130
- };
131
- }
132
-
133
- // ============================================================================
134
- // Drag Constraint Styles
135
- // ============================================================================
136
-
137
- /**
138
- * Resolve a DragConstraint into CSS style properties that restrict
139
- * drag movement along an axis or within bounds.
140
- *
141
- * @param constraint - DragConstraint configuration from the spec
142
- * @returns CSS styles object for constraining drag movement
143
- */
144
- export function resolveDragConstraints(constraint: DragConstraint): DragConstraintStyles {
145
- const styles: DragConstraintStyles = {
146
- position: 'relative',
147
- touchAction: 'none',
148
- };
149
-
150
- const axis = constraint.axis ?? 'both';
151
- if (axis === 'x') {
152
- styles.touchAction = 'pan-y';
153
- } else if (axis === 'y') {
154
- styles.touchAction = 'pan-x';
155
- }
156
-
157
- const bounds = constraint.bounds ?? 'none';
158
- if (bounds === 'parent') {
159
- styles.overflow = 'hidden';
160
- }
161
-
162
- if (constraint.grid) {
163
- styles['--drag-grid-x'] = `${constraint.grid[0]}px`;
164
- styles['--drag-grid-y'] = `${constraint.grid[1]}px`;
165
- }
166
-
167
- return styles;
168
- }