@forinda/kickjs-cli 1.1.3 → 1.2.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generators/module.ts","../src/utils/fs.ts","../src/utils/naming.ts","../src/generators/templates/module-index.ts","../src/generators/templates/controller.ts","../src/generators/templates/constants.ts","../src/generators/templates/dtos.ts","../src/generators/templates/use-cases.ts","../src/generators/templates/repository.ts","../src/generators/templates/domain.ts","../src/generators/templates/tests.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/config.ts"],"sourcesContent":["import { join } from 'node:path'\nimport { writeFileSafe, fileExists } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase, pluralize, pluralizePascal } from '../utils/naming'\nimport { readFile, writeFile } from 'node:fs/promises'\nimport {\n generateModuleIndex,\n generateController,\n generateConstants,\n generateCreateDTO,\n generateUpdateDTO,\n generateResponseDTO,\n generateUseCases,\n generateRepositoryInterface,\n generateInMemoryRepository,\n generateDomainService,\n generateEntity,\n generateValueObject,\n generateControllerTest,\n generateRepositoryTest,\n} from './templates'\n\ninterface GenerateModuleOptions {\n name: string\n modulesDir: string\n noEntity?: boolean\n noTests?: boolean\n repo?: 'drizzle' | 'inmemory'\n minimal?: boolean\n}\n\n/**\n * Generate a full DDD module with all layers:\n * presentation/ — controller\n * application/ — use-cases, DTOs\n * domain/ — entity, value objects, repository interface, domain service\n * infrastructure/ — repository implementation\n */\nexport async function generateModule(options: GenerateModuleOptions): Promise<string[]> {\n const { name, modulesDir, noEntity, noTests, repo = 'inmemory', minimal } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const camel = toCamelCase(name)\n const plural = pluralize(kebab)\n const pluralPascal = pluralizePascal(pascal)\n const moduleDir = join(modulesDir, plural)\n\n const files: string[] = []\n\n const write = async (relativePath: string, content: string) => {\n const fullPath = join(moduleDir, relativePath)\n await writeFileSafe(fullPath, content)\n files.push(fullPath)\n }\n\n // ── Module Index ────────────────────────────────────────────────────\n await write('index.ts', generateModuleIndex(pascal, kebab, plural, repo))\n\n // ── Constants ──────────────────────────────────────────────────────\n await write('constants.ts', generateConstants(pascal))\n\n // ── Controller ──────────────────────────────────────────────────────\n await write(\n `presentation/${kebab}.controller.ts`,\n generateController(pascal, kebab, plural, pluralPascal),\n )\n\n // ── DTOs ────────────────────────────────────────────────────────────\n await write(`application/dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab))\n await write(`application/dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab))\n await write(`application/dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab))\n\n // ── Use Cases ───────────────────────────────────────────────────────\n const useCases = generateUseCases(pascal, kebab, plural, pluralPascal)\n for (const uc of useCases) {\n await write(`application/use-cases/${uc.file}`, uc.content)\n }\n\n // ── Domain: Repository Interface ────────────────────────────────────\n await write(\n `domain/repositories/${kebab}.repository.ts`,\n generateRepositoryInterface(pascal, kebab),\n )\n\n // ── Domain: Service ─────────────────────────────────────────────────\n await write(`domain/services/${kebab}-domain.service.ts`, generateDomainService(pascal, kebab))\n\n // ── Infrastructure: Repository Implementation ──────────────────────\n if (repo === 'inmemory') {\n await write(\n `infrastructure/repositories/in-memory-${kebab}.repository.ts`,\n generateInMemoryRepository(pascal, kebab),\n )\n }\n\n // ── Entity & Value Objects ──────────────────────────────────────────\n if (!noEntity && !minimal) {\n await write(`domain/entities/${kebab}.entity.ts`, generateEntity(pascal, kebab))\n await write(`domain/value-objects/${kebab}-id.vo.ts`, generateValueObject(pascal, kebab))\n }\n\n // ── Tests ──────────────────────────────────────────────────────────\n if (!noTests) {\n await write(\n `__tests__/${kebab}.controller.test.ts`,\n generateControllerTest(pascal, kebab, plural),\n )\n await write(\n `__tests__/${kebab}.repository.test.ts`,\n generateRepositoryTest(pascal, kebab, plural),\n )\n }\n\n // ── Auto-register in modules index ──────────────────────────────────\n await autoRegisterModule(modulesDir, pascal, plural)\n\n return files\n}\n\n/** Add the new module to src/modules/index.ts */\nasync function autoRegisterModule(\n modulesDir: string,\n pascal: string,\n plural: string,\n): Promise<void> {\n const indexPath = join(modulesDir, 'index.ts')\n const exists = await fileExists(indexPath)\n\n if (!exists) {\n await writeFileSafe(\n indexPath,\n `import type { AppModuleClass } from '@forinda/kickjs-core'\nimport { ${pascal}Module } from './${plural}'\n\nexport const modules: AppModuleClass[] = [${pascal}Module]\n`,\n )\n return\n }\n\n let content = await readFile(indexPath, 'utf-8')\n\n // Add import if not present\n const importLine = `import { ${pascal}Module } from './${plural}'`\n if (!content.includes(`${pascal}Module`)) {\n // Insert import after last existing import\n const lastImportIdx = content.lastIndexOf('import ')\n if (lastImportIdx !== -1) {\n const lineEnd = content.indexOf('\\n', lastImportIdx)\n content = content.slice(0, lineEnd + 1) + importLine + '\\n' + content.slice(lineEnd + 1)\n } else {\n content = importLine + '\\n' + content\n }\n\n // Add to modules array — handle both empty and existing entries\n // Match the array assignment: `= [...]` or `= [\\n...\\n]`\n content = content.replace(/(=\\s*\\[)([\\s\\S]*?)(])/, (_match, open, existing, close) => {\n const trimmed = existing.trim()\n if (!trimmed) {\n // Empty array: `= []`\n return `${open}${pascal}Module${close}`\n }\n // Existing entries: append with comma\n const needsComma = trimmed.endsWith(',') ? '' : ','\n return `${open}${existing.trimEnd()}${needsComma} ${pascal}Module${close}`\n })\n }\n\n await writeFile(indexPath, content, 'utf-8')\n}\n","import { writeFile, mkdir, access, readFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\n/** Write a file, creating parent directories if needed */\nexport async function writeFileSafe(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true })\n await writeFile(filePath, content, 'utf-8')\n}\n\n/** Ensure a directory exists */\nexport async function ensureDirectory(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true })\n}\n\n/** Check if a file exists */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath)\n return true\n } catch {\n return false\n }\n}\n\n/** Read a JSON file */\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\n const content = await readFile(filePath, 'utf-8')\n return JSON.parse(content)\n}\n","/** Convert a name to PascalCase */\nexport function toPascalCase(name: string): string {\n return name\n .replace(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^(.)/, (c) => c.toUpperCase())\n}\n\n/** Convert a name to camelCase */\nexport function toCamelCase(name: string): string {\n const pascal = toPascalCase(name)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\n/** Convert a name to kebab-case */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase()\n}\n\n/**\n * Pluralize a kebab-case name for directory/file names.\n * If already plural (ends in 's'), returns as-is.\n */\nexport function pluralize(name: string): string {\n if (name.endsWith('s')) return name\n if (name.endsWith('x') || name.endsWith('z')) return name + 'es'\n if (name.endsWith('sh') || name.endsWith('ch')) return name + 'es'\n if (name.endsWith('y') && !/[aeiou]y$/.test(name)) return name.slice(0, -1) + 'ies'\n return name + 's'\n}\n\n/**\n * Pluralize a PascalCase name for class identifiers.\n * If already plural (ends in 's'), returns as-is.\n * Used for `List${pluralPascal}UseCase` to avoid `ListUserssUseCase`.\n */\nexport function pluralizePascal(name: string): string {\n if (name.endsWith('s')) return name\n if (name.endsWith('x') || name.endsWith('z')) return name + 'es'\n if (name.endsWith('sh') || name.endsWith('ch')) return name + 'es'\n if (name.endsWith('y') && !/[aeiou]y$/i.test(name)) return name.slice(0, -1) + 'ies'\n return name + 's'\n}\n","export function generateModuleIndex(\n pascal: string,\n kebab: string,\n plural: string,\n repo: 'drizzle' | 'inmemory',\n): string {\n const repoClass =\n repo === 'inmemory' ? `InMemory${pascal}Repository` : `Drizzle${pascal}Repository`\n const repoFile = repo === 'inmemory' ? `in-memory-${kebab}` : `drizzle-${kebab}`\n\n return `/**\n * ${pascal} Module\n *\n * Self-contained feature module following Domain-Driven Design (DDD).\n * Registers dependencies in the DI container and declares HTTP routes.\n *\n * Structure:\n * presentation/ — HTTP controllers (entry points)\n * application/ — Use cases (orchestration) and DTOs (validation)\n * domain/ — Entities, value objects, repository interfaces, domain services\n * infrastructure/ — Repository implementations (in-memory, Drizzle, Prisma, etc.)\n */\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'\nimport { buildRoutes } from '@forinda/kickjs-http'\nimport { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'\nimport { ${repoClass} } from './infrastructure/repositories/${repoFile}.repository'\nimport { ${pascal}Controller } from './presentation/${kebab}.controller'\n\n// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container\nimport.meta.glob(\n ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],\n { eager: true },\n)\n\nexport class ${pascal}Module implements AppModule {\n /**\n * Register module dependencies in the DI container.\n * Bind repository interface tokens to their implementations here.\n * To swap implementations (e.g. in-memory -> Drizzle), change the factory target.\n */\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n }\n\n /**\n * Declare HTTP routes for this module.\n * The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${plural}).\n * Passing 'controller' enables automatic OpenAPI spec generation via SwaggerAdapter.\n */\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n router: buildRoutes(${pascal}Controller),\n controller: ${pascal}Controller,\n }\n }\n}\n`\n}\n","export function generateController(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): string {\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'\nimport { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'\nimport { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'\nimport { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}.use-case'\nimport { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'\nimport { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../constants'\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private create${pascal}UseCase!: Create${pascal}UseCase\n @Autowired() private get${pascal}UseCase!: Get${pascal}UseCase\n @Autowired() private list${pluralPascal}UseCase!: List${pluralPascal}UseCase\n @Autowired() private update${pascal}UseCase!: Update${pascal}UseCase\n @Autowired() private delete${pascal}UseCase!: Delete${pascal}UseCase\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: RequestContext) {\n return ctx.paginate(\n (parsed) => this.list${pluralPascal}UseCase.execute(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: RequestContext) {\n const result = await this.get${pascal}UseCase.execute(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: RequestContext) {\n const result = await this.create${pascal}UseCase.execute(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: RequestContext) {\n const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: RequestContext) {\n await this.delete${pascal}UseCase.execute(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n","export function generateConstants(pascal: string): string {\n return `import type { QueryParamsConfig } from '@forinda/kickjs-core'\n\nexport const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {\n filterable: ['name'],\n sortable: ['name', 'createdAt'],\n searchable: ['name'],\n}\n`\n}\n","export function generateCreateDTO(pascal: string, kebab: string): string {\n return `import { z } from 'zod'\n\n/**\n * Create ${pascal} DTO — Zod schema for validating POST request bodies.\n * This schema is passed to @Post('/', { body: create${pascal}Schema }) for automatic validation.\n * It also generates OpenAPI request body docs when SwaggerAdapter is used.\n *\n * Add more fields as needed. Supported Zod types:\n * z.string(), z.number(), z.boolean(), z.enum([...]),\n * z.array(), z.object(), .optional(), .default(), .transform()\n */\nexport const create${pascal}Schema = z.object({\n name: z.string().min(1, 'Name is required').max(200),\n})\n\nexport type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>\n`\n}\n\nexport function generateUpdateDTO(pascal: string, kebab: string): string {\n return `import { z } from 'zod'\n\nexport const update${pascal}Schema = z.object({\n name: z.string().min(1).max(200).optional(),\n})\n\nexport type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>\n`\n}\n\nexport function generateResponseDTO(pascal: string, kebab: string): string {\n return `export interface ${pascal}ResponseDTO {\n id: string\n name: string\n createdAt: string\n updatedAt: string\n}\n`\n}\n","export function generateUseCases(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): { file: string; content: string }[] {\n return [\n {\n file: `create-${kebab}.use-case.ts`,\n content: `/**\n * Create ${pascal} Use Case\n *\n * Application layer — orchestrates a single business operation.\n * Use cases are thin: validate input (via DTO), call domain/repo, return response.\n * Keep business rules in the domain service, not here.\n */\nimport { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Create${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.create(dto)\n }\n}\n`,\n },\n {\n file: `get-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Get${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n}\n`,\n },\n {\n file: `list-${plural}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\n\n@Service()\nexport class List${pluralPascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(parsed: ParsedQuery) {\n return this.repo.findPaginated(parsed)\n }\n}\n`,\n },\n {\n file: `update-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Update${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.update(id, dto)\n }\n}\n`,\n },\n {\n file: `delete-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\n\n@Service()\nexport class Delete${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<void> {\n await this.repo.delete(id)\n }\n}\n`,\n },\n ]\n}\n","export function generateRepositoryInterface(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} Repository Interface\n *\n * Domain layer — defines the contract for data access.\n * The interface lives in the domain layer; implementations live in infrastructure.\n * This inversion of dependencies keeps the domain pure and testable.\n *\n * To swap implementations (e.g. in-memory -> Drizzle -> Prisma),\n * change the factory in the module's register() method.\n */\nimport type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\n\nexport interface I${pascal}Repository {\n findById(id: string): Promise<${pascal}ResponseDTO | null>\n findAll(): Promise<${pascal}ResponseDTO[]>\n findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }>\n create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>\n update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>\n delete(id: string): Promise<void>\n}\n\nexport const ${pascal.toUpperCase()}_REPOSITORY = Symbol('I${pascal}Repository')\n`\n}\n\nexport function generateInMemoryRepository(pascal: string, kebab: string): string {\n return `/**\n * In-Memory ${pascal} Repository\n *\n * Infrastructure layer — implements the repository interface using a Map.\n * Useful for prototyping and testing. Replace with a database implementation\n * (Drizzle, Prisma, etc.) for production use.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { randomUUID } from 'node:crypto'\nimport { Repository, HttpException } from '@forinda/kickjs-core'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\nimport type { I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'\n\n@Repository()\nexport class InMemory${pascal}Repository implements I${pascal}Repository {\n private store = new Map<string, ${pascal}ResponseDTO>()\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.store.get(id) ?? null\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return Array.from(this.store.values())\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n const all = Array.from(this.store.values())\n const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)\n return { data, total: all.length }\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const now = new Date().toISOString()\n const entity: ${pascal}ResponseDTO = {\n id: randomUUID(),\n name: dto.name,\n createdAt: now,\n updatedAt: now,\n }\n this.store.set(entity.id, entity)\n return entity\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const existing = this.store.get(id)\n if (!existing) throw HttpException.notFound('${pascal} not found')\n const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }\n this.store.set(id, updated)\n return updated\n }\n\n async delete(id: string): Promise<void> {\n if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')\n this.store.delete(id)\n }\n}\n`\n}\n","export function generateDomainService(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} Domain Service\n *\n * Domain layer — contains business rules that don't belong to a single entity.\n * Use this for cross-entity logic, validation rules, and domain invariants.\n * Keep it free of HTTP/framework concerns.\n */\nimport { Service, Inject, HttpException } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'\n\n@Service()\nexport class ${pascal}DomainService {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async ensureExists(id: string): Promise<void> {\n const entity = await this.repo.findById(id)\n if (!entity) {\n throw HttpException.notFound('${pascal} not found')\n }\n }\n}\n`\n}\n\nexport function generateEntity(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} Entity\n *\n * Domain layer — the core business object.\n * Uses a private constructor with static factory methods (create, reconstitute)\n * to enforce invariants. Properties are accessed via getters to maintain encapsulation.\n *\n * Patterns used:\n * - Private constructor: prevents direct instantiation\n * - create(): factory for new entities (generates ID, sets timestamps)\n * - reconstitute(): factory for rebuilding from persistence (no side effects)\n * - changeName(): mutation method that enforces business rules\n */\nimport { ${pascal}Id } from '../value-objects/${kebab}-id.vo'\n\ninterface ${pascal}Props {\n id: ${pascal}Id\n name: string\n createdAt: Date\n updatedAt: Date\n}\n\nexport class ${pascal} {\n private constructor(private props: ${pascal}Props) {}\n\n static create(params: { name: string }): ${pascal} {\n const now = new Date()\n return new ${pascal}({\n id: ${pascal}Id.create(),\n name: params.name,\n createdAt: now,\n updatedAt: now,\n })\n }\n\n static reconstitute(props: ${pascal}Props): ${pascal} {\n return new ${pascal}(props)\n }\n\n get id(): ${pascal}Id {\n return this.props.id\n }\n get name(): string {\n return this.props.name\n }\n get createdAt(): Date {\n return this.props.createdAt\n }\n get updatedAt(): Date {\n return this.props.updatedAt\n }\n\n changeName(name: string): void {\n if (!name || name.trim().length === 0) {\n throw new Error('Name cannot be empty')\n }\n this.props.name = name.trim()\n this.props.updatedAt = new Date()\n }\n\n toJSON() {\n return {\n id: this.props.id.toString(),\n name: this.props.name,\n createdAt: this.props.createdAt.toISOString(),\n updatedAt: this.props.updatedAt.toISOString(),\n }\n }\n}\n`\n}\n\nexport function generateValueObject(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} ID Value Object\n *\n * Domain layer — wraps a primitive ID with type safety and validation.\n * Value objects are immutable and compared by value, not reference.\n *\n * ${pascal}Id.create() — generate a new UUID\n * ${pascal}Id.from(id) — wrap an existing ID string (validates non-empty)\n * id.equals(other) — compare two IDs by value\n */\nimport { randomUUID } from 'node:crypto'\n\nexport class ${pascal}Id {\n private constructor(private readonly value: string) {}\n\n static create(): ${pascal}Id {\n return new ${pascal}Id(randomUUID())\n }\n\n static from(id: string): ${pascal}Id {\n if (!id || id.trim().length === 0) {\n throw new Error('${pascal}Id cannot be empty')\n }\n return new ${pascal}Id(id)\n }\n\n toString(): string {\n return this.value\n }\n\n equals(other: ${pascal}Id): boolean {\n return this.value === other.value\n }\n}\n`\n}\n","export function generateControllerTest(pascal: string, kebab: string, plural: string): string {\n return `import { describe, it, expect, beforeEach } from 'vitest'\nimport { Container } from '@forinda/kickjs-core'\n\ndescribe('${pascal}Controller', () => {\n beforeEach(() => {\n Container.reset()\n })\n\n it('should be defined', () => {\n expect(true).toBe(true)\n })\n\n describe('POST /${plural}', () => {\n it('should create a new ${kebab}', async () => {\n // TODO: Set up test module, call create endpoint, assert 201\n expect(true).toBe(true)\n })\n })\n\n describe('GET /${plural}', () => {\n it('should return paginated ${plural}', async () => {\n // TODO: Set up test module, call list endpoint, assert { data, meta }\n expect(true).toBe(true)\n })\n })\n\n describe('GET /${plural}/:id', () => {\n it('should return a ${kebab} by id', async () => {\n // TODO: Create a ${kebab}, then fetch by id, assert match\n expect(true).toBe(true)\n })\n\n it('should return 404 for non-existent ${kebab}', async () => {\n // TODO: Fetch non-existent id, assert 404\n expect(true).toBe(true)\n })\n })\n\n describe('PUT /${plural}/:id', () => {\n it('should update an existing ${kebab}', async () => {\n // TODO: Create, update, assert changes\n expect(true).toBe(true)\n })\n })\n\n describe('DELETE /${plural}/:id', () => {\n it('should delete a ${kebab}', async () => {\n // TODO: Create, delete, assert gone\n expect(true).toBe(true)\n })\n })\n})\n`\n}\n\nexport function generateRepositoryTest(pascal: string, kebab: string, plural: string): string {\n return `import { describe, it, expect, beforeEach } from 'vitest'\nimport { InMemory${pascal}Repository } from '../infrastructure/repositories/in-memory-${kebab}.repository'\n\ndescribe('InMemory${pascal}Repository', () => {\n let repo: InMemory${pascal}Repository\n\n beforeEach(() => {\n repo = new InMemory${pascal}Repository()\n })\n\n it('should create and retrieve a ${kebab}', async () => {\n const created = await repo.create({ name: 'Test ${pascal}' })\n expect(created).toBeDefined()\n expect(created.name).toBe('Test ${pascal}')\n expect(created.id).toBeDefined()\n\n const found = await repo.findById(created.id)\n expect(found).toEqual(created)\n })\n\n it('should return null for non-existent id', async () => {\n const found = await repo.findById('non-existent')\n expect(found).toBeNull()\n })\n\n it('should list all ${plural}', async () => {\n await repo.create({ name: '${pascal} 1' })\n await repo.create({ name: '${pascal} 2' })\n\n const all = await repo.findAll()\n expect(all).toHaveLength(2)\n })\n\n it('should return paginated results', async () => {\n await repo.create({ name: '${pascal} 1' })\n await repo.create({ name: '${pascal} 2' })\n await repo.create({ name: '${pascal} 3' })\n\n const result = await repo.findPaginated({\n filters: [],\n sort: [],\n search: '',\n pagination: { page: 1, limit: 2, offset: 0 },\n })\n\n expect(result.data).toHaveLength(2)\n expect(result.total).toBe(3)\n })\n\n it('should update a ${kebab}', async () => {\n const created = await repo.create({ name: 'Original' })\n const updated = await repo.update(created.id, { name: 'Updated' })\n expect(updated.name).toBe('Updated')\n })\n\n it('should delete a ${kebab}', async () => {\n const created = await repo.create({ name: 'To Delete' })\n await repo.delete(created.id)\n const found = await repo.findById(created.id)\n expect(found).toBeNull()\n })\n})\n`\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateAdapterOptions {\n name: string\n outDir: string\n}\n\nexport async function generateAdapter(options: GenerateAdapterOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.adapter.ts`)\n await writeFileSafe(\n filePath,\n `import type { Express } from 'express'\nimport type { AppAdapter, AdapterMiddleware, Container } from '@forinda/kickjs-core'\n\nexport interface ${pascal}AdapterOptions {\n // Add your adapter configuration here\n}\n\n/**\n * ${pascal} adapter.\n *\n * Hooks into the Application lifecycle to add middleware, routes,\n * or external service connections.\n *\n * Usage:\n * bootstrap({\n * adapters: [new ${pascal}Adapter({ ... })],\n * })\n */\nexport class ${pascal}Adapter implements AppAdapter {\n name = '${pascal}Adapter'\n\n constructor(private options: ${pascal}AdapterOptions = {}) {}\n\n /**\n * Return middleware entries that the Application will mount.\n * Use \\`phase\\` to control where in the pipeline they run:\n * 'beforeGlobal' | 'afterGlobal' | 'beforeRoutes' | 'afterRoutes'\n */\n middleware(): AdapterMiddleware[] {\n return [\n // Example: add a custom header to all responses\n // {\n // phase: 'beforeGlobal',\n // handler: (_req: any, res: any, next: any) => {\n // res.setHeader('X-${pascal}', 'true')\n // next()\n // },\n // },\n // Example: scope middleware to a specific path\n // {\n // phase: 'beforeRoutes',\n // path: '/api/v1/admin',\n // handler: myAdminMiddleware(),\n // },\n ]\n }\n\n /**\n * Called before global middleware.\n * Use this to mount routes that bypass the middleware stack\n * (health checks, docs UI, static assets).\n */\n beforeMount(app: Express, container: Container): void {\n // Example: mount a status route\n // app.get('/${kebab}/status', (_req, res) => {\n // res.json({ status: 'ok' })\n // })\n }\n\n /**\n * Called after modules and routes are registered, before the server starts.\n * Use this for late-stage DI registrations or config validation.\n */\n beforeStart(app: Express, container: Container): void {\n // Example: register a service in the DI container\n // container.registerInstance(MY_TOKEN, new MyService(this.options))\n }\n\n /**\n * Called after the HTTP server is listening.\n * Use this to attach to the raw http.Server (Socket.IO, gRPC, etc).\n */\n afterStart(server: any, container: Container): void {\n // Example: attach Socket.IO\n // const io = new Server(server)\n // container.registerInstance(SOCKET_IO, io)\n }\n\n /**\n * Called on graceful shutdown. Clean up connections.\n */\n async shutdown(): Promise<void> {\n // Example: close a connection pool\n // await this.pool.end()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateMiddlewareOptions {\n name: string\n outDir: string\n}\n\nexport async function generateMiddleware(options: GenerateMiddlewareOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.middleware.ts`)\n await writeFileSafe(\n filePath,\n `import type { Request, Response, NextFunction } from 'express'\n\nexport interface ${toPascalCase(name)}Options {\n // Add configuration options here\n}\n\n/**\n * ${toPascalCase(name)} middleware.\n *\n * Usage in bootstrap:\n * middleware: [${camel}()]\n *\n * Usage with adapter:\n * middleware() { return [{ handler: ${camel}(), phase: 'afterGlobal' }] }\n *\n * Usage with @Middleware decorator:\n * @Middleware(${camel}())\n */\nexport function ${camel}(options: ${toPascalCase(name)}Options = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n // Implement your middleware logic here\n next()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateGuardOptions {\n name: string\n outDir: string\n}\n\nexport async function generateGuard(options: GenerateGuardOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.guard.ts`)\n await writeFileSafe(\n filePath,\n `import { Container, HttpException } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n/**\n * ${pascal} guard.\n *\n * Guards protect routes by checking conditions before the handler runs.\n * Return early with an error response to block access.\n *\n * Usage:\n * @Middleware(${camel}Guard)\n * @Get('/protected')\n * async handler(ctx: RequestContext) { ... }\n */\nexport async function ${camel}Guard(ctx: RequestContext, next: () => void): Promise<void> {\n // Example: check for an authorization header\n const header = ctx.headers.authorization\n if (!header?.startsWith('Bearer ')) {\n ctx.res.status(401).json({ message: 'Missing or invalid authorization header' })\n return\n }\n\n const token = header.slice(7)\n\n try {\n // Verify the token using a service from the DI container\n // const container = Container.getInstance()\n // const authService = container.resolve(AuthService)\n // const payload = authService.verifyToken(token)\n // ctx.set('auth', payload)\n\n next()\n } catch {\n ctx.res.status(401).json({ message: 'Invalid or expired token' })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateServiceOptions {\n name: string\n outDir: string\n}\n\nexport async function generateService(options: GenerateServiceOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.service.ts`)\n await writeFileSafe(\n filePath,\n `import { Service } from '@forinda/kickjs-core'\n\n@Service()\nexport class ${pascal}Service {\n // Inject dependencies via constructor\n // constructor(\n // @Inject(MY_REPO) private readonly repo: IMyRepository,\n // ) {}\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateControllerOptions {\n name: string\n outDir: string\n}\n\nexport async function generateController(options: GenerateControllerOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.controller.ts`)\n await writeFileSafe(\n filePath,\n `import { Controller, Get, Post, Autowired } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n@Controller()\nexport class ${pascal}Controller {\n // @Autowired() private myService!: MyService\n\n @Get('/')\n async list(ctx: RequestContext) {\n ctx.json({ message: '${pascal} list' })\n }\n\n @Post('/')\n async create(ctx: RequestContext) {\n ctx.created({ message: '${pascal} created', data: ctx.body })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\n\ninterface GenerateDtoOptions {\n name: string\n outDir: string\n}\n\nexport async function generateDto(options: GenerateDtoOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.dto.ts`)\n await writeFileSafe(\n filePath,\n `import { z } from 'zod'\n\nexport const ${camel}Schema = z.object({\n // Define your schema fields here\n name: z.string().min(1).max(200),\n})\n\nexport type ${pascal}DTO = z.infer<typeof ${camel}Schema>\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join, dirname } from 'node:path'\nimport { execSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { writeFileSafe } from '../utils/fs'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst cliPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))\nconst KICKJS_VERSION = `^${cliPkg.version}`\n\ntype ProjectTemplate = 'rest' | 'graphql' | 'ddd' | 'microservice' | 'minimal'\n\ninterface InitProjectOptions {\n name: string\n directory: string\n packageManager?: 'pnpm' | 'npm' | 'yarn'\n initGit?: boolean\n installDeps?: boolean\n template?: ProjectTemplate\n}\n\n/** Scaffold a new KickJS project */\nexport async function initProject(options: InitProjectOptions): Promise<void> {\n const { name, directory, packageManager = 'pnpm', template = 'rest' } = options\n const dir = directory\n\n console.log(`\\n Creating KickJS project: ${name}\\n`)\n\n // ── package.json — template-aware deps ────────────────────────────\n const baseDeps: Record<string, string> = {\n '@forinda/kickjs-core': KICKJS_VERSION,\n '@forinda/kickjs-http': KICKJS_VERSION,\n '@forinda/kickjs-config': KICKJS_VERSION,\n express: '^5.1.0',\n 'reflect-metadata': '^0.2.2',\n zod: '^4.3.6',\n pino: '^10.3.1',\n 'pino-pretty': '^13.1.3',\n }\n\n // Add template-specific deps\n if (template !== 'minimal') {\n baseDeps['@forinda/kickjs-swagger'] = KICKJS_VERSION\n }\n if (template === 'graphql') {\n baseDeps['@forinda/kickjs-graphql'] = KICKJS_VERSION\n baseDeps['graphql'] = '^16.11.0'\n }\n if (template === 'microservice') {\n baseDeps['@forinda/kickjs-queue'] = KICKJS_VERSION\n baseDeps['@forinda/kickjs-otel'] = KICKJS_VERSION\n }\n if (template === 'ddd') {\n baseDeps['@forinda/kickjs-swagger'] = KICKJS_VERSION\n }\n\n await writeFileSafe(\n join(dir, 'package.json'),\n JSON.stringify(\n {\n name,\n version: cliPkg.version,\n type: 'module',\n scripts: {\n dev: 'kick dev',\n 'dev:debug': 'kick dev:debug',\n build: 'kick build',\n start: 'kick start',\n test: 'vitest run',\n 'test:watch': 'vitest',\n typecheck: 'tsc --noEmit',\n lint: 'eslint src/',\n format: 'prettier --write src/',\n },\n dependencies: baseDeps,\n devDependencies: {\n '@forinda/kickjs-cli': KICKJS_VERSION,\n '@swc/core': '^1.7.28',\n '@types/express': '^5.0.6',\n '@types/node': '^24.5.2',\n 'unplugin-swc': '^1.5.9',\n vite: '^7.3.1',\n 'vite-node': '^5.3.0',\n vitest: '^3.2.4',\n typescript: '^5.9.2',\n prettier: '^3.8.1',\n },\n },\n null,\n 2,\n ),\n )\n\n // ── vite.config.ts — enables HMR + SWC for decorators ──────────────\n await writeFileSafe(\n join(dir, 'vite.config.ts'),\n `import { defineConfig } from 'vite'\nimport { resolve } from 'path'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n resolve: {\n alias: {\n '@': resolve(__dirname, 'src'),\n },\n },\n server: {\n watch: { usePolling: false },\n hmr: true,\n },\n build: {\n target: 'node20',\n ssr: true,\n outDir: 'dist',\n sourcemap: true,\n rollupOptions: {\n input: resolve(__dirname, 'src/index.ts'),\n output: { format: 'esm' },\n },\n },\n})\n`,\n )\n\n // ── tsconfig.json ───────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'tsconfig.json'),\n JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['node'],\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n sourceMap: true,\n declaration: true,\n experimentalDecorators: true,\n emitDecoratorMetadata: true,\n outDir: 'dist',\n rootDir: 'src',\n paths: { '@/*': ['./src/*'] },\n },\n include: ['src'],\n },\n null,\n 2,\n ),\n )\n\n // ── .prettierrc ─────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.prettierrc'),\n JSON.stringify(\n {\n semi: false,\n singleQuote: true,\n trailingComma: 'all',\n printWidth: 100,\n tabWidth: 2,\n },\n null,\n 2,\n ),\n )\n\n // ── .gitignore ──────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.gitignore'),\n `node_modules/\ndist/\n.env\ncoverage/\n.DS_Store\n*.tsbuildinfo\n`,\n )\n\n // ── .env ────────────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.env'),\n `PORT=3000\nNODE_ENV=development\n`,\n )\n\n await writeFileSafe(\n join(dir, '.env.example'),\n `PORT=3000\nNODE_ENV=development\n`,\n )\n\n // ── src/index.ts — template-aware entry point ─────────────────────\n await writeFileSafe(join(dir, 'src/index.ts'), getEntryFile(name, template))\n\n // ── src/modules/index.ts ────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'src/modules/index.ts'),\n `import type { AppModuleClass } from '@forinda/kickjs-core'\n\nexport const modules: AppModuleClass[] = []\n`,\n )\n\n // ── Template-specific files ─────────────────────────────────────────\n if (template === 'graphql') {\n await writeFileSafe(join(dir, 'src/resolvers/.gitkeep'), '')\n }\n\n // ── kick.config.ts — CLI configuration ─────────────────────────────\n await writeFileSafe(\n join(dir, 'kick.config.ts'),\n `import { defineConfig } from '@forinda/kickjs-cli'\n\nexport default defineConfig({\n pattern: '${template}',\n modulesDir: 'src/modules',\n defaultRepo: 'inmemory',\n\n commands: [\n {\n name: 'test',\n description: 'Run tests with Vitest',\n steps: 'npx vitest run',\n },\n {\n name: 'format',\n description: 'Format code with Prettier',\n steps: 'npx prettier --write src/',\n },\n {\n name: 'format:check',\n description: 'Check formatting without writing',\n steps: 'npx prettier --check src/',\n },\n {\n name: 'check',\n description: 'Run typecheck + format check',\n steps: ['npx tsc --noEmit', 'npx prettier --check src/'],\n aliases: ['verify', 'ci'],\n },\n ],\n})\n`,\n )\n\n // ── vitest.config.ts ────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'vitest.config.ts'),\n `import { defineConfig } from 'vitest/config'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n test: {\n globals: true,\n environment: 'node',\n include: ['src/**/*.test.ts'],\n },\n})\n`,\n )\n\n // ── Git Init ─────────────────────────────────────────────────────────\n if (options.initGit) {\n try {\n execSync('git init', { cwd: dir, stdio: 'pipe' })\n execSync('git add -A', { cwd: dir, stdio: 'pipe' })\n execSync('git commit -m \"chore: initial commit from kick new\"', {\n cwd: dir,\n stdio: 'pipe',\n })\n console.log(' Git repository initialized')\n } catch {\n console.log(' Warning: git init failed (git may not be installed)')\n }\n }\n\n // ── Install Dependencies ────────────────────────────────────────────\n if (options.installDeps) {\n console.log(`\\n Installing dependencies with ${packageManager}...\\n`)\n try {\n execSync(`${packageManager} install`, { cwd: dir, stdio: 'inherit' })\n console.log('\\n Dependencies installed successfully!')\n } catch {\n console.log(`\\n Warning: ${packageManager} install failed. Run it manually.`)\n }\n }\n\n console.log('\\n Project scaffolded successfully!')\n console.log()\n\n const needsCd = dir !== process.cwd()\n console.log(' Next steps:')\n if (needsCd) console.log(` cd ${name}`)\n if (!options.installDeps) console.log(` ${packageManager} install`)\n\n const genHint: Record<string, string> = {\n rest: 'kick g module user',\n graphql: 'kick g resolver user',\n ddd: 'kick g module user --repo drizzle',\n microservice: 'kick g module user && kick g job email',\n minimal: '# add your routes to src/index.ts',\n }\n console.log(` ${genHint[template] ?? genHint.rest}`)\n console.log(' kick dev')\n console.log()\n console.log(' Commands:')\n console.log(' kick dev Start dev server with Vite HMR')\n console.log(' kick build Production build via Vite')\n console.log(' kick start Run production build')\n console.log(` kick g module X Generate a DDD module`)\n if (template === 'graphql') console.log(' kick g resolver X Generate a GraphQL resolver')\n if (template === 'microservice')\n console.log(' kick g job X Generate a queue job processor')\n console.log()\n}\n\n// ── Entry file templates ─────────────────────────────────────────────────\n\nfunction getEntryFile(name: string, template: ProjectTemplate): string {\n switch (template) {\n case 'graphql':\n return `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { DevToolsAdapter } from '@forinda/kickjs-devtools'\nimport { GraphQLAdapter } from '@forinda/kickjs-graphql'\nimport { modules } from './modules'\n\n// Import your resolvers here\n// import { UserResolver } from './resolvers/user.resolver'\n\nbootstrap({\n modules,\n adapters: [\n new DevToolsAdapter(),\n new GraphQLAdapter({\n resolvers: [/* UserResolver */],\n // Add custom type definitions here:\n // typeDefs: userTypeDefs,\n }),\n ],\n})\n`\n\n case 'microservice':\n return `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { DevToolsAdapter } from '@forinda/kickjs-devtools'\nimport { SwaggerAdapter } from '@forinda/kickjs-swagger'\nimport { OtelAdapter } from '@forinda/kickjs-otel'\n// import { QueueAdapter, BullMQProvider } from '@forinda/kickjs-queue'\nimport { modules } from './modules'\n\nbootstrap({\n modules,\n adapters: [\n new OtelAdapter({ serviceName: '${name}' }),\n new DevToolsAdapter(),\n new SwaggerAdapter({\n info: { title: '${name}', version: '${cliPkg.version}' },\n }),\n // Uncomment when Redis is available:\n // new QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],\n})\n`\n\n case 'minimal':\n return `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { modules } from './modules'\n\nbootstrap({ modules })\n`\n\n case 'ddd':\n case 'rest':\n default:\n return `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { DevToolsAdapter } from '@forinda/kickjs-devtools'\nimport { SwaggerAdapter } from '@forinda/kickjs-swagger'\nimport { modules } from './modules'\n\nbootstrap({\n modules,\n adapters: [\n new DevToolsAdapter(),\n new SwaggerAdapter({\n info: { title: '${name}', version: '${cliPkg.version}' },\n }),\n ],\n})\n`\n }\n}\n","import { readFile, access } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n/** A custom command that developers can register via kick.config.ts */\nexport interface KickCommandDefinition {\n /** The command name (e.g. 'db:migrate', 'seed', 'proto:gen') */\n name: string\n /** Description shown in --help */\n description: string\n /**\n * Shell command(s) to run. Can be a single string or an array of\n * sequential steps. Use {args} as a placeholder for CLI arguments.\n *\n * @example\n * 'npx drizzle-kit migrate'\n * ['npx drizzle-kit generate', 'npx drizzle-kit migrate']\n */\n steps: string | string[]\n /** Optional aliases (e.g. ['migrate'] for 'db:migrate') */\n aliases?: string[]\n}\n\n/** Project pattern — controls what generators produce and which deps are installed */\nexport type ProjectPattern = 'rest' | 'graphql' | 'ddd' | 'microservice' | 'minimal'\n\n/** Configuration for the kick.config.ts file */\nexport interface KickConfig {\n /**\n * Project pattern — controls default generator behavior.\n * - 'rest' — Express + Swagger (default)\n * - 'graphql' — GraphQL + GraphiQL\n * - 'ddd' — Full DDD modules with use cases, entities, value objects\n * - 'microservice' — REST + queue workers\n * - 'minimal' — Bare Express with no scaffolding\n */\n pattern?: ProjectPattern\n /** Where modules live (default: 'src/modules') */\n modulesDir?: string\n /** Default repository implementation for generators */\n defaultRepo?: 'drizzle' | 'inmemory' | 'prisma'\n /** Drizzle schema output directory */\n schemaDir?: string\n /**\n * Directories to copy to dist/ after build.\n * Useful for EJS templates, email templates, static assets, etc.\n *\n * @example\n * ```ts\n * copyDirs: [\n * 'src/views', // copies to dist/src/views\n * { src: 'src/views', dest: 'dist/views' }, // custom dest\n * 'src/emails',\n * ]\n * ```\n */\n copyDirs?: Array<string | { src: string; dest?: string }>\n /** Custom commands that extend the CLI */\n commands?: KickCommandDefinition[]\n /** Code style overrides (auto-detected from prettier when possible) */\n style?: {\n semicolons?: boolean\n quotes?: 'single' | 'double'\n trailingComma?: 'all' | 'es5' | 'none'\n indent?: number\n }\n}\n\n/** Helper to define a type-safe kick.config.ts */\nexport function defineConfig(config: KickConfig): KickConfig {\n return config\n}\n\nconst CONFIG_FILES = ['kick.config.ts', 'kick.config.js', 'kick.config.mjs', 'kick.config.json']\n\n/** Load kick.config.* from the project root */\nexport async function loadKickConfig(cwd: string): Promise<KickConfig | null> {\n for (const filename of CONFIG_FILES) {\n const filepath = join(cwd, filename)\n try {\n await access(filepath)\n } catch {\n continue\n }\n\n if (filename.endsWith('.json')) {\n const content = await readFile(filepath, 'utf-8')\n return JSON.parse(content)\n }\n\n // For .ts/.js/.mjs — dynamic import (use file URL for cross-platform compat)\n try {\n const { pathToFileURL } = await import('node:url')\n const mod = await import(pathToFileURL(filepath).href)\n return mod.default ?? mod\n } catch (err) {\n if (filename.endsWith('.ts')) {\n console.warn(\n `Warning: Failed to load ${filename}. TypeScript config files require ` +\n 'a runtime loader (e.g. tsx, ts-node) or use kick.config.js/.mjs instead.',\n )\n }\n continue\n }\n }\n return null\n}\n"],"mappings":";;;;AAAA,SAASA,YAAY;;;ACArB,SAASC,WAAWC,OAAOC,QAAQC,gBAAgB;AACnD,SAASC,eAAe;AAGxB,eAAsBC,cAAcC,UAAkBC,SAAe;AACnE,QAAMC,MAAMC,QAAQH,QAAAA,GAAW;IAAEI,WAAW;EAAK,CAAA;AACjD,QAAMC,UAAUL,UAAUC,SAAS,OAAA;AACrC;AAHsBF;AAWtB,eAAsBO,WAAWC,UAAgB;AAC/C,MAAI;AACF,UAAMC,OAAOD,QAAAA;AACb,WAAO;EACT,QAAQ;AACN,WAAO;EACT;AACF;AAPsBD;;;ACdf,SAASG,aAAaC,MAAY;AACvC,SAAOA,KACJC,QAAQ,gBAAgB,CAACC,GAAGC,MAAOA,IAAIA,EAAEC,YAAW,IAAK,EAAA,EACzDH,QAAQ,QAAQ,CAACE,MAAMA,EAAEC,YAAW,CAAA;AACzC;AAJgBL;AAOT,SAASM,YAAYL,MAAY;AACtC,QAAMM,SAASP,aAAaC,IAAAA;AAC5B,SAAOM,OAAOC,OAAO,CAAA,EAAGC,YAAW,IAAKF,OAAOG,MAAM,CAAA;AACvD;AAHgBJ;AAMT,SAASK,YAAYV,MAAY;AACtC,SAAOA,KACJC,QAAQ,mBAAmB,OAAA,EAC3BA,QAAQ,WAAW,GAAA,EACnBO,YAAW;AAChB;AALgBE;AAWT,SAASC,UAAUX,MAAY;AACpC,MAAIA,KAAKY,SAAS,GAAA,EAAM,QAAOZ;AAC/B,MAAIA,KAAKY,SAAS,GAAA,KAAQZ,KAAKY,SAAS,GAAA,EAAM,QAAOZ,OAAO;AAC5D,MAAIA,KAAKY,SAAS,IAAA,KAASZ,KAAKY,SAAS,IAAA,EAAO,QAAOZ,OAAO;AAC9D,MAAIA,KAAKY,SAAS,GAAA,KAAQ,CAAC,YAAYC,KAAKb,IAAAA,EAAO,QAAOA,KAAKS,MAAM,GAAG,EAAC,IAAK;AAC9E,SAAOT,OAAO;AAChB;AANgBW;AAaT,SAASG,gBAAgBd,MAAY;AAC1C,MAAIA,KAAKY,SAAS,GAAA,EAAM,QAAOZ;AAC/B,MAAIA,KAAKY,SAAS,GAAA,KAAQZ,KAAKY,SAAS,GAAA,EAAM,QAAOZ,OAAO;AAC5D,MAAIA,KAAKY,SAAS,IAAA,KAASZ,KAAKY,SAAS,IAAA,EAAO,QAAOZ,OAAO;AAC9D,MAAIA,KAAKY,SAAS,GAAA,KAAQ,CAAC,aAAaC,KAAKb,IAAAA,EAAO,QAAOA,KAAKS,MAAM,GAAG,EAAC,IAAK;AAC/E,SAAOT,OAAO;AAChB;AANgBc;;;AFnChB,SAASC,YAAAA,WAAUC,aAAAA,kBAAiB;;;AGH7B,SAASC,oBACdC,QACAC,OACAC,QACAC,MAA4B;AAE5B,QAAMC,YACJD,SAAS,aAAa,WAAWH,MAAAA,eAAqB,UAAUA,MAAAA;AAClE,QAAMK,WAAWF,SAAS,aAAa,aAAaF,KAAAA,KAAU,WAAWA,KAAAA;AAEzE,SAAO;KACJD,MAAAA;;;;;;;;;;;;;WAaMA,OAAOM,YAAW,CAAA,6CAA+CL,KAAAA;WACjEG,SAAAA,0CAAmDC,QAAAA;WACnDL,MAAAA,qCAA2CC,KAAAA;;;;;;;;eAQvCD,MAAAA;;;;;;;gCAOiBA,OAAOM,YAAW,CAAA;0BACxBF,SAAAA;;;;;;gFAMsDF,MAAAA;;;;;gBAKhEA,MAAAA;4BACYF,MAAAA;oBACRA,MAAAA;;;;;AAKpB;AA5DgBD;;;ACAT,SAASQ,mBACdC,QACAC,OACAC,QACAC,cAAoB;AAEpB,SAAO;;;iBAGQH,MAAAA,mDAAyDC,KAAAA;cAC5DD,MAAAA,gDAAsDC,KAAAA;eACrDE,YAAAA,iDAA6DD,MAAAA;iBAC3DF,MAAAA,mDAAyDC,KAAAA;iBACzDD,MAAAA,mDAAyDC,KAAAA;iBACzDD,MAAAA,6CAAmDC,KAAAA;iBACnDD,MAAAA,6CAAmDC,KAAAA;WACzDD,OAAOI,YAAW,CAAA;;;eAGdJ,MAAAA;+BACgBA,MAAAA,mBAAyBA,MAAAA;4BAC5BA,MAAAA,gBAAsBA,MAAAA;6BACrBG,YAAAA,iBAA6BA,YAAAA;+BAC3BH,MAAAA,mBAAyBA,MAAAA;+BACzBA,MAAAA,mBAAyBA,MAAAA;;;cAG1CA,MAAAA;oBACMA,OAAOI,YAAW,CAAA;;;6BAGTD,YAAAA;QACrBH,OAAOI,YAAW,CAAA;;;;;cAKZJ,MAAAA;;mCAEqBA,MAAAA;wCACKA,MAAAA;;;;6BAIXA,MAAAA,wBAA8BA,MAAAA;cAC7CA,MAAAA;;sCAEwBA,MAAAA;;;;+BAIPA,MAAAA,wBAA8BA,MAAAA;cAC/CA,MAAAA;;sCAEwBA,MAAAA;;;;;cAKxBA,MAAAA;;uBAESA,MAAAA;;;;;AAKvB;AAlEgBD;;;ACAT,SAASM,kBAAkBC,QAAc;AAC9C,SAAO;;eAEMA,OAAOC,YAAW,CAAA;;;;;;AAMjC;AATgBF;;;ACAT,SAASG,kBAAkBC,QAAgBC,OAAa;AAC7D,SAAO;;;YAGGD,MAAAA;uDAC2CA,MAAAA;;;;;;;qBAOlCA,MAAAA;;;;oBAIDA,MAAAA,8BAAoCA,MAAAA;;AAExD;AAlBgBD;AAoBT,SAASG,kBAAkBF,QAAgBC,OAAa;AAC7D,SAAO;;qBAEYD,MAAAA;;;;oBAIDA,MAAAA,8BAAoCA,MAAAA;;AAExD;AATgBE;AAWT,SAASC,oBAAoBH,QAAgBC,OAAa;AAC/D,SAAO,oBAAoBD,MAAAA;;;;;;;AAO7B;AARgBG;;;AC/BT,SAASC,iBACdC,QACAC,OACAC,QACAC,cAAoB;AAEpB,SAAO;IACL;MACEC,MAAM,UAAUH,KAAAA;MAChBI,SAAS;YACHL,MAAAA;;;;;;;WAODA,OAAOM,YAAW,CAAA,sBAAwBN,MAAAA,gDAAsDC,KAAAA;sBACrFD,MAAAA,8BAAoCC,KAAAA;gBAC1CD,MAAAA,+BAAqCC,KAAAA;;;qBAGhCD,MAAAA;;cAEPA,OAAOM,YAAW,CAAA,wCAA0CN,MAAAA;;;6BAG7CA,MAAAA,iBAAuBA,MAAAA;;;;;IAKhD;IACA;MACEI,MAAM,OAAOH,KAAAA;MACbI,SAAS;WACJL,OAAOM,YAAW,CAAA,sBAAwBN,MAAAA,gDAAsDC,KAAAA;gBAC3FD,MAAAA,+BAAqCC,KAAAA;;;kBAGnCD,MAAAA;;cAEJA,OAAOM,YAAW,CAAA,wCAA0CN,MAAAA;;;uCAGnCA,MAAAA;;;;;IAKnC;IACA;MACEI,MAAM,QAAQF,MAAAA;MACdG,SAAS;WACJL,OAAOM,YAAW,CAAA,sBAAwBN,MAAAA,gDAAsDC,KAAAA;;;;mBAIxFE,YAAAA;;cAELH,OAAOM,YAAW,CAAA,wCAA0CN,MAAAA;;;;;;;;IAQtE;IACA;MACEI,MAAM,UAAUH,KAAAA;MAChBI,SAAS;WACJL,OAAOM,YAAW,CAAA,sBAAwBN,MAAAA,gDAAsDC,KAAAA;sBACrFD,MAAAA,8BAAoCC,KAAAA;gBAC1CD,MAAAA,+BAAqCC,KAAAA;;;qBAGhCD,MAAAA;;cAEPA,OAAOM,YAAW,CAAA,wCAA0CN,MAAAA;;;yCAGjCA,MAAAA,iBAAuBA,MAAAA;;;;;IAK5D;IACA;MACEI,MAAM,UAAUH,KAAAA;MAChBI,SAAS;WACJL,OAAOM,YAAW,CAAA,sBAAwBN,MAAAA,gDAAsDC,KAAAA;;;qBAGtFD,MAAAA;;cAEPA,OAAOM,YAAW,CAAA,wCAA0CN,MAAAA;;;;;;;;IAQtE;;AAEJ;AA1GgBD;;;ACAT,SAASQ,4BAA4BC,QAAgBC,OAAa;AACvE,SAAO;KACJD,MAAAA;;;;;;;;;gBASWA,MAAAA,8CAAoDC,KAAAA;sBAC9CD,MAAAA,6CAAmDC,KAAAA;sBACnDD,MAAAA,6CAAmDC,KAAAA;;;oBAGrDD,MAAAA;kCACcA,MAAAA;uBACXA,MAAAA;wDACiCA,MAAAA;sBAClCA,MAAAA,iBAAuBA,MAAAA;kCACXA,MAAAA,iBAAuBA,MAAAA;;;;eAI1CA,OAAOE,YAAW,CAAA,0BAA4BF,MAAAA;;AAE7D;AA3BgBD;AA6BT,SAASI,2BAA2BH,QAAgBC,OAAa;AACtE,SAAO;eACMD,MAAAA;;;;;;;;;;;iBAWEA,MAAAA,gDAAsDC,KAAAA;gBACvDD,MAAAA,8CAAoDC,KAAAA;sBAC9CD,MAAAA,6CAAmDC,KAAAA;sBACnDD,MAAAA,6CAAmDC,KAAAA;;;uBAGlDD,MAAAA,0BAAgCA,MAAAA;oCACnBA,MAAAA;;wCAEIA,MAAAA;;;;6BAIXA,MAAAA;;;;8DAIiCA,MAAAA;;;;;;4BAMlCA,MAAAA,iBAAuBA,MAAAA;;oBAE/BA,MAAAA;;;;;;;;;;wCAUoBA,MAAAA,iBAAuBA,MAAAA;;mDAEZA,MAAAA;;;;;;;6DAOUA,MAAAA;;;;;AAK7D;AA9DgBG;;;AC7BT,SAASC,sBAAsBC,QAAgBC,OAAa;AACjE,SAAO;KACJD,MAAAA;;;;;;;WAOMA,OAAOE,YAAW,CAAA,sBAAwBF,MAAAA,sCAA4CC,KAAAA;;;eAGlFD,MAAAA;;cAEDA,OAAOE,YAAW,CAAA,wCAA0CF,MAAAA;;;;;;sCAMpCA,MAAAA;;;;;AAKtC;AAzBgBD;AA2BT,SAASI,eAAeH,QAAgBC,OAAa;AAC1D,SAAO;KACJD,MAAAA;;;;;;;;;;;;WAYMA,MAAAA,+BAAqCC,KAAAA;;YAEpCD,MAAAA;QACJA,MAAAA;;;;;;eAMOA,MAAAA;uCACwBA,MAAAA;;6CAEMA,MAAAA;;iBAE5BA,MAAAA;YACLA,MAAAA;;;;;;;+BAOmBA,MAAAA,WAAiBA,MAAAA;iBAC/BA,MAAAA;;;cAGHA,MAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+Bd;AAvEgBG;AAyET,SAASC,oBAAoBJ,QAAgBC,OAAa;AAC/D,SAAO;KACJD,MAAAA;;;;;OAKEA,MAAAA;OACAA,MAAAA;;;;;eAKQA,MAAAA;;;qBAGMA,MAAAA;iBACJA,MAAAA;;;6BAGYA,MAAAA;;yBAEJA,MAAAA;;iBAERA,MAAAA;;;;;;;kBAOCA,MAAAA;;;;;AAKlB;AApCgBI;;;ACpGT,SAASC,uBAAuBC,QAAgBC,OAAeC,QAAc;AAClF,SAAO;;;YAGGF,MAAAA;;;;;;;;;oBASQE,MAAAA;8BACUD,KAAAA;;;;;;mBAMXC,MAAAA;kCACeA,MAAAA;;;;;;mBAMfA,MAAAA;0BACOD,KAAAA;0BACAA,KAAAA;;;;6CAImBA,KAAAA;;;;;;mBAM1BC,MAAAA;oCACiBD,KAAAA;;;;;;sBAMdC,MAAAA;0BACID,KAAAA;;;;;;;AAO1B;AAtDgBF;AAwDT,SAASI,uBAAuBH,QAAgBC,OAAeC,QAAc;AAClF,SAAO;mBACUF,MAAAA,+DAAqEC,KAAAA;;oBAEpED,MAAAA;sBACEA,MAAAA;;;yBAGGA,MAAAA;;;qCAGYC,KAAAA;sDACiBD,MAAAA;;sCAEhBA,MAAAA;;;;;;;;;;;;wBAYdE,MAAAA;iCACSF,MAAAA;iCACAA,MAAAA;;;;;;;iCAOAA,MAAAA;iCACAA,MAAAA;iCACAA,MAAAA;;;;;;;;;;;;;wBAaTC,KAAAA;;;;;;wBAMAA,KAAAA;;;;;;;;AAQxB;AAhEgBE;;;AVnBhB,eAAsBC,eAAeC,SAA8B;AACjE,QAAM,EAAEC,MAAMC,YAAYC,UAAUC,SAASC,OAAO,YAAYC,QAAO,IAAKN;AAC5E,QAAMO,QAAQC,YAAYP,IAAAA;AAC1B,QAAMQ,SAASC,aAAaT,IAAAA;AAC5B,QAAMU,QAAQC,YAAYX,IAAAA;AAC1B,QAAMY,SAASC,UAAUP,KAAAA;AACzB,QAAMQ,eAAeC,gBAAgBP,MAAAA;AACrC,QAAMQ,YAAYC,KAAKhB,YAAYW,MAAAA;AAEnC,QAAMM,QAAkB,CAAA;AAExB,QAAMC,QAAQ,8BAAOC,cAAsBC,YAAAA;AACzC,UAAMC,WAAWL,KAAKD,WAAWI,YAAAA;AACjC,UAAMG,cAAcD,UAAUD,OAAAA;AAC9BH,UAAMM,KAAKF,QAAAA;EACb,GAJc;AAOd,QAAMH,MAAM,YAAYM,oBAAoBjB,QAAQF,OAAOM,QAAQR,IAAAA,CAAAA;AAGnE,QAAMe,MAAM,gBAAgBO,kBAAkBlB,MAAAA,CAAAA;AAG9C,QAAMW,MACJ,gBAAgBb,KAAAA,kBAChBqB,mBAAmBnB,QAAQF,OAAOM,QAAQE,YAAAA,CAAAA;AAI5C,QAAMK,MAAM,2BAA2Bb,KAAAA,WAAgBsB,kBAAkBpB,QAAQF,KAAAA,CAAAA;AACjF,QAAMa,MAAM,2BAA2Bb,KAAAA,WAAgBuB,kBAAkBrB,QAAQF,KAAAA,CAAAA;AACjF,QAAMa,MAAM,oBAAoBb,KAAAA,oBAAyBwB,oBAAoBtB,QAAQF,KAAAA,CAAAA;AAGrF,QAAMyB,WAAWC,iBAAiBxB,QAAQF,OAAOM,QAAQE,YAAAA;AACzD,aAAWmB,MAAMF,UAAU;AACzB,UAAMZ,MAAM,yBAAyBc,GAAGC,IAAI,IAAID,GAAGZ,OAAO;EAC5D;AAGA,QAAMF,MACJ,uBAAuBb,KAAAA,kBACvB6B,4BAA4B3B,QAAQF,KAAAA,CAAAA;AAItC,QAAMa,MAAM,mBAAmBb,KAAAA,sBAA2B8B,sBAAsB5B,QAAQF,KAAAA,CAAAA;AAGxF,MAAIF,SAAS,YAAY;AACvB,UAAMe,MACJ,yCAAyCb,KAAAA,kBACzC+B,2BAA2B7B,QAAQF,KAAAA,CAAAA;EAEvC;AAGA,MAAI,CAACJ,YAAY,CAACG,SAAS;AACzB,UAAMc,MAAM,mBAAmBb,KAAAA,cAAmBgC,eAAe9B,QAAQF,KAAAA,CAAAA;AACzE,UAAMa,MAAM,wBAAwBb,KAAAA,aAAkBiC,oBAAoB/B,QAAQF,KAAAA,CAAAA;EACpF;AAGA,MAAI,CAACH,SAAS;AACZ,UAAMgB,MACJ,aAAab,KAAAA,uBACbkC,uBAAuBhC,QAAQF,OAAOM,MAAAA,CAAAA;AAExC,UAAMO,MACJ,aAAab,KAAAA,uBACbmC,uBAAuBjC,QAAQF,OAAOM,MAAAA,CAAAA;EAE1C;AAGA,QAAM8B,mBAAmBzC,YAAYO,QAAQI,MAAAA;AAE7C,SAAOM;AACT;AA/EsBpB;AAkFtB,eAAe4C,mBACbzC,YACAO,QACAI,QAAc;AAEd,QAAM+B,YAAY1B,KAAKhB,YAAY,UAAA;AACnC,QAAM2C,SAAS,MAAMC,WAAWF,SAAAA;AAEhC,MAAI,CAACC,QAAQ;AACX,UAAMrB,cACJoB,WACA;WACKnC,MAAAA,oBAA0BI,MAAAA;;4CAEOJ,MAAAA;CAC3C;AAEG;EACF;AAEA,MAAIa,UAAU,MAAMyB,UAASH,WAAW,OAAA;AAGxC,QAAMI,aAAa,YAAYvC,MAAAA,oBAA0BI,MAAAA;AACzD,MAAI,CAACS,QAAQ2B,SAAS,GAAGxC,MAAAA,QAAc,GAAG;AAExC,UAAMyC,gBAAgB5B,QAAQ6B,YAAY,SAAA;AAC1C,QAAID,kBAAkB,IAAI;AACxB,YAAME,UAAU9B,QAAQ+B,QAAQ,MAAMH,aAAAA;AACtC5B,gBAAUA,QAAQgC,MAAM,GAAGF,UAAU,CAAA,IAAKJ,aAAa,OAAO1B,QAAQgC,MAAMF,UAAU,CAAA;IACxF,OAAO;AACL9B,gBAAU0B,aAAa,OAAO1B;IAChC;AAIAA,cAAUA,QAAQiC,QAAQ,yBAAyB,CAACC,QAAQC,MAAMC,UAAUC,UAAAA;AAC1E,YAAMC,UAAUF,SAASG,KAAI;AAC7B,UAAI,CAACD,SAAS;AAEZ,eAAO,GAAGH,IAAAA,GAAOhD,MAAAA,SAAekD,KAAAA;MAClC;AAEA,YAAMG,aAAaF,QAAQG,SAAS,GAAA,IAAO,KAAK;AAChD,aAAO,GAAGN,IAAAA,GAAOC,SAASM,QAAO,CAAA,GAAKF,UAAAA,IAAcrD,MAAAA,SAAekD,KAAAA;IACrE,CAAA;EACF;AAEA,QAAMM,WAAUrB,WAAWtB,SAAS,OAAA;AACtC;AAjDeqB;;;AWvHf,SAASuB,QAAAA,aAAY;AASrB,eAAsBC,gBAAgBC,SAA+B;AACnE,QAAM,EAAEC,MAAMC,OAAM,IAAKF;AACzB,QAAMG,QAAQC,YAAYH,IAAAA;AAC1B,QAAMI,SAASC,aAAaL,IAAAA;AAC5B,QAAMM,QAAkB,CAAA;AAExB,QAAMC,WAAWC,MAAKP,QAAQ,GAAGC,KAAAA,aAAkB;AACnD,QAAMO,cACJF,UACA;;;mBAGeH,MAAAA;;;;;KAKdA,MAAAA;;;;;;;wBAOmBA,MAAAA;;;eAGTA,MAAAA;YACHA,MAAAA;;iCAEqBA,MAAAA;;;;;;;;;;;;;gCAaDA,MAAAA;;;;;;;;;;;;;;;;;;;;mBAoBbF,KAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgClB;AAECI,QAAMI,KAAKH,QAAAA;AAEX,SAAOD;AACT;AApGsBR;;;ACTtB,SAASa,QAAAA,aAAY;AASrB,eAAsBC,mBAAmBC,SAAkC;AACzE,QAAM,EAAEC,MAAMC,OAAM,IAAKF;AACzB,QAAMG,QAAQC,YAAYH,IAAAA;AAC1B,QAAMI,QAAQC,YAAYL,IAAAA;AAC1B,QAAMM,QAAkB,CAAA;AAExB,QAAMC,WAAWC,MAAKP,QAAQ,GAAGC,KAAAA,gBAAqB;AACtD,QAAMO,cACJF,UACA;;mBAEeG,aAAaV,IAAAA,CAAAA;;;;;KAK3BU,aAAaV,IAAAA,CAAAA;;;oBAGEI,KAAAA;;;yCAGqBA,KAAAA;;;mBAGtBA,KAAAA;;kBAEDA,KAAAA,aAAkBM,aAAaV,IAAAA,CAAAA;;;;;;CAMhD;AAECM,QAAMK,KAAKJ,QAAAA;AAEX,SAAOD;AACT;AAtCsBR;;;ACTtB,SAASc,QAAAA,aAAY;AASrB,eAAsBC,cAAcC,SAA6B;AAC/D,QAAM,EAAEC,MAAMC,OAAM,IAAKF;AACzB,QAAMG,QAAQC,YAAYH,IAAAA;AAC1B,QAAMI,QAAQC,YAAYL,IAAAA;AAC1B,QAAMM,SAASC,aAAaP,IAAAA;AAC5B,QAAMQ,QAAkB,CAAA;AAExB,QAAMC,WAAWC,MAAKT,QAAQ,GAAGC,KAAAA,WAAgB;AACjD,QAAMS,cACJF,UACA;;;;KAICH,MAAAA;;;;;;mBAMcF,KAAAA;;;;wBAIKA,KAAAA;;;;;;;;;;;;;;;;;;;;;;CAsBvB;AAECI,QAAMI,KAAKH,QAAAA;AAEX,SAAOD;AACT;AAnDsBV;;;ACTtB,SAASe,QAAAA,aAAY;AASrB,eAAsBC,gBAAgBC,SAA+B;AACnE,QAAM,EAAEC,MAAMC,OAAM,IAAKF;AACzB,QAAMG,QAAQC,YAAYH,IAAAA;AAC1B,QAAMI,SAASC,aAAaL,IAAAA;AAC5B,QAAMM,QAAkB,CAAA;AAExB,QAAMC,WAAWC,MAAKP,QAAQ,GAAGC,KAAAA,aAAkB;AACnD,QAAMO,cACJF,UACA;;;eAGWH,MAAAA;;;;;;CAMd;AAECE,QAAMI,KAAKH,QAAAA;AAEX,SAAOD;AACT;AAvBsBR;;;ACTtB,SAASa,QAAAA,aAAY;AASrB,eAAsBC,oBAAmBC,SAAkC;AACzE,QAAM,EAAEC,MAAMC,OAAM,IAAKF;AACzB,QAAMG,QAAQC,YAAYH,IAAAA;AAC1B,QAAMI,SAASC,aAAaL,IAAAA;AAC5B,QAAMM,QAAkB,CAAA;AAExB,QAAMC,WAAWC,MAAKP,QAAQ,GAAGC,KAAAA,gBAAqB;AACtD,QAAMO,cACJF,UACA;;;;eAIWH,MAAAA;;;;;2BAKYA,MAAAA;;;;;8BAKGA,MAAAA;;;CAG7B;AAECE,QAAMI,KAAKH,QAAAA;AAEX,SAAOD;AACT;AA/BsBR,OAAAA,qBAAAA;;;ACTtB,SAASa,QAAAA,aAAY;AASrB,eAAsBC,YAAYC,SAA2B;AAC3D,QAAM,EAAEC,MAAMC,OAAM,IAAKF;AACzB,QAAMG,QAAQC,YAAYH,IAAAA;AAC1B,QAAMI,SAASC,aAAaL,IAAAA;AAC5B,QAAMM,QAAQC,YAAYP,IAAAA;AAC1B,QAAMQ,QAAkB,CAAA;AAExB,QAAMC,WAAWC,MAAKT,QAAQ,GAAGC,KAAAA,SAAc;AAC/C,QAAMS,cACJF,UACA;;eAEWH,KAAAA;;;;;cAKDF,MAAAA,wBAA8BE,KAAAA;CAC3C;AAECE,QAAMI,KAAKH,QAAAA;AAEX,SAAOD;AACT;AAvBsBV;;;ACTtB,SAASe,QAAAA,OAAMC,WAAAA,gBAAe;AAC9B,SAASC,gBAAgB;AACzB,SAASC,oBAAoB;AAC7B,SAASC,qBAAqB;AAG9B,IAAMC,YAAYC,SAAQC,cAAc,YAAYC,GAAG,CAAA;AACvD,IAAMC,SAASC,KAAKC,MAAMC,aAAaC,MAAKR,WAAW,MAAM,cAAA,GAAiB,OAAA,CAAA;AAC9E,IAAMS,iBAAiB,IAAIL,OAAOM,OAAO;AAczC,eAAsBC,YAAYC,SAA2B;AAC3D,QAAM,EAAEC,MAAMC,WAAWC,iBAAiB,QAAQC,WAAW,OAAM,IAAKJ;AACxE,QAAMK,MAAMH;AAEZI,UAAQC,IAAI;6BAAgCN,IAAAA;CAAQ;AAGpD,QAAMO,WAAmC;IACvC,wBAAwBX;IACxB,wBAAwBA;IACxB,0BAA0BA;IAC1BY,SAAS;IACT,oBAAoB;IACpBC,KAAK;IACLC,MAAM;IACN,eAAe;EACjB;AAGA,MAAIP,aAAa,WAAW;AAC1BI,aAAS,yBAAA,IAA6BX;EACxC;AACA,MAAIO,aAAa,WAAW;AAC1BI,aAAS,yBAAA,IAA6BX;AACtCW,aAAS,SAAA,IAAa;EACxB;AACA,MAAIJ,aAAa,gBAAgB;AAC/BI,aAAS,uBAAA,IAA2BX;AACpCW,aAAS,sBAAA,IAA0BX;EACrC;AACA,MAAIO,aAAa,OAAO;AACtBI,aAAS,yBAAA,IAA6BX;EACxC;AAEA,QAAMe,cACJhB,MAAKS,KAAK,cAAA,GACVZ,KAAKoB,UACH;IACEZ;IACAH,SAASN,OAAOM;IAChBgB,MAAM;IACNC,SAAS;MACPC,KAAK;MACL,aAAa;MACbC,OAAO;MACPC,OAAO;MACPC,MAAM;MACN,cAAc;MACdC,WAAW;MACXC,MAAM;MACNC,QAAQ;IACV;IACAC,cAAcf;IACdgB,iBAAiB;MACf,uBAAuB3B;MACvB,aAAa;MACb,kBAAkB;MAClB,eAAe;MACf,gBAAgB;MAChB4B,MAAM;MACN,aAAa;MACbC,QAAQ;MACRC,YAAY;MACZC,UAAU;IACZ;EACF,GACA,MACA,CAAA,CAAA;AAKJ,QAAMhB,cACJhB,MAAKS,KAAK,gBAAA,GACV;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BH;AAIC,QAAMO,cACJhB,MAAKS,KAAK,eAAA,GACVZ,KAAKoB,UACH;IACEgB,iBAAiB;MACfC,QAAQ;MACRC,QAAQ;MACRC,kBAAkB;MAClBC,KAAK;QAAC;;MACNC,OAAO;QAAC;;MACRC,QAAQ;MACRC,iBAAiB;MACjBC,cAAc;MACdC,WAAW;MACXC,aAAa;MACbC,wBAAwB;MACxBC,uBAAuB;MACvBC,QAAQ;MACRC,SAAS;MACTC,OAAO;QAAE,OAAO;UAAC;;MAAW;IAC9B;IACAC,SAAS;MAAC;;EACZ,GACA,MACA,CAAA,CAAA;AAKJ,QAAMjC,cACJhB,MAAKS,KAAK,aAAA,GACVZ,KAAKoB,UACH;IACEiC,MAAM;IACNC,aAAa;IACbC,eAAe;IACfC,YAAY;IACZC,UAAU;EACZ,GACA,MACA,CAAA,CAAA;AAKJ,QAAMtC,cACJhB,MAAKS,KAAK,YAAA,GACV;;;;;;CAMH;AAIC,QAAMO,cACJhB,MAAKS,KAAK,MAAA,GACV;;CAEH;AAGC,QAAMO,cACJhB,MAAKS,KAAK,cAAA,GACV;;CAEH;AAIC,QAAMO,cAAchB,MAAKS,KAAK,cAAA,GAAiB8C,aAAalD,MAAMG,QAAAA,CAAAA;AAGlE,QAAMQ,cACJhB,MAAKS,KAAK,sBAAA,GACV;;;CAGH;AAIC,MAAID,aAAa,WAAW;AAC1B,UAAMQ,cAAchB,MAAKS,KAAK,wBAAA,GAA2B,EAAA;EAC3D;AAGA,QAAMO,cACJhB,MAAKS,KAAK,gBAAA,GACV;;;cAGUD,QAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Bb;AAIC,QAAMQ,cACJhB,MAAKS,KAAK,kBAAA,GACV;;;;;;;;;;;CAWH;AAIC,MAAIL,QAAQoD,SAAS;AACnB,QAAI;AACFC,eAAS,YAAY;QAAEC,KAAKjD;QAAKkD,OAAO;MAAO,CAAA;AAC/CF,eAAS,cAAc;QAAEC,KAAKjD;QAAKkD,OAAO;MAAO,CAAA;AACjDF,eAAS,uDAAuD;QAC9DC,KAAKjD;QACLkD,OAAO;MACT,CAAA;AACAjD,cAAQC,IAAI,8BAAA;IACd,QAAQ;AACND,cAAQC,IAAI,uDAAA;IACd;EACF;AAGA,MAAIP,QAAQwD,aAAa;AACvBlD,YAAQC,IAAI;iCAAoCJ,cAAAA;CAAqB;AACrE,QAAI;AACFkD,eAAS,GAAGlD,cAAAA,YAA0B;QAAEmD,KAAKjD;QAAKkD,OAAO;MAAU,CAAA;AACnEjD,cAAQC,IAAI,0CAAA;IACd,QAAQ;AACND,cAAQC,IAAI;aAAgBJ,cAAAA,mCAAiD;IAC/E;EACF;AAEAG,UAAQC,IAAI,sCAAA;AACZD,UAAQC,IAAG;AAEX,QAAMkD,UAAUpD,QAAQqD,QAAQJ,IAAG;AACnChD,UAAQC,IAAI,eAAA;AACZ,MAAIkD,QAASnD,SAAQC,IAAI,UAAUN,IAAAA,EAAM;AACzC,MAAI,CAACD,QAAQwD,YAAalD,SAAQC,IAAI,OAAOJ,cAAAA,UAAwB;AAErE,QAAMwD,UAAkC;IACtCC,MAAM;IACNC,SAAS;IACTC,KAAK;IACLC,cAAc;IACdC,SAAS;EACX;AACA1D,UAAQC,IAAI,OAAOoD,QAAQvD,QAAAA,KAAauD,QAAQC,IAAI,EAAE;AACtDtD,UAAQC,IAAI,cAAA;AACZD,UAAQC,IAAG;AACXD,UAAQC,IAAI,aAAA;AACZD,UAAQC,IAAI,qDAAA;AACZD,UAAQC,IAAI,gDAAA;AACZD,UAAQC,IAAI,2CAAA;AACZD,UAAQC,IAAI,4CAA4C;AACxD,MAAIH,aAAa,UAAWE,SAAQC,IAAI,oDAAA;AACxC,MAAIH,aAAa,eACfE,SAAQC,IAAI,uDAAA;AACdD,UAAQC,IAAG;AACb;AA3SsBR;AA+StB,SAASoD,aAAalD,MAAcG,UAAyB;AAC3D,UAAQA,UAAAA;IACN,KAAK;AACH,aAAO;;;;;;;;;;;;;;;;;;;;;IAsBT,KAAK;AACH,aAAO;;;;;;;;;;;sCAWyBH,IAAAA;;;wBAGdA,IAAAA,gBAAoBT,OAAOM,OAAO;;;;;;;;;IAUtD,KAAK;AACH,aAAO;;;;;;IAOT,KAAK;IACL,KAAK;IACL;AACE,aAAO;;;;;;;;;;;wBAWWG,IAAAA,gBAAoBT,OAAOM,OAAO;;;;;EAKxD;AACF;AA9ESqD;;;ACrUT,SAASc,YAAAA,WAAUC,UAAAA,eAAc;AACjC,SAASC,QAAAA,aAAY;AAmEd,SAASC,aAAaC,QAAkB;AAC7C,SAAOA;AACT;AAFgBD;AAIhB,IAAME,eAAe;EAAC;EAAkB;EAAkB;EAAmB;;AAG7E,eAAsBC,eAAeC,KAAW;AAC9C,aAAWC,YAAYH,cAAc;AACnC,UAAMI,WAAWC,MAAKH,KAAKC,QAAAA;AAC3B,QAAI;AACF,YAAMG,QAAOF,QAAAA;IACf,QAAQ;AACN;IACF;AAEA,QAAID,SAASI,SAAS,OAAA,GAAU;AAC9B,YAAMC,UAAU,MAAMC,UAASL,UAAU,OAAA;AACzC,aAAOM,KAAKC,MAAMH,OAAAA;IACpB;AAGA,QAAI;AACF,YAAM,EAAEI,cAAa,IAAK,MAAM,OAAO,KAAA;AACvC,YAAMC,MAAM,MAAM,OAAOD,cAAcR,QAAAA,EAAUU;AACjD,aAAOD,IAAIE,WAAWF;IACxB,SAASG,KAAK;AACZ,UAAIb,SAASI,SAAS,KAAA,GAAQ;AAC5BU,gBAAQC,KACN,2BAA2Bf,QAAAA,4GACzB;MAEN;AACA;IACF;EACF;AACA,SAAO;AACT;AA9BsBF;","names":["join","writeFile","mkdir","access","readFile","dirname","writeFileSafe","filePath","content","mkdir","dirname","recursive","writeFile","fileExists","filePath","access","toPascalCase","name","replace","_","c","toUpperCase","toCamelCase","pascal","charAt","toLowerCase","slice","toKebabCase","pluralize","endsWith","test","pluralizePascal","readFile","writeFile","generateModuleIndex","pascal","kebab","plural","repo","repoClass","repoFile","toUpperCase","generateController","pascal","kebab","plural","pluralPascal","toUpperCase","generateConstants","pascal","toUpperCase","generateCreateDTO","pascal","kebab","generateUpdateDTO","generateResponseDTO","generateUseCases","pascal","kebab","plural","pluralPascal","file","content","toUpperCase","generateRepositoryInterface","pascal","kebab","toUpperCase","generateInMemoryRepository","generateDomainService","pascal","kebab","toUpperCase","generateEntity","generateValueObject","generateControllerTest","pascal","kebab","plural","generateRepositoryTest","generateModule","options","name","modulesDir","noEntity","noTests","repo","minimal","kebab","toKebabCase","pascal","toPascalCase","camel","toCamelCase","plural","pluralize","pluralPascal","pluralizePascal","moduleDir","join","files","write","relativePath","content","fullPath","writeFileSafe","push","generateModuleIndex","generateConstants","generateController","generateCreateDTO","generateUpdateDTO","generateResponseDTO","useCases","generateUseCases","uc","file","generateRepositoryInterface","generateDomainService","generateInMemoryRepository","generateEntity","generateValueObject","generateControllerTest","generateRepositoryTest","autoRegisterModule","indexPath","exists","fileExists","readFile","importLine","includes","lastImportIdx","lastIndexOf","lineEnd","indexOf","slice","replace","_match","open","existing","close","trimmed","trim","needsComma","endsWith","trimEnd","writeFile","join","generateAdapter","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateMiddleware","options","name","outDir","kebab","toKebabCase","camel","toCamelCase","files","filePath","join","writeFileSafe","toPascalCase","push","join","generateGuard","options","name","outDir","kebab","toKebabCase","camel","toCamelCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateService","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateController","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateDto","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","camel","toCamelCase","files","filePath","join","writeFileSafe","push","join","dirname","execSync","readFileSync","fileURLToPath","__dirname","dirname","fileURLToPath","url","cliPkg","JSON","parse","readFileSync","join","KICKJS_VERSION","version","initProject","options","name","directory","packageManager","template","dir","console","log","baseDeps","express","zod","pino","writeFileSafe","stringify","type","scripts","dev","build","start","test","typecheck","lint","format","dependencies","devDependencies","vite","vitest","typescript","prettier","compilerOptions","target","module","moduleResolution","lib","types","strict","esModuleInterop","skipLibCheck","sourceMap","declaration","experimentalDecorators","emitDecoratorMetadata","outDir","rootDir","paths","include","semi","singleQuote","trailingComma","printWidth","tabWidth","getEntryFile","initGit","execSync","cwd","stdio","installDeps","needsCd","process","genHint","rest","graphql","ddd","microservice","minimal","readFile","access","join","defineConfig","config","CONFIG_FILES","loadKickConfig","cwd","filename","filepath","join","access","endsWith","content","readFile","JSON","parse","pathToFileURL","mod","href","default","err","console","warn"]}
1
+ {"version":3,"sources":["../src/generators/module.ts","../src/utils/fs.ts","../src/utils/naming.ts","../src/generators/templates/module-index.ts","../src/generators/templates/controller.ts","../src/generators/templates/constants.ts","../src/generators/templates/dtos.ts","../src/generators/templates/use-cases.ts","../src/generators/templates/repository.ts","../src/generators/templates/domain.ts","../src/generators/templates/tests.ts","../src/generators/templates/rest-service.ts","../src/generators/templates/cqrs.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/utils/resolve-out-dir.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/config.ts"],"sourcesContent":["import { join } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { writeFileSafe, fileExists } from '../utils/fs'\nimport { toPascalCase, toKebabCase, pluralize, pluralizePascal } from '../utils/naming'\nimport { readFile, writeFile } from 'node:fs/promises'\nimport type { ProjectPattern } from '../config'\nimport {\n generateModuleIndex,\n generateRestModuleIndex,\n generateMinimalModuleIndex,\n generateController,\n generateRestController,\n generateConstants,\n generateCreateDTO,\n generateUpdateDTO,\n generateResponseDTO,\n generateUseCases,\n generateRepositoryInterface,\n generateInMemoryRepository,\n generateDrizzleRepository,\n generatePrismaRepository,\n generateDomainService,\n generateEntity,\n generateValueObject,\n generateControllerTest,\n generateRepositoryTest,\n generateRestService,\n generateRestConstants,\n generateCqrsModuleIndex,\n generateCqrsController,\n generateCqrsCommands,\n generateCqrsQueries,\n generateCqrsEvents,\n} from './templates'\n\nexport type RepoType = 'drizzle' | 'inmemory' | 'prisma'\n\ninterface GenerateModuleOptions {\n name: string\n modulesDir: string\n noEntity?: boolean\n noTests?: boolean\n repo?: RepoType\n minimal?: boolean\n force?: boolean\n pattern?: ProjectPattern\n}\n\n/** Prompt the user for a single-line answer via stdin */\nfunction promptUser(question: string): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close()\n resolve(answer.trim().toLowerCase())\n })\n })\n}\n\n/** Shared context passed to each pattern generator */\ninterface ModuleContext {\n kebab: string\n pascal: string\n plural: string\n pluralPascal: string\n moduleDir: string\n repo: RepoType\n noEntity: boolean\n noTests: boolean\n write: (relativePath: string, content: string) => Promise<void>\n files: string[]\n}\n\n/**\n * Generate a module — structure depends on the project pattern.\n *\n * Patterns:\n * rest — flat folder: controller + service + DTOs + repo\n * ddd — nested DDD: presentation/ application/ domain/ infrastructure/\n * graphql — flat folder: resolver + service + DTOs + repo (future)\n * cqrs — commands, queries, events with WS/queue integration\n * minimal — just controller + module index\n */\nexport async function generateModule(options: GenerateModuleOptions): Promise<string[]> {\n const { name, modulesDir, noEntity, noTests, repo = 'inmemory', force } = options\n\n let pattern = options.pattern ?? 'ddd'\n if (options.minimal) pattern = 'minimal'\n\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const plural = pluralize(kebab)\n const pluralPascal = pluralizePascal(pascal)\n const moduleDir = join(modulesDir, plural)\n\n const files: string[] = []\n let overwriteAll = force ?? false\n\n const write = async (relativePath: string, content: string) => {\n const fullPath = join(moduleDir, relativePath)\n if (!overwriteAll && (await fileExists(fullPath))) {\n const answer = await promptUser(\n ` File already exists: ${relativePath}\\n Overwrite? (y/n/a = yes/no/all) `,\n )\n if (answer === 'a') {\n overwriteAll = true\n } else if (answer !== 'y') {\n console.log(` Skipped: ${relativePath}`)\n return\n }\n }\n await writeFileSafe(fullPath, content)\n files.push(fullPath)\n }\n\n const ctx: ModuleContext = {\n kebab,\n pascal,\n plural,\n pluralPascal,\n moduleDir,\n repo,\n noEntity: noEntity ?? false,\n noTests: noTests ?? false,\n write,\n files,\n }\n\n switch (pattern) {\n case 'minimal':\n await generateMinimalFiles(ctx)\n break\n case 'rest':\n await generateRestFiles(ctx)\n break\n case 'cqrs':\n await generateCqrsFiles(ctx)\n break\n case 'graphql':\n case 'ddd':\n default:\n await generateDddFiles(ctx)\n break\n }\n\n // Auto-register in modules index (all patterns need this)\n await autoRegisterModule(modulesDir, pascal, plural)\n\n return files\n}\n\n// ── Pattern: minimal ────────────────────────────────────────────────────\n\nasync function generateMinimalFiles(ctx: ModuleContext): Promise<void> {\n const { pascal, kebab, plural, write } = ctx\n\n await write('index.ts', generateMinimalModuleIndex(pascal, kebab, plural))\n\n await write(\n `${kebab}.controller.ts`,\n `import { Controller, Get } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n@Controller()\nexport class ${pascal}Controller {\n @Get('/')\n async list(ctx: RequestContext) {\n ctx.json({ message: '${pascal} list' })\n }\n}\n`,\n )\n}\n\n// ── Pattern: rest ───────────────────────────────────────────────────────\n\nasync function generateRestFiles(ctx: ModuleContext): Promise<void> {\n const { pascal, kebab, plural, pluralPascal, repo, noTests, write } = ctx\n\n // Module index\n await write('index.ts', generateRestModuleIndex(pascal, kebab, plural, repo))\n\n // Constants\n await write(`${kebab}.constants.ts`, generateRestConstants(pascal))\n\n // Controller (injects service)\n await write(`${kebab}.controller.ts`, generateRestController(pascal, kebab, plural, pluralPascal))\n\n // Service (wraps repository)\n await write(`${kebab}.service.ts`, generateRestService(pascal, kebab))\n\n // DTOs\n await write(`dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab))\n await write(`dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab))\n await write(`dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab))\n\n // Repository interface (flat imports)\n await write(`${kebab}.repository.ts`, generateRepositoryInterface(pascal, kebab, './dtos'))\n\n // Repository implementation (flat imports)\n const repoFileMap: Record<RepoType, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n const repoGeneratorMap: Record<RepoType, () => string> = {\n inmemory: () => generateInMemoryRepository(pascal, kebab, '.', './dtos'),\n drizzle: () => generateDrizzleRepository(pascal, kebab, '.', './dtos'),\n prisma: () => generatePrismaRepository(pascal, kebab, '.', './dtos'),\n }\n await write(`${repoFileMap[repo]}.repository.ts`, repoGeneratorMap[repo]())\n\n // Tests\n if (!noTests) {\n await write(\n `__tests__/${kebab}.controller.test.ts`,\n generateControllerTest(pascal, kebab, plural),\n )\n await write(\n `__tests__/${kebab}.repository.test.ts`,\n generateRepositoryTest(pascal, kebab, plural, `../${repoFileMap.inmemory}.repository`),\n )\n }\n}\n\n// ── Pattern: cqrs ───────────────────────────────────────────────────────\n\nasync function generateCqrsFiles(ctx: ModuleContext): Promise<void> {\n const { pascal, kebab, plural, pluralPascal, repo, noTests, write } = ctx\n\n // Module index\n await write('index.ts', generateCqrsModuleIndex(pascal, kebab, plural, repo))\n\n // Constants\n await write(`${kebab}.constants.ts`, generateRestConstants(pascal))\n\n // Controller (dispatches commands/queries)\n await write(`${kebab}.controller.ts`, generateCqrsController(pascal, kebab, plural, pluralPascal))\n\n // DTOs\n await write(`dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab))\n await write(`dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab))\n await write(`dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab))\n\n // Commands\n const commands = generateCqrsCommands(pascal, kebab)\n for (const cmd of commands) {\n await write(`commands/${cmd.file}`, cmd.content)\n }\n\n // Queries\n const queries = generateCqrsQueries(pascal, kebab, plural, pluralPascal)\n for (const q of queries) {\n await write(`queries/${q.file}`, q.content)\n }\n\n // Events\n const events = generateCqrsEvents(pascal, kebab)\n for (const e of events) {\n await write(`events/${e.file}`, e.content)\n }\n\n // Repository interface (flat imports)\n await write(`${kebab}.repository.ts`, generateRepositoryInterface(pascal, kebab, './dtos'))\n\n // Repository implementation (flat imports)\n const repoFileMap: Record<RepoType, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n const repoGeneratorMap: Record<RepoType, () => string> = {\n inmemory: () => generateInMemoryRepository(pascal, kebab, '.', './dtos'),\n drizzle: () => generateDrizzleRepository(pascal, kebab, '.', './dtos'),\n prisma: () => generatePrismaRepository(pascal, kebab, '.', './dtos'),\n }\n await write(`${repoFileMap[repo]}.repository.ts`, repoGeneratorMap[repo]())\n\n // Tests\n if (!noTests) {\n await write(\n `__tests__/${kebab}.controller.test.ts`,\n generateControllerTest(pascal, kebab, plural),\n )\n await write(\n `__tests__/${kebab}.repository.test.ts`,\n generateRepositoryTest(pascal, kebab, plural, `../${repoFileMap.inmemory}.repository`),\n )\n }\n}\n\n// ── Pattern: ddd ────────────────────────────────────────────────────────\n\nasync function generateDddFiles(ctx: ModuleContext): Promise<void> {\n const { pascal, kebab, plural, pluralPascal, repo, noEntity, noTests, write } = ctx\n\n // Module index\n await write('index.ts', generateModuleIndex(pascal, kebab, plural, repo))\n\n // Constants\n await write('constants.ts', generateConstants(pascal))\n\n // Controller (injects use-cases)\n await write(\n `presentation/${kebab}.controller.ts`,\n generateController(pascal, kebab, plural, pluralPascal),\n )\n\n // DTOs\n await write(`application/dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab))\n await write(`application/dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab))\n await write(`application/dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab))\n\n // Use Cases\n const useCases = generateUseCases(pascal, kebab, plural, pluralPascal)\n for (const uc of useCases) {\n await write(`application/use-cases/${uc.file}`, uc.content)\n }\n\n // Repository Interface\n await write(\n `domain/repositories/${kebab}.repository.ts`,\n generateRepositoryInterface(pascal, kebab),\n )\n\n // Domain Service\n await write(`domain/services/${kebab}-domain.service.ts`, generateDomainService(pascal, kebab))\n\n // Repository Implementation\n const repoFileMap: Record<RepoType, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n const repoGeneratorMap: Record<RepoType, () => string> = {\n inmemory: () => generateInMemoryRepository(pascal, kebab),\n drizzle: () => generateDrizzleRepository(pascal, kebab),\n prisma: () => generatePrismaRepository(pascal, kebab),\n }\n await write(\n `infrastructure/repositories/${repoFileMap[repo]}.repository.ts`,\n repoGeneratorMap[repo](),\n )\n\n // Entity & Value Objects\n if (!noEntity) {\n await write(`domain/entities/${kebab}.entity.ts`, generateEntity(pascal, kebab))\n await write(`domain/value-objects/${kebab}-id.vo.ts`, generateValueObject(pascal, kebab))\n }\n\n // Tests\n if (!noTests) {\n await write(\n `__tests__/${kebab}.controller.test.ts`,\n generateControllerTest(pascal, kebab, plural),\n )\n await write(\n `__tests__/${kebab}.repository.test.ts`,\n generateRepositoryTest(pascal, kebab, plural),\n )\n }\n}\n\n// ── Auto-register in modules index ──────────────────────────────────────\n\n/** Add the new module to src/modules/index.ts */\nasync function autoRegisterModule(\n modulesDir: string,\n pascal: string,\n plural: string,\n): Promise<void> {\n const indexPath = join(modulesDir, 'index.ts')\n const exists = await fileExists(indexPath)\n\n if (!exists) {\n await writeFileSafe(\n indexPath,\n `import type { AppModuleClass } from '@forinda/kickjs-core'\nimport { ${pascal}Module } from './${plural}'\n\nexport const modules: AppModuleClass[] = [${pascal}Module]\n`,\n )\n return\n }\n\n let content = await readFile(indexPath, 'utf-8')\n\n // Add import if not present\n const importLine = `import { ${pascal}Module } from './${plural}'`\n if (!content.includes(`${pascal}Module`)) {\n // Insert import after last existing import\n const lastImportIdx = content.lastIndexOf('import ')\n if (lastImportIdx !== -1) {\n const lineEnd = content.indexOf('\\n', lastImportIdx)\n content = content.slice(0, lineEnd + 1) + importLine + '\\n' + content.slice(lineEnd + 1)\n } else {\n content = importLine + '\\n' + content\n }\n\n // Add to modules array — handle both empty and existing entries\n // Match the array assignment: `= [...]` or `= [\\n...\\n]`\n content = content.replace(/(=\\s*\\[)([\\s\\S]*?)(])/, (_match, open, existing, close) => {\n const trimmed = existing.trim()\n if (!trimmed) {\n // Empty array: `= []`\n return `${open}${pascal}Module${close}`\n }\n // Existing entries: append with comma\n const needsComma = trimmed.endsWith(',') ? '' : ','\n return `${open}${existing.trimEnd()}${needsComma} ${pascal}Module${close}`\n })\n }\n\n await writeFile(indexPath, content, 'utf-8')\n}\n","import { writeFile, mkdir, access, readFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\n/** Write a file, creating parent directories if needed */\nexport async function writeFileSafe(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true })\n await writeFile(filePath, content, 'utf-8')\n}\n\n/** Ensure a directory exists */\nexport async function ensureDirectory(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true })\n}\n\n/** Check if a file exists */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath)\n return true\n } catch {\n return false\n }\n}\n\n/** Read a JSON file */\nexport async function readJsonFile<T = any>(filePath: string): Promise<T> {\n const content = await readFile(filePath, 'utf-8')\n return JSON.parse(content)\n}\n","/** Convert a name to PascalCase */\nexport function toPascalCase(name: string): string {\n return name\n .replace(/[-_\\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))\n .replace(/^(.)/, (c) => c.toUpperCase())\n}\n\n/** Convert a name to camelCase */\nexport function toCamelCase(name: string): string {\n const pascal = toPascalCase(name)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\n/** Convert a name to kebab-case */\nexport function toKebabCase(name: string): string {\n return name\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase()\n}\n\n/**\n * Pluralize a kebab-case name for directory/file names.\n * If already plural (ends in 's'), returns as-is.\n */\nexport function pluralize(name: string): string {\n if (name.endsWith('s')) return name\n if (name.endsWith('x') || name.endsWith('z')) return name + 'es'\n if (name.endsWith('sh') || name.endsWith('ch')) return name + 'es'\n if (name.endsWith('y') && !/[aeiou]y$/.test(name)) return name.slice(0, -1) + 'ies'\n return name + 's'\n}\n\n/**\n * Pluralize a PascalCase name for class identifiers.\n * If already plural (ends in 's'), returns as-is.\n * Used for `List${pluralPascal}UseCase` to avoid `ListUserssUseCase`.\n */\nexport function pluralizePascal(name: string): string {\n if (name.endsWith('s')) return name\n if (name.endsWith('x') || name.endsWith('z')) return name + 'es'\n if (name.endsWith('sh') || name.endsWith('ch')) return name + 'es'\n if (name.endsWith('y') && !/[aeiou]y$/i.test(name)) return name.slice(0, -1) + 'ies'\n return name + 's'\n}\n","import type { RepoType } from '../module'\n\nfunction repoMaps(pascal: string, kebab: string, repo: RepoType) {\n const repoClassMap: Record<string, string> = {\n inmemory: `InMemory${pascal}Repository`,\n drizzle: `Drizzle${pascal}Repository`,\n prisma: `Prisma${pascal}Repository`,\n }\n const repoFileMap: Record<string, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n return {\n repoClass: repoClassMap[repo] ?? repoClassMap.inmemory,\n repoFile: repoFileMap[repo] ?? repoFileMap.inmemory,\n }\n}\n\n/** DDD module index — nested folders, use-cases, domain services */\nexport function generateModuleIndex(\n pascal: string,\n kebab: string,\n plural: string,\n repo: RepoType,\n): string {\n const { repoClass, repoFile } = repoMaps(pascal, kebab, repo)\n\n return `/**\n * ${pascal} Module\n *\n * Self-contained feature module following Domain-Driven Design (DDD).\n * Registers dependencies in the DI container and declares HTTP routes.\n *\n * Structure:\n * presentation/ — HTTP controllers (entry points)\n * application/ — Use cases (orchestration) and DTOs (validation)\n * domain/ — Entities, value objects, repository interfaces, domain services\n * infrastructure/ — Repository implementations (in-memory, Drizzle, Prisma, etc.)\n */\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'\nimport { buildRoutes } from '@forinda/kickjs-http'\nimport { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'\nimport { ${repoClass} } from './infrastructure/repositories/${repoFile}.repository'\nimport { ${pascal}Controller } from './presentation/${kebab}.controller'\n\n// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container\nimport.meta.glob(\n ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],\n { eager: true },\n)\n\nexport class ${pascal}Module implements AppModule {\n /**\n * Register module dependencies in the DI container.\n * Bind repository interface tokens to their implementations here.\n * To swap implementations (e.g. in-memory -> Drizzle), change the factory target.\n */\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n }\n\n /**\n * Declare HTTP routes for this module.\n * The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${plural}).\n * Passing 'controller' enables automatic OpenAPI spec generation via SwaggerAdapter.\n */\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n router: buildRoutes(${pascal}Controller),\n controller: ${pascal}Controller,\n }\n }\n}\n`\n}\n\n/** REST module index — flat folder, service + controller, no use-cases */\nexport function generateRestModuleIndex(\n pascal: string,\n kebab: string,\n plural: string,\n repo: RepoType,\n): string {\n const { repoClass, repoFile } = repoMaps(pascal, kebab, repo)\n\n return `/**\n * ${pascal} Module\n *\n * REST module with a flat folder structure.\n * Controller delegates to service, service wraps the repository.\n *\n * Structure:\n * ${kebab}.controller.ts — HTTP routes (CRUD)\n * ${kebab}.service.ts — Business logic\n * ${kebab}.repository.ts — Repository interface\n * ${repoFile}.repository.ts — Repository implementation\n * dtos/ — Request/response schemas\n */\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'\nimport { buildRoutes } from '@forinda/kickjs-http'\nimport { ${pascal.toUpperCase()}_REPOSITORY } from './${kebab}.repository'\nimport { ${repoClass} } from './${repoFile}.repository'\nimport { ${pascal}Controller } from './${kebab}.controller'\n\n// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container\nimport.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })\n\nexport class ${pascal}Module implements AppModule {\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n }\n\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n router: buildRoutes(${pascal}Controller),\n controller: ${pascal}Controller,\n }\n }\n}\n`\n}\n\n/** Minimal module index — just controller, no service/repo */\nexport function generateMinimalModuleIndex(pascal: string, kebab: string, plural: string): string {\n return `import { type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'\nimport { buildRoutes } from '@forinda/kickjs-http'\nimport { ${pascal}Controller } from './${kebab}.controller'\n\nexport class ${pascal}Module implements AppModule {\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n router: buildRoutes(${pascal}Controller),\n controller: ${pascal}Controller,\n }\n }\n}\n`\n}\n","/** DDD controller — injects use-cases, nested import paths */\nexport function generateController(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): string {\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'\nimport { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'\nimport { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'\nimport { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}.use-case'\nimport { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'\nimport { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../constants'\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private create${pascal}UseCase!: Create${pascal}UseCase\n @Autowired() private get${pascal}UseCase!: Get${pascal}UseCase\n @Autowired() private list${pluralPascal}UseCase!: List${pluralPascal}UseCase\n @Autowired() private update${pascal}UseCase!: Update${pascal}UseCase\n @Autowired() private delete${pascal}UseCase!: Delete${pascal}UseCase\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: RequestContext) {\n return ctx.paginate(\n (parsed) => this.list${pluralPascal}UseCase.execute(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: RequestContext) {\n const result = await this.get${pascal}UseCase.execute(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: RequestContext) {\n const result = await this.create${pascal}UseCase.execute(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: RequestContext) {\n const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: RequestContext) {\n await this.delete${pascal}UseCase.execute(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n\n/** REST controller — injects service directly, flat import paths */\nexport function generateRestController(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): string {\n const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1)\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { ${pascal}Service } from './${kebab}.service'\nimport { create${pascal}Schema } from './dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from './dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from './${kebab}.constants'\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private ${camel}Service!: ${pascal}Service\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: RequestContext) {\n return ctx.paginate(\n (parsed) => this.${camel}Service.findPaginated(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: RequestContext) {\n const result = await this.${camel}Service.findById(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: RequestContext) {\n const result = await this.${camel}Service.create(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: RequestContext) {\n const result = await this.${camel}Service.update(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: RequestContext) {\n await this.${camel}Service.delete(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n","export function generateConstants(pascal: string): string {\n return `import type { QueryParamsConfig } from '@forinda/kickjs-core'\n\nexport const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {\n filterable: ['name'],\n sortable: ['name', 'createdAt'],\n searchable: ['name'],\n}\n`\n}\n","export function generateCreateDTO(pascal: string, kebab: string): string {\n return `import { z } from 'zod'\n\n/**\n * Create ${pascal} DTO — Zod schema for validating POST request bodies.\n * This schema is passed to @Post('/', { body: create${pascal}Schema }) for automatic validation.\n * It also generates OpenAPI request body docs when SwaggerAdapter is used.\n *\n * Add more fields as needed. Supported Zod types:\n * z.string(), z.number(), z.boolean(), z.enum([...]),\n * z.array(), z.object(), .optional(), .default(), .transform()\n */\nexport const create${pascal}Schema = z.object({\n name: z.string().min(1, 'Name is required').max(200),\n})\n\nexport type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>\n`\n}\n\nexport function generateUpdateDTO(pascal: string, kebab: string): string {\n return `import { z } from 'zod'\n\nexport const update${pascal}Schema = z.object({\n name: z.string().min(1).max(200).optional(),\n})\n\nexport type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>\n`\n}\n\nexport function generateResponseDTO(pascal: string, kebab: string): string {\n return `export interface ${pascal}ResponseDTO {\n id: string\n name: string\n createdAt: string\n updatedAt: string\n}\n`\n}\n","export function generateUseCases(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): { file: string; content: string }[] {\n return [\n {\n file: `create-${kebab}.use-case.ts`,\n content: `/**\n * Create ${pascal} Use Case\n *\n * Application layer — orchestrates a single business operation.\n * Use cases are thin: validate input (via DTO), call domain/repo, return response.\n * Keep business rules in the domain service, not here.\n */\nimport { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Create${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.create(dto)\n }\n}\n`,\n },\n {\n file: `get-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Get${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n}\n`,\n },\n {\n file: `list-${plural}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\n\n@Service()\nexport class List${pluralPascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(parsed: ParsedQuery) {\n return this.repo.findPaginated(parsed)\n }\n}\n`,\n },\n {\n file: `update-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\nimport type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Update${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.update(id, dto)\n }\n}\n`,\n },\n {\n file: `delete-${kebab}.use-case.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'\n\n@Service()\nexport class Delete${pascal}UseCase {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<void> {\n await this.repo.delete(id)\n }\n}\n`,\n },\n ]\n}\n","export function generateRepositoryInterface(\n pascal: string,\n kebab: string,\n dtoPrefix = '../../application/dtos',\n): string {\n return `/**\n * ${pascal} Repository Interface\n *\n * Defines the contract for data access.\n * The interface declares what operations are available;\n * implementations (in-memory, Drizzle, Prisma) fulfill the contract.\n *\n * To swap implementations, change the factory in the module's register() method.\n */\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\n\nexport interface I${pascal}Repository {\n findById(id: string): Promise<${pascal}ResponseDTO | null>\n findAll(): Promise<${pascal}ResponseDTO[]>\n findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }>\n create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>\n update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>\n delete(id: string): Promise<void>\n}\n\nexport const ${pascal.toUpperCase()}_REPOSITORY = Symbol('I${pascal}Repository')\n`\n}\n\nexport function generateInMemoryRepository(\n pascal: string,\n kebab: string,\n repoPrefix = '../../domain/repositories',\n dtoPrefix = '../../application/dtos',\n): string {\n return `/**\n * In-Memory ${pascal} Repository\n *\n * Implements the repository interface using a Map.\n * Useful for prototyping and testing. Replace with a database implementation\n * (Drizzle, Prisma, etc.) for production use.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { randomUUID } from 'node:crypto'\nimport { Repository, HttpException } from '@forinda/kickjs-core'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\nimport type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\n\n@Repository()\nexport class InMemory${pascal}Repository implements I${pascal}Repository {\n private store = new Map<string, ${pascal}ResponseDTO>()\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.store.get(id) ?? null\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return Array.from(this.store.values())\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n const all = Array.from(this.store.values())\n const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)\n return { data, total: all.length }\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const now = new Date().toISOString()\n const entity: ${pascal}ResponseDTO = {\n id: randomUUID(),\n name: dto.name,\n createdAt: now,\n updatedAt: now,\n }\n this.store.set(entity.id, entity)\n return entity\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const existing = this.store.get(id)\n if (!existing) throw HttpException.notFound('${pascal} not found')\n const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }\n this.store.set(id, updated)\n return updated\n }\n\n async delete(id: string): Promise<void> {\n if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')\n this.store.delete(id)\n }\n}\n`\n}\n\nexport function generateDrizzleRepository(\n pascal: string,\n kebab: string,\n repoPrefix = '../../domain/repositories',\n dtoPrefix = '../../application/dtos',\n): string {\n return `/**\n * Drizzle ${pascal} Repository\n *\n * Implements the repository interface using Drizzle ORM.\n * Requires a Drizzle database instance injected via the DI container.\n *\n * TODO: Update the schema import to match your Drizzle schema file.\n * TODO: Replace 'db' injection token with your actual database token.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { eq, sql } from 'drizzle-orm'\nimport { Repository, HttpException, Autowired } from '@forinda/kickjs-core'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\nimport type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\n\n// TODO: Import your Drizzle schema table — e.g.:\n// import { ${kebab}s } from '@/db/schema'\n\n// TODO: Import your Drizzle DB injection token — e.g.:\n// import { DRIZZLE_DB } from '@/db/drizzle.provider'\n\n@Repository()\nexport class Drizzle${pascal}Repository implements I${pascal}Repository {\n // TODO: Uncomment and configure your Drizzle DB injection:\n // @Autowired(DRIZZLE_DB) private db!: DrizzleDB\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n // TODO: Implement with Drizzle\n // const [row] = await this.db.select().from(${kebab}s).where(eq(${kebab}s.id, id))\n // return row ?? null\n throw new Error('Drizzle ${pascal} repository not yet implemented — update schema imports and queries')\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n // TODO: Implement with Drizzle\n // return this.db.select().from(${kebab}s)\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n // TODO: Implement with Drizzle\n // const data = await this.db.select().from(${kebab}s)\n // .limit(parsed.pagination.limit)\n // .offset(parsed.pagination.offset)\n // const [{ count }] = await this.db.select({ count: sql\\`count(*)\\` }).from(${kebab}s)\n // return { data, total: Number(count) }\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n // TODO: Implement with Drizzle\n // const [row] = await this.db.insert(${kebab}s).values(dto).returning()\n // return row\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n // TODO: Implement with Drizzle\n // const [row] = await this.db.update(${kebab}s).set(dto).where(eq(${kebab}s.id, id)).returning()\n // if (!row) throw HttpException.notFound('${pascal} not found')\n // return row\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n\n async delete(id: string): Promise<void> {\n // TODO: Implement with Drizzle\n // const result = await this.db.delete(${kebab}s).where(eq(${kebab}s.id, id))\n // if (!result.rowCount) throw HttpException.notFound('${pascal} not found')\n throw new Error('Drizzle ${pascal} repository not yet implemented')\n }\n}\n`\n}\n\nexport function generatePrismaRepository(\n pascal: string,\n kebab: string,\n repoPrefix = '../../domain/repositories',\n dtoPrefix = '../../application/dtos',\n): string {\n const camel = kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase())\n return `/**\n * Prisma ${pascal} Repository\n *\n * Implements the repository interface using Prisma Client.\n * Requires a PrismaClient instance injected via the DI container.\n *\n * TODO: Ensure your Prisma schema has a '${pascal}' model defined.\n * TODO: Replace 'PRISMA_CLIENT' with your actual Prisma injection token.\n *\n * @Repository() registers this class in the DI container as a singleton.\n */\nimport { Repository, HttpException, Autowired } from '@forinda/kickjs-core'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\nimport type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'\n\n// TODO: Import your Prisma injection token — e.g.:\n// import { PRISMA_CLIENT } from '@/db/prisma.provider'\n// import type { PrismaClient } from '@prisma/client'\n\n@Repository()\nexport class Prisma${pascal}Repository implements I${pascal}Repository {\n // TODO: Uncomment and configure your Prisma injection:\n // @Autowired(PRISMA_CLIENT) private prisma!: PrismaClient\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n // TODO: Implement with Prisma\n // return this.prisma.${camel}.findUnique({ where: { id } })\n throw new Error('Prisma ${pascal} repository not yet implemented — update Prisma imports and queries')\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n // TODO: Implement with Prisma\n // return this.prisma.${camel}.findMany()\n throw new Error('Prisma ${pascal} repository not yet implemented')\n }\n\n async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {\n // TODO: Implement with Prisma\n // const [data, total] = await Promise.all([\n // this.prisma.${camel}.findMany({\n // skip: parsed.pagination.offset,\n // take: parsed.pagination.limit,\n // }),\n // this.prisma.${camel}.count(),\n // ])\n // return { data, total }\n throw new Error('Prisma ${pascal} repository not yet implemented')\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n // TODO: Implement with Prisma\n // return this.prisma.${camel}.create({ data: dto })\n throw new Error('Prisma ${pascal} repository not yet implemented')\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n // TODO: Implement with Prisma\n // const row = await this.prisma.${camel}.update({ where: { id }, data: dto })\n // if (!row) throw HttpException.notFound('${pascal} not found')\n // return row\n throw new Error('Prisma ${pascal} repository not yet implemented')\n }\n\n async delete(id: string): Promise<void> {\n // TODO: Implement with Prisma\n // await this.prisma.${camel}.delete({ where: { id } })\n throw new Error('Prisma ${pascal} repository not yet implemented')\n }\n}\n`\n}\n","export function generateDomainService(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} Domain Service\n *\n * Domain layer — contains business rules that don't belong to a single entity.\n * Use this for cross-entity logic, validation rules, and domain invariants.\n * Keep it free of HTTP/framework concerns.\n */\nimport { Service, Inject, HttpException } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'\n\n@Service()\nexport class ${pascal}DomainService {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async ensureExists(id: string): Promise<void> {\n const entity = await this.repo.findById(id)\n if (!entity) {\n throw HttpException.notFound('${pascal} not found')\n }\n }\n}\n`\n}\n\nexport function generateEntity(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} Entity\n *\n * Domain layer — the core business object.\n * Uses a private constructor with static factory methods (create, reconstitute)\n * to enforce invariants. Properties are accessed via getters to maintain encapsulation.\n *\n * Patterns used:\n * - Private constructor: prevents direct instantiation\n * - create(): factory for new entities (generates ID, sets timestamps)\n * - reconstitute(): factory for rebuilding from persistence (no side effects)\n * - changeName(): mutation method that enforces business rules\n */\nimport { ${pascal}Id } from '../value-objects/${kebab}-id.vo'\n\ninterface ${pascal}Props {\n id: ${pascal}Id\n name: string\n createdAt: Date\n updatedAt: Date\n}\n\nexport class ${pascal} {\n private constructor(private props: ${pascal}Props) {}\n\n static create(params: { name: string }): ${pascal} {\n const now = new Date()\n return new ${pascal}({\n id: ${pascal}Id.create(),\n name: params.name,\n createdAt: now,\n updatedAt: now,\n })\n }\n\n static reconstitute(props: ${pascal}Props): ${pascal} {\n return new ${pascal}(props)\n }\n\n get id(): ${pascal}Id {\n return this.props.id\n }\n get name(): string {\n return this.props.name\n }\n get createdAt(): Date {\n return this.props.createdAt\n }\n get updatedAt(): Date {\n return this.props.updatedAt\n }\n\n changeName(name: string): void {\n if (!name || name.trim().length === 0) {\n throw new Error('Name cannot be empty')\n }\n this.props.name = name.trim()\n this.props.updatedAt = new Date()\n }\n\n toJSON() {\n return {\n id: this.props.id.toString(),\n name: this.props.name,\n createdAt: this.props.createdAt.toISOString(),\n updatedAt: this.props.updatedAt.toISOString(),\n }\n }\n}\n`\n}\n\nexport function generateValueObject(pascal: string, kebab: string): string {\n return `/**\n * ${pascal} ID Value Object\n *\n * Domain layer — wraps a primitive ID with type safety and validation.\n * Value objects are immutable and compared by value, not reference.\n *\n * ${pascal}Id.create() — generate a new UUID\n * ${pascal}Id.from(id) — wrap an existing ID string (validates non-empty)\n * id.equals(other) — compare two IDs by value\n */\nimport { randomUUID } from 'node:crypto'\n\nexport class ${pascal}Id {\n private constructor(private readonly value: string) {}\n\n static create(): ${pascal}Id {\n return new ${pascal}Id(randomUUID())\n }\n\n static from(id: string): ${pascal}Id {\n if (!id || id.trim().length === 0) {\n throw new Error('${pascal}Id cannot be empty')\n }\n return new ${pascal}Id(id)\n }\n\n toString(): string {\n return this.value\n }\n\n equals(other: ${pascal}Id): boolean {\n return this.value === other.value\n }\n}\n`\n}\n","export function generateControllerTest(pascal: string, kebab: string, plural: string): string {\n return `import { describe, it, expect, beforeEach } from 'vitest'\nimport { Container } from '@forinda/kickjs-core'\n\ndescribe('${pascal}Controller', () => {\n beforeEach(() => {\n Container.reset()\n })\n\n it('should be defined', () => {\n expect(true).toBe(true)\n })\n\n describe('POST /${plural}', () => {\n it('should create a new ${kebab}', async () => {\n // TODO: Set up test module, call create endpoint, assert 201\n expect(true).toBe(true)\n })\n })\n\n describe('GET /${plural}', () => {\n it('should return paginated ${plural}', async () => {\n // TODO: Set up test module, call list endpoint, assert { data, meta }\n expect(true).toBe(true)\n })\n })\n\n describe('GET /${plural}/:id', () => {\n it('should return a ${kebab} by id', async () => {\n // TODO: Create a ${kebab}, then fetch by id, assert match\n expect(true).toBe(true)\n })\n\n it('should return 404 for non-existent ${kebab}', async () => {\n // TODO: Fetch non-existent id, assert 404\n expect(true).toBe(true)\n })\n })\n\n describe('PUT /${plural}/:id', () => {\n it('should update an existing ${kebab}', async () => {\n // TODO: Create, update, assert changes\n expect(true).toBe(true)\n })\n })\n\n describe('DELETE /${plural}/:id', () => {\n it('should delete a ${kebab}', async () => {\n // TODO: Create, delete, assert gone\n expect(true).toBe(true)\n })\n })\n})\n`\n}\n\nexport function generateRepositoryTest(\n pascal: string,\n kebab: string,\n plural: string,\n repoImport = `../infrastructure/repositories/in-memory-${kebab}.repository`,\n): string {\n return `import { describe, it, expect, beforeEach } from 'vitest'\nimport { InMemory${pascal}Repository } from '${repoImport}'\n\ndescribe('InMemory${pascal}Repository', () => {\n let repo: InMemory${pascal}Repository\n\n beforeEach(() => {\n repo = new InMemory${pascal}Repository()\n })\n\n it('should create and retrieve a ${kebab}', async () => {\n const created = await repo.create({ name: 'Test ${pascal}' })\n expect(created).toBeDefined()\n expect(created.name).toBe('Test ${pascal}')\n expect(created.id).toBeDefined()\n\n const found = await repo.findById(created.id)\n expect(found).toEqual(created)\n })\n\n it('should return null for non-existent id', async () => {\n const found = await repo.findById('non-existent')\n expect(found).toBeNull()\n })\n\n it('should list all ${plural}', async () => {\n await repo.create({ name: '${pascal} 1' })\n await repo.create({ name: '${pascal} 2' })\n\n const all = await repo.findAll()\n expect(all).toHaveLength(2)\n })\n\n it('should return paginated results', async () => {\n await repo.create({ name: '${pascal} 1' })\n await repo.create({ name: '${pascal} 2' })\n await repo.create({ name: '${pascal} 3' })\n\n const result = await repo.findPaginated({\n filters: [],\n sort: [],\n search: '',\n pagination: { page: 1, limit: 2, offset: 0 },\n })\n\n expect(result.data).toHaveLength(2)\n expect(result.total).toBe(3)\n })\n\n it('should update a ${kebab}', async () => {\n const created = await repo.create({ name: 'Original' })\n const updated = await repo.update(created.id, { name: 'Updated' })\n expect(updated.name).toBe('Updated')\n })\n\n it('should delete a ${kebab}', async () => {\n const created = await repo.create({ name: 'To Delete' })\n await repo.delete(created.id)\n const found = await repo.findById(created.id)\n expect(found).toBeNull()\n })\n})\n`\n}\n","/** REST service — wraps repository with CRUD methods, replaces use-cases for flat pattern */\nexport function generateRestService(pascal: string, kebab: string): string {\n return `import { Service, Inject, HttpException } from '@forinda/kickjs-core'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from './${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from './dtos/${kebab}-response.dto'\nimport type { Create${pascal}DTO } from './dtos/create-${kebab}.dto'\nimport type { Update${pascal}DTO } from './dtos/update-${kebab}.dto'\n\n@Service()\nexport class ${pascal}Service {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async findById(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n\n async findAll(): Promise<${pascal}ResponseDTO[]> {\n return this.repo.findAll()\n }\n\n async findPaginated(parsed: ParsedQuery) {\n return this.repo.findPaginated(parsed)\n }\n\n async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.create(dto)\n }\n\n async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n return this.repo.update(id, dto)\n }\n\n async delete(id: string): Promise<void> {\n await this.repo.delete(id)\n }\n}\n`\n}\n\n/** REST constants — query config for flat pattern */\nexport function generateRestConstants(pascal: string): string {\n return `import type { QueryFieldConfig } from '@forinda/kickjs-http'\n\nexport const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {\n filterable: ['name'],\n sortable: ['name', 'createdAt'],\n searchable: ['name'],\n}\n`\n}\n","/** CQRS module index — commands, queries, events, WebSocket + queue integration */\nexport function generateCqrsModuleIndex(\n pascal: string,\n kebab: string,\n plural: string,\n repo: string,\n): string {\n const repoClassMap: Record<string, string> = {\n inmemory: `InMemory${pascal}Repository`,\n drizzle: `Drizzle${pascal}Repository`,\n prisma: `Prisma${pascal}Repository`,\n }\n const repoFileMap: Record<string, string> = {\n inmemory: `in-memory-${kebab}`,\n drizzle: `drizzle-${kebab}`,\n prisma: `prisma-${kebab}`,\n }\n const repoClass = repoClassMap[repo] ?? repoClassMap.inmemory\n const repoFile = repoFileMap[repo] ?? repoFileMap.inmemory\n\n return `/**\n * ${pascal} Module — CQRS Pattern\n *\n * Separates read (queries) and write (commands) operations.\n * Events are emitted after state changes and can be handled via\n * WebSocket broadcasts, queue jobs, or ETL pipelines.\n *\n * Structure:\n * commands/ — Write operations (create, update, delete)\n * queries/ — Read operations (get, list)\n * events/ — Domain events + handlers (WS broadcast, queue dispatch)\n * dtos/ — Request/response schemas\n */\nimport { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'\nimport { buildRoutes } from '@forinda/kickjs-http'\nimport { ${pascal.toUpperCase()}_REPOSITORY } from './${kebab}.repository'\nimport { ${repoClass} } from './${repoFile}.repository'\nimport { ${pascal}Controller } from './${kebab}.controller'\n\n// Eagerly load decorated classes\nimport.meta.glob(\n [\n './commands/**/*.ts',\n './queries/**/*.ts',\n './events/**/*.ts',\n '!./**/*.test.ts',\n ],\n { eager: true },\n)\n\nexport class ${pascal}Module implements AppModule {\n register(container: Container): void {\n container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>\n container.resolve(${repoClass}),\n )\n }\n\n routes(): ModuleRoutes {\n return {\n path: '/${plural}',\n router: buildRoutes(${pascal}Controller),\n controller: ${pascal}Controller,\n }\n }\n}\n`\n}\n\n/** CQRS controller — dispatches to command/query handlers */\nexport function generateCqrsController(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): string {\n const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1)\n return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\nimport { ApiTags } from '@forinda/kickjs-swagger'\nimport { Create${pascal}Command } from './commands/create-${kebab}.command'\nimport { Update${pascal}Command } from './commands/update-${kebab}.command'\nimport { Delete${pascal}Command } from './commands/delete-${kebab}.command'\nimport { Get${pascal}Query } from './queries/get-${kebab}.query'\nimport { List${pluralPascal}Query } from './queries/list-${plural}.query'\nimport { create${pascal}Schema } from './dtos/create-${kebab}.dto'\nimport { update${pascal}Schema } from './dtos/update-${kebab}.dto'\nimport { ${pascal.toUpperCase()}_QUERY_CONFIG } from './${kebab}.constants'\n\n@Controller()\nexport class ${pascal}Controller {\n @Autowired() private create${pascal}Command!: Create${pascal}Command\n @Autowired() private update${pascal}Command!: Update${pascal}Command\n @Autowired() private delete${pascal}Command!: Delete${pascal}Command\n @Autowired() private get${pascal}Query!: Get${pascal}Query\n @Autowired() private list${pluralPascal}Query!: List${pluralPascal}Query\n\n @Get('/')\n @ApiTags('${pascal}')\n @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)\n async list(ctx: RequestContext) {\n return ctx.paginate(\n (parsed) => this.list${pluralPascal}Query.execute(parsed),\n ${pascal.toUpperCase()}_QUERY_CONFIG,\n )\n }\n\n @Get('/:id')\n @ApiTags('${pascal}')\n async getById(ctx: RequestContext) {\n const result = await this.get${pascal}Query.execute(ctx.params.id)\n if (!result) return ctx.notFound('${pascal} not found')\n ctx.json(result)\n }\n\n @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })\n @ApiTags('${pascal}')\n async create(ctx: RequestContext) {\n const result = await this.create${pascal}Command.execute(ctx.body)\n ctx.created(result)\n }\n\n @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })\n @ApiTags('${pascal}')\n async update(ctx: RequestContext) {\n const result = await this.update${pascal}Command.execute(ctx.params.id, ctx.body)\n ctx.json(result)\n }\n\n @Delete('/:id')\n @ApiTags('${pascal}')\n async remove(ctx: RequestContext) {\n await this.delete${pascal}Command.execute(ctx.params.id)\n ctx.noContent()\n }\n}\n`\n}\n\n/** CQRS commands — write operations that emit events */\nexport function generateCqrsCommands(\n pascal: string,\n kebab: string,\n): { file: string; content: string }[] {\n return [\n {\n file: `create-${kebab}.command.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\nimport { ${pascal}Events } from '../events/${kebab}.events'\n\n@Service()\nexport class Create${pascal}Command {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n @Inject(${pascal}Events) private readonly events: ${pascal}Events,\n ) {}\n\n async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const result = await this.repo.create(dto)\n this.events.emit('${kebab}.created', result)\n return result\n }\n}\n`,\n },\n {\n file: `update-${kebab}.command.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\nimport { ${pascal}Events } from '../events/${kebab}.events'\n\n@Service()\nexport class Update${pascal}Command {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n @Inject(${pascal}Events) private readonly events: ${pascal}Events,\n ) {}\n\n async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {\n const result = await this.repo.update(id, dto)\n this.events.emit('${kebab}.updated', result)\n return result\n }\n}\n`,\n },\n {\n file: `delete-${kebab}.command.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport { ${pascal}Events } from '../events/${kebab}.events'\n\n@Service()\nexport class Delete${pascal}Command {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n @Inject(${pascal}Events) private readonly events: ${pascal}Events,\n ) {}\n\n async execute(id: string): Promise<void> {\n await this.repo.delete(id)\n this.events.emit('${kebab}.deleted', { id })\n }\n}\n`,\n },\n ]\n}\n\n/** CQRS queries — read operations */\nexport function generateCqrsQueries(\n pascal: string,\n kebab: string,\n plural: string,\n pluralPascal: string,\n): { file: string; content: string }[] {\n return [\n {\n file: `get-${kebab}.query.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n@Service()\nexport class Get${pascal}Query {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(id: string): Promise<${pascal}ResponseDTO | null> {\n return this.repo.findById(id)\n }\n}\n`,\n },\n {\n file: `list-${plural}.query.ts`,\n content: `import { Service, Inject } from '@forinda/kickjs-core'\nimport { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'\nimport type { ParsedQuery } from '@forinda/kickjs-http'\n\n@Service()\nexport class List${pluralPascal}Query {\n constructor(\n @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,\n ) {}\n\n async execute(parsed: ParsedQuery) {\n return this.repo.findPaginated(parsed)\n }\n}\n`,\n },\n ]\n}\n\n/** CQRS events — domain event emitter + handler with WS/queue integration */\nexport function generateCqrsEvents(\n pascal: string,\n kebab: string,\n): { file: string; content: string }[] {\n return [\n {\n file: `${kebab}.events.ts`,\n content: `import { Service } from '@forinda/kickjs-core'\nimport { EventEmitter } from 'node:events'\nimport type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'\n\n/**\n * ${pascal} domain event types.\n *\n * These events are emitted by commands after state changes.\n * Subscribe to them in event handlers for side effects:\n * - WebSocket broadcasts (real-time UI updates)\n * - Queue jobs (async processing, ETL pipelines)\n * - Audit logging\n * - Cache invalidation\n */\nexport interface ${pascal}EventMap {\n '${kebab}.created': ${pascal}ResponseDTO\n '${kebab}.updated': ${pascal}ResponseDTO\n '${kebab}.deleted': { id: string }\n}\n\n@Service()\nexport class ${pascal}Events {\n private emitter = new EventEmitter()\n\n emit<K extends keyof ${pascal}EventMap>(event: K, data: ${pascal}EventMap[K]): void {\n this.emitter.emit(event, data)\n }\n\n on<K extends keyof ${pascal}EventMap>(event: K, handler: (data: ${pascal}EventMap[K]) => void): void {\n this.emitter.on(event, handler)\n }\n\n off<K extends keyof ${pascal}EventMap>(event: K, handler: (data: ${pascal}EventMap[K]) => void): void {\n this.emitter.off(event, handler)\n }\n}\n`,\n },\n {\n file: `on-${kebab}-change.handler.ts`,\n content: `import { Service, Autowired } from '@forinda/kickjs-core'\nimport { ${pascal}Events } from './${kebab}.events'\n\n/**\n * ${pascal} Change Event Handler\n *\n * Reacts to domain events emitted by commands.\n * Wire up side effects here:\n *\n * 1. WebSocket broadcast — notify connected clients in real-time\n * import { WsGateway } from '@forinda/kickjs-ws'\n * this.ws.broadcast('${kebab}-channel', { event, data })\n *\n * 2. Queue dispatch — offload heavy processing to background workers\n * import { QueueService } from '@forinda/kickjs-queue'\n * this.queue.add('${kebab}-etl', { action: event, payload: data })\n *\n * 3. ETL pipeline — transform and load data to external systems\n * await this.etlPipeline.process(data)\n */\n@Service()\nexport class On${pascal}ChangeHandler {\n @Autowired() private events!: ${pascal}Events\n\n // Uncomment to inject WebSocket and Queue services:\n // @Autowired() private ws!: WsGateway\n // @Autowired() private queue!: QueueService\n\n onInit(): void {\n this.events.on('${kebab}.created', (data) => {\n console.log('[${pascal}] Created:', data.id)\n // TODO: Broadcast via WebSocket\n // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.created', data })\n // TODO: Dispatch to queue for async processing / ETL\n // this.queue.add('${kebab}-etl', { action: 'create', payload: data })\n })\n\n this.events.on('${kebab}.updated', (data) => {\n console.log('[${pascal}] Updated:', data.id)\n // TODO: Broadcast via WebSocket\n // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.updated', data })\n })\n\n this.events.on('${kebab}.deleted', (data) => {\n console.log('[${pascal}] Deleted:', data.id)\n // TODO: Broadcast via WebSocket\n // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.deleted', data })\n })\n }\n}\n`,\n },\n ]\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\n\ninterface GenerateAdapterOptions {\n name: string\n outDir: string\n}\n\nexport async function generateAdapter(options: GenerateAdapterOptions): Promise<string[]> {\n const { name, outDir } = options\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.adapter.ts`)\n await writeFileSafe(\n filePath,\n `import type { Express } from 'express'\nimport type { AppAdapter, AdapterMiddleware, Container } from '@forinda/kickjs-core'\n\nexport interface ${pascal}AdapterOptions {\n // Add your adapter configuration here\n}\n\n/**\n * ${pascal} adapter.\n *\n * Hooks into the Application lifecycle to add middleware, routes,\n * or external service connections.\n *\n * Usage:\n * bootstrap({\n * adapters: [new ${pascal}Adapter({ ... })],\n * })\n */\nexport class ${pascal}Adapter implements AppAdapter {\n name = '${pascal}Adapter'\n\n constructor(private options: ${pascal}AdapterOptions = {}) {}\n\n /**\n * Return middleware entries that the Application will mount.\n * Use \\`phase\\` to control where in the pipeline they run:\n * 'beforeGlobal' | 'afterGlobal' | 'beforeRoutes' | 'afterRoutes'\n */\n middleware(): AdapterMiddleware[] {\n return [\n // Example: add a custom header to all responses\n // {\n // phase: 'beforeGlobal',\n // handler: (_req: any, res: any, next: any) => {\n // res.setHeader('X-${pascal}', 'true')\n // next()\n // },\n // },\n // Example: scope middleware to a specific path\n // {\n // phase: 'beforeRoutes',\n // path: '/api/v1/admin',\n // handler: myAdminMiddleware(),\n // },\n ]\n }\n\n /**\n * Called before global middleware.\n * Use this to mount routes that bypass the middleware stack\n * (health checks, docs UI, static assets).\n */\n beforeMount(app: Express, container: Container): void {\n // Example: mount a status route\n // app.get('/${kebab}/status', (_req, res) => {\n // res.json({ status: 'ok' })\n // })\n }\n\n /**\n * Called after modules and routes are registered, before the server starts.\n * Use this for late-stage DI registrations or config validation.\n */\n beforeStart(app: Express, container: Container): void {\n // Example: register a service in the DI container\n // container.registerInstance(MY_TOKEN, new MyService(this.options))\n }\n\n /**\n * Called after the HTTP server is listening.\n * Use this to attach to the raw http.Server (Socket.IO, gRPC, etc).\n */\n afterStart(server: any, container: Container): void {\n // Example: attach Socket.IO\n // const io = new Server(server)\n // container.registerInstance(SOCKET_IO, io)\n }\n\n /**\n * Called on graceful shutdown. Clean up connections.\n */\n async shutdown(): Promise<void> {\n // Example: close a connection pool\n // await this.pool.end()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateMiddlewareOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n}\n\nexport async function generateMiddleware(options: GenerateMiddlewareOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'middleware',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/middleware',\n pattern,\n })\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.middleware.ts`)\n await writeFileSafe(\n filePath,\n `import type { Request, Response, NextFunction } from 'express'\n\nexport interface ${toPascalCase(name)}Options {\n // Add configuration options here\n}\n\n/**\n * ${toPascalCase(name)} middleware.\n *\n * Usage in bootstrap:\n * middleware: [${camel}()]\n *\n * Usage with adapter:\n * middleware() { return [{ handler: ${camel}(), phase: 'afterGlobal' }] }\n *\n * Usage with @Middleware decorator:\n * @Middleware(${camel}())\n */\nexport function ${camel}(options: ${toPascalCase(name)}Options = {}) {\n return (req: Request, res: Response, next: NextFunction) => {\n // Implement your middleware logic here\n next()\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { resolve, join } from 'node:path'\nimport { pluralize, toKebabCase } from './naming'\nimport type { ProjectPattern } from '../config'\n\n/**\n * DDD folder mapping — nested layered architecture.\n */\nconst DDD_FOLDER_MAP: Record<string, string> = {\n controller: 'presentation',\n service: 'domain/services',\n dto: 'application/dtos',\n guard: 'presentation/guards',\n middleware: 'middleware',\n}\n\n/**\n * Flat folder mapping — REST/GraphQL/minimal patterns.\n * Files live at the module root or in minimal subdirectories.\n */\nconst FLAT_FOLDER_MAP: Record<string, string> = {\n controller: '',\n service: '',\n dto: 'dtos',\n guard: 'guards',\n middleware: 'middleware',\n}\n\n/**\n * CQRS folder mapping — commands, queries, events.\n */\nconst CQRS_FOLDER_MAP: Record<string, string> = {\n controller: '',\n service: '',\n dto: 'dtos',\n guard: 'guards',\n middleware: 'middleware',\n command: 'commands',\n query: 'queries',\n event: 'events',\n}\n\nexport interface ResolveOutDirOptions {\n /** The artifact type (controller, service, dto, guard, middleware) */\n type: string\n /** Explicit -o / --out dir from CLI flag (takes highest priority) */\n outDir?: string\n /** Module name from --module flag */\n moduleName?: string\n /** Modules directory (from config or default) */\n modulesDir?: string\n /** Standalone default directory when no --module is used (e.g. 'src/controllers') */\n defaultDir: string\n /** Project pattern — determines folder structure inside modules */\n pattern?: ProjectPattern\n}\n\n/**\n * Resolve the output directory for a generator artifact.\n *\n * Priority:\n * 1. Explicit --out flag (always wins)\n * 2. --module flag → maps into module's folder (DDD or flat based on pattern)\n * 3. Standalone default directory\n */\nexport function resolveOutDir(options: ResolveOutDirOptions): string {\n const {\n type,\n outDir,\n moduleName,\n modulesDir = 'src/modules',\n defaultDir,\n pattern = 'ddd',\n } = options\n\n // Explicit --out always wins\n if (outDir) return resolve(outDir)\n\n // Module-scoped: place inside the module's folder\n if (moduleName) {\n const folderMap =\n pattern === 'ddd' ? DDD_FOLDER_MAP : pattern === 'cqrs' ? CQRS_FOLDER_MAP : FLAT_FOLDER_MAP\n const kebab = toKebabCase(moduleName)\n const plural = pluralize(kebab)\n const subfolder = folderMap[type] ?? ''\n const base = join(modulesDir, plural)\n return resolve(subfolder ? join(base, subfolder) : base)\n }\n\n // Standalone default\n return resolve(defaultDir)\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateGuardOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n}\n\nexport async function generateGuard(options: GenerateGuardOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'guard',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/guards',\n pattern,\n })\n const kebab = toKebabCase(name)\n const camel = toCamelCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.guard.ts`)\n await writeFileSafe(\n filePath,\n `import { Container, HttpException } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n/**\n * ${pascal} guard.\n *\n * Guards protect routes by checking conditions before the handler runs.\n * Return early with an error response to block access.\n *\n * Usage:\n * @Middleware(${camel}Guard)\n * @Get('/protected')\n * async handler(ctx: RequestContext) { ... }\n */\nexport async function ${camel}Guard(ctx: RequestContext, next: () => void): Promise<void> {\n // Example: check for an authorization header\n const header = ctx.headers.authorization\n if (!header?.startsWith('Bearer ')) {\n ctx.res.status(401).json({ message: 'Missing or invalid authorization header' })\n return\n }\n\n const token = header.slice(7)\n\n try {\n // Verify the token using a service from the DI container\n // const container = Container.getInstance()\n // const authService = container.resolve(AuthService)\n // const payload = authService.verifyToken(token)\n // ctx.set('auth', payload)\n\n next()\n } catch {\n ctx.res.status(401).json({ message: 'Invalid or expired token' })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateServiceOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n}\n\nexport async function generateService(options: GenerateServiceOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'service',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/services',\n pattern,\n })\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.service.ts`)\n await writeFileSafe(\n filePath,\n `import { Service } from '@forinda/kickjs-core'\n\n@Service()\nexport class ${pascal}Service {\n // Inject dependencies via constructor\n // constructor(\n // @Inject(MY_REPO) private readonly repo: IMyRepository,\n // ) {}\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateControllerOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n}\n\nexport async function generateController(options: GenerateControllerOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'controller',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/controllers',\n pattern,\n })\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.controller.ts`)\n await writeFileSafe(\n filePath,\n `import { Controller, Get, Post, Autowired } from '@forinda/kickjs-core'\nimport type { RequestContext } from '@forinda/kickjs-http'\n\n@Controller()\nexport class ${pascal}Controller {\n // @Autowired() private myService!: MyService\n\n @Get('/')\n async list(ctx: RequestContext) {\n ctx.json({ message: '${pascal} list' })\n }\n\n @Post('/')\n async create(ctx: RequestContext) {\n ctx.created({ message: '${pascal} created', data: ctx.body })\n }\n}\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join } from 'node:path'\nimport { writeFileSafe } from '../utils/fs'\nimport { toPascalCase, toKebabCase, toCamelCase } from '../utils/naming'\nimport { resolveOutDir } from '../utils/resolve-out-dir'\nimport type { ProjectPattern } from '../config'\n\ninterface GenerateDtoOptions {\n name: string\n outDir?: string\n moduleName?: string\n modulesDir?: string\n pattern?: ProjectPattern\n}\n\nexport async function generateDto(options: GenerateDtoOptions): Promise<string[]> {\n const { name, moduleName, modulesDir, pattern } = options\n const outDir = resolveOutDir({\n type: 'dto',\n outDir: options.outDir,\n moduleName,\n modulesDir,\n defaultDir: 'src/dtos',\n pattern,\n })\n const kebab = toKebabCase(name)\n const pascal = toPascalCase(name)\n const camel = toCamelCase(name)\n const files: string[] = []\n\n const filePath = join(outDir, `${kebab}.dto.ts`)\n await writeFileSafe(\n filePath,\n `import { z } from 'zod'\n\nexport const ${camel}Schema = z.object({\n // Define your schema fields here\n name: z.string().min(1).max(200),\n})\n\nexport type ${pascal}DTO = z.infer<typeof ${camel}Schema>\n`,\n )\n files.push(filePath)\n\n return files\n}\n","import { join, dirname } from 'node:path'\nimport { execSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { writeFileSafe } from '../utils/fs'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst cliPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))\nconst KICKJS_VERSION = `^${cliPkg.version}`\n\ntype ProjectTemplate = 'rest' | 'graphql' | 'ddd' | 'cqrs' | 'minimal'\n\ninterface InitProjectOptions {\n name: string\n directory: string\n packageManager?: 'pnpm' | 'npm' | 'yarn'\n initGit?: boolean\n installDeps?: boolean\n template?: ProjectTemplate\n}\n\n/** Scaffold a new KickJS project */\nexport async function initProject(options: InitProjectOptions): Promise<void> {\n const { name, directory, packageManager = 'pnpm', template = 'rest' } = options\n const dir = directory\n\n console.log(`\\n Creating KickJS project: ${name}\\n`)\n\n // ── package.json — template-aware deps ────────────────────────────\n const baseDeps: Record<string, string> = {\n '@forinda/kickjs-core': KICKJS_VERSION,\n '@forinda/kickjs-http': KICKJS_VERSION,\n '@forinda/kickjs-config': KICKJS_VERSION,\n express: '^5.1.0',\n 'reflect-metadata': '^0.2.2',\n zod: '^4.3.6',\n pino: '^10.3.1',\n 'pino-pretty': '^13.1.3',\n }\n\n // Add template-specific deps\n if (template !== 'minimal') {\n baseDeps['@forinda/kickjs-swagger'] = KICKJS_VERSION\n baseDeps['@forinda/kickjs-devtools'] = KICKJS_VERSION\n }\n if (template === 'graphql') {\n baseDeps['@forinda/kickjs-graphql'] = KICKJS_VERSION\n baseDeps['graphql'] = '^16.11.0'\n }\n if (template === 'cqrs') {\n baseDeps['@forinda/kickjs-queue'] = KICKJS_VERSION\n baseDeps['@forinda/kickjs-ws'] = KICKJS_VERSION\n baseDeps['@forinda/kickjs-otel'] = KICKJS_VERSION\n }\n if (template === 'ddd') {\n baseDeps['@forinda/kickjs-swagger'] = KICKJS_VERSION\n }\n\n await writeFileSafe(\n join(dir, 'package.json'),\n JSON.stringify(\n {\n name,\n version: cliPkg.version,\n type: 'module',\n scripts: {\n dev: 'kick dev',\n 'dev:debug': 'kick dev:debug',\n build: 'kick build',\n start: 'kick start',\n test: 'vitest run',\n 'test:watch': 'vitest',\n typecheck: 'tsc --noEmit',\n lint: 'eslint src/',\n format: 'prettier --write src/',\n },\n dependencies: baseDeps,\n devDependencies: {\n '@forinda/kickjs-cli': KICKJS_VERSION,\n '@swc/core': '^1.7.28',\n '@types/express': '^5.0.6',\n '@types/node': '^24.5.2',\n 'unplugin-swc': '^1.5.9',\n vite: '^7.3.1',\n 'vite-node': '^5.3.0',\n vitest: '^3.2.4',\n typescript: '^5.9.2',\n prettier: '^3.8.1',\n },\n },\n null,\n 2,\n ),\n )\n\n // ── vite.config.ts — enables HMR + SWC for decorators ──────────────\n await writeFileSafe(\n join(dir, 'vite.config.ts'),\n `import { defineConfig } from 'vite'\nimport { resolve } from 'path'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n resolve: {\n alias: {\n '@': resolve(__dirname, 'src'),\n },\n },\n server: {\n watch: { usePolling: false },\n hmr: true,\n },\n build: {\n target: 'node20',\n ssr: true,\n outDir: 'dist',\n sourcemap: true,\n rollupOptions: {\n input: resolve(__dirname, 'src/index.ts'),\n output: { format: 'esm' },\n },\n },\n})\n`,\n )\n\n // ── tsconfig.json ───────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'tsconfig.json'),\n JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['node'],\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n sourceMap: true,\n declaration: true,\n experimentalDecorators: true,\n emitDecoratorMetadata: true,\n outDir: 'dist',\n rootDir: 'src',\n paths: { '@/*': ['./src/*'] },\n },\n include: ['src'],\n },\n null,\n 2,\n ),\n )\n\n // ── .prettierrc ─────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.prettierrc'),\n JSON.stringify(\n {\n semi: false,\n singleQuote: true,\n trailingComma: 'all',\n printWidth: 100,\n tabWidth: 2,\n },\n null,\n 2,\n ),\n )\n\n // ── .editorconfig ─────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.editorconfig'),\n `# https://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n`,\n )\n\n // ── .gitignore ──────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.gitignore'),\n `node_modules/\ndist/\n.env\ncoverage/\n.DS_Store\n*.tsbuildinfo\n`,\n )\n\n // ── .gitattributes ────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.gitattributes'),\n `# Auto-detect text files and normalise line endings to LF\n* text=auto eol=lf\n\n# Explicitly mark generated / binary files\n*.png binary\n*.jpg binary\n*.jpeg binary\n*.gif binary\n*.ico binary\n*.woff binary\n*.woff2 binary\n*.ttf binary\n*.eot binary\n\n# Lock files — treat as generated\npnpm-lock.yaml -diff linguist-generated\nyarn.lock -diff linguist-generated\npackage-lock.json -diff linguist-generated\n`,\n )\n\n // ── .env ────────────────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, '.env'),\n `PORT=3000\nNODE_ENV=development\n`,\n )\n\n await writeFileSafe(\n join(dir, '.env.example'),\n `PORT=3000\nNODE_ENV=development\n`,\n )\n\n // ── src/index.ts — template-aware entry point ─────────────────────\n await writeFileSafe(join(dir, 'src/index.ts'), getEntryFile(name, template))\n\n // ── src/modules/index.ts ────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'src/modules/index.ts'),\n `import type { AppModuleClass } from '@forinda/kickjs-core'\n\nexport const modules: AppModuleClass[] = []\n`,\n )\n\n // ── Template-specific files ─────────────────────────────────────────\n if (template === 'graphql') {\n await writeFileSafe(join(dir, 'src/resolvers/.gitkeep'), '')\n }\n\n // ── kick.config.ts — CLI configuration ─────────────────────────────\n await writeFileSafe(\n join(dir, 'kick.config.ts'),\n `import { defineConfig } from '@forinda/kickjs-cli'\n\nexport default defineConfig({\n pattern: '${template}',\n modulesDir: 'src/modules',\n defaultRepo: 'inmemory',\n\n commands: [\n {\n name: 'test',\n description: 'Run tests with Vitest',\n steps: 'npx vitest run',\n },\n {\n name: 'format',\n description: 'Format code with Prettier',\n steps: 'npx prettier --write src/',\n },\n {\n name: 'format:check',\n description: 'Check formatting without writing',\n steps: 'npx prettier --check src/',\n },\n {\n name: 'check',\n description: 'Run typecheck + format check',\n steps: ['npx tsc --noEmit', 'npx prettier --check src/'],\n aliases: ['verify', 'ci'],\n },\n ],\n})\n`,\n )\n\n // ── vitest.config.ts ────────────────────────────────────────────────\n await writeFileSafe(\n join(dir, 'vitest.config.ts'),\n `import { defineConfig } from 'vitest/config'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n test: {\n globals: true,\n environment: 'node',\n include: ['src/**/*.test.ts'],\n },\n})\n`,\n )\n\n // ── Git Init ─────────────────────────────────────────────────────────\n if (options.initGit) {\n try {\n execSync('git init', { cwd: dir, stdio: 'pipe' })\n execSync('git add -A', { cwd: dir, stdio: 'pipe' })\n execSync('git commit -m \"chore: initial commit from kick new\"', {\n cwd: dir,\n stdio: 'pipe',\n })\n console.log(' Git repository initialized')\n } catch {\n console.log(' Warning: git init failed (git may not be installed)')\n }\n }\n\n // ── Install Dependencies ────────────────────────────────────────────\n if (options.installDeps) {\n console.log(`\\n Installing dependencies with ${packageManager}...\\n`)\n try {\n execSync(`${packageManager} install`, { cwd: dir, stdio: 'inherit' })\n console.log('\\n Dependencies installed successfully!')\n } catch {\n console.log(`\\n Warning: ${packageManager} install failed. Run it manually.`)\n }\n }\n\n console.log('\\n Project scaffolded successfully!')\n console.log()\n\n const needsCd = dir !== process.cwd()\n console.log(' Next steps:')\n if (needsCd) console.log(` cd ${name}`)\n if (!options.installDeps) console.log(` ${packageManager} install`)\n\n const genHint: Record<string, string> = {\n rest: 'kick g module user',\n graphql: 'kick g resolver user',\n ddd: 'kick g module user --repo drizzle',\n cqrs: 'kick g module user --pattern cqrs',\n minimal: '# add your routes to src/index.ts',\n }\n console.log(` ${genHint[template] ?? genHint.rest}`)\n console.log(' kick dev')\n console.log()\n console.log(' Commands:')\n console.log(' kick dev Start dev server with Vite HMR')\n console.log(' kick build Production build via Vite')\n console.log(' kick start Run production build')\n console.log()\n console.log(' Generators:')\n console.log(' kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)')\n console.log(' kick g scaffold <n> <f..> CRUD module from field definitions')\n console.log(' kick g controller <name> Standalone controller')\n console.log(' kick g service <name> @Service() class')\n console.log(' kick g middleware <name> Express middleware')\n console.log(' kick g guard <name> Route guard (auth, roles, etc.)')\n console.log(' kick g adapter <name> AppAdapter with lifecycle hooks')\n console.log(' kick g dto <name> Zod DTO schema')\n if (template === 'graphql') console.log(' kick g resolver <name> GraphQL resolver')\n if (template === 'cqrs') console.log(' kick g job <name> Queue job processor')\n console.log(' kick g config Generate kick.config.ts')\n console.log()\n console.log(' Add packages:')\n console.log(' kick add <pkg> Install a KickJS package + peers')\n console.log(' kick add --list Show all available packages')\n console.log()\n console.log(' Available: auth, swagger, graphql, drizzle, prisma, ws,')\n console.log(' cron, queue, mailer, otel, multi-tenant, notifications, testing')\n console.log()\n}\n\n// ── Entry file templates ─────────────────────────────────────────────────\n\nfunction getEntryFile(name: string, template: ProjectTemplate): string {\n switch (template) {\n case 'graphql':\n return `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { DevToolsAdapter } from '@forinda/kickjs-devtools'\nimport { GraphQLAdapter } from '@forinda/kickjs-graphql'\nimport { modules } from './modules'\n\n// Import your resolvers here\n// import { UserResolver } from './resolvers/user.resolver'\n\nbootstrap({\n modules,\n adapters: [\n new DevToolsAdapter(),\n new GraphQLAdapter({\n resolvers: [/* UserResolver */],\n // Add custom type definitions here:\n // typeDefs: userTypeDefs,\n }),\n ],\n})\n`\n\n case 'cqrs':\n return `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { DevToolsAdapter } from '@forinda/kickjs-devtools'\nimport { SwaggerAdapter } from '@forinda/kickjs-swagger'\nimport { OtelAdapter } from '@forinda/kickjs-otel'\n// import { WsAdapter } from '@forinda/kickjs-ws'\n// import { QueueAdapter, BullMQProvider } from '@forinda/kickjs-queue'\nimport { modules } from './modules'\n\nbootstrap({\n modules,\n adapters: [\n new OtelAdapter({ serviceName: '${name}' }),\n new DevToolsAdapter(),\n new SwaggerAdapter({\n info: { title: '${name}', version: '${cliPkg.version}' },\n }),\n // Uncomment for WebSocket support:\n // new WsAdapter(),\n // Uncomment when Redis is available:\n // new QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],\n})\n`\n\n case 'minimal':\n return `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { modules } from './modules'\n\nbootstrap({ modules })\n`\n\n case 'ddd':\n case 'rest':\n default:\n return `import 'reflect-metadata'\nimport { bootstrap } from '@forinda/kickjs-http'\nimport { DevToolsAdapter } from '@forinda/kickjs-devtools'\nimport { SwaggerAdapter } from '@forinda/kickjs-swagger'\nimport { modules } from './modules'\n\nbootstrap({\n modules,\n adapters: [\n new DevToolsAdapter(),\n new SwaggerAdapter({\n info: { title: '${name}', version: '${cliPkg.version}' },\n }),\n ],\n})\n`\n }\n}\n","import { readFile, access } from 'node:fs/promises'\nimport { join } from 'node:path'\n\n/** A custom command that developers can register via kick.config.ts */\nexport interface KickCommandDefinition {\n /** The command name (e.g. 'db:migrate', 'seed', 'proto:gen') */\n name: string\n /** Description shown in --help */\n description: string\n /**\n * Shell command(s) to run. Can be a single string or an array of\n * sequential steps. Use {args} as a placeholder for CLI arguments.\n *\n * @example\n * 'npx drizzle-kit migrate'\n * ['npx drizzle-kit generate', 'npx drizzle-kit migrate']\n */\n steps: string | string[]\n /** Optional aliases (e.g. ['migrate'] for 'db:migrate') */\n aliases?: string[]\n}\n\n/** Project pattern — controls what generators produce and which deps are installed */\nexport type ProjectPattern = 'rest' | 'graphql' | 'ddd' | 'cqrs' | 'minimal'\n\n/** Configuration for the kick.config.ts file */\nexport interface KickConfig {\n /**\n * Project pattern — controls default generator behavior.\n * - 'rest' — Express + Swagger (default)\n * - 'graphql' — GraphQL + GraphiQL\n * - 'ddd' — Full DDD modules with use cases, entities, value objects\n * - 'cqrs' — CQRS with commands, queries, events, WebSocket + queue\n * - 'minimal' — Bare Express with no scaffolding\n */\n pattern?: ProjectPattern\n /** Where modules live (default: 'src/modules') */\n modulesDir?: string\n /** Default repository implementation for generators */\n defaultRepo?: 'drizzle' | 'inmemory' | 'prisma'\n /** Drizzle schema output directory */\n schemaDir?: string\n /**\n * Directories to copy to dist/ after build.\n * Useful for EJS templates, email templates, static assets, etc.\n *\n * @example\n * ```ts\n * copyDirs: [\n * 'src/views', // copies to dist/src/views\n * { src: 'src/views', dest: 'dist/views' }, // custom dest\n * 'src/emails',\n * ]\n * ```\n */\n copyDirs?: Array<string | { src: string; dest?: string }>\n /** Custom commands that extend the CLI */\n commands?: KickCommandDefinition[]\n /** Code style overrides (auto-detected from prettier when possible) */\n style?: {\n semicolons?: boolean\n quotes?: 'single' | 'double'\n trailingComma?: 'all' | 'es5' | 'none'\n indent?: number\n }\n}\n\n/** Helper to define a type-safe kick.config.ts */\nexport function defineConfig(config: KickConfig): KickConfig {\n return config\n}\n\nconst CONFIG_FILES = ['kick.config.ts', 'kick.config.js', 'kick.config.mjs', 'kick.config.json']\n\n/** Load kick.config.* from the project root */\nexport async function loadKickConfig(cwd: string): Promise<KickConfig | null> {\n for (const filename of CONFIG_FILES) {\n const filepath = join(cwd, filename)\n try {\n await access(filepath)\n } catch {\n continue\n }\n\n if (filename.endsWith('.json')) {\n const content = await readFile(filepath, 'utf-8')\n return JSON.parse(content)\n }\n\n // For .ts/.js/.mjs — dynamic import (use file URL for cross-platform compat)\n try {\n const { pathToFileURL } = await import('node:url')\n const mod = await import(pathToFileURL(filepath).href)\n return mod.default ?? mod\n } catch (err) {\n if (filename.endsWith('.ts')) {\n console.warn(\n `Warning: Failed to load ${filename}. TypeScript config files require ` +\n 'a runtime loader (e.g. tsx, ts-node) or use kick.config.js/.mjs instead.',\n )\n }\n continue\n }\n }\n return null\n}\n"],"mappings":"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","names":["join","createInterface","writeFile","mkdir","access","readFile","dirname","writeFileSafe","filePath","content","mkdir","dirname","recursive","writeFile","fileExists","filePath","access","toPascalCase","name","replace","_","c","toUpperCase","toCamelCase","pascal","charAt","toLowerCase","slice","toKebabCase","pluralize","endsWith","test","pluralizePascal","readFile","writeFile","repoMaps","pascal","kebab","repo","repoClassMap","inmemory","drizzle","prisma","repoFileMap","repoClass","repoFile","generateModuleIndex","plural","toUpperCase","generateRestModuleIndex","generateMinimalModuleIndex","generateController","pascal","kebab","plural","pluralPascal","toUpperCase","generateRestController","camel","charAt","toLowerCase","slice","generateConstants","pascal","toUpperCase","generateCreateDTO","pascal","kebab","generateUpdateDTO","generateResponseDTO","generateUseCases","pascal","kebab","plural","pluralPascal","file","content","toUpperCase","generateRepositoryInterface","pascal","kebab","dtoPrefix","toUpperCase","generateInMemoryRepository","repoPrefix","generateDrizzleRepository","generatePrismaRepository","camel","replace","_","c","generateDomainService","pascal","kebab","toUpperCase","generateEntity","generateValueObject","generateControllerTest","pascal","kebab","plural","generateRepositoryTest","repoImport","generateRestService","pascal","kebab","toUpperCase","generateRestConstants","generateCqrsModuleIndex","pascal","kebab","plural","repo","repoClassMap","inmemory","drizzle","prisma","repoFileMap","repoClass","repoFile","toUpperCase","generateCqrsController","pluralPascal","camel","charAt","toLowerCase","slice","generateCqrsCommands","file","content","generateCqrsQueries","generateCqrsEvents","promptUser","question","rl","createInterface","input","process","stdin","output","stdout","Promise","resolve","answer","close","trim","toLowerCase","generateModule","options","name","modulesDir","noEntity","noTests","repo","force","pattern","minimal","kebab","toKebabCase","pascal","toPascalCase","plural","pluralize","pluralPascal","pluralizePascal","moduleDir","join","files","overwriteAll","write","relativePath","content","fullPath","fileExists","console","log","writeFileSafe","push","ctx","generateMinimalFiles","generateRestFiles","generateCqrsFiles","generateDddFiles","autoRegisterModule","generateMinimalModuleIndex","generateRestModuleIndex","generateRestConstants","generateRestController","generateRestService","generateCreateDTO","generateUpdateDTO","generateResponseDTO","generateRepositoryInterface","repoFileMap","inmemory","drizzle","prisma","repoGeneratorMap","generateInMemoryRepository","generateDrizzleRepository","generatePrismaRepository","generateControllerTest","generateRepositoryTest","generateCqrsModuleIndex","generateCqrsController","commands","generateCqrsCommands","cmd","file","queries","generateCqrsQueries","q","events","generateCqrsEvents","e","generateModuleIndex","generateConstants","generateController","useCases","generateUseCases","uc","generateDomainService","generateEntity","generateValueObject","indexPath","exists","readFile","importLine","includes","lastImportIdx","lastIndexOf","lineEnd","indexOf","slice","replace","_match","open","existing","trimmed","needsComma","endsWith","trimEnd","writeFile","join","generateAdapter","options","name","outDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","resolve","join","DDD_FOLDER_MAP","controller","service","dto","guard","middleware","FLAT_FOLDER_MAP","CQRS_FOLDER_MAP","command","query","event","resolveOutDir","options","type","outDir","moduleName","modulesDir","defaultDir","pattern","resolve","folderMap","kebab","toKebabCase","plural","pluralize","subfolder","base","join","generateMiddleware","options","name","moduleName","modulesDir","pattern","outDir","resolveOutDir","type","defaultDir","kebab","toKebabCase","camel","toCamelCase","files","filePath","join","writeFileSafe","toPascalCase","push","join","generateGuard","options","name","moduleName","modulesDir","pattern","outDir","resolveOutDir","type","defaultDir","kebab","toKebabCase","camel","toCamelCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateService","options","name","moduleName","modulesDir","pattern","outDir","resolveOutDir","type","defaultDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateController","options","name","moduleName","modulesDir","pattern","outDir","resolveOutDir","type","defaultDir","kebab","toKebabCase","pascal","toPascalCase","files","filePath","join","writeFileSafe","push","join","generateDto","options","name","moduleName","modulesDir","pattern","outDir","resolveOutDir","type","defaultDir","kebab","toKebabCase","pascal","toPascalCase","camel","toCamelCase","files","filePath","join","writeFileSafe","push","join","dirname","execSync","readFileSync","fileURLToPath","__dirname","dirname","fileURLToPath","url","cliPkg","JSON","parse","readFileSync","join","KICKJS_VERSION","version","initProject","options","name","directory","packageManager","template","dir","console","log","baseDeps","express","zod","pino","writeFileSafe","stringify","type","scripts","dev","build","start","test","typecheck","lint","format","dependencies","devDependencies","vite","vitest","typescript","prettier","compilerOptions","target","module","moduleResolution","lib","types","strict","esModuleInterop","skipLibCheck","sourceMap","declaration","experimentalDecorators","emitDecoratorMetadata","outDir","rootDir","paths","include","semi","singleQuote","trailingComma","printWidth","tabWidth","getEntryFile","initGit","execSync","cwd","stdio","installDeps","needsCd","process","genHint","rest","graphql","ddd","cqrs","minimal","readFile","access","join","defineConfig","config","CONFIG_FILES","loadKickConfig","cwd","filename","filepath","join","access","endsWith","content","readFile","JSON","parse","pathToFileURL","mod","href","default","err","console","warn"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forinda/kickjs-cli",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "description": "CLI for KickJS — project scaffolding, DDD module generation, dev/build/start",
5
5
  "keywords": [
6
6
  "kickjs",