UNPKG

node-apis

Version:

🚀 Advanced TypeScript API generator with clean architecture, comprehensive testing, and automatic formatting. Generate production-ready Node.js APIs with complete integration test suites.

306 lines (294 loc) • 12.1 kB
"use strict"; /** * Typed CRUD validator templates with automatic field detection */ Object.defineProperty(exports, "__esModule", { value: true }); exports.generateCrudValidatorContent = exports.getCrudValidatorFileNames = void 0; const naming_utils_1 = require("../shared/utils/naming.utils"); /** * Gets the list of CRUD validator file names for a module */ const getCrudValidatorFileNames = ({ moduleName }) => { const naming = (0, naming_utils_1.getModuleNaming)(moduleName); return [ `create.${naming.file}.ts`, `get.${naming.file}.ts`, `list.${naming.file}.ts`, `delete.${naming.file}.ts`, `update.${naming.file}.ts`, ]; }; exports.getCrudValidatorFileNames = getCrudValidatorFileNames; /** * Generates TypeScript validator file content for CRUD operations with parsed types */ const generateCrudValidatorContent = ({ operation, moduleName, parsedType, }) => { const naming = (0, naming_utils_1.getModuleNaming)(moduleName); const capitalizedOperation = operation.charAt(0).toUpperCase() + operation.slice(1); // Generate operation-specific validator content switch (operation) { case 'create': return generateTypedCreateValidatorContent(naming, capitalizedOperation, parsedType); case 'get': return generateTypedGetValidatorContent(naming, capitalizedOperation, parsedType); case 'list': return generateTypedListValidatorContent(naming, capitalizedOperation, parsedType); case 'update': return generateTypedUpdateValidatorContent(naming, capitalizedOperation, parsedType); case 'delete': return generateTypedDeleteValidatorContent(naming, capitalizedOperation, parsedType); default: return generateGenericValidatorContent(naming, capitalizedOperation, operation, parsedType); } }; exports.generateCrudValidatorContent = generateCrudValidatorContent; /** * Converts TypeScript type to Zod validation schema */ const typeToZodSchema = (field) => { const { type, optional } = field; const fieldName = field.name.toLowerCase(); let baseSchema; // Field name-based intelligent validation if (fieldName.includes('email')) { baseSchema = 'z.string().email().min(3).max(255)'; } else if (fieldName.includes('url') || fieldName.includes('link')) { baseSchema = 'z.string().url().min(3).max(2048)'; } else if (fieldName.includes('phone') || fieldName.includes('mobile')) { baseSchema = 'z.string().min(10).max(20)'; } else if ((fieldName.includes('id') || fieldName.endsWith('id')) && type === 'string') { baseSchema = 'z.string().min(1).max(255)'; // Customize based on your ID format (UUID, nanoid, etc.) } else if (fieldName.includes('name') || fieldName.includes('title')) { baseSchema = 'z.string().min(1).max(255)'; } else if (fieldName.includes('description') || fieldName.includes('content') || fieldName.includes('text')) { baseSchema = 'z.string().min(1).max(5000)'; } else if (fieldName.includes('code') || fieldName.includes('slug') || fieldName.includes('key')) { baseSchema = 'z.string().min(1).max(100)'; } else if (fieldName.includes('password')) { baseSchema = 'z.string().min(8).max(255)'; } else if (fieldName.includes('age')) { baseSchema = 'z.number().int().min(0).max(150)'; } else if (fieldName.includes('price') || fieldName.includes('amount') || fieldName.includes('cost')) { baseSchema = 'z.number().min(0).max(999999999)'; } else if (fieldName.includes('quantity') || fieldName.includes('count')) { baseSchema = 'z.number().int().min(0).max(999999999)'; } else if (type === 'string') { baseSchema = 'z.string().min(1).max(500)'; // Customize length limits as needed } else if (type === 'number') { baseSchema = 'z.number().min(-999999999).max(999999999)'; // Adjust range as needed } else if (type === 'boolean') { baseSchema = 'z.boolean()'; } else if (type === 'Date') { baseSchema = 'z.date()'; } else if (type.includes('[]')) { // Array types const arrayType = type.replace('[]', ''); if (arrayType === 'string') { baseSchema = 'z.array(z.string().min(1).max(500)).min(0).max(1000)'; // Customize array and element limits } else if (arrayType === 'number') { baseSchema = 'z.array(z.number().min(-999999999).max(999999999)).min(0).max(1000)'; } else { baseSchema = 'z.array(z.any()).min(0).max(1000)'; } } else if (type.includes('|')) { // Union types baseSchema = 'z.string().min(1).max(500)'; } else { // Fallback for complex types baseSchema = 'z.any()'; } return optional ? `${baseSchema}.optional()` : baseSchema; }; /** * Finds the ID field in the parsed type (either 'id' or '${moduleName}Id') */ const findIdField = (parsedType, moduleName) => { const moduleIdField = `${moduleName}Id`; // Prefer module-specific ID field if it exists if (parsedType.fields.some(f => f.name === moduleIdField)) { return moduleIdField; } // Fallback to generic 'id' field if (parsedType.fields.some(f => f.name === 'id')) { return 'id'; } return null; }; /** * Generates CREATE validator content with parsed types */ const generateTypedCreateValidatorContent = (naming, _capitalizedOperation, parsedType) => { const zodFields = parsedType.fields.length > 0 ? parsedType.fields.map(field => ` ${field.name}: ${typeToZodSchema(field)},`).join('\n') : ' // No fields defined in typePayload'; return `import { z } from 'zod'; import type { typePayload } from '../types/create.${naming.file}'; export const payloadSchema = z.object({ ${zodFields} }); export const validatePayload = (data: unknown): { success: true; data: typePayload } | { success: false; error: z.ZodError } => { const result = payloadSchema.safeParse(data); if (result.success) { return { success: true, data: result.data as typePayload }; } return { success: false, error: result.error }; }; `; }; /** * Generates GET validator content with parsed types */ const generateTypedGetValidatorContent = (naming, _capitalizedOperation, parsedType) => { // Generate validation for all fields in the typePayload const zodFields = parsedType.fields.length > 0 ? parsedType.fields.map(field => ` ${field.name}: ${typeToZodSchema(field)},`).join('\n') : ` // No fields defined in typePayload`; return `import { z } from 'zod'; import type { typePayload } from '../types/get.${naming.file}'; export const payloadSchema = z.object({ ${zodFields} }); export const validatePayload = (data: unknown): { success: true; data: typePayload } | { success: false; error: z.ZodError } => { const result = payloadSchema.safeParse(data); if (result.success) { return { success: true, data: result.data as typePayload }; } return { success: false, error: result.error }; }; `; }; /** * Generates LIST validator content with parsed types */ const generateTypedListValidatorContent = (naming, _capitalizedOperation, parsedType) => { // List operations typically have pagination + filtering fields const hasCustomFilters = parsedType.fields.some(f => !['page', 'limit', 'sort_by', 'sort_order', 'search'].includes(f.name)); let zodFields = ` page: z.number().int().positive().min(1).max(1000000).optional(), limit: z.number().int().positive().min(1).max(100).optional(), sort_by: z.string().min(1).max(100).optional(), sort_order: z.enum(['asc', 'desc']).optional(), search: z.string().min(1).max(500).optional(),`; if (hasCustomFilters) { const filterFields = parsedType.fields .filter(field => !['page', 'limit', 'sort_by', 'sort_order', 'search'].includes(field.name)) .map(field => ` ${field.name}: ${typeToZodSchema(field)},`) .join('\n'); if (filterFields) { zodFields += '\n' + filterFields; } } return `import { z } from 'zod'; import type { typePayload } from '../types/list.${naming.file}'; export const payloadSchema = z.object({ ${zodFields} }); export const validatePayload = (data: unknown): { success: true; data: typePayload } | { success: false; error: z.ZodError } => { const result = payloadSchema.safeParse(data); if (result.success) { return { success: true, data: result.data as typePayload }; } return { success: false, error: result.error }; }; `; }; /** * Generates UPDATE validator content with parsed types */ const generateTypedUpdateValidatorContent = (naming, _capitalizedOperation, parsedType) => { const idField = findIdField(parsedType, naming.variable); // Generate validation for all fields, making non-ID fields optional for partial updates const zodFields = parsedType.fields.length > 0 ? parsedType.fields .map(field => { if (field.name === idField) { // ID field is required return ` ${field.name}: ${typeToZodSchema(field)},`; } else { // Make other fields optional for partial updates const schema = typeToZodSchema(field); const optionalSchema = schema.includes('.optional()') ? schema : `${schema}.optional()`; return ` ${field.name}: ${optionalSchema},`; } }) .join('\n') : ` // No fields defined in typePayload`; return `import { z } from 'zod'; import type { typePayload } from '../types/update.${naming.file}'; export const payloadSchema = z.object({ ${zodFields} }); export const validatePayload = (data: unknown): { success: true; data: typePayload } | { success: false; error: z.ZodError } => { const result = payloadSchema.safeParse(data); if (result.success) { return { success: true, data: result.data as typePayload }; } return { success: false, error: result.error }; }; `; }; /** * Generates DELETE validator content with parsed types */ const generateTypedDeleteValidatorContent = (naming, _capitalizedOperation, parsedType) => { // Generate validation for all fields in the typePayload const zodFields = parsedType.fields.length > 0 ? parsedType.fields.map(field => ` ${field.name}: ${typeToZodSchema(field)},`).join('\n') : ` // No fields defined in typePayload`; return `import { z } from 'zod'; import type { typePayload } from '../types/delete.${naming.file}'; export const payloadSchema = z.object({ ${zodFields} }); export const validatePayload = (data: unknown): { success: true; data: typePayload } | { success: false; error: z.ZodError } => { const result = payloadSchema.safeParse(data); if (result.success) { return { success: true, data: result.data as typePayload }; } return { success: false, error: result.error }; }; `; }; /** * Generates generic validator content with parsed types (fallback) */ const generateGenericValidatorContent = (naming, _capitalizedOperation, operation, parsedType) => { const zodFields = parsedType.fields.length > 0 ? parsedType.fields.map(field => ` ${field.name}: ${typeToZodSchema(field)},`).join('\n') : ` // Define validation rules for ${operation} ${naming.variable}`; return `import { z } from 'zod'; import type { typePayload } from '../types/${operation}.${naming.file}'; export const payloadSchema = z.object({ ${zodFields} }); export const validatePayload = (data: unknown): { success: true; data: typePayload } | { success: false; error: z.ZodError } => { const result = payloadSchema.safeParse(data); if (result.success) { return { success: true, data: result.data as typePayload }; } return { success: false, error: result.error }; }; `; }; //# sourceMappingURL=typed-crud.validators.js.map