UNPKG

@mindmakr/gs-websdk

Version:

Web SDK for Guru SaaS System - Complete JavaScript/TypeScript SDK for building applications with dynamic schema management

438 lines 16.7 kB
"use strict"; /** * Schema Helper Utilities * Provides utility functions for working with JSON schemas and form generation */ Object.defineProperty(exports, "__esModule", { value: true }); exports.cleanSchemaForStorage = exports.mergeSchemas = exports.getFieldTypeFromSchema = exports.isFieldRequired = exports.getFieldConfig = exports.extractFieldKeys = exports.validateFormData = exports.generateDefaultFormData = exports.convertSchemaToFormConfig = exports.createSchemaWithUISchema = exports.generateUISchemaFromJSONSchema = void 0; /** * Generate UI schema from JSON schema for better form rendering */ function generateUISchemaFromJSONSchema(jsonSchema) { const uiSchema = {}; if (jsonSchema.properties) { Object.entries(jsonSchema.properties).forEach(([key, value]) => { const fieldUISchema = {}; // Set widget based on format if (value.format === 'textarea') { fieldUISchema['ui:widget'] = 'textarea'; fieldUISchema['ui:options'] = { rows: 4 }; } else if (value.format === 'password') { fieldUISchema['ui:widget'] = 'password'; } else if (value.format === 'date') { fieldUISchema['ui:widget'] = 'date'; } else if (value.format === 'datetime') { fieldUISchema['ui:widget'] = 'datetime'; } else if (value.format === 'email') { fieldUISchema['ui:widget'] = 'email'; } else if (value.format === 'color') { fieldUISchema['ui:widget'] = 'color'; } else if (value.format === 'richtext') { fieldUISchema['ui:widget'] = 'richtext'; } else if (value.format === 'image-url-or-upload') { fieldUISchema['ui:widget'] = 'ImageInputWidget'; } else if (value.format === 'video-url-or-upload') { fieldUISchema['ui:widget'] = 'VideoInputWidget'; } else if (value.format === 'audio-url-or-upload') { fieldUISchema['ui:widget'] = 'AudioInputWidget'; } else if (value.format === 'reference') { fieldUISchema['ui:widget'] = 'ReferenceFieldWidget'; } // Set widget for arrays and selection types if (value.type === 'array' && value.items?.enum) { fieldUISchema['ui:widget'] = 'checkboxes'; } else if (value.enum && value['ui:widget'] === 'radio') { fieldUISchema['ui:widget'] = 'radio'; } // Add placeholder if available if (value.description) { fieldUISchema['ui:placeholder'] = value.description; } // Add help text if (value.helpText) { fieldUISchema['ui:help'] = value.helpText; } // Layout information if (value['ui:layout'] || value.layout) { const layout = value['ui:layout'] || value.layout; fieldUISchema['ui:layout'] = layout; } // Add custom options if (value['ui:options']) { fieldUISchema['ui:options'] = value['ui:options']; } // Only add if we have UI-specific properties if (Object.keys(fieldUISchema).length > 0) { uiSchema[key] = fieldUISchema; } // Handle nested objects if (value.type === 'object' && value.properties) { uiSchema[key] = { ...fieldUISchema, ...generateUISchemaFromJSONSchema(value) }; } }); } // Add field ordering if available if (jsonSchema['x-field-order']) { uiSchema['ui:order'] = jsonSchema['x-field-order']; } return uiSchema; } exports.generateUISchemaFromJSONSchema = generateUISchemaFromJSONSchema; /** * Create schema with UI schema information embedded */ function createSchemaWithUISchema(jsonSchema, uiSchema) { const enhancedSchema = JSON.parse(JSON.stringify(jsonSchema)); // Deep clone if (enhancedSchema.properties && uiSchema) { Object.entries(uiSchema).forEach(([key, uiConfig]) => { if (enhancedSchema.properties[key] && typeof uiConfig === 'object') { // Merge UI config into the property enhancedSchema.properties[key] = { ...enhancedSchema.properties[key], ...Object.fromEntries(Object.entries(uiConfig).filter(([k]) => k.startsWith('ui:'))) }; } }); } return enhancedSchema; } exports.createSchemaWithUISchema = createSchemaWithUISchema; /** * Convert schema template to a format suitable for form rendering */ function convertSchemaToFormConfig(template) { const schema = template.schema_definition; const uiSchema = generateUISchemaFromJSONSchema(schema); // Generate default form data const formData = generateDefaultFormData(schema); return { schema, uiSchema, formData }; } exports.convertSchemaToFormConfig = convertSchemaToFormConfig; /** * Generate default form data from schema */ function generateDefaultFormData(schema) { const formData = {}; if (schema.properties) { Object.entries(schema.properties).forEach(([key, value]) => { if (value.default !== undefined) { formData[key] = value.default; } else if (value.type === 'boolean') { formData[key] = false; } else if (value.type === 'array') { formData[key] = []; } else if (value.type === 'object') { formData[key] = value.properties ? generateDefaultFormData(value) : {}; } }); } return formData; } exports.generateDefaultFormData = generateDefaultFormData; /** * Validate form data against schema */ function validateFormData(formData, schema) { const errors = []; if (!schema.properties) { return { isValid: true, errors: [] }; } // Check required fields if (schema.required && Array.isArray(schema.required)) { schema.required.forEach((field) => { if (!formData[field] || formData[field] === '') { errors.push({ field, message: `${field} is required`, type: 'required' }); } }); } // Validate field types and constraints Object.entries(schema.properties).forEach(([field, fieldSchema]) => { const value = formData[field]; if (value !== undefined && value !== null && value !== '') { // Type validation if (fieldSchema.type === 'string' && typeof value !== 'string') { errors.push({ field, message: `${field} must be a string`, type: 'type' }); } else if (fieldSchema.type === 'number' && typeof value !== 'number') { errors.push({ field, message: `${field} must be a number`, type: 'type' }); } else if (fieldSchema.type === 'boolean' && typeof value !== 'boolean') { errors.push({ field, message: `${field} must be a boolean`, type: 'type' }); } else if (fieldSchema.type === 'array' && !Array.isArray(value)) { errors.push({ field, message: `${field} must be an array`, type: 'type' }); } // String constraints if (fieldSchema.type === 'string' && typeof value === 'string') { if (fieldSchema.minLength && value.length < fieldSchema.minLength) { errors.push({ field, message: `${field} must be at least ${fieldSchema.minLength} characters`, type: 'minLength' }); } if (fieldSchema.maxLength && value.length > fieldSchema.maxLength) { errors.push({ field, message: `${field} must be no more than ${fieldSchema.maxLength} characters`, type: 'maxLength' }); } if (fieldSchema.pattern && !new RegExp(fieldSchema.pattern).test(value)) { errors.push({ field, message: `${field} format is invalid`, type: 'pattern' }); } } // Number constraints if (fieldSchema.type === 'number' && typeof value === 'number') { if (fieldSchema.minimum !== undefined && value < fieldSchema.minimum) { errors.push({ field, message: `${field} must be at least ${fieldSchema.minimum}`, type: 'minimum' }); } if (fieldSchema.maximum !== undefined && value > fieldSchema.maximum) { errors.push({ field, message: `${field} must be no more than ${fieldSchema.maximum}`, type: 'maximum' }); } } // Array constraints if (fieldSchema.type === 'array' && Array.isArray(value)) { if (fieldSchema.minItems && value.length < fieldSchema.minItems) { errors.push({ field, message: `${field} must have at least ${fieldSchema.minItems} items`, type: 'minItems' }); } if (fieldSchema.maxItems && value.length > fieldSchema.maxItems) { errors.push({ field, message: `${field} must have no more than ${fieldSchema.maxItems} items`, type: 'maxItems' }); } } // Enum validation if (fieldSchema.enum && !fieldSchema.enum.includes(value)) { errors.push({ field, message: `${field} must be one of: ${fieldSchema.enum.join(', ')}`, type: 'enum' }); } // Format validation if (fieldSchema.format && typeof value === 'string') { const formatValidators = { email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, url: /^https?:\/\/.+/, date: /^\d{4}-\d{2}-\d{2}$/, time: /^\d{2}:\d{2}$/, 'date-time': /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/, phone: /^[\+]?[\d\s\-\(\)]+$/ }; const validator = formatValidators[fieldSchema.format]; if (validator && !validator.test(value)) { errors.push({ field, message: `${field} must be a valid ${fieldSchema.format}`, type: 'format' }); } } } }); return { isValid: errors.length === 0, errors }; } exports.validateFormData = validateFormData; /** * Extract field keys from schema in order */ function extractFieldKeys(schema) { if (!schema.properties) { return []; } // Use custom field order if available if (schema['x-field-order'] && Array.isArray(schema['x-field-order'])) { return schema['x-field-order']; } // Otherwise, use the order from properties return Object.keys(schema.properties); } exports.extractFieldKeys = extractFieldKeys; /** * Get field configuration from schema */ function getFieldConfig(schema, fieldKey) { if (!schema.properties || !schema.properties[fieldKey]) { return null; } return schema.properties[fieldKey]; } exports.getFieldConfig = getFieldConfig; /** * Check if field is required */ function isFieldRequired(schema, fieldKey) { return schema.required && Array.isArray(schema.required) && schema.required.includes(fieldKey); } exports.isFieldRequired = isFieldRequired; /** * Generate field type from JSON schema property */ function getFieldTypeFromSchema(fieldSchema) { if (fieldSchema.enum) return 'select'; if (fieldSchema.format === 'textarea') return 'textarea'; if (fieldSchema.format === 'email') return 'email'; if (fieldSchema.format === 'password') return 'password'; if (fieldSchema.format === 'date') return 'date'; if (fieldSchema.format === 'datetime') return 'datetime'; if (fieldSchema.format === 'time') return 'time'; if (fieldSchema.format === 'url') return 'url'; if (fieldSchema.format === 'color') return 'color'; if (fieldSchema.format === 'richtext') return 'richtext'; if (fieldSchema.format === 'image-url-or-upload') return 'image'; if (fieldSchema.format === 'video-url-or-upload') return 'video'; if (fieldSchema.format === 'audio-url-or-upload') return 'audio'; if (fieldSchema.format === 'reference') return 'reference'; if (fieldSchema.type === 'boolean') return 'boolean'; if (fieldSchema.type === 'number' || fieldSchema.type === 'integer') return 'number'; if (fieldSchema.type === 'array' && fieldSchema.items?.enum) return 'multiselect'; if (fieldSchema.type === 'array') return 'array'; if (fieldSchema.type === 'object') return 'object'; return 'text'; } exports.getFieldTypeFromSchema = getFieldTypeFromSchema; /** * Merge schemas (useful for composition) */ function mergeSchemas(baseSchema, extendingSchema, strategy = 'merge') { const merged = JSON.parse(JSON.stringify(baseSchema)); // Deep clone if (extendingSchema.properties) { merged.properties = merged.properties || {}; if (strategy === 'override') { Object.assign(merged.properties, extendingSchema.properties); } else { // Merge strategy Object.entries(extendingSchema.properties).forEach(([key, value]) => { if (merged.properties[key] && typeof merged.properties[key] === 'object' && typeof value === 'object') { // Deep merge for objects merged.properties[key] = { ...merged.properties[key], ...value }; } else { merged.properties[key] = value; } }); } } // Merge other properties ['title', 'description', 'required'].forEach(prop => { if (extendingSchema[prop]) { if (prop === 'required' && Array.isArray(extendingSchema[prop])) { merged[prop] = [...(merged[prop] || []), ...extendingSchema[prop]]; // Remove duplicates merged[prop] = [...new Set(merged[prop])]; } else { merged[prop] = extendingSchema[prop]; } } }); return merged; } exports.mergeSchemas = mergeSchemas; /** * Clean schema by removing UI-specific properties for storage */ function cleanSchemaForStorage(schema) { const cleaned = JSON.parse(JSON.stringify(schema)); // Deep clone function removeUIProperties(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } if (Array.isArray(obj)) { return obj.map(removeUIProperties); } const cleanedObj = {}; Object.entries(obj).forEach(([key, value]) => { if (!key.startsWith('ui:') && key !== 'layout') { cleanedObj[key] = removeUIProperties(value); } }); return cleanedObj; } return removeUIProperties(cleaned); } exports.cleanSchemaForStorage = cleanSchemaForStorage; //# sourceMappingURL=schema-helpers.js.map