openapi-domainify 0.1.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 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/generator.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * openapi-domainify CLI\n */\n\nimport { writeFileSync, existsSync } from 'fs'\nimport { resolve } from 'path'\nimport { loadConfig } from './config'\nimport { generate } from './generator'\n\nconst INIT_CONFIG = `import { defineConfig } from 'openapi-domainify'\n\nexport default defineConfig({\n // OpenAPI spec source (URL or local file path)\n input: 'https://api.example.com/openapi.json',\n \n // Where to write generated code\n // Creates: output/generated/, output/overrides/, output/index.ts, output/http.ts\n output: './src/api',\n\n // Domain splitting rules (order matters - first match wins)\n domains: [\n { name: 'auth', prefix: '/auth/', className: 'Auth' },\n { name: 'users', prefix: '/users/', className: 'Users' },\n ],\n\n // Catch-all for unmatched paths\n fallback: { name: 'api', prefix: '/', className: 'Api' },\n})\n`\n\nconst HELP = `\nopenapi-domainify - Split OpenAPI specs into domain-based TypeScript services\n\nUSAGE\n npx openapi-domainify <command> [options]\n\nCOMMANDS\n init Create a domainify.config.ts file\n generate Generate domain services from OpenAPI spec\n help Show this help message\n\nOPTIONS\n -c, --config Path to config file (default: domainify.config.ts)\n\nEXAMPLES\n npx openapi-domainify init\n npx openapi-domainify generate\n npx openapi-domainify generate --config ./custom.config.ts\n`\n\nasync function main() {\n const args = process.argv.slice(2)\n const command = args[0]\n\n if (!command || command === 'help' || command === '--help' || command === '-h') {\n console.log(HELP)\n process.exit(0)\n }\n\n if (command === 'init') {\n const configPath = resolve('domainify.config.ts')\n if (existsSync(configPath)) {\n console.error('❌ domainify.config.ts already exists')\n process.exit(1)\n }\n\n writeFileSync(configPath, INIT_CONFIG)\n console.log('✅ Created domainify.config.ts')\n console.log('\\nNext steps:')\n console.log(' 1. Edit domainify.config.ts with your OpenAPI spec URL')\n console.log(' 2. Run: npx openapi-domainify generate')\n process.exit(0)\n }\n\n if (command === 'generate') {\n let configPath: string | undefined\n\n const configIdx = args.findIndex((a) => a === '-c' || a === '--config')\n if (configIdx !== -1 && args[configIdx + 1]) {\n configPath = args[configIdx + 1]\n }\n\n try {\n const config = await loadConfig(configPath)\n await generate(config)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n console.error(`\\n❌ ${message}`)\n process.exit(1)\n }\n\n process.exit(0)\n }\n\n console.error(`Unknown command: ${command}`)\n console.log(HELP)\n process.exit(1)\n}\n\nmain()\n","/**\n * Config Loading\n */\n\nimport { existsSync } from 'fs'\nimport { pathToFileURL } from 'url'\nimport { resolve } from 'path'\nimport type { Config, ResolvedConfig } from './types'\n\nconst CONFIG_FILES = [\n 'domainify.config.ts',\n 'domainify.config.js',\n 'domainify.config.mjs',\n]\n\nexport async function loadConfig(configPath?: string): Promise<ResolvedConfig> {\n let resolvedPath: string | undefined\n\n if (configPath) {\n resolvedPath = resolve(configPath)\n if (!existsSync(resolvedPath)) {\n throw new Error(`Config file not found: ${configPath}`)\n }\n } else {\n for (const file of CONFIG_FILES) {\n const fullPath = resolve(process.cwd(), file)\n if (existsSync(fullPath)) {\n resolvedPath = fullPath\n break\n }\n }\n }\n\n if (!resolvedPath) {\n throw new Error(\n `No config file found. Create one of: ${CONFIG_FILES.join(', ')}\\n` +\n `Or run: npx openapi-domainify init`\n )\n }\n\n // Dynamic import - works with both JS and TS (via tsx/ts-node)\n const configModule = await import(pathToFileURL(resolvedPath).href)\n const config: Config = configModule.default\n\n if (!config.input) {\n throw new Error('Config missing required field: input')\n }\n if (!config.output) {\n throw new Error('Config missing required field: output')\n }\n if (!config.domains || config.domains.length === 0) {\n throw new Error('Config missing required field: domains (must have at least one)')\n }\n\n return resolveConfig(config)\n}\n\nexport function resolveConfig(config: Config): ResolvedConfig {\n return {\n ...config,\n fallback: config.fallback ?? { name: 'api', prefix: '/', className: 'Api' },\n httpClientImport: config.httpClientImport ?? '../../http',\n overridesDir: config.overridesDir ?? './overrides',\n generateIndex: config.generateIndex ?? true,\n }\n}\n","/**\n * OpenAPI Domain Generator\n *\n * Generates domain-based services and types from OpenAPI spec.\n */\n\nimport { writeFileSync, mkdirSync, existsSync, rmSync, readFileSync } from 'fs'\nimport { join, dirname, resolve } from 'path'\nimport { execSync } from 'child_process'\nimport * as ts from 'typescript'\nimport type { ResolvedConfig, DomainConfig, Endpoint } from './types'\n\n// ─── Spec Loading ─────────────────────────────────────────────────────────────\n\nasync function loadSpec(config: ResolvedConfig): Promise<string> {\n const { input } = config\n\n if (input.startsWith('http://') || input.startsWith('https://')) {\n console.log('Fetching OpenAPI spec...')\n console.log(` Source: ${input}`)\n\n const response = await fetch(input)\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n const text = await response.text()\n console.log(` Fetched successfully`)\n return text\n }\n\n // Local file\n const filePath = resolve(input)\n if (!existsSync(filePath)) {\n throw new Error(`OpenAPI spec not found: ${filePath}`)\n }\n\n console.log('Loading OpenAPI spec...')\n console.log(` Source: ${filePath}`)\n return readFileSync(filePath, 'utf-8')\n}\n\nfunction parseSpec(content: string): object {\n // Try JSON first\n try {\n return JSON.parse(content)\n } catch {\n // Try YAML\n // Note: We just support JSON for now to avoid yaml dependency\n // Users can convert YAML to JSON externally if needed\n throw new Error(\n 'Failed to parse OpenAPI spec. Currently only JSON format is supported.\\n' +\n 'If you have a YAML spec, convert it to JSON first.'\n )\n }\n}\n\n// ─── Type Generation ──────────────────────────────────────────────────────────\n\nfunction generateOpenapiTypes(specPath: string, outputPath: string): void {\n console.log('\\nGenerating TypeScript types from OpenAPI spec...')\n\n try {\n execSync(`npx openapi-typescript \"${specPath}\" -o \"${outputPath}\"`, {\n stdio: 'pipe',\n encoding: 'utf-8',\n })\n console.log(` Generated base types`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n throw new Error(`Failed to generate types: ${message}`)\n }\n}\n\n// ─── TypeScript Compiler API ──────────────────────────────────────────────────\n\nfunction createProgram(openapiTsPath: string) {\n if (!existsSync(openapiTsPath)) {\n throw new Error(`Generated types not found: ${openapiTsPath}`)\n }\n\n const program = ts.createProgram([openapiTsPath], {\n target: ts.ScriptTarget.ESNext,\n module: ts.ModuleKind.ESNext,\n strict: true,\n })\n\n const checker = program.getTypeChecker()\n const sourceFile = program.getSourceFile(openapiTsPath)\n\n if (!sourceFile) {\n throw new Error(`Could not load ${openapiTsPath}`)\n }\n\n let pathsSymbol: ts.Symbol | undefined\n\n ts.forEachChild(sourceFile, (node) => {\n if (ts.isInterfaceDeclaration(node) && node.name.text === 'paths') {\n pathsSymbol = checker.getSymbolAtLocation(node.name)\n }\n })\n\n if (!pathsSymbol) {\n throw new Error('Could not find \"paths\" interface in generated types')\n }\n\n const pathsType = checker.getDeclaredTypeOfSymbol(pathsSymbol)\n\n return { checker, sourceFile, pathsType }\n}\n\nfunction extractEndpoints(\n checker: ts.TypeChecker,\n sourceFile: ts.SourceFile,\n pathsType: ts.Type\n): Endpoint[] {\n const endpoints: Endpoint[] = []\n const methods = ['get', 'post', 'put', 'patch', 'delete'] as const\n\n for (const pathProp of pathsType.getProperties()) {\n const path = pathProp.name\n const pathType = checker.getTypeOfSymbolAtLocation(pathProp, sourceFile)\n\n for (const method of methods) {\n const methodProp = pathType.getProperty(method)\n if (!methodProp) continue\n\n const methodType = checker.getTypeOfSymbolAtLocation(methodProp, sourceFile)\n const typeString = checker.typeToString(methodType)\n\n if (typeString === 'undefined' || typeString === 'never') continue\n\n const operationType = checker.getApparentType(methodType)\n const pathParams = (path.match(/\\{([^}]+)\\}/g) || []).map((p) => p.slice(1, -1))\n\n const queryParams: string[] = []\n const parametersProp = operationType.getProperty('parameters')\n if (parametersProp) {\n const paramsType = checker.getApparentType(\n checker.getTypeOfSymbolAtLocation(parametersProp, sourceFile)\n )\n const queryProp = paramsType.getProperty('query')\n if (queryProp) {\n const queryType = checker.getTypeOfSymbolAtLocation(queryProp, sourceFile)\n for (const prop of queryType.getProperties()) {\n queryParams.push(prop.name)\n }\n }\n }\n\n const requestBodyProp = operationType.getProperty('requestBody')\n let hasBody = false\n if (requestBodyProp) {\n const bodyType = checker.getTypeOfSymbolAtLocation(requestBodyProp, sourceFile)\n hasBody = !(bodyType.flags & (ts.TypeFlags.Never | ts.TypeFlags.Undefined))\n }\n\n endpoints.push({\n path,\n method,\n pathParams,\n queryParams,\n hasBody,\n })\n }\n }\n\n return endpoints\n}\n\n// ─── Type Extractor ───────────────────────────────────────────────────────────\n\nfunction createTypeExtractor(\n checker: ts.TypeChecker,\n sourceFile: ts.SourceFile,\n pathsType: ts.Type\n) {\n const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed })\n\n function formatType(type: ts.Type): string {\n const typeNode = checker.typeToTypeNode(\n type,\n sourceFile,\n ts.NodeBuilderFlags.NoTruncation | ts.NodeBuilderFlags.MultilineObjectLiterals\n )\n if (!typeNode) {\n return checker.typeToString(type, sourceFile, ts.TypeFormatFlags.NoTruncation)\n }\n return printer.printNode(ts.EmitHint.Unspecified, typeNode, sourceFile)\n }\n\n function getNestedType(type: ts.Type, ...propertyPath: string[]): ts.Type | undefined {\n let currentType = type\n\n for (const prop of propertyPath) {\n currentType = checker.getApparentType(currentType)\n const property = currentType.getProperty(prop)\n if (!property) return undefined\n currentType = checker.getTypeOfSymbolAtLocation(property, sourceFile)\n }\n\n return checker.getApparentType(currentType)\n }\n\n function getMethodType(path: string, method: string): ts.Type | undefined {\n const pathProp = pathsType.getProperty(path)\n if (!pathProp) return undefined\n\n const pathType = checker.getTypeOfSymbolAtLocation(pathProp, sourceFile)\n const methodProp = pathType.getProperty(method)\n if (!methodProp) return undefined\n\n return checker.getApparentType(checker.getTypeOfSymbolAtLocation(methodProp, sourceFile))\n }\n\n function isUnknownType(typeStr: string): boolean {\n return typeStr === 'unknown' || typeStr === 'never' || typeStr === 'undefined'\n }\n\n return {\n getResponseType(path: string, method: string): string | null {\n const methodType = getMethodType(path, method)\n if (!methodType) return null\n\n const responseType = getNestedType(methodType, 'responses', '200', 'content', 'application/json')\n if (!responseType) return null\n\n const formatted = formatType(responseType)\n return isUnknownType(formatted) ? null : formatted\n },\n\n getRequestType(path: string, method: string): string | null {\n const methodType = getMethodType(path, method)\n if (!methodType) return null\n\n const requestBodyProp = methodType.getProperty('requestBody')\n if (!requestBodyProp) return null\n\n let requestBodyType = checker.getTypeOfSymbolAtLocation(requestBodyProp, sourceFile)\n\n if (requestBodyType.isUnion()) {\n const nonUndefinedTypes = requestBodyType.types.filter(\n (t) => !(t.flags & ts.TypeFlags.Undefined)\n )\n if (nonUndefinedTypes.length === 1) {\n requestBodyType = nonUndefinedTypes[0]\n }\n }\n\n requestBodyType = checker.getApparentType(requestBodyType)\n const requestType = getNestedType(requestBodyType, 'content', 'application/json')\n if (!requestType) return null\n\n const formatted = formatType(requestType)\n return isUnknownType(formatted) ? null : formatted\n },\n\n getQueryType(path: string, method: string): string | null {\n const methodType = getMethodType(path, method)\n if (!methodType) return null\n\n const queryType = getNestedType(methodType, 'parameters', 'query')\n if (!queryType) return null\n\n const formatted = formatType(queryType)\n return isUnknownType(formatted) ? null : formatted\n },\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction getAllDomains(config: ResolvedConfig): DomainConfig[] {\n return [...config.domains, config.fallback]\n}\n\nfunction getDomain(path: string, config: ResolvedConfig): string {\n const strippedPath = config.stripPrefix ? path.replace(config.stripPrefix, '') : path\n\n for (const domain of config.domains) {\n if (strippedPath.startsWith(domain.prefix)) {\n return domain.name\n }\n }\n return config.fallback.name\n}\n\nfunction getDomainConfig(name: string, config: ResolvedConfig): DomainConfig {\n const found = getAllDomains(config).find((d) => d.name === name)\n if (!found) {\n throw new Error(`Unknown domain: ${name}`)\n }\n return found\n}\n\nfunction pascalCase(str: string): string {\n return str\n .replace(/[-_\\/]([a-z])/g, (_, c) => c.toUpperCase())\n .replace(/^[a-z]/, (c) => c.toUpperCase())\n .replace(/[{}]/g, '')\n}\n\nfunction camelCase(str: string): string {\n return str\n .replace(/[-_\\/]([a-z])/g, (_, c) => c.toUpperCase())\n .replace(/^[A-Z]/, (c) => c.toLowerCase())\n .replace(/[{}]/g, '')\n}\n\nfunction singularize(word: string): string {\n if (word.endsWith('ies')) return word.slice(0, -3) + 'y'\n if (/(x|ch|sh|ss|z)es$/i.test(word)) return word.slice(0, -2)\n if (word.endsWith('s') && !word.endsWith('ss')) return word.slice(0, -1)\n return word\n}\n\nfunction getMethodName(endpoint: Endpoint, domain: string, config: ResolvedConfig): string {\n const domainConfig = getDomainConfig(domain, config)\n let relativePath = endpoint.path\n\n if (config.stripPrefix) {\n relativePath = relativePath.replace(config.stripPrefix, '')\n }\n relativePath = relativePath.replace(domainConfig.prefix, '').replace(/^\\//, '')\n\n const allSegments = relativePath.split('/').filter(Boolean)\n const endsWithParam = allSegments.length > 0 && allSegments[allSegments.length - 1].startsWith('{')\n const segments = allSegments.filter((seg) => !seg.startsWith('{'))\n\n if (segments.length === 0) {\n return endpoint.method\n }\n\n const resource = segments[0]\n const subPath = segments.slice(1)\n\n const resourceName = endsWithParam ? singularize(pascalCase(resource)) : pascalCase(resource)\n const subPathPart = subPath.map(pascalCase).join('')\n const fullPath = resourceName + subPathPart\n\n switch (endpoint.method) {\n case 'get':\n if (endsWithParam || subPath.length > 0) {\n return `get${fullPath}`\n }\n return camelCase(fullPath)\n case 'post':\n return `create${singularize(fullPath)}`\n case 'put':\n return `update${singularize(fullPath)}`\n case 'patch':\n return `patch${singularize(fullPath)}`\n case 'delete':\n return `delete${singularize(fullPath)}`\n default:\n return `${endpoint.method}${fullPath}`\n }\n}\n\nfunction getTypeName(path: string, method: string, kind: 'Request' | 'Response' | 'Query', config: ResolvedConfig): string {\n let cleanPath = path\n if (config.stripPrefix) {\n cleanPath = cleanPath.replace(config.stripPrefix, '')\n }\n\n cleanPath = cleanPath\n .replace(/^\\//, '')\n .replace(/\\{[^}]+\\}/g, 'ById')\n .split('/')\n .map(pascalCase)\n .join('')\n\n const methodPrefix = method.charAt(0).toUpperCase() + method.slice(1)\n return `${methodPrefix}${cleanPath}${kind}`\n}\n\nfunction groupByDomain(endpoints: Endpoint[], config: ResolvedConfig): Map<string, Endpoint[]> {\n const groups = new Map<string, Endpoint[]>()\n\n for (const domain of getAllDomains(config)) {\n groups.set(domain.name, [])\n }\n\n for (const endpoint of endpoints) {\n const domain = getDomain(endpoint.path, config)\n const domainEndpoints = groups.get(domain)\n if (domainEndpoints) {\n domainEndpoints.push(endpoint)\n }\n }\n\n return groups\n}\n\n// ─── Code Generation ──────────────────────────────────────────────────────────\n\nfunction generateTypes(\n domain: string,\n endpoints: Endpoint[],\n extractor: ReturnType<typeof createTypeExtractor>,\n config: ResolvedConfig\n): string {\n const lines: string[] = []\n const domainConfig = getDomainConfig(domain, config)\n\n const allTypes: string[] = []\n\n // Request types\n const requestTypes: string[] = []\n for (const endpoint of endpoints) {\n if (endpoint.hasBody) {\n const typeBody = extractor.getRequestType(endpoint.path, endpoint.method)\n if (typeBody) {\n const typeName = getTypeName(endpoint.path, endpoint.method, 'Request', config)\n requestTypes.push(`export type ${typeName} = ${typeBody}`)\n }\n }\n }\n allTypes.push(...requestTypes)\n\n // Query types\n const queryTypes: string[] = []\n for (const endpoint of endpoints) {\n if (endpoint.queryParams.length > 0) {\n const typeBody = extractor.getQueryType(endpoint.path, endpoint.method)\n if (typeBody) {\n const typeName = getTypeName(endpoint.path, endpoint.method, 'Query', config)\n queryTypes.push(`export type ${typeName} = ${typeBody}`)\n }\n }\n }\n allTypes.push(...queryTypes)\n\n // Response types\n const responseTypes: string[] = []\n for (const endpoint of endpoints) {\n const typeBody = extractor.getResponseType(endpoint.path, endpoint.method)\n if (typeBody) {\n const typeName = getTypeName(endpoint.path, endpoint.method, 'Response', config)\n responseTypes.push(`export type ${typeName} = ${typeBody}`)\n }\n }\n allTypes.push(...responseTypes)\n\n const usesComponents = allTypes.some((t) => t.includes('components['))\n\n lines.push(`/**`)\n lines.push(` * ${domainConfig.className} Domain Types`)\n lines.push(` * Auto-generated - DO NOT EDIT`)\n lines.push(` */`)\n\n if (usesComponents) {\n lines.push(`import type { components } from '../openapi'`)\n }\n\n lines.push(``)\n\n // Common types\n lines.push(`// ─── Common Types ─────────────────────────────────────────────────────────────`)\n lines.push(``)\n lines.push(`export interface PaginatedResponse<T> {`)\n lines.push(` data: T[]`)\n lines.push(` current_page: number`)\n lines.push(` last_page: number`)\n lines.push(` per_page: number`)\n lines.push(` total: number`)\n lines.push(` from: number | null`)\n lines.push(` to: number | null`)\n lines.push(`}`)\n lines.push(``)\n\n if (requestTypes.length > 0) {\n lines.push(`// ─── Request Types ────────────────────────────────────────────────────────────`)\n lines.push(``)\n lines.push(...requestTypes)\n lines.push(``)\n }\n\n if (queryTypes.length > 0) {\n lines.push(`// ─── Query Types ──────────────────────────────────────────────────────────────`)\n lines.push(``)\n lines.push(...queryTypes)\n lines.push(``)\n }\n\n if (responseTypes.length > 0) {\n lines.push(`// ─── Response Types ───────────────────────────────────────────────────────────`)\n lines.push(``)\n lines.push(...responseTypes)\n lines.push(``)\n }\n\n return lines.join('\\n')\n}\n\nfunction generateService(\n domain: string,\n endpoints: Endpoint[],\n extractor: ReturnType<typeof createTypeExtractor>,\n config: ResolvedConfig\n): string {\n const lines: string[] = []\n const domainConfig = getDomainConfig(domain, config)\n const className = `${domainConfig.className}Service`\n\n lines.push(`/**`)\n lines.push(` * ${domainConfig.className} Domain Service`)\n lines.push(` * Auto-generated - DO NOT EDIT`)\n lines.push(` */`)\n lines.push(`import type { HttpClient } from '${config.httpClientImport}'`)\n lines.push(`import type * as T from './types'`)\n lines.push(``)\n lines.push(`export class ${className} {`)\n lines.push(` constructor(private http: HttpClient) {}`)\n\n const usedNames = new Map<string, number>()\n const resourceGroups = new Map<string, Endpoint[]>()\n\n for (const endpoint of endpoints) {\n let relativePath = endpoint.path\n if (config.stripPrefix) {\n relativePath = relativePath.replace(config.stripPrefix, '')\n }\n relativePath = relativePath.replace(domainConfig.prefix, '').replace(/^\\//, '')\n\n const resource = relativePath.split('/')[0]?.replace(/\\{[^}]+\\}/, '') || 'root'\n if (!resourceGroups.has(resource)) resourceGroups.set(resource, [])\n resourceGroups.get(resource)!.push(endpoint)\n }\n\n for (const [resource, resourceEndpoints] of resourceGroups) {\n lines.push(``)\n lines.push(` // ─── ${pascalCase(resource) || 'Root'} ────────────────────────────────────────────`)\n\n for (const endpoint of resourceEndpoints) {\n let methodName = getMethodName(endpoint, domain, config)\n\n const count = usedNames.get(methodName) || 0\n if (count > 0) {\n methodName = `${methodName}${count + 1}`\n }\n usedNames.set(methodName, count + 1)\n\n const params: string[] = []\n\n for (const param of endpoint.pathParams) {\n const paramName = camelCase(param.replace(/_id$/, 'Id'))\n params.push(`${paramName}: string | number`)\n }\n\n if (endpoint.queryParams.length > 0) {\n const queryType = extractor.getQueryType(endpoint.path, endpoint.method)\n if (queryType) {\n const queryTypeName = getTypeName(endpoint.path, endpoint.method, 'Query', config)\n params.push(`params?: T.${queryTypeName}`)\n } else {\n params.push(`params?: Record<string, unknown>`)\n }\n }\n\n if (endpoint.hasBody && endpoint.method !== 'get') {\n const requestType = extractor.getRequestType(endpoint.path, endpoint.method)\n if (requestType) {\n const bodyTypeName = getTypeName(endpoint.path, endpoint.method, 'Request', config)\n params.push(`data: T.${bodyTypeName}`)\n } else {\n params.push(`data: unknown`)\n }\n }\n\n const responseType = extractor.getResponseType(endpoint.path, endpoint.method)\n let returnType: string\n if (responseType) {\n const responseTypeName = getTypeName(endpoint.path, endpoint.method, 'Response', config)\n returnType = `T.${responseTypeName}`\n } else {\n returnType = 'unknown'\n }\n\n let urlPath = endpoint.path\n if (config.stripPrefix) {\n urlPath = urlPath.replace(config.stripPrefix, '')\n }\n\n for (const param of endpoint.pathParams) {\n const paramName = camelCase(param.replace(/_id$/, 'Id'))\n urlPath = urlPath.replace(`{${param}}`, `\\${${paramName}}`)\n }\n const pathLiteral = urlPath.includes('$') ? '`' + urlPath + '`' : `'${urlPath}'`\n\n lines.push(` ${methodName}(${params.join(', ')}): Promise<${returnType}> {`)\n\n const hasParams = endpoint.queryParams.length > 0\n switch (endpoint.method) {\n case 'get':\n lines.push(hasParams ? ` return this.http.get(${pathLiteral}, { params })` : ` return this.http.get(${pathLiteral})`)\n break\n case 'post':\n lines.push(endpoint.hasBody ? ` return this.http.post(${pathLiteral}, data)` : ` return this.http.post(${pathLiteral})`)\n break\n case 'put':\n lines.push(endpoint.hasBody ? ` return this.http.put(${pathLiteral}, data)` : ` return this.http.put(${pathLiteral})`)\n break\n case 'patch':\n lines.push(endpoint.hasBody ? ` return this.http.patch(${pathLiteral}, data)` : ` return this.http.patch(${pathLiteral})`)\n break\n case 'delete':\n lines.push(hasParams ? ` return this.http.delete(${pathLiteral}, { params })` : ` return this.http.delete(${pathLiteral})`)\n break\n }\n\n lines.push(` }`)\n }\n }\n\n lines.push(`}`)\n lines.push(``)\n\n return lines.join('\\n')\n}\n\n// ─── Scaffolding Generation ───────────────────────────────────────────────────\n\nfunction generateOverrideTemplate(domain: string, config: ResolvedConfig): string {\n const domainConfig = getDomainConfig(domain, config)\n const className = `${domainConfig.className}Service`\n\n return `/**\n * ${domainConfig.className} Service Overrides\n *\n * Extend or override methods from the generated ${className}.\n * This file is NOT overwritten by the generator - safe to edit.\n *\n * To enable: update index.ts to import from here instead of generated.\n */\nimport { ${className} as Generated${className} } from '../../generated/${domain}/service'\nimport type { HttpClient } from '../../http'\n\nexport class ${className} extends Generated${className} {\n constructor(http: HttpClient) {\n super(http)\n }\n\n // Example: Override a method that has issues in the OpenAPI spec\n // async someMethod(): Promise<SomeType> {\n // // Custom implementation or fix\n // return super.someMethod()\n // }\n\n // Example: Add a method not in the OpenAPI spec\n // async customMethod(): Promise<void> {\n // // Custom logic\n // }\n}\n`\n}\n\nfunction generateHttpTemplate(config: ResolvedConfig): string {\n return `/**\n * HTTP Client Interface\n *\n * Implement this interface with your preferred HTTP library.\n * This file is NOT overwritten by the generator - safe to edit.\n */\n\nexport interface HttpClient {\n get<T>(url: string, options?: { params?: Record<string, unknown> }): Promise<T>\n post<T>(url: string, body?: Record<string, unknown>): Promise<T>\n put<T>(url: string, body?: Record<string, unknown>): Promise<T>\n patch<T>(url: string, body?: Record<string, unknown>): Promise<T>\n delete<T>(url: string, options?: { params?: Record<string, unknown> }): Promise<T>\n}\n\n// ─── Example Implementation (using fetch) ─────────────────────────────────────\n\nexport interface HttpClientOptions {\n baseUrl: string\n getToken?: () => string | null | Promise<string | null>\n onError?: (error: ApiError) => void\n}\n\nexport interface ApiError {\n status: number\n statusText: string\n message: string\n data?: unknown\n}\n\nexport class FetchHttpClient implements HttpClient {\n constructor(private options: HttpClientOptions) {}\n\n private async request<T>(\n method: string,\n url: string,\n body?: unknown,\n params?: Record<string, unknown>\n ): Promise<T> {\n const query = params\n ? '?' + new URLSearchParams(\n Object.entries(params)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => [k, String(v)])\n ).toString()\n : ''\n\n const token = await this.options.getToken?.()\n\n const response = await fetch(this.options.baseUrl + url + query, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...(token && { Authorization: \\`Bearer \\${token}\\` }),\n },\n body: body ? JSON.stringify(body) : undefined,\n })\n\n if (!response.ok) {\n const error: ApiError = {\n status: response.status,\n statusText: response.statusText,\n message: response.statusText,\n }\n try {\n error.data = await response.json()\n error.message = (error.data as { message?: string })?.message || error.statusText\n } catch {}\n this.options.onError?.(error)\n throw error\n }\n\n return response.json()\n }\n\n get<T>(url: string, opts?: { params?: Record<string, unknown> }) {\n return this.request<T>('GET', url, undefined, opts?.params)\n }\n post<T>(url: string, body?: Record<string, unknown>) {\n return this.request<T>('POST', url, body)\n }\n put<T>(url: string, body?: Record<string, unknown>) {\n return this.request<T>('PUT', url, body)\n }\n patch<T>(url: string, body?: Record<string, unknown>) {\n return this.request<T>('PATCH', url, body)\n }\n delete<T>(url: string, opts?: { params?: Record<string, unknown> }) {\n return this.request<T>('DELETE', url, undefined, opts?.params)\n }\n}\n`\n}\n\nfunction generateIndexFile(\n activeDomains: string[],\n config: ResolvedConfig,\n overridesDir: string\n): string {\n const lines: string[] = []\n\n lines.push(`/**`)\n lines.push(` * API Client`)\n lines.push(` *`)\n lines.push(` * Auto-generated index that wires domain services together.`)\n lines.push(` * This file IS overwritten by the generator.`)\n lines.push(` *`)\n lines.push(` * To use overrides: uncomment the override import and comment out the generated import.`)\n lines.push(` */`)\n lines.push(`import type { HttpClient } from './http'`)\n lines.push(``)\n\n // Import services - check if override exists\n for (const domain of activeDomains) {\n const domainConfig = getDomainConfig(domain, config)\n const className = `${domainConfig.className}Service`\n const overridePath = join(overridesDir, domain, 'service.ts')\n const hasOverride = existsSync(overridePath)\n\n if (hasOverride) {\n // Check if override has actual content (not just template)\n const content = readFileSync(overridePath, 'utf-8')\n const isCustomized = !content.includes('// Example: Override a method')\n || content.split('async ').length > 2 // Has more than just the commented examples\n\n if (isCustomized) {\n lines.push(`import { ${className} } from './overrides/${domain}/service'`)\n } else {\n lines.push(`// import { ${className} } from './overrides/${domain}/service'`)\n lines.push(`import { ${className} } from './generated/${domain}/service'`)\n }\n } else {\n lines.push(`import { ${className} } from './generated/${domain}/service'`)\n }\n }\n\n lines.push(``)\n lines.push(`// ─── API Client ───────────────────────────────────────────────────────────────`)\n lines.push(``)\n lines.push(`export class ApiClient {`)\n lines.push(` constructor(private http: HttpClient) {`)\n\n for (const domain of activeDomains) {\n const domainConfig = getDomainConfig(domain, config)\n lines.push(` this.${domain} = new ${domainConfig.className}Service(http)`)\n }\n\n lines.push(` }`)\n lines.push(``)\n\n for (const domain of activeDomains) {\n const domainConfig = getDomainConfig(domain, config)\n lines.push(` readonly ${domain}: ${domainConfig.className}Service`)\n }\n\n lines.push(`}`)\n lines.push(``)\n lines.push(`export function createApiClient(http: HttpClient): ApiClient {`)\n lines.push(` return new ApiClient(http)`)\n lines.push(`}`)\n lines.push(``)\n lines.push(`// ─── Re-exports ────────────────────────────────────────────────────────────────`)\n lines.push(``)\n\n for (const domain of activeDomains) {\n const domainConfig = getDomainConfig(domain, config)\n lines.push(`export * as ${domainConfig.className}Types from './generated/${domain}/types'`)\n }\n\n lines.push(``)\n\n return lines.join('\\n')\n}\n\nfunction writeFileSafe(path: string, content: string, overwrite: boolean = true): boolean {\n if (!overwrite && existsSync(path)) {\n return false\n }\n writeFileSync(path, content)\n return true\n}\n\n// ─── Main Generator ───────────────────────────────────────────────────────────\n\nexport async function generate(config: ResolvedConfig): Promise<void> {\n console.log('╔══════════════════════════════════════════════════════════════════╗')\n console.log('║ openapi-domainify ║')\n console.log('╚══════════════════════════════════════════════════════════════════╝\\n')\n\n // Step 1: Load spec\n const specContent = await loadSpec(config)\n const spec = parseSpec(specContent)\n\n // Create output directory structure\n const outputDir = resolve(config.output)\n const generatedDir = join(outputDir, 'generated')\n const overridesDir = resolve(outputDir, config.overridesDir)\n\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true })\n }\n if (!existsSync(generatedDir)) {\n mkdirSync(generatedDir, { recursive: true })\n }\n\n // Save spec\n const specPath = config.specOutput\n ? resolve(config.specOutput)\n : join(generatedDir, 'openapi.json')\n\n const specDir = dirname(specPath)\n if (!existsSync(specDir)) {\n mkdirSync(specDir, { recursive: true })\n }\n writeFileSync(specPath, JSON.stringify(spec, null, 2))\n\n // Step 2: Generate base TypeScript types\n const openapiTsPath = join(generatedDir, 'openapi.ts')\n generateOpenapiTypes(specPath, openapiTsPath)\n\n // Step 3: Parse and extract\n console.log('\\n📊 Generating domain services...')\n const { checker, sourceFile, pathsType } = createProgram(openapiTsPath)\n\n console.log(' Extracting endpoints...')\n const endpoints = extractEndpoints(checker, sourceFile, pathsType)\n console.log(` Found ${endpoints.length} endpoints`)\n\n const extractor = createTypeExtractor(checker, sourceFile, pathsType)\n const domains = groupByDomain(endpoints, config)\n\n // Track which domains have endpoints\n const activeDomains: string[] = []\n\n // Step 4: Generate domain files\n for (const [domain, domainEndpoints] of domains) {\n if (domainEndpoints.length === 0) continue\n\n activeDomains.push(domain)\n\n const domainDir = join(generatedDir, domain)\n if (existsSync(domainDir)) {\n rmSync(domainDir, { recursive: true })\n }\n mkdirSync(domainDir, { recursive: true })\n\n console.log(`\\n📁 generated/${domain}/ (${domainEndpoints.length} endpoints)`)\n\n const typesContent = generateTypes(domain, domainEndpoints, extractor, config)\n writeFileSync(join(domainDir, 'types.ts'), typesContent)\n console.log(` ✨ types.ts`)\n\n const serviceContent = generateService(domain, domainEndpoints, extractor, config)\n writeFileSync(join(domainDir, 'service.ts'), serviceContent)\n console.log(` ✨ service.ts`)\n }\n\n // Step 5: Generate scaffolding (only if doesn't exist)\n console.log('\\n🏗️ Scaffolding...')\n\n // Create overrides directory if needed\n if (!existsSync(overridesDir)) {\n mkdirSync(overridesDir, { recursive: true })\n }\n\n // Generate override templates for each domain\n for (const domain of activeDomains) {\n const overrideDomainDir = join(overridesDir, domain)\n if (!existsSync(overrideDomainDir)) {\n mkdirSync(overrideDomainDir, { recursive: true })\n }\n\n const overrideServicePath = join(overrideDomainDir, 'service.ts')\n if (writeFileSafe(overrideServicePath, generateOverrideTemplate(domain, config), false)) {\n console.log(` 📝 overrides/${domain}/service.ts (new)`)\n }\n }\n\n // Generate http.ts template\n const httpPath = join(outputDir, 'http.ts')\n if (writeFileSafe(httpPath, generateHttpTemplate(config), false)) {\n console.log(` 📝 http.ts (new)`)\n }\n\n // Step 6: Generate index.ts (always overwritten)\n if (config.generateIndex) {\n const indexContent = generateIndexFile(activeDomains, config, overridesDir)\n writeFileSync(join(outputDir, 'index.ts'), indexContent)\n console.log(` ✨ index.ts`)\n }\n\n console.log('\\n╔══════════════════════════════════════════════════════════════════╗')\n console.log('║ ✅ Generation Complete! ║')\n console.log('╚══════════════════════════════════════════════════════════════════╝')\n console.log(`\\nOutput: ${outputDir}`)\n console.log(`\\nStructure:`)\n console.log(` ${outputDir}/`)\n console.log(` ├── index.ts # API client (regenerated)`)\n console.log(` ├── http.ts # HTTP client template (safe to edit)`)\n console.log(` ├── generated/ # Auto-generated (DO NOT EDIT)`)\n for (const domain of activeDomains) {\n console.log(` │ └── ${domain}/`)\n }\n console.log(` └── overrides/ # Your customizations (safe to edit)`)\n for (const domain of activeDomains) {\n console.log(` └── ${domain}/`)\n }\n}\n\n"],"mappings":"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","names":["writeFileSync","existsSync","resolve","existsSync","resolve","createProgram","resolve","existsSync","writeFileSync"]}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Configuration Types
3
+ */
4
+ interface DomainConfig {
5
+ /** Directory name for this domain */
6
+ name: string;
7
+ /** URL path prefix to match (e.g., '/auth/') */
8
+ prefix: string;
9
+ /** Class name suffix for the generated service */
10
+ className: string;
11
+ }
12
+ interface Config {
13
+ /** OpenAPI spec source - URL or local file path */
14
+ input: string;
15
+ /** Output directory for generated code */
16
+ output: string;
17
+ /** Domain splitting rules - order matters, first match wins */
18
+ domains: DomainConfig[];
19
+ /** Catch-all for unmatched paths */
20
+ fallback?: DomainConfig;
21
+ /** Strip this prefix from all paths before matching */
22
+ stripPrefix?: string;
23
+ /** Path to save downloaded spec (for caching/debugging) */
24
+ specOutput?: string;
25
+ /** Custom HTTP client import path (default: '../../http') */
26
+ httpClientImport?: string;
27
+ /** Directory for manual overrides (default: '../overrides' relative to output) */
28
+ overridesDir?: string;
29
+ /** Generate index.ts that wires services together (default: true) */
30
+ generateIndex?: boolean;
31
+ }
32
+ interface ResolvedConfig extends Config {
33
+ fallback: DomainConfig;
34
+ httpClientImport: string;
35
+ overridesDir: string;
36
+ generateIndex: boolean;
37
+ }
38
+ interface Endpoint {
39
+ path: string;
40
+ method: 'get' | 'post' | 'put' | 'patch' | 'delete';
41
+ summary?: string;
42
+ pathParams: string[];
43
+ queryParams: string[];
44
+ hasBody: boolean;
45
+ }
46
+ /**
47
+ * Helper to define config with type inference
48
+ */
49
+ declare function defineConfig(config: Config): Config;
50
+
51
+ /**
52
+ * Config Loading
53
+ */
54
+
55
+ declare function loadConfig(configPath?: string): Promise<ResolvedConfig>;
56
+ declare function resolveConfig(config: Config): ResolvedConfig;
57
+
58
+ /**
59
+ * OpenAPI Domain Generator
60
+ *
61
+ * Generates domain-based services and types from OpenAPI spec.
62
+ */
63
+
64
+ declare function generate(config: ResolvedConfig): Promise<void>;
65
+
66
+ export { type Config, type DomainConfig, type Endpoint, type ResolvedConfig, defineConfig, generate, loadConfig, resolveConfig };