@object-ui/core 0.3.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.
@@ -0,0 +1,77 @@
1
+ import type { SchemaNode } from '../types';
2
+
3
+ export type ComponentRenderer<T = any> = T;
4
+
5
+ export type ComponentInput = {
6
+ name: string;
7
+ type: 'string' | 'number' | 'boolean' | 'enum' | 'array' | 'object' | 'color' | 'date' | 'code' | 'file' | 'slot';
8
+ label?: string;
9
+ defaultValue?: any;
10
+ required?: boolean;
11
+ enum?: string[] | { label: string; value: any }[];
12
+ description?: string;
13
+ advanced?: boolean;
14
+ inputType?: string;
15
+ };
16
+
17
+ export type ComponentMeta = {
18
+ label?: string; // Display name in designer
19
+ icon?: string; // Icon name or svg string
20
+ category?: string; // Grouping category
21
+ inputs?: ComponentInput[];
22
+ defaultProps?: Record<string, any>; // Default props when dropped
23
+ defaultChildren?: SchemaNode[]; // Default children when dropped
24
+ examples?: Record<string, any>; // Example configurations
25
+ isContainer?: boolean; // Whether the component can have children
26
+ resizable?: boolean; // Whether the component can be resized in the designer
27
+ resizeConstraints?: {
28
+ width?: boolean;
29
+ height?: boolean;
30
+ minWidth?: number;
31
+ maxWidth?: number;
32
+ minHeight?: number;
33
+ maxHeight?: number;
34
+ };
35
+ };
36
+
37
+ export type ComponentConfig<T = any> = ComponentMeta & {
38
+ type: string;
39
+ component: ComponentRenderer<T>;
40
+ };
41
+
42
+ export class Registry<T = any> {
43
+ private components = new Map<string, ComponentConfig<T>>();
44
+
45
+ register(type: string, component: ComponentRenderer<T>, meta?: ComponentMeta) {
46
+ if (this.components.has(type)) {
47
+ console.warn(`Component type "${type}" is already registered. Overwriting.`);
48
+ }
49
+ this.components.set(type, {
50
+ type,
51
+ component,
52
+ ...meta
53
+ });
54
+ }
55
+
56
+ get(type: string): ComponentRenderer<T> | undefined {
57
+ return this.components.get(type)?.component;
58
+ }
59
+
60
+ getConfig(type: string): ComponentConfig<T> | undefined {
61
+ return this.components.get(type);
62
+ }
63
+
64
+ has(type: string): boolean {
65
+ return this.components.has(type);
66
+ }
67
+
68
+ getAllTypes(): string[] {
69
+ return Array.from(this.components.keys());
70
+ }
71
+
72
+ getAllConfigs(): ComponentConfig<T>[] {
73
+ return Array.from(this.components.values());
74
+ }
75
+ }
76
+
77
+ export const ComponentRegistry = new Registry<any>();
@@ -0,0 +1,12 @@
1
+ export interface SchemaNode {
2
+ type: string;
3
+ id?: string;
4
+ className?: string;
5
+ data?: any;
6
+ body?: SchemaNode | SchemaNode[];
7
+ [key: string]: any;
8
+ }
9
+ export interface ComponentRendererProps {
10
+ schema: SchemaNode;
11
+ [key: string]: any;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ export interface SchemaNode {
2
+ type: string;
3
+ id?: string;
4
+ className?: string;
5
+ data?: any;
6
+ body?: SchemaNode | SchemaNode[];
7
+ [key: string]: any;
8
+ }
9
+
10
+ export interface ComponentRendererProps {
11
+ schema: SchemaNode;
12
+ [key: string]: any;
13
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @object-ui/core - Schema Validation
3
+ *
4
+ * Runtime validation utilities for Object UI schemas.
5
+ * These utilities help ensure schemas are valid before rendering.
6
+ *
7
+ * @module validation
8
+ * @packageDocumentation
9
+ */
10
+ import type { BaseSchema } from '@object-ui/types';
11
+ /**
12
+ * Validation error details
13
+ */
14
+ export interface ValidationError {
15
+ path: string;
16
+ message: string;
17
+ type: 'error' | 'warning';
18
+ code?: string;
19
+ }
20
+ /**
21
+ * Validation result
22
+ */
23
+ export interface ValidationResult {
24
+ valid: boolean;
25
+ errors: ValidationError[];
26
+ warnings: ValidationError[];
27
+ }
28
+ /**
29
+ * Validate a complete schema
30
+ *
31
+ * @param schema - The schema to validate
32
+ * @param options - Validation options
33
+ * @returns Validation result with errors and warnings
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const result = validateSchema({
38
+ * type: 'form',
39
+ * fields: [
40
+ * { name: 'email', type: 'input' }
41
+ * ]
42
+ * });
43
+ *
44
+ * if (!result.valid) {
45
+ * console.error('Validation errors:', result.errors);
46
+ * }
47
+ * ```
48
+ */
49
+ export declare function validateSchema(schema: any, path?: string): ValidationResult;
50
+ /**
51
+ * Assert that a schema is valid, throwing an error if not
52
+ *
53
+ * @param schema - The schema to validate
54
+ * @throws Error if schema is invalid
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * try {
59
+ * assertValidSchema(schema);
60
+ * // Schema is valid, continue rendering
61
+ * } catch (error) {
62
+ * console.error('Invalid schema:', error.message);
63
+ * }
64
+ * ```
65
+ */
66
+ export declare function assertValidSchema(schema: any): asserts schema is BaseSchema;
67
+ /**
68
+ * Check if a value is a valid schema
69
+ *
70
+ * @param value - The value to check
71
+ * @returns True if the value is a valid schema
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * if (isValidSchema(data)) {
76
+ * renderSchema(data);
77
+ * }
78
+ * ```
79
+ */
80
+ export declare function isValidSchema(value: any): value is BaseSchema;
81
+ /**
82
+ * Get a human-readable error summary
83
+ *
84
+ * @param result - The validation result
85
+ * @returns Formatted error summary
86
+ */
87
+ export declare function formatValidationErrors(result: ValidationResult): string;
@@ -0,0 +1,280 @@
1
+ /**
2
+ * @object-ui/core - Schema Validation
3
+ *
4
+ * Runtime validation utilities for Object UI schemas.
5
+ * These utilities help ensure schemas are valid before rendering.
6
+ *
7
+ * @module validation
8
+ * @packageDocumentation
9
+ */
10
+ /**
11
+ * Validation rules for base schema
12
+ */
13
+ const BASE_SCHEMA_RULES = {
14
+ type: {
15
+ required: true,
16
+ validate: (value) => typeof value === 'string' && value.length > 0,
17
+ message: 'type must be a non-empty string'
18
+ },
19
+ id: {
20
+ required: false,
21
+ validate: (value) => typeof value === 'string',
22
+ message: 'id must be a string'
23
+ },
24
+ className: {
25
+ required: false,
26
+ validate: (value) => typeof value === 'string',
27
+ message: 'className must be a string'
28
+ },
29
+ visible: {
30
+ required: false,
31
+ validate: (value) => typeof value === 'boolean',
32
+ message: 'visible must be a boolean'
33
+ },
34
+ disabled: {
35
+ required: false,
36
+ validate: (value) => typeof value === 'boolean',
37
+ message: 'disabled must be a boolean'
38
+ }
39
+ };
40
+ /**
41
+ * Validate a schema against base rules
42
+ */
43
+ function validateBaseSchema(schema, path = 'schema') {
44
+ const errors = [];
45
+ if (!schema || typeof schema !== 'object') {
46
+ errors.push({
47
+ path,
48
+ message: 'Schema must be an object',
49
+ type: 'error',
50
+ code: 'INVALID_SCHEMA'
51
+ });
52
+ return errors;
53
+ }
54
+ // Validate required and optional properties
55
+ Object.entries(BASE_SCHEMA_RULES).forEach(([key, rule]) => {
56
+ const value = schema[key];
57
+ if (rule.required && value === undefined) {
58
+ errors.push({
59
+ path: `${path}.${key}`,
60
+ message: `${key} is required`,
61
+ type: 'error',
62
+ code: 'MISSING_REQUIRED'
63
+ });
64
+ }
65
+ if (value !== undefined && !rule.validate(value)) {
66
+ errors.push({
67
+ path: `${path}.${key}`,
68
+ message: rule.message,
69
+ type: 'error',
70
+ code: 'INVALID_TYPE'
71
+ });
72
+ }
73
+ });
74
+ return errors;
75
+ }
76
+ /**
77
+ * Validate CRUD schema specific properties
78
+ */
79
+ function validateCRUDSchema(schema, path = 'schema') {
80
+ const errors = [];
81
+ if (schema.type === 'crud') {
82
+ // Check required properties for CRUD
83
+ if (!schema.columns || !Array.isArray(schema.columns)) {
84
+ errors.push({
85
+ path: `${path}.columns`,
86
+ message: 'CRUD schema requires columns array',
87
+ type: 'error',
88
+ code: 'MISSING_COLUMNS'
89
+ });
90
+ }
91
+ if (!schema.api && !schema.dataSource) {
92
+ errors.push({
93
+ path: `${path}.api`,
94
+ message: 'CRUD schema requires api or dataSource',
95
+ type: 'warning',
96
+ code: 'MISSING_DATA_SOURCE'
97
+ });
98
+ }
99
+ // Validate columns
100
+ if (schema.columns && Array.isArray(schema.columns)) {
101
+ schema.columns.forEach((column, index) => {
102
+ if (!column.name) {
103
+ errors.push({
104
+ path: `${path}.columns[${index}]`,
105
+ message: 'Column requires name property',
106
+ type: 'error',
107
+ code: 'MISSING_COLUMN_NAME'
108
+ });
109
+ }
110
+ });
111
+ }
112
+ // Validate fields if present
113
+ if (schema.fields && Array.isArray(schema.fields)) {
114
+ schema.fields.forEach((field, index) => {
115
+ if (!field.name) {
116
+ errors.push({
117
+ path: `${path}.fields[${index}]`,
118
+ message: 'Field requires name property',
119
+ type: 'error',
120
+ code: 'MISSING_FIELD_NAME'
121
+ });
122
+ }
123
+ });
124
+ }
125
+ }
126
+ return errors;
127
+ }
128
+ /**
129
+ * Validate form schema specific properties
130
+ */
131
+ function validateFormSchema(schema, path = 'schema') {
132
+ const errors = [];
133
+ if (schema.type === 'form') {
134
+ if (schema.fields && Array.isArray(schema.fields)) {
135
+ schema.fields.forEach((field, index) => {
136
+ if (!field.name) {
137
+ errors.push({
138
+ path: `${path}.fields[${index}]`,
139
+ message: 'Form field requires name property',
140
+ type: 'error',
141
+ code: 'MISSING_FIELD_NAME'
142
+ });
143
+ }
144
+ // Check for duplicate field names
145
+ const duplicates = schema.fields.filter((f) => f.name === field.name);
146
+ if (duplicates.length > 1) {
147
+ errors.push({
148
+ path: `${path}.fields[${index}]`,
149
+ message: `Duplicate field name: ${field.name}`,
150
+ type: 'warning',
151
+ code: 'DUPLICATE_FIELD_NAME'
152
+ });
153
+ }
154
+ });
155
+ }
156
+ }
157
+ return errors;
158
+ }
159
+ /**
160
+ * Validate child schemas recursively
161
+ */
162
+ function validateChildren(schema, path = 'schema') {
163
+ const errors = [];
164
+ const children = schema.children || schema.body;
165
+ if (children) {
166
+ if (Array.isArray(children)) {
167
+ children.forEach((child, index) => {
168
+ if (typeof child === 'object' && child !== null) {
169
+ const childResult = validateSchema(child, `${path}.children[${index}]`);
170
+ errors.push(...childResult.errors, ...childResult.warnings);
171
+ }
172
+ });
173
+ }
174
+ else if (typeof children === 'object' && children !== null) {
175
+ const childResult = validateSchema(children, `${path}.children`);
176
+ errors.push(...childResult.errors, ...childResult.warnings);
177
+ }
178
+ }
179
+ return errors;
180
+ }
181
+ /**
182
+ * Validate a complete schema
183
+ *
184
+ * @param schema - The schema to validate
185
+ * @param options - Validation options
186
+ * @returns Validation result with errors and warnings
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * const result = validateSchema({
191
+ * type: 'form',
192
+ * fields: [
193
+ * { name: 'email', type: 'input' }
194
+ * ]
195
+ * });
196
+ *
197
+ * if (!result.valid) {
198
+ * console.error('Validation errors:', result.errors);
199
+ * }
200
+ * ```
201
+ */
202
+ export function validateSchema(schema, path = 'schema') {
203
+ const allErrors = [];
204
+ // Validate base schema
205
+ allErrors.push(...validateBaseSchema(schema, path));
206
+ // Validate type-specific schemas
207
+ allErrors.push(...validateCRUDSchema(schema, path));
208
+ allErrors.push(...validateFormSchema(schema, path));
209
+ // Validate children recursively
210
+ allErrors.push(...validateChildren(schema, path));
211
+ const errors = allErrors.filter(e => e.type === 'error');
212
+ const warnings = allErrors.filter(e => e.type === 'warning');
213
+ return {
214
+ valid: errors.length === 0,
215
+ errors,
216
+ warnings
217
+ };
218
+ }
219
+ /**
220
+ * Assert that a schema is valid, throwing an error if not
221
+ *
222
+ * @param schema - The schema to validate
223
+ * @throws Error if schema is invalid
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * try {
228
+ * assertValidSchema(schema);
229
+ * // Schema is valid, continue rendering
230
+ * } catch (error) {
231
+ * console.error('Invalid schema:', error.message);
232
+ * }
233
+ * ```
234
+ */
235
+ export function assertValidSchema(schema) {
236
+ const result = validateSchema(schema);
237
+ if (!result.valid) {
238
+ const errorMessages = result.errors.map(e => `${e.path}: ${e.message}`).join('\n');
239
+ throw new Error(`Schema validation failed:\n${errorMessages}`);
240
+ }
241
+ }
242
+ /**
243
+ * Check if a value is a valid schema
244
+ *
245
+ * @param value - The value to check
246
+ * @returns True if the value is a valid schema
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * if (isValidSchema(data)) {
251
+ * renderSchema(data);
252
+ * }
253
+ * ```
254
+ */
255
+ export function isValidSchema(value) {
256
+ const result = validateSchema(value);
257
+ return result.valid;
258
+ }
259
+ /**
260
+ * Get a human-readable error summary
261
+ *
262
+ * @param result - The validation result
263
+ * @returns Formatted error summary
264
+ */
265
+ export function formatValidationErrors(result) {
266
+ const parts = [];
267
+ if (result.errors.length > 0) {
268
+ parts.push('Errors:');
269
+ result.errors.forEach(error => {
270
+ parts.push(` - ${error.path}: ${error.message}`);
271
+ });
272
+ }
273
+ if (result.warnings.length > 0) {
274
+ parts.push('Warnings:');
275
+ result.warnings.forEach(warning => {
276
+ parts.push(` - ${warning.path}: ${warning.message}`);
277
+ });
278
+ }
279
+ return parts.join('\n');
280
+ }