@object-ui/core 0.3.0 → 0.5.0

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 (90) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +8 -0
  3. package/dist/actions/ActionRunner.d.ts +40 -0
  4. package/dist/actions/ActionRunner.js +160 -0
  5. package/dist/actions/index.d.ts +8 -0
  6. package/dist/actions/index.js +8 -0
  7. package/dist/adapters/index.d.ts +7 -0
  8. package/dist/adapters/index.js +10 -0
  9. package/dist/builder/schema-builder.d.ts +7 -0
  10. package/dist/builder/schema-builder.js +4 -6
  11. package/dist/evaluator/ExpressionCache.d.ts +101 -0
  12. package/dist/evaluator/ExpressionCache.js +135 -0
  13. package/dist/evaluator/ExpressionContext.d.ts +51 -0
  14. package/dist/evaluator/ExpressionContext.js +110 -0
  15. package/dist/evaluator/ExpressionEvaluator.d.ts +117 -0
  16. package/dist/evaluator/ExpressionEvaluator.js +220 -0
  17. package/dist/evaluator/index.d.ts +10 -0
  18. package/dist/evaluator/index.js +10 -0
  19. package/dist/index.d.ts +17 -4
  20. package/dist/index.js +16 -5
  21. package/dist/query/index.d.ts +6 -0
  22. package/dist/query/index.js +6 -0
  23. package/dist/query/query-ast.d.ts +32 -0
  24. package/dist/query/query-ast.js +268 -0
  25. package/dist/registry/PluginScopeImpl.d.ts +80 -0
  26. package/dist/registry/PluginScopeImpl.js +243 -0
  27. package/dist/registry/PluginSystem.d.ts +66 -0
  28. package/dist/registry/PluginSystem.js +142 -0
  29. package/dist/registry/Registry.d.ts +80 -4
  30. package/dist/registry/Registry.js +119 -7
  31. package/dist/types/index.d.ts +7 -0
  32. package/dist/types/index.js +7 -0
  33. package/dist/utils/filter-converter.d.ts +57 -0
  34. package/dist/utils/filter-converter.js +100 -0
  35. package/dist/validation/index.d.ts +9 -0
  36. package/dist/validation/index.js +9 -0
  37. package/dist/validation/schema-validator.d.ts +7 -0
  38. package/dist/validation/schema-validator.js +4 -6
  39. package/dist/validation/validation-engine.d.ts +70 -0
  40. package/dist/validation/validation-engine.js +363 -0
  41. package/dist/validation/validators/index.d.ts +16 -0
  42. package/dist/validation/validators/index.js +16 -0
  43. package/dist/validation/validators/object-validation-engine.d.ts +118 -0
  44. package/dist/validation/validators/object-validation-engine.js +538 -0
  45. package/package.json +26 -7
  46. package/src/actions/ActionRunner.ts +195 -0
  47. package/src/actions/index.ts +9 -0
  48. package/src/adapters/README.md +180 -0
  49. package/src/adapters/index.ts +10 -0
  50. package/src/builder/schema-builder.ts +8 -0
  51. package/src/evaluator/ExpressionCache.ts +192 -0
  52. package/src/evaluator/ExpressionContext.ts +118 -0
  53. package/src/evaluator/ExpressionEvaluator.ts +267 -0
  54. package/src/evaluator/__tests__/ExpressionCache.test.ts +135 -0
  55. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +101 -0
  56. package/src/evaluator/index.ts +11 -0
  57. package/src/index.test.ts +8 -0
  58. package/src/index.ts +18 -5
  59. package/src/query/__tests__/query-ast.test.ts +211 -0
  60. package/src/query/__tests__/window-functions.test.ts +275 -0
  61. package/src/query/index.ts +7 -0
  62. package/src/query/query-ast.ts +341 -0
  63. package/src/registry/PluginScopeImpl.ts +259 -0
  64. package/src/registry/PluginSystem.ts +161 -0
  65. package/src/registry/Registry.ts +133 -8
  66. package/src/registry/__tests__/PluginSystem.test.ts +226 -0
  67. package/src/registry/__tests__/Registry.test.ts +293 -0
  68. package/src/registry/__tests__/plugin-scope-integration.test.ts +283 -0
  69. package/src/types/index.ts +8 -0
  70. package/src/utils/__tests__/filter-converter.test.ts +118 -0
  71. package/src/utils/filter-converter.ts +133 -0
  72. package/src/validation/__tests__/object-validation-engine.test.ts +567 -0
  73. package/src/validation/__tests__/validation-engine.test.ts +102 -0
  74. package/src/validation/index.ts +10 -0
  75. package/src/validation/schema-validator.ts +8 -0
  76. package/src/validation/validation-engine.ts +461 -0
  77. package/src/validation/validators/index.ts +25 -0
  78. package/src/validation/validators/object-validation-engine.ts +722 -0
  79. package/tsconfig.tsbuildinfo +1 -1
  80. package/vitest.config.ts +2 -0
  81. package/src/builder/schema-builder.d.ts +0 -287
  82. package/src/builder/schema-builder.js +0 -505
  83. package/src/index.d.ts +0 -4
  84. package/src/index.js +0 -7
  85. package/src/registry/Registry.d.ts +0 -49
  86. package/src/registry/Registry.js +0 -36
  87. package/src/types/index.d.ts +0 -12
  88. package/src/types/index.js +0 -1
  89. package/src/validation/schema-validator.d.ts +0 -87
  90. package/src/validation/schema-validator.js +0 -280
@@ -0,0 +1,118 @@
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
+ });
@@ -0,0 +1,133 @@
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
+ * Filter Converter Utilities
11
+ *
12
+ * Shared utilities for converting MongoDB-like filter operators
13
+ * to ObjectStack FilterNode AST format.
14
+ */
15
+
16
+ /**
17
+ * FilterNode AST type definition
18
+ * Represents a filter condition or a logical combination of conditions
19
+ *
20
+ * @example
21
+ * // Simple condition
22
+ * ['status', '=', 'active']
23
+ *
24
+ * // Logical combination
25
+ * ['and', ['age', '>=', 18], ['status', '=', 'active']]
26
+ */
27
+ export type FilterNode =
28
+ | [string, string, any] // [field, operator, value]
29
+ | [string, ...FilterNode[]]; // [logic, ...conditions]
30
+
31
+ /**
32
+ * Map MongoDB-like operators to ObjectStack filter operators.
33
+ *
34
+ * @param operator - MongoDB-style operator (e.g., '$gte', '$in')
35
+ * @returns ObjectStack operator or null if not recognized
36
+ */
37
+ export function convertOperatorToAST(operator: string): string | null {
38
+ const operatorMap: Record<string, string> = {
39
+ '$eq': '=',
40
+ '$ne': '!=',
41
+ '$gt': '>',
42
+ '$gte': '>=',
43
+ '$lt': '<',
44
+ '$lte': '<=',
45
+ '$in': 'in',
46
+ '$nin': 'notin',
47
+ '$notin': 'notin',
48
+ '$contains': 'contains',
49
+ '$startswith': 'startswith',
50
+ '$between': 'between',
51
+ };
52
+
53
+ return operatorMap[operator] || null;
54
+ }
55
+
56
+ /**
57
+ * Convert object-based filters to ObjectStack FilterNode AST format.
58
+ * Converts MongoDB-like operators to ObjectStack filter expressions.
59
+ *
60
+ * @param filter - Object-based filter with optional operators
61
+ * @returns FilterNode AST array
62
+ *
63
+ * @example
64
+ * // Simple filter - converted to AST
65
+ * convertFiltersToAST({ status: 'active' })
66
+ * // => ['status', '=', 'active']
67
+ *
68
+ * @example
69
+ * // Complex filter with operators
70
+ * convertFiltersToAST({ age: { $gte: 18 } })
71
+ * // => ['age', '>=', 18]
72
+ *
73
+ * @example
74
+ * // Multiple conditions
75
+ * convertFiltersToAST({ age: { $gte: 18, $lte: 65 }, status: 'active' })
76
+ * // => ['and', ['age', '>=', 18], ['age', '<=', 65], ['status', '=', 'active']]
77
+ *
78
+ * @throws {Error} If an unknown operator is encountered
79
+ */
80
+ export function convertFiltersToAST(filter: Record<string, any>): FilterNode | Record<string, any> {
81
+ const conditions: FilterNode[] = [];
82
+
83
+ for (const [field, value] of Object.entries(filter)) {
84
+ if (value === null || value === undefined) continue;
85
+
86
+ // Check if value is a complex operator object
87
+ if (typeof value === 'object' && !Array.isArray(value)) {
88
+ // Handle operator-based filters
89
+ for (const [operator, operatorValue] of Object.entries(value)) {
90
+ // Special handling for $regex - warn users about limited support
91
+ if (operator === '$regex') {
92
+ console.warn(
93
+ `[ObjectUI] Warning: $regex operator is not fully supported. ` +
94
+ `Converting to 'contains' which only supports substring matching, not regex patterns. ` +
95
+ `Field: '${field}', Value: ${JSON.stringify(operatorValue)}. ` +
96
+ `Consider using $contains or $startswith instead.`
97
+ );
98
+ conditions.push([field, 'contains', operatorValue]);
99
+ continue;
100
+ }
101
+
102
+ const astOperator = convertOperatorToAST(operator);
103
+
104
+ if (astOperator) {
105
+ conditions.push([field, astOperator, operatorValue]);
106
+ } else {
107
+ // Unknown operator - throw error to avoid silent failure
108
+ throw new Error(
109
+ `[ObjectUI] Unknown filter operator '${operator}' for field '${field}'. ` +
110
+ `Supported operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $contains, $startswith, $between. ` +
111
+ `If you need exact object matching, use the value directly without an operator.`
112
+ );
113
+ }
114
+ }
115
+ } else {
116
+ // Simple equality filter
117
+ conditions.push([field, '=', value]);
118
+ }
119
+ }
120
+
121
+ // If no conditions, return original filter
122
+ if (conditions.length === 0) {
123
+ return filter;
124
+ }
125
+
126
+ // If only one condition, return it directly
127
+ if (conditions.length === 1) {
128
+ return conditions[0];
129
+ }
130
+
131
+ // Multiple conditions: combine with 'and'
132
+ return ['and', ...conditions];
133
+ }