UNPKG

apx-toolkit

Version:

Automatically discover APIs and generate complete integration packages: code in 12 languages, TypeScript types, test suites, SDK packages, API documentation, mock servers, performance reports, and contract tests. Saves 2-4 weeks of work in seconds.

194 lines 6.98 kB
/** * TypeScript Type Generator * Generates TypeScript type definitions from API responses * Makes the tool a developer's dream with full type safety */ /** * Generates TypeScript type definitions from API response structure */ export function generateTypeScriptTypes(api, responseExample) { const interfaces = []; const url = new URL(api.baseUrl); const pathName = url.pathname.replace(/[^a-zA-Z0-9]/g, '') || 'Api'; const baseName = `${capitalize(pathName)}${api.method}`; // Generate response interface if (responseExample) { const responseInterface = generateInterfaceFromJSON(responseExample, `${baseName}Response`); interfaces.push(responseInterface); } else { // Generate generic response interface interfaces.push(generateGenericResponseInterface(baseName, api)); } // Generate request interface for POST if (api.method === 'POST' && api.body) { const requestInterface = generateInterfaceFromJSON(api.body, `${baseName}Request`); interfaces.push(requestInterface); } // Generate pagination types if applicable if (api.paginationInfo) { interfaces.push(generatePaginationTypes(baseName, api.paginationInfo)); } // Combine all interfaces const types = interfaces.join('\n\n'); return { types, interfaces: interfaces.map(i => extractInterfaceName(i)), filename: `${baseName.toLowerCase()}.d.ts`, }; } /** * Generates TypeScript interface from JSON object */ function generateInterfaceFromJSON(json, interfaceName, depth = 0) { if (depth > 5) { // Prevent infinite recursion return `export interface ${interfaceName} {\n [key: string]: unknown;\n}`; } if (json === null || json === undefined) { return `export interface ${interfaceName} {\n // null or undefined\n}`; } if (Array.isArray(json)) { if (json.length === 0) { return `export type ${interfaceName} = unknown[];`; } const itemType = inferType(json[0], `${interfaceName}Item`, depth + 1); return `export type ${interfaceName} = ${extractInterfaceName(itemType)}[];`; } if (typeof json !== 'object') { return `export type ${interfaceName} = ${typeof json};`; } const obj = json; const properties = []; for (const [key, value] of Object.entries(obj)) { const propName = sanitizePropertyName(key); const propType = inferType(value, `${interfaceName}${capitalize(key)}`, depth + 1); const typeName = extractInterfaceName(propType); const optional = value === null || value === undefined ? '?' : ''; properties.push(` ${propName}${optional}: ${typeName};`); } return `export interface ${interfaceName} {\n${properties.join('\n')}\n}`; } /** * Infers TypeScript type from value */ function inferType(value, suggestedName, depth) { if (value === null) { return 'null'; } if (value === undefined) { return 'undefined'; } if (Array.isArray(value)) { if (value.length === 0) { return 'unknown[]'; } const itemType = inferType(value[0], `${suggestedName}Item`, depth + 1); return `${itemType}[]`; } if (typeof value === 'object') { return generateInterfaceFromJSON(value, suggestedName, depth); } return typeof value; } /** * Generates generic response interface when no example available */ function generateGenericResponseInterface(baseName, api) { const properties = []; // Add data path if known if (api.dataPath) { const dataType = api.paginationInfo ? `${baseName}Item[]` : 'unknown[]'; properties.push(` data?: ${dataType};`); } else { properties.push(` data?: unknown;`); } // Add meta if pagination exists if (api.paginationInfo) { properties.push(` meta?: ${baseName}Meta;`); } return `export interface ${baseName}Response {\n${properties.join('\n')}\n}`; } /** * Generates pagination-related types */ function generatePaginationTypes(baseName, paginationInfo) { if (!paginationInfo) { return ''; } const metaProperties = []; if (paginationInfo.type === 'page') { metaProperties.push(' page?: number;'); metaProperties.push(' totalPages?: number;'); } else if (paginationInfo.type === 'offset') { metaProperties.push(' offset?: number;'); metaProperties.push(' limit?: number;'); } else if (paginationInfo.type === 'cursor') { metaProperties.push(' cursor?: string;'); metaProperties.push(' nextCursor?: string;'); } metaProperties.push(' total?: number;'); metaProperties.push(' hasNext?: boolean;'); return `export interface ${baseName}Meta {\n${metaProperties.join('\n')}\n}`; } /** * Generates TypeScript types for all discovered APIs */ export function generateAllTypeScriptTypes(apis, responseExamples) { const result = {}; for (const api of apis) { const url = new URL(api.baseUrl); const key = `${api.method}_${url.pathname}`; const example = responseExamples?.get(api.url); result[key] = generateTypeScriptTypes(api, example); } return result; } /** * Generates a complete TypeScript declaration file for all APIs */ export function generateTypeScriptDeclarationFile(apis, responseExamples, packageName = 'discovered-api') { const allTypes = generateAllTypeScriptTypes(apis, responseExamples); const interfaces = []; const imports = []; // Collect all interfaces for (const types of Object.values(allTypes)) { interfaces.push(types.types); } // Generate index file let content = `/**\n * Auto-generated TypeScript types for discovered APIs\n`; content += ` * Generated by APX\n`; content += ` * Package: ${packageName}\n`; content += ` * Generated at: ${new Date().toISOString()}\n */\n\n`; if (imports.length > 0) { content += imports.join('\n') + '\n\n'; } content += interfaces.join('\n\n'); // Add utility types content += '\n\n// Utility types\n'; content += 'export type ApiResponse<T> = T;\n'; content += 'export type ApiError = {\n message: string;\n code?: string;\n};\n'; return content; } // Helper functions function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } function sanitizePropertyName(name) { // Replace invalid characters let sanitized = name.replace(/[^a-zA-Z0-9_$]/g, '_'); // Can't start with number if (/^\d/.test(sanitized)) { sanitized = '_' + sanitized; } return sanitized; } function extractInterfaceName(interfaceCode) { // Extract interface name from interface code const match = interfaceCode.match(/export\s+(?:interface|type)\s+(\w+)/); return match ? match[1] : 'unknown'; } //# sourceMappingURL=typescript-generator.js.map