@object-ui/core 0.5.0 → 2.0.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/actions/ActionRunner.d.ts +228 -4
- package/dist/actions/ActionRunner.js +397 -45
- package/dist/actions/TransactionManager.d.ts +193 -0
- package/dist/actions/TransactionManager.js +410 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/adapters/ApiDataSource.d.ts +69 -0
- package/dist/adapters/ApiDataSource.js +293 -0
- package/dist/adapters/ValueDataSource.d.ts +55 -0
- package/dist/adapters/ValueDataSource.js +287 -0
- package/dist/adapters/index.d.ts +3 -0
- package/dist/adapters/index.js +5 -2
- package/dist/adapters/resolveDataSource.d.ts +40 -0
- package/dist/adapters/resolveDataSource.js +59 -0
- package/dist/data-scope/DataScopeManager.d.ts +127 -0
- package/dist/data-scope/DataScopeManager.js +229 -0
- package/dist/data-scope/index.d.ts +10 -0
- package/dist/data-scope/index.js +10 -0
- package/dist/evaluator/ExpressionEvaluator.d.ts +11 -1
- package/dist/evaluator/ExpressionEvaluator.js +32 -8
- package/dist/evaluator/FormulaFunctions.d.ts +58 -0
- package/dist/evaluator/FormulaFunctions.js +350 -0
- package/dist/evaluator/index.d.ts +1 -0
- package/dist/evaluator/index.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -2
- package/dist/query/query-ast.d.ts +2 -2
- package/dist/query/query-ast.js +3 -3
- package/dist/registry/Registry.d.ts +10 -0
- package/dist/registry/Registry.js +2 -1
- package/dist/registry/WidgetRegistry.d.ts +120 -0
- package/dist/registry/WidgetRegistry.js +275 -0
- package/dist/theme/ThemeEngine.d.ts +82 -0
- package/dist/theme/ThemeEngine.js +400 -0
- package/dist/theme/index.d.ts +8 -0
- package/dist/theme/index.js +8 -0
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/validation-engine.d.ts +19 -1
- package/dist/validation/validation-engine.js +67 -2
- package/dist/validation/validators/index.d.ts +1 -1
- package/dist/validation/validators/index.js +1 -1
- package/dist/validation/validators/object-validation-engine.d.ts +2 -2
- package/dist/validation/validators/object-validation-engine.js +1 -1
- package/package.json +4 -3
- package/src/actions/ActionRunner.ts +577 -55
- package/src/actions/TransactionManager.ts +521 -0
- package/src/actions/__tests__/ActionRunner.params.test.ts +134 -0
- package/src/actions/__tests__/ActionRunner.test.ts +711 -0
- package/src/actions/__tests__/TransactionManager.test.ts +447 -0
- package/src/actions/index.ts +1 -0
- package/src/adapters/ApiDataSource.ts +349 -0
- package/src/adapters/ValueDataSource.ts +332 -0
- package/src/adapters/__tests__/ApiDataSource.test.ts +418 -0
- package/src/adapters/__tests__/ValueDataSource.test.ts +325 -0
- package/src/adapters/__tests__/resolveDataSource.test.ts +144 -0
- package/src/adapters/index.ts +6 -1
- package/src/adapters/resolveDataSource.ts +79 -0
- package/src/builder/__tests__/schema-builder.test.ts +235 -0
- package/src/data-scope/DataScopeManager.ts +269 -0
- package/src/data-scope/__tests__/DataScopeManager.test.ts +211 -0
- package/src/data-scope/index.ts +16 -0
- package/src/evaluator/ExpressionEvaluator.ts +34 -8
- package/src/evaluator/FormulaFunctions.ts +398 -0
- package/src/evaluator/__tests__/ExpressionContext.test.ts +110 -0
- package/src/evaluator/__tests__/FormulaFunctions.test.ts +447 -0
- package/src/evaluator/index.ts +1 -0
- package/src/index.ts +4 -3
- package/src/query/__tests__/window-functions.test.ts +1 -1
- package/src/query/query-ast.ts +3 -3
- package/src/registry/Registry.ts +12 -1
- package/src/registry/WidgetRegistry.ts +316 -0
- package/src/registry/__tests__/WidgetRegistry.test.ts +321 -0
- package/src/theme/ThemeEngine.ts +452 -0
- package/src/theme/__tests__/ThemeEngine.test.ts +606 -0
- package/src/theme/index.ts +22 -0
- package/src/validation/__tests__/object-validation-engine.test.ts +1 -1
- package/src/validation/__tests__/schema-validator.test.ts +118 -0
- package/src/validation/index.ts +1 -1
- package/src/validation/validation-engine.ts +61 -2
- package/src/validation/validators/index.ts +1 -1
- package/src/validation/validators/object-validation-engine.ts +2 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
validateSchema,
|
|
4
|
+
assertValidSchema,
|
|
5
|
+
isValidSchema,
|
|
6
|
+
formatValidationErrors,
|
|
7
|
+
} from '../../validation/schema-validator';
|
|
8
|
+
|
|
9
|
+
describe('schema-validator', () => {
|
|
10
|
+
describe('validateSchema', () => {
|
|
11
|
+
it('validates a minimal valid schema', () => {
|
|
12
|
+
const result = validateSchema({ type: 'form' });
|
|
13
|
+
expect(result.valid).toBe(true);
|
|
14
|
+
expect(result.errors).toHaveLength(0);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('rejects schema without type', () => {
|
|
18
|
+
const result = validateSchema({} as any);
|
|
19
|
+
expect(result.valid).toBe(false);
|
|
20
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('validates CRUD schema with columns', () => {
|
|
24
|
+
const result = validateSchema({
|
|
25
|
+
type: 'crud',
|
|
26
|
+
columns: [{ name: 'id', label: 'ID' }],
|
|
27
|
+
api: '/api/users',
|
|
28
|
+
});
|
|
29
|
+
expect(result.valid).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('warns about CRUD without columns', () => {
|
|
33
|
+
const result = validateSchema({ type: 'crud' });
|
|
34
|
+
const hasColumnsIssue = [...result.errors, ...result.warnings].some(
|
|
35
|
+
(e) => e.message.toLowerCase().includes('column'),
|
|
36
|
+
);
|
|
37
|
+
expect(hasColumnsIssue).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('validates form with fields', () => {
|
|
41
|
+
const result = validateSchema({
|
|
42
|
+
type: 'form',
|
|
43
|
+
fields: [
|
|
44
|
+
{ name: 'email', label: 'Email', type: 'string' },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
expect(result.valid).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('detects duplicate field names in forms', () => {
|
|
51
|
+
const result = validateSchema({
|
|
52
|
+
type: 'form',
|
|
53
|
+
fields: [
|
|
54
|
+
{ name: 'email', label: 'Email', type: 'string' },
|
|
55
|
+
{ name: 'email', label: 'Email 2', type: 'string' },
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
const hasDuplicateWarning = [...result.errors, ...result.warnings].some(
|
|
59
|
+
(e) => e.message.toLowerCase().includes('duplicate'),
|
|
60
|
+
);
|
|
61
|
+
expect(hasDuplicateWarning).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('validates nested children', () => {
|
|
65
|
+
const result = validateSchema({
|
|
66
|
+
type: 'grid',
|
|
67
|
+
children: [
|
|
68
|
+
{ type: 'button', label: 'OK' },
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
expect(result.valid).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('isValidSchema', () => {
|
|
76
|
+
it('returns true for valid schema', () => {
|
|
77
|
+
expect(isValidSchema({ type: 'form' })).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('returns false for invalid schema', () => {
|
|
81
|
+
expect(isValidSchema({})).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('returns false for empty object', () => {
|
|
85
|
+
expect(isValidSchema({} as any)).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('returns false for non-object values', () => {
|
|
89
|
+
expect(isValidSchema('string' as any)).toBe(false);
|
|
90
|
+
expect(isValidSchema(42 as any)).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('assertValidSchema', () => {
|
|
95
|
+
it('does not throw for valid schema', () => {
|
|
96
|
+
expect(() => assertValidSchema({ type: 'form' })).not.toThrow();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('throws for invalid schema', () => {
|
|
100
|
+
expect(() => assertValidSchema({} as any)).toThrow();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('formatValidationErrors', () => {
|
|
105
|
+
it('formats validation errors', () => {
|
|
106
|
+
const result = validateSchema({} as any);
|
|
107
|
+
const formatted = formatValidationErrors(result);
|
|
108
|
+
expect(typeof formatted).toBe('string');
|
|
109
|
+
expect(formatted.length).toBeGreaterThan(0);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('returns empty string for valid schemas', () => {
|
|
113
|
+
const result = validateSchema({ type: 'form' });
|
|
114
|
+
const formatted = formatValidationErrors(result);
|
|
115
|
+
expect(formatted).toBe('');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
package/src/validation/index.ts
CHANGED
|
@@ -34,6 +34,39 @@ import type {
|
|
|
34
34
|
* Validation Engine - Executes validation rules
|
|
35
35
|
*/
|
|
36
36
|
export class ValidationEngine {
|
|
37
|
+
private customValidators = new Map<string, ValidationFunction>();
|
|
38
|
+
private customAsyncValidators = new Map<string, AsyncValidationFunction>();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Register a custom synchronous validator by name
|
|
42
|
+
*/
|
|
43
|
+
registerValidator(name: string, fn: ValidationFunction): void {
|
|
44
|
+
this.customValidators.set(name, fn);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Register a custom asynchronous validator by name
|
|
49
|
+
*/
|
|
50
|
+
registerAsyncValidator(name: string, fn: AsyncValidationFunction): void {
|
|
51
|
+
this.customAsyncValidators.set(name, fn);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if a custom validator is registered
|
|
56
|
+
*/
|
|
57
|
+
hasValidator(name: string): boolean {
|
|
58
|
+
return this.customValidators.has(name) || this.customAsyncValidators.has(name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get all registered custom validator names
|
|
63
|
+
*/
|
|
64
|
+
getValidatorNames(): string[] {
|
|
65
|
+
return [
|
|
66
|
+
...Array.from(this.customValidators.keys()),
|
|
67
|
+
...Array.from(this.customAsyncValidators.keys()),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
37
70
|
/**
|
|
38
71
|
* Validate a value against validation schema
|
|
39
72
|
*/
|
|
@@ -80,7 +113,7 @@ export class ValidationEngine {
|
|
|
80
113
|
rule: AdvancedValidationRule,
|
|
81
114
|
context?: ValidationContext
|
|
82
115
|
): Promise<string | null> {
|
|
83
|
-
// Custom async validator
|
|
116
|
+
// Custom async validator (inline)
|
|
84
117
|
if (rule.async_validator) {
|
|
85
118
|
const result = await rule.async_validator(value, context);
|
|
86
119
|
if (result === false) {
|
|
@@ -92,7 +125,7 @@ export class ValidationEngine {
|
|
|
92
125
|
return null;
|
|
93
126
|
}
|
|
94
127
|
|
|
95
|
-
// Custom sync validator
|
|
128
|
+
// Custom sync validator (inline)
|
|
96
129
|
if (rule.validator) {
|
|
97
130
|
const result = rule.validator(value, context);
|
|
98
131
|
if (result === false) {
|
|
@@ -104,6 +137,32 @@ export class ValidationEngine {
|
|
|
104
137
|
return null;
|
|
105
138
|
}
|
|
106
139
|
|
|
140
|
+
// Registered custom async validator (by name)
|
|
141
|
+
const registeredAsync = this.customAsyncValidators.get(rule.type);
|
|
142
|
+
if (registeredAsync) {
|
|
143
|
+
const result = await registeredAsync(value, context);
|
|
144
|
+
if (result === false) {
|
|
145
|
+
return rule.message || 'Async validation failed';
|
|
146
|
+
}
|
|
147
|
+
if (typeof result === 'string') {
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Registered custom sync validator (by name)
|
|
154
|
+
const registeredSync = this.customValidators.get(rule.type);
|
|
155
|
+
if (registeredSync) {
|
|
156
|
+
const result = registeredSync(value, context);
|
|
157
|
+
if (result === false) {
|
|
158
|
+
return rule.message || 'Validation failed';
|
|
159
|
+
}
|
|
160
|
+
if (typeof result === 'string') {
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
107
166
|
// Built-in validators
|
|
108
167
|
return this.validateBuiltInRule(value, rule, context);
|
|
109
168
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
/**
|
|
10
10
|
* @object-ui/core - Object-Level Validation Engine
|
|
11
11
|
*
|
|
12
|
-
* ObjectStack Spec
|
|
12
|
+
* ObjectStack Spec v2.0.1 compliant validation engine for object-level validation rules.
|
|
13
13
|
* Supports all 9 validation types from the specification:
|
|
14
14
|
* - ScriptValidation
|
|
15
15
|
* - UniquenessValidation
|
|
@@ -256,7 +256,7 @@ class SimpleExpressionEvaluator implements ValidationExpressionEvaluator {
|
|
|
256
256
|
|
|
257
257
|
/**
|
|
258
258
|
* Object-Level Validation Engine
|
|
259
|
-
* Implements ObjectStack Spec
|
|
259
|
+
* Implements ObjectStack Spec v2.0.1 validation framework
|
|
260
260
|
*/
|
|
261
261
|
export class ObjectValidationEngine {
|
|
262
262
|
private expressionEvaluator: ValidationExpressionEvaluator;
|