UNPKG

arela

Version:

AI-powered CTO with multi-agent orchestration, code summarization, visual testing (web + mobile) for blazing fast development.

276 lines 7.87 kB
/** * Generate OpenAPI spec for a set of endpoints */ export function generateOpenAPISpec(sliceName, endpoints, version = '1.0.0') { const spec = { openapi: '3.0.0', info: { title: `${formatTitle(sliceName)} API`, version, description: `API contract for ${sliceName} slice`, }, paths: {}, }; // Group endpoints by path const pathMap = new Map(); for (const endpoint of endpoints) { const path = endpoint.path; if (!pathMap.has(path)) { pathMap.set(path, []); } pathMap.get(path).push(endpoint); } // Generate path items for (const [path, pathEndpoints] of pathMap) { spec.paths[path] = {}; for (const endpoint of pathEndpoints) { const method = endpoint.method.toLowerCase(); spec.paths[path][method] = generateOperation(endpoint); } } return spec; } /** * Generate OpenAPI operation for an endpoint */ function generateOperation(endpoint) { const operation = { summary: generateSummary(endpoint), description: `Endpoint defined at line ${endpoint.lineNumber || 'unknown'}`, parameters: extractParameters(endpoint.path), responses: { '200': { description: 'Successful response', content: { 'application/json': { schema: { type: 'object', additionalProperties: true, }, }, }, }, '400': { description: 'Bad Request', }, '404': { description: 'Not Found', }, '500': { description: 'Internal Server Error', }, }, }; // Add request body for POST/PUT/PATCH if (['POST', 'PUT', 'PATCH'].includes(endpoint.method)) { operation.requestBody = { content: { 'application/json': { schema: { type: 'object', additionalProperties: true, }, }, }, }; } return operation; } /** * Generate operation summary from endpoint */ function generateSummary(endpoint) { const method = endpoint.method.toLowerCase(); const parts = endpoint.path.split('/').filter(p => p && !p.startsWith(':')); let action = 'Handle'; switch (method) { case 'get': action = parts.some(p => p.match(/^\w+$/) && p.length > 1) ? `Get ${parts[parts.length - 1]}` : 'Get'; break; case 'post': action = `Create ${parts[parts.length - 1]}`; break; case 'put': action = `Update ${parts[parts.length - 1]}`; break; case 'patch': action = `Partial update ${parts[parts.length - 1]}`; break; case 'delete': action = `Delete ${parts[parts.length - 1]}`; break; } return action; } /** * Extract path parameters from endpoint path */ function extractParameters(path) { const parameters = []; const paramRegex = /:([a-zA-Z_][a-zA-Z0-9_]*)/g; let match; while ((match = paramRegex.exec(path)) !== null) { const paramName = match[1]; // Determine parameter type let paramType = 'string'; if (paramName === 'id' || paramName === 'userId' || paramName === 'workoutId') { paramType = 'string'; } parameters.push({ name: paramName, in: 'path', required: true, schema: { type: paramType, }, description: `The ${paramName}`, }); } return parameters; } /** * Format title from slice name */ function formatTitle(name) { return name .split(/[-_]/) .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) .join(' '); } /** * Convert OpenAPI spec to YAML string */ export function specToYaml(spec) { return formatYamlObject(spec, 0); } /** * Format object as YAML with proper indentation */ function formatYamlObject(obj, indent = 0) { if (obj === null || obj === undefined) { return 'null'; } if (typeof obj !== 'object') { // Escape strings if needed if (typeof obj === 'string') { if (obj.includes('\n') || obj.includes(':') || obj.includes('"')) { return `"${obj.replace(/"/g, '\\"')}"`; } return obj; } return String(obj); } if (Array.isArray(obj)) { if (obj.length === 0) { return '[]'; } const indentStr = ' '.repeat(indent); const itemIndentStr = ' '.repeat(indent + 2); const items = obj .map(item => `${itemIndentStr}- ${formatYamlValue(item, indent + 2)}`) .join('\n'); return `\n${items}`; } // Object const indentStr = ' '.repeat(indent); const keyIndentStr = ' '.repeat(indent + 2); const keys = Object.keys(obj).sort(); if (keys.length === 0) { return '{}'; } const lines = keys.map(key => { const value = obj[key]; const formattedValue = formatYamlValue(value, indent + 2); if (formattedValue.startsWith('\n')) { return `${keyIndentStr}${key}:${formattedValue}`; } else { return `${keyIndentStr}${key}: ${formattedValue}`; } }); return `\n${lines.join('\n')}`; } /** * Format a value for YAML */ function formatYamlValue(value, indent) { if (value === null || value === undefined) { return 'null'; } if (typeof value === 'string') { if (value.includes('\n') || value.includes(':')) { return `"${value.replace(/"/g, '\\"')}"`; } return value; } if (typeof value === 'number' || typeof value === 'boolean') { return String(value); } if (Array.isArray(value)) { if (value.length === 0) { return '[]'; } const itemIndentStr = ' '.repeat(indent + 2); const items = value .map(item => `${itemIndentStr}- ${formatYamlValue(item, indent + 2)}`) .join('\n'); return `\n${items}`; } if (typeof value === 'object') { return formatYamlObject(value, indent); } return String(value); } /** * Convert OpenAPI spec to JSON string */ export function specToJson(spec) { return JSON.stringify(spec, null, 2); } /** * Get common response definitions */ export function getCommonResponses() { return { '200': { description: 'Successful response', content: { 'application/json': { schema: { type: 'object', additionalProperties: true, }, }, }, }, '201': { description: 'Created', content: { 'application/json': { schema: { type: 'object', additionalProperties: true, }, }, }, }, '400': { description: 'Bad Request', }, '401': { description: 'Unauthorized', }, '403': { description: 'Forbidden', }, '404': { description: 'Not Found', }, '500': { description: 'Internal Server Error', }, }; } //# sourceMappingURL=openapi-generator.js.map