@forinda/kickjs-cli 5.3.2 → 5.4.1

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.
Files changed (28) hide show
  1. package/dist/{builtins-CnQ6lxcV.mjs → builtins-BCRm8cUp.mjs} +163 -100
  2. package/dist/builtins-BCRm8cUp.mjs.map +1 -0
  3. package/dist/{builtins-BxGfcEP6.mjs → builtins-BbJqgLmS.mjs} +432 -220
  4. package/dist/cli.mjs +2 -2
  5. package/dist/config-cyBKEKWL.mjs +12 -0
  6. package/dist/config-cyBKEKWL.mjs.map +1 -0
  7. package/dist/config-q_jBP6Ln.mjs +11 -0
  8. package/dist/{generator-extension-BNgcYlom.mjs → generator-extension-CsT2e6Fj.mjs} +274 -125
  9. package/dist/generator-extension-CsT2e6Fj.mjs.map +1 -0
  10. package/dist/index.d.mts +45 -0
  11. package/dist/index.d.mts.map +1 -1
  12. package/dist/index.mjs +2 -2
  13. package/dist/{plugin-Ccvf-gn6.mjs → plugin-8R67gfJG.mjs} +3 -3
  14. package/dist/{plugin-Ccvf-gn6.mjs.map → plugin-8R67gfJG.mjs.map} +1 -1
  15. package/dist/{plugin-B56zClEX.mjs → plugin-D6nfJWY0.mjs} +2 -2
  16. package/dist/{rolldown-runtime-CP9PNXAB.mjs → rolldown-runtime-B9Lxtctb.mjs} +1 -1
  17. package/dist/{run-plugins-sjeIm8hS.mjs → run-plugins-CYRt77yP.mjs} +2 -2
  18. package/dist/{typegen-CFW1vIgv.mjs → typegen-CgWQM1FH.mjs} +3 -3
  19. package/dist/{typegen-CYA1y8NJ.mjs → typegen-SyGEEyKv.mjs} +4 -4
  20. package/dist/{typegen-CYA1y8NJ.mjs.map → typegen-SyGEEyKv.mjs.map} +1 -1
  21. package/dist/{types-2ICiQzlQ.mjs → types-dPuCyHXz.mjs} +2 -2
  22. package/dist/{types-2ICiQzlQ.mjs.map → types-dPuCyHXz.mjs.map} +1 -1
  23. package/package.json +2 -2
  24. package/dist/builtins-CnQ6lxcV.mjs.map +0 -1
  25. package/dist/config-B5g_GsV2.mjs +0 -12
  26. package/dist/config-B5g_GsV2.mjs.map +0 -1
  27. package/dist/config-DE9Vo6LN.mjs +0 -11
  28. package/dist/generator-extension-BNgcYlom.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-cli v5.3.2
2
+ * @forinda/kickjs-cli v5.4.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{t as e}from"./rolldown-runtime-CP9PNXAB.mjs";import{a as t,i as n,r,t as i}from"./config-DE9Vo6LN.mjs";import{n as a,r as o}from"./plugin-B56zClEX.mjs";import{a as s,i as c,o as l,r as u,s as d,t as f}from"./typegen-CFW1vIgv.mjs";import{createRequire as p}from"node:module";import{cpSync as m,existsSync as h,mkdirSync as g,readFileSync as _,readdirSync as ee,rmSync as te,statSync as ne,writeFileSync as re}from"node:fs";import v,{basename as ie,dirname as y,extname as ae,isAbsolute as oe,join as b,relative as x,resolve as S,sep as se}from"node:path";import{fileURLToPath as ce,pathToFileURL as C}from"node:url";import{execSync as w,fork as le,spawn as ue,spawnSync as de}from"node:child_process";import{access as fe,mkdir as pe,readFile as T,rm as me,writeFile as E}from"node:fs/promises";import*as D from"@clack/prompts";import O from"picocolors";import he from"pluralize";import{glob as ge}from"glob";import{groupAssetKeys as _e}from"@forinda/kickjs";import{arch as ve,platform as ye,release as be}from"node:os";import{generate as xe,migrateDown as Se,migrateLatest as Ce,migrateRollback as we,migrateStatus as Te,migrateUp as Ee,renderSchemaSource as De,resolveDbConfig as Oe}from"@forinda/kickjs-db";function ke(e,t,n){w(e,{cwd:t,stdio:`inherit`,env:n?{...process.env,...n}:process.env})}function Ae(e,t,n){let r=de(process.execPath,[e],{cwd:n,stdio:`inherit`,env:{...process.env,...t}});r.status!==0&&process.exit(r.status??1)}let je=!1;function k(e){je=e}const Me=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function A(e,t){je||(await pe(y(e),{recursive:!0}),await E(e,t,`utf-8`),Me.has(ae(e))&&await Pe(e,t).catch(()=>{}))}let j;async function Ne(e){if(j!==void 0)return j;try{j=await import(p(b(e,`package.json`)).resolve(`oxfmt`))}catch{j=null}return j}async function Pe(e,t){let n=await Ne(process.cwd());if(!n)return;let r=await Fe(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await E(e,i.code,`utf-8`)}const M=new Map;async function Fe(e){let t=y(e),n=t;if(M.has(n))return M.get(n);for(;;){let e=b(t,`.oxfmtrc.json`);if(h(e))try{let t=await T(e,`utf-8`),r=JSON.parse(t);return delete r.$schema,delete r.ignorePatterns,M.set(n,r),r}catch{return M.set(n,null),null}let r=y(t);if(r===t)return M.set(n,null),null;t=r}}async function N(e){try{return await fe(e),!0}catch{return!1}}const Ie={auth:`@forinda/kickjs-auth`,swagger:`@forinda/kickjs-swagger`,ws:`@forinda/kickjs-ws`,queue:`@forinda/kickjs-queue`,devtools:`@forinda/kickjs-devtools`};function Le(e,t,n,r=[]){let i={"@forinda/kickjs":n,dotenv:`^17.3.1`,express:`^5.1.0`,"reflect-metadata":`^0.2.2`,zod:`^4.3.6`,pino:`^10.3.1`,"pino-pretty":`^13.1.3`};for(let e of r){let t=Ie[e];t&&!i[t]&&(i[t]=n)}return JSON.stringify({name:e,version:n.replace(`^`,``),type:`module`,scripts:{dev:`vite`,"dev:debug":`kick dev:debug`,build:`kick build`,start:`kick start`,test:`vitest run`,"test:watch":`vitest`,typecheck:`tsc --noEmit`,typegen:`kick typegen`,lint:`eslint src/`,format:`prettier --write src/`},dependencies:i,devDependencies:{"@forinda/kickjs-cli":n,"@forinda/kickjs-vite":n,"@swc/core":`^1.15.21`,"@types/express":`^5.0.6`,"@types/node":`^25.0.0`,"unplugin-swc":`^1.5.9`,vite:`^8.0.3`,vitest:`^4.1.2`,typescript:`^6.0.3`,prettier:`^3.8.1`}},null,2)}function Re(){return`import { defineConfig } from 'vite'
11
+ import{t as e}from"./rolldown-runtime-B9Lxtctb.mjs";import{a as t,i as n,r,t as i}from"./config-q_jBP6Ln.mjs";import{n as a,r as o}from"./plugin-D6nfJWY0.mjs";import{a as s,i as c,o as l,r as u,s as d,t as f}from"./typegen-CgWQM1FH.mjs";import{createRequire as p}from"node:module";import{cpSync as m,existsSync as h,mkdirSync as g,readFileSync as _,readdirSync as ee,rmSync as v,statSync as te,writeFileSync as ne}from"node:fs";import y,{basename as re,dirname as b,extname as ie,isAbsolute as ae,join as x,relative as S,resolve as C,sep as oe}from"node:path";import{fileURLToPath as se,pathToFileURL as ce}from"node:url";import{execFileSync as le,execSync as w,fork as ue,spawn as de,spawnSync as fe}from"node:child_process";import{access as pe,copyFile as me,mkdir as he,readFile as T,readdir as ge,rm as _e,stat as ve,writeFile as E}from"node:fs/promises";import*as D from"@clack/prompts";import O from"picocolors";import ye from"pluralize";import{glob as be}from"glob";import{groupAssetKeys as xe}from"@forinda/kickjs";import{arch as Se,platform as Ce,release as we}from"node:os";import{generate as Te,migrateDown as Ee,migrateLatest as De,migrateRollback as Oe,migrateStatus as ke,migrateUp as Ae,renderSchemaSource as je,resolveDbConfig as Me}from"@forinda/kickjs-db";function Ne(e,t,n){w(e,{cwd:t,stdio:`inherit`,env:n?{...process.env,...n}:process.env})}function Pe(e,t,n){let r=fe(process.execPath,[e],{cwd:n,stdio:`inherit`,env:{...process.env,...t}});r.status!==0&&process.exit(r.status??1)}let Fe=!1;function k(e){Fe=e}const Ie=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function A(e,t){Fe||(await he(b(e),{recursive:!0}),await E(e,t,`utf-8`),Ie.has(ie(e))&&await Re(e,t).catch(()=>{}))}let j;async function Le(e){if(j!==void 0)return j;try{j=await import(p(x(e,`package.json`)).resolve(`oxfmt`))}catch{j=null}return j}async function Re(e,t){let n=await Le(process.cwd());if(!n)return;let r=await ze(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await E(e,i.code,`utf-8`)}const M=new Map;async function ze(e){let t=b(e),n=t;if(M.has(n))return M.get(n);for(;;){let e=x(t,`.oxfmtrc.json`);if(h(e))try{let t=await T(e,`utf-8`),r=JSON.parse(t);return delete r.$schema,delete r.ignorePatterns,M.set(n,r),r}catch{return M.set(n,null),null}let r=b(t);if(r===t)return M.set(n,null),null;t=r}}async function Be(e){try{return await pe(e),!0}catch{return!1}}const Ve={auth:`@forinda/kickjs-auth`,swagger:`@forinda/kickjs-swagger`,ws:`@forinda/kickjs-ws`,queue:`@forinda/kickjs-queue`,devtools:`@forinda/kickjs-devtools`};function He(e,t){let n=e[t];if(!n)throw Error(`generatePackageJson: missing resolved version for ${t}. Add it to SIBLING_PACKAGES in generators/project.ts.`);return n}function Ue(e,t,n,r=[]){let i={"@forinda/kickjs":He(n,`@forinda/kickjs`),dotenv:`^17.3.1`,express:`^5.1.0`,"reflect-metadata":`^0.2.2`,zod:`^4.3.6`,pino:`^10.3.1`,"pino-pretty":`^13.1.3`};for(let e of r){let t=Ve[e];t&&!i[t]&&(i[t]=He(n,t))}return JSON.stringify({name:e,version:`0.0.0`,type:`module`,scripts:{dev:`vite`,"dev:debug":`kick dev:debug`,build:`kick build`,start:`kick start`,test:`vitest run`,"test:watch":`vitest`,typecheck:`tsc --noEmit`,typegen:`kick typegen`,lint:`eslint src/`,format:`prettier --write src/`},dependencies:i,devDependencies:{"@forinda/kickjs-cli":He(n,`@forinda/kickjs-cli`),"@forinda/kickjs-vite":He(n,`@forinda/kickjs-vite`),"@swc/core":`^1.15.21`,"@types/express":`^5.0.6`,"@types/node":`^25.0.0`,"unplugin-swc":`^1.5.9`,vite:`^8.0.3`,vitest:`^4.1.2`,typescript:`^6.0.3`,prettier:`^3.8.1`}},null,2)}function We(){return`import { defineConfig } from 'vite'
12
12
  import { resolve } from 'node:path'
13
13
  import swc from 'unplugin-swc'
14
14
  import { kickjsVitePlugin, envWatchPlugin } from '@forinda/kickjs-vite'
@@ -43,7 +43,7 @@ export default defineConfig({
43
43
  },
44
44
  },
45
45
  })
46
- `}function ze(){return JSON.stringify({compilerOptions:{target:`ES2022`,module:`ESNext`,moduleResolution:`bundler`,lib:[`ES2022`],types:[`node`,`vite/client`],strict:!0,esModuleInterop:!0,skipLibCheck:!0,sourceMap:!0,declaration:!0,experimentalDecorators:!0,emitDecoratorMetadata:!0,outDir:`dist`,paths:{"@/*":[`./src/*`]}},include:[`src`,`.kickjs/types/**/*.d.ts`,`.kickjs/types/**/*.ts`]},null,2)}function Be(){return JSON.stringify({semi:!1,singleQuote:!0,trailingComma:`all`,printWidth:100,tabWidth:2},null,2)}function Ve(){return`# https://editorconfig.org
46
+ `}function Ge(){return JSON.stringify({compilerOptions:{target:`ES2022`,module:`ESNext`,moduleResolution:`bundler`,lib:[`ES2022`],types:[`node`,`vite/client`],strict:!0,esModuleInterop:!0,skipLibCheck:!0,sourceMap:!0,declaration:!0,experimentalDecorators:!0,emitDecoratorMetadata:!0,outDir:`dist`,paths:{"@/*":[`./src/*`]}},include:[`src`,`.kickjs/types/**/*.d.ts`,`.kickjs/types/**/*.ts`]},null,2)}function Ke(){return JSON.stringify({semi:!1,singleQuote:!0,trailingComma:`all`,printWidth:100,tabWidth:2},null,2)}function qe(){return`# https://editorconfig.org
47
47
  root = true
48
48
 
49
49
  [*]
@@ -56,14 +56,14 @@ insert_final_newline = true
56
56
 
57
57
  [*.md]
58
58
  trim_trailing_whitespace = false
59
- `}function He(){return`node_modules/
59
+ `}function Je(){return`node_modules/
60
60
  dist/
61
61
  .env
62
62
  coverage/
63
63
  .DS_Store
64
64
  *.tsbuildinfo
65
65
  .kickjs/
66
- `}function Ue(){return`# Auto-detect text files and normalise line endings to LF
66
+ `}function Ye(){return`# Auto-detect text files and normalise line endings to LF
67
67
  * text=auto eol=lf
68
68
 
69
69
  # Explicitly mark generated / binary files
@@ -81,11 +81,11 @@ coverage/
81
81
  pnpm-lock.yaml -diff linguist-generated
82
82
  yarn.lock -diff linguist-generated
83
83
  package-lock.json -diff linguist-generated
84
- `}function We(){return`PORT=3000
84
+ `}function Xe(){return`PORT=3000
85
85
  NODE_ENV=development
86
- `}function Ge(){return`PORT=3000
86
+ `}function Ze(){return`PORT=3000
87
87
  NODE_ENV=development
88
- `}function Ke(){return`import { defineConfig } from 'vitest/config'
88
+ `}function Qe(){return`import { defineConfig } from 'vitest/config'
89
89
  import swc from 'unplugin-swc'
90
90
 
91
91
  export default defineConfig({
@@ -96,7 +96,7 @@ export default defineConfig({
96
96
  include: ['src/**/*.test.ts'],
97
97
  },
98
98
  })
99
- `}function qe(e,t,n,r=[]){switch(t){case`cqrs`:{let t=[],i=[];return r.includes(`devtools`)&&(t.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`),i.push(` DevToolsAdapter(),`)),r.includes(`swagger`)&&(t.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`),i.push(` SwaggerAdapter({\n info: { title: '${e}', version: '${n}' },\n }),`)),`import 'reflect-metadata'
99
+ `}function $e(e,t,n,r=[]){switch(t){case`cqrs`:{let t=[],i=[];return r.includes(`devtools`)&&(t.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`),i.push(` DevToolsAdapter(),`)),r.includes(`swagger`)&&(t.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`),i.push(` SwaggerAdapter({\n info: { title: '${e}', version: '${n}' },\n }),`)),`import 'reflect-metadata'
100
100
  // Side-effect import — registers the extended env schema with kickjs
101
101
  // **before** any controller / service / @Value gets resolved. Without
102
102
  // this line ConfigService.get('YOUR_KEY') returns undefined because the
@@ -166,12 +166,14 @@ export const app = await bootstrap({
166
166
  express.json(),
167
167
  ],
168
168
  })
169
- `}}}function Je(){return`import type { AppModuleClass } from '@forinda/kickjs'
169
+ `}}}function et(){return`import { defineModules } from '@forinda/kickjs'
170
170
  import { HelloModule } from './hello/hello.module'
171
171
 
172
172
  // Remove HelloModule and run: kick g module <name>
173
- export const modules: AppModuleClass[] = [HelloModule]
174
- `}function Ye(){return`import { defineEnv, loadEnv } from '@forinda/kickjs/config'
173
+ // \`defineModules()\` returns a chainable list — \`kick g module\` appends
174
+ // \`.mount(NewModule())\` to the chain on every generation.
175
+ export const modules = defineModules().mount(HelloModule())
176
+ `}function tt(){return`import { defineEnv, loadEnv } from '@forinda/kickjs/config'
175
177
  import { z } from 'zod'
176
178
 
177
179
  /**
@@ -206,7 +208,7 @@ const envSchema = defineEnv((base) =>
206
208
  export const env = loadEnv(envSchema)
207
209
 
208
210
  export default envSchema
209
- `}function Xe(){return`import { Service } from '@forinda/kickjs'
211
+ `}function nt(){return`import { Service } from '@forinda/kickjs'
210
212
 
211
213
  @Service()
212
214
  export class HelloService {
@@ -218,7 +220,7 @@ export class HelloService {
218
220
  return { status: 'ok', uptime: process.uptime() }
219
221
  }
220
222
  }
221
- `}function Ze(){return`import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'
223
+ `}function rt(){return`import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'
222
224
  import { HelloService } from './hello.service'
223
225
 
224
226
  // \`Ctx<KickRoutes.HelloController['<method>']>\` is generated by
@@ -240,26 +242,28 @@ export class HelloController {
240
242
  ctx.json(this.helloService.healthCheck())
241
243
  }
242
244
  }
243
- `}function Qe(){return`import { type AppModule, type ModuleRoutes, buildRoutes } from '@forinda/kickjs'
245
+ `}function it(){return`import { defineModule } from '@forinda/kickjs'
244
246
  import { HelloController } from './hello.controller'
245
247
 
246
- export class HelloModule implements AppModule {
247
- // \`register(container)\` is optional — only implement it when you need
248
- // to bind a token to a concrete implementation, e.g.
249
- // register(container) {
250
- // container.registerFactory(USER_REPOSITORY, () => container.resolve(InMemoryUserRepository))
251
- // }
252
- // The HelloService uses @Service() so the decorator handles registration.
253
-
254
- routes(): ModuleRoutes {
255
- return {
256
- path: '/hello',
257
- router: buildRoutes(HelloController),
258
- controller: HelloController,
259
- }
260
- }
261
- }
262
- `}function $e(e,t=`inmemory`,n=`pnpm`){return`import { defineConfig } from '@forinda/kickjs-cli'
248
+ export const HelloModule = defineModule({
249
+ name: 'HelloModule',
250
+ build: () => ({
251
+ // \`register(container)\` is optional — only implement it when you need
252
+ // to bind a token to a concrete implementation, e.g.
253
+ // register(container) {
254
+ // container.registerFactory(USER_REPOSITORY, () => container.resolve(InMemoryUserRepository))
255
+ // }
256
+ // The HelloService uses @Service() so the decorator handles registration.
257
+
258
+ routes() {
259
+ return {
260
+ path: '/hello',
261
+ controller: HelloController,
262
+ }
263
+ },
264
+ }),
265
+ })
266
+ `}function at(e,t=`inmemory`,n=`pnpm`){return`import { defineConfig } from '@forinda/kickjs-cli'
263
267
 
264
268
  export default defineConfig({
265
269
  pattern: '${e}',
@@ -303,7 +307,7 @@ export default defineConfig({
303
307
  },
304
308
  ],
305
309
  })
306
- `}function et(e,t,n){let r={rest:`REST API`,ddd:`Domain-Driven Design`,cqrs:`CQRS + Event-Driven`,minimal:`Minimal`},i=[`@forinda/kickjs`,`@forinda/kickjs-vite`];return t!==`minimal`&&i.push(`@forinda/kickjs-swagger`,`@forinda/kickjs-devtools`),t===`cqrs`&&i.push(`@forinda/kickjs-queue`,`@forinda/kickjs-ws`),`# ${e}
310
+ `}function ot(e,t,n){let r={rest:`REST API`,ddd:`Domain-Driven Design`,cqrs:`CQRS + Event-Driven`,minimal:`Minimal`},i=[`@forinda/kickjs`,`@forinda/kickjs-vite`];return t!==`minimal`&&i.push(`@forinda/kickjs-swagger`,`@forinda/kickjs-devtools`),t===`cqrs`&&i.push(`@forinda/kickjs-queue`,`@forinda/kickjs-ws`),`# ${e}
307
311
 
308
312
  A **${r[t]??`REST API`}** built with [KickJS](https://forinda.github.io/kick-js/) — a decorator-driven Node.js framework on Express 5 and TypeScript.
309
313
 
@@ -366,7 +370,7 @@ Copy \`.env.example\` to \`.env\` and configure:
366
370
 
367
371
  - [KickJS Documentation](https://forinda.github.io/kick-js/)
368
372
  - [CLI Reference](https://forinda.github.io/kick-js/api/cli.html)
369
- `}function tt(e,t,n){return`# CLAUDE.md — ${e}
373
+ `}function st(e,t,n){return`# CLAUDE.md — ${e}
370
374
 
371
375
  **Read \`./AGENTS.md\` first.** It is the canonical, multi-agent
372
376
  reference for this project (Claude, Copilot, Codex, Gemini, etc.) —
@@ -432,7 +436,7 @@ When generating or modifying code in this project, stay aligned with the v4 conv
432
436
  - **Refresh these files**: \`kick g agents -f\` regenerates \`AGENTS.md\` + \`CLAUDE.md\` from the latest CLI templates. Hand-edited content is overwritten — keep customisation in \`AGENTS.local.md\`.
433
437
 
434
438
  For everything else (controllers, services, modules, RequestContext API, generators, CLI commands, package additions, env wiring, troubleshooting) → \`AGENTS.md\`.
435
- `}function nt(e,t,n){return`# AGENTS.md — AI Agent Guide for ${e}
439
+ `}function ct(e,t,n){return`# AGENTS.md — AI Agent Guide for ${e}
436
440
 
437
441
  This guide is the **canonical, multi-agent reference** for this KickJS
438
442
  application — Claude, Copilot, Codex, Gemini, etc. all read it first.
@@ -536,8 +540,10 @@ mistakes:
536
540
  inline registration in the entry file:
537
541
 
538
542
  \`\`\`ts
539
- // src/modules/index.ts
540
- export const modules: AppModuleClass[] = [HelloModule, UsersModule, ...]
543
+ // src/modules/index.ts — fluent chain (default for \`modules.style: 'define'\`)
544
+ export const modules = defineModules().mount(HelloModule()).mount(UsersModule())
545
+ // OR with \`modules.style: 'class'\`:
546
+ // export const modules: AppModuleEntry[] = [HelloModule, UsersModule]
541
547
 
542
548
  // src/middleware/index.ts
543
549
  export const middleware = [helmet(), cors(), requestId(), ...]
@@ -601,7 +607,7 @@ ${t===`ddd`?`\`\`\`
601
607
  ├── <name>.repository.ts # Data access (@Repository)
602
608
  ├── <name>.dto.ts # Request/response schemas (Zod)
603
609
  ├── <name>.entity.ts # Domain entity (optional)
604
- └── <name>.module.ts # Module definition (implements AppModule)
610
+ └── <name>.module.ts # Module definition (defineModule factory)
605
611
  \`\`\`
606
612
  `:t===`cqrs`?`\`\`\`
607
613
  <name>/
@@ -615,14 +621,14 @@ ${t===`ddd`?`\`\`\`
615
621
  │ └── <name>-created.event.ts
616
622
  ├── <name>.controller.ts # HTTP routes
617
623
  ├── <name>.repository.ts # Data access
618
- └── <name>.module.ts # Module definition (implements AppModule)
624
+ └── <name>.module.ts # Module definition (defineModule factory)
619
625
  \`\`\`
620
626
  `:t===`rest`?`\`\`\`
621
627
  <name>/
622
628
  ├── <name>.controller.ts # HTTP routes (@Controller)
623
629
  ├── <name>.service.ts # Business logic (@Service)
624
630
  ├── <name>.dto.ts # Request/response schemas (Zod)
625
- └── <name>.module.ts # Module definition (implements AppModule)
631
+ └── <name>.module.ts # Module definition (defineModule factory)
626
632
  \`\`\`
627
633
  `:"```\nsrc/\n├── index.ts # Add routes here\n└── ... # Custom structure\n```\n"}
628
634
 
@@ -653,8 +659,8 @@ If not using generators:
653
659
  - [ ] Create \`src/modules/<name>/<name>.controller.ts\`
654
660
  - [ ] Add \`@Controller()\` decorator
655
661
  - [ ] Add route handlers with \`@Get()\`, \`@Post()\`, etc.
656
- - [ ] Create module file implementing \`AppModule\` with \`routes()\` returning \`{ path, router: buildRoutes(Controller), controller }\`
657
- - [ ] Register module in \`src/modules/index.ts\` (\`AppModuleClass[]\` array)
662
+ - [ ] Create module file with \`defineModule({ name, build: () => ({ routes() { return { path, controller } } }) })\` — the framework derives the Express router from the controller. Class-form (\`class XModule implements AppModule\`) is the legacy alternative; toggle via \`kick.config.ts > modules.style\`.
663
+ - [ ] Register module in \`src/modules/index.ts\`. Default form is the fluent chain: \`defineModules().mount(MyModule()).mount(...)\`. \`kick g module <name>\` appends \`.mount(NewModule())\` automatically.
658
664
  - [ ] Test with \`kick dev\`
659
665
 
660
666
  ### Manual Service
@@ -856,7 +862,9 @@ These work anywhere — scripts, plain files, outside \`@Service\`/\`@Controller
856
862
  ### Dependency Injection
857
863
  | Decorator | Purpose |
858
864
  |-----------|---------|
859
- | \`AppModule\` interface | Define feature module (implements \`routes()\`) |
865
+ | \`defineModule({...})\` | Define feature module (factory; preferred — paired with \`defineModules()\` registry) |
866
+ | \`defineModules()\` | Build the modules registry as a chainable list (\`.mount(X())\`) |
867
+ | \`AppModule\` interface | Legacy module shape — \`class X implements AppModule\` (toggle via \`modules.style: 'class'\`) |
860
868
  | \`@Service()\` | Register singleton service |
861
869
  | \`@Repository()\` | Register repository |
862
870
  | \`@Autowired()\` | Property injection |
@@ -873,7 +881,7 @@ is a value other code reads off \`ctx\`.
873
881
  |---------|----------------|
874
882
  | \`defineContextDecorator({ key, deps, dependsOn, optional, onError, resolve })\` | \`@forinda/kickjs\` |
875
883
  | Method/class decorator | \`@LoadX\` on a controller method/class |
876
- | Module hook | \`AppModule.contributors?(): ContributorRegistration[]\` |
884
+ | Module hook | \`build: () => ({ contributors() { return [...] } })\` (\`defineModule\`) — or \`AppModule.contributors?()\` for class form |
877
885
  | Adapter hook | \`AppAdapter.contributors?(): ContributorRegistration[]\` |
878
886
  | Global registration | \`bootstrap({ contributors: [LoadX.registration] })\` |
879
887
  | Type augmentation | \`declare module '@forinda/kickjs' { interface ContextMeta { ... } }\` |
@@ -935,7 +943,7 @@ ${t===`cqrs`?`### Background Jobs
935
943
  - [Decorators Guide](https://forinda.github.io/kick-js/guide/decorators.html)
936
944
  - [DI System](https://forinda.github.io/kick-js/guide/dependency-injection.html)
937
945
  - [Testing](https://forinda.github.io/kick-js/api/testing.html)
938
- `}function rt(e,t,n){return`# kickjs-skills.md — Task Skills for AI Agents (${e})
946
+ `}function lt(e,t,n){return`# kickjs-skills.md — Task Skills for AI Agents (${e})
939
947
 
940
948
  This file is the agent-facing **skills index** for KickJS work in this
941
949
  repo. Each block below is a short, rigid workflow keyed to a specific
@@ -1070,8 +1078,10 @@ description: Use when src/index.ts is accumulating module/middleware/plugin/adap
1070
1078
  **Refactor target**:
1071
1079
 
1072
1080
  \`\`\`ts
1073
- // src/modules/index.ts
1074
- export const modules: AppModuleClass[] = [HelloModule, UsersModule, ...]
1081
+ // src/modules/index.ts — fluent chain (default for \`modules.style: 'define'\`)
1082
+ export const modules = defineModules().mount(HelloModule()).mount(UsersModule())
1083
+ // OR for class-form projects (\`modules.style: 'class'\`):
1084
+ // export const modules: AppModuleEntry[] = [HelloModule, UsersModule]
1075
1085
 
1076
1086
  // src/middleware/index.ts
1077
1087
  export const middleware = [helmet(), cors(), requestId(), ...]
@@ -1182,16 +1192,16 @@ description: Patterns to refuse outright when the user asks for them — they br
1182
1192
  - [Decorators](https://forinda.github.io/kick-js/guide/decorators.html)
1183
1193
  - [Context Decorators](https://forinda.github.io/kick-js/guide/context-decorators.html)
1184
1194
  - [Testing](https://forinda.github.io/kick-js/api/testing.html)
1185
- `}const it=y(ce(import.meta.url)),at=JSON.parse(_(b(it,`..`,`package.json`),`utf-8`)),ot=`^${at.version}`;async function st(e){let{name:t,directory:n,packageManager:r=`pnpm`,template:i=`rest`,defaultRepo:a=`inmemory`,packages:o=[]}=e,s=n,c=e=>console.log(` ${e}`);if(console.log(`\n Creating KickJS project: ${t}\n`),await A(b(s,`package.json`),Le(t,i,ot,o)),await A(b(s,`vite.config.ts`),Re()),await A(b(s,`tsconfig.json`),ze()),await A(b(s,`.prettierrc`),Be()),await A(b(s,`.editorconfig`),Ve()),await A(b(s,`.gitignore`),He()),await A(b(s,`.gitattributes`),Ue()),await A(b(s,`.env`),We()),await A(b(s,`.env.example`),Ge()),await A(b(s,`src/config/index.ts`),Ye()),await A(b(s,`src/index.ts`),qe(t,i,at.version,o)),await A(b(s,`src/modules/index.ts`),Je()),await A(b(s,`src/modules/hello/hello.service.ts`),Xe()),await A(b(s,`src/modules/hello/hello.controller.ts`),Ze()),await A(b(s,`src/modules/hello/hello.module.ts`),Qe()),await A(b(s,`kick.config.ts`),$e(i,a,r)),await A(b(s,`vitest.config.ts`),Ke()),await A(b(s,`README.md`),et(t,i,r)),await A(b(s,`CLAUDE.md`),tt(t,i,r)),await A(b(s,`AGENTS.md`),nt(t,i,r)),await A(b(s,`kickjs-skills.md`),rt(t,i,r)),e.installDeps){console.log(`\n Installing dependencies with ${r}...\n`);try{w(`${r} install`,{cwd:s,stdio:`inherit`}),console.log(`
1186
- Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${r} install failed. Run it manually.`)}}try{let{runTypegen:e}=await import(`./typegen-CFW1vIgv.mjs`).then(e=>e.n);await e({cwd:s,allowDuplicates:!0,silent:!0})}catch{}if(e.initGit)try{w(`git init`,{cwd:s,stdio:`pipe`}),w(`git branch -M main`,{cwd:s,stdio:`pipe`}),w(`git add -A`,{cwd:s,stdio:`pipe`}),w(`git commit -m "chore: initial commit from kick new"`,{cwd:s,stdio:`pipe`}),c(`Git repository initialized`)}catch{c(`Warning: git init failed (git may not be installed)`)}console.log(`
1187
- Project scaffolded successfully!`),console.log();let l=s!==process.cwd();c(`Next steps:`),l&&c(` cd ${t}`),e.installDeps||c(` ${r} install`);let u={rest:`kick g module user`,ddd:`kick g module user --repo drizzle`,cqrs:`kick g module user --pattern cqrs`,minimal:`# add your routes to src/index.ts`};c(` ${u[i]??u.rest}`),c(` kick dev`),c(``),c(`Commands:`),c(` kick dev Start dev server with Vite HMR`),c(` kick build Production build via Vite`),c(` kick start Run production build`),c(``),c(`Generators:`),c(` kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)`),c(` kick g scaffold <n> <f..> CRUD module from field definitions`),c(` kick g controller <name> Standalone controller`),c(` kick g service <name> @Service() class`),c(` kick g middleware <name> Express middleware`),c(` kick g guard <name> Route guard (auth, roles, etc.)`),c(` kick g adapter <name> AppAdapter with lifecycle hooks`),c(` kick g dto <name> Zod DTO schema`),i===`cqrs`&&c(` kick g job <name> Queue job processor`),c(` kick g config Generate kick.config.ts`),c(``),c(`Add packages:`),c(` kick add <pkg> Install a KickJS package + peers`),c(` kick add --list Show all available packages`),c(``),c(`Available: auth, swagger, drizzle, prisma, ws, queue, devtools, mcp, testing`),c(``)}const ct={GET:O.green,POST:O.cyan,PUT:O.yellow,PATCH:O.magenta,DELETE:O.red};function lt(e){return(ct[e]??O.dim)(e.padEnd(7))}function ut(e){let t=`[${e}]`.padEnd(10);switch(e){case`CRITICAL`:return O.red(t);case`WARNING`:return O.yellow(t);case`INFO`:return O.blue(O.dim(t));default:return t}}O.green(`✓`),O.red(`✖`),O.yellow(`⚠`),O.blue(`ℹ`);function dt(e){D.intro(O.bgCyan(O.black(` ${e} `)))}function P(e){D.outro(e)}function ft(e){D.isCancel(e)&&(D.cancel(`Operation cancelled.`),process.exit(0))}async function pt(e){let t=await D.text(e);return ft(t),t}async function mt(e){let t=await D.select(e);return ft(t),t}async function ht(e){let t=await D.multiselect(e);return ft(t),t}async function F(e){let t=await D.confirm(e);return ft(t),t}function gt(){return D.spinner()}const I=D.log,_t={kickjs:{pkg:`@forinda/kickjs`,peers:[`express`],description:`Unified framework: DI, decorators, routing, middleware`,core:!0},vite:{pkg:`@forinda/kickjs-vite`,peers:[`vite`],description:`Vite plugin: dev server, HMR, module discovery`,dev:!0,core:!0},cli:{pkg:`@forinda/kickjs-cli`,peers:[],description:`CLI tool and code generators`,dev:!0,core:!0},swagger:{pkg:`@forinda/kickjs-swagger`,peers:[],description:`OpenAPI spec + Swagger UI + ReDoc`},db:{pkg:`@forinda/kickjs-db`,peers:[],description:`kick/db core — schema DSL, migrations, KickDbClient, customType`},"db-pg":{pkg:`@forinda/kickjs-db-pg`,peers:[`pg`],description:`kick/db PostgreSQL dialect + adapter (pgDialect, pgAdapter)`},drizzle:{pkg:`@forinda/kickjs-drizzle`,peers:[`drizzle-orm`],description:`Drizzle ORM adapter + query builder`},prisma:{pkg:`@forinda/kickjs-prisma`,peers:[`@prisma/client`],description:`Prisma adapter + query builder`},ws:{pkg:`@forinda/kickjs-ws`,peers:[`socket.io`],description:`WebSocket with @WsController decorators`},devtools:{pkg:`@forinda/kickjs-devtools`,peers:[],description:`Development dashboard — routes, DI, metrics, health`,dev:!0},auth:{pkg:`@forinda/kickjs-auth`,peers:[`jsonwebtoken`],description:`Authentication — JWT, API key, and custom strategies`},queue:{pkg:`@forinda/kickjs-queue`,peers:[],description:`Queue adapter (BullMQ/RabbitMQ/Kafka)`},"queue:bullmq":{pkg:`@forinda/kickjs-queue`,peers:[`bullmq`,`ioredis`],description:`Queue with BullMQ + Redis`},"queue:rabbitmq":{pkg:`@forinda/kickjs-queue`,peers:[`amqplib`],description:`Queue with RabbitMQ`},"queue:kafka":{pkg:`@forinda/kickjs-queue`,peers:[`kafkajs`],description:`Queue with Kafka`},mcp:{pkg:`@forinda/kickjs-mcp`,peers:[`@modelcontextprotocol/sdk`],description:`Model Context Protocol server — expose @Controller endpoints as AI tools`},testing:{pkg:`@forinda/kickjs-testing`,peers:[],description:`Test utilities and TestModule builder`,dev:!0}};function L(e,t=process.cwd()){let n=t;for(;;){if(h(S(n,e)))return n;let t=y(n);if(t===n)return null;n=t}}function vt(){return L(`pnpm-lock.yaml`)?`pnpm`:L(`yarn.lock`)?`yarn`:L(`bun.lockb`)||L(`bun.lock`)?`bun`:L(`package-lock.json`)?`npm`:null}function yt(){let e=process.cwd();for(;e;){let t=S(e,`package.json`);if(h(t))try{let e=JSON.parse(_(t,`utf-8`)).packageManager;if(typeof e==`string`){let t=e.split(`@`)[0];if(i.includes(t))return t}}catch{}let n=y(e);if(n===e)return null;e=n}return null}async function bt(e){if(e&&i.includes(e))return{pm:e,source:`flag`};let t=await r(process.cwd());if(t?.packageManager&&i.includes(t.packageManager))return{pm:t.packageManager,source:`config`};let n=yt();if(n)return{pm:n,source:`package.json`};let a=vt();return a?{pm:a,source:`lockfile`}:{pm:`npm`,source:`default`}}async function xt(e){let{pm:t}=await bt(e);return t}function St(e=!1){let t=Object.entries(_t),n=Math.max(...t.map(([e])=>e.length)),r=t.filter(([,e])=>e.core),i=t.filter(([,e])=>!e.core),a=([e,t])=>{let r=e.padEnd(n+2),i=t.peers.length?` (+ ${t.peers.join(`, `)})`:``;return` ${r} ${t.description}${i}`};console.log(`
1195
+ `}const ut=b(se(import.meta.url)),dt=JSON.parse(_(x(ut,`..`,`package.json`),`utf-8`)),ft=`^${dt.version}`,pt=[`@forinda/kickjs`,`@forinda/kickjs-cli`,`@forinda/kickjs-vite`,`@forinda/kickjs-auth`,`@forinda/kickjs-swagger`,`@forinda/kickjs-ws`,`@forinda/kickjs-queue`,`@forinda/kickjs-devtools`,`@forinda/kickjs-testing`];async function mt(){let e=await Promise.all(pt.map(async e=>{try{let t=le(`npm`,[`view`,e,`version`],{encoding:`utf-8`,timeout:5e3,stdio:[`ignore`,`pipe`,`ignore`]}).toString().trim();if(t&&/^\d+\.\d+\.\d+/.test(t))return[e,`^${t}`]}catch{}return[e,ft]}));return Object.fromEntries(e)}async function ht(e){let{name:t,directory:n,packageManager:r=`pnpm`,template:i=`rest`,defaultRepo:a=`inmemory`,packages:o=[]}=e,s=n,c=e=>console.log(` ${e}`);console.log(`\n Creating KickJS project: ${t}\n`),c(`Resolving package versions...`);let l=await mt();if(await A(x(s,`package.json`),Ue(t,i,l,o)),await A(x(s,`vite.config.ts`),We()),await A(x(s,`tsconfig.json`),Ge()),await A(x(s,`.prettierrc`),Ke()),await A(x(s,`.editorconfig`),qe()),await A(x(s,`.gitignore`),Je()),await A(x(s,`.gitattributes`),Ye()),await A(x(s,`.env`),Xe()),await A(x(s,`.env.example`),Ze()),await A(x(s,`src/config/index.ts`),tt()),await A(x(s,`src/index.ts`),$e(t,i,dt.version,o)),await A(x(s,`src/modules/index.ts`),et()),await A(x(s,`src/modules/hello/hello.service.ts`),nt()),await A(x(s,`src/modules/hello/hello.controller.ts`),rt()),await A(x(s,`src/modules/hello/hello.module.ts`),it()),await A(x(s,`kick.config.ts`),at(i,a,r)),await A(x(s,`vitest.config.ts`),Qe()),await A(x(s,`README.md`),ot(t,i,r)),await A(x(s,`CLAUDE.md`),st(t,i,r)),await A(x(s,`AGENTS.md`),ct(t,i,r)),await A(x(s,`kickjs-skills.md`),lt(t,i,r)),e.installDeps){console.log(`\n Installing dependencies with ${r}...\n`);try{w(`${r} install`,{cwd:s,stdio:`inherit`}),console.log(`
1196
+ Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${r} install failed. Run it manually.`)}}try{let{runTypegen:e}=await import(`./typegen-CgWQM1FH.mjs`).then(e=>e.n);await e({cwd:s,allowDuplicates:!0,silent:!0})}catch{}if(e.initGit)try{w(`git init`,{cwd:s,stdio:`pipe`}),w(`git branch -M main`,{cwd:s,stdio:`pipe`}),w(`git add -A`,{cwd:s,stdio:`pipe`}),w(`git commit -m "chore: initial commit from kick new"`,{cwd:s,stdio:`pipe`}),c(`Git repository initialized`)}catch{c(`Warning: git init failed (git may not be installed)`)}console.log(`
1197
+ Project scaffolded successfully!`),console.log();let u=s!==process.cwd();c(`Next steps:`),u&&c(` cd ${t}`),e.installDeps||c(` ${r} install`);let d={rest:`kick g module user`,ddd:`kick g module user --repo drizzle`,cqrs:`kick g module user --pattern cqrs`,minimal:`# add your routes to src/index.ts`};c(` ${d[i]??d.rest}`),c(` kick dev`),c(``),c(`Commands:`),c(` kick dev Start dev server with Vite HMR`),c(` kick build Production build via Vite`),c(` kick start Run production build`),c(``),c(`Generators:`),c(` kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)`),c(` kick g scaffold <n> <f..> CRUD module from field definitions`),c(` kick g controller <name> Standalone controller`),c(` kick g service <name> @Service() class`),c(` kick g middleware <name> Express middleware`),c(` kick g guard <name> Route guard (auth, roles, etc.)`),c(` kick g adapter <name> AppAdapter with lifecycle hooks`),c(` kick g dto <name> Zod DTO schema`),i===`cqrs`&&c(` kick g job <name> Queue job processor`),c(` kick g config Generate kick.config.ts`),c(``),c(`Add packages:`),c(` kick add <pkg> Install a KickJS package + peers`),c(` kick add --list Show all available packages`),c(``),c(`Available: auth, swagger, drizzle, prisma, ws, queue, devtools, mcp, testing`),c(``)}const gt={GET:O.green,POST:O.cyan,PUT:O.yellow,PATCH:O.magenta,DELETE:O.red};function _t(e){return(gt[e]??O.dim)(e.padEnd(7))}function vt(e){let t=`[${e}]`.padEnd(10);switch(e){case`CRITICAL`:return O.red(t);case`WARNING`:return O.yellow(t);case`INFO`:return O.blue(O.dim(t));default:return t}}O.green(`✓`),O.red(`✖`),O.yellow(`⚠`),O.blue(`ℹ`);function yt(e){D.intro(O.bgCyan(O.black(` ${e} `)))}function N(e){D.outro(e)}function bt(e){D.isCancel(e)&&(D.cancel(`Operation cancelled.`),process.exit(0))}async function xt(e){let t=await D.text(e);return bt(t),t}async function St(e){let t=await D.select(e);return bt(t),t}async function Ct(e){let t=await D.multiselect(e);return bt(t),t}async function P(e){let t=await D.confirm(e);return bt(t),t}function wt(){return D.spinner()}const F=D.log,Tt={kickjs:{pkg:`@forinda/kickjs`,peers:[`express`],description:`Unified framework: DI, decorators, routing, middleware`,core:!0},vite:{pkg:`@forinda/kickjs-vite`,peers:[`vite`],description:`Vite plugin: dev server, HMR, module discovery`,dev:!0,core:!0},cli:{pkg:`@forinda/kickjs-cli`,peers:[],description:`CLI tool and code generators`,dev:!0,core:!0},swagger:{pkg:`@forinda/kickjs-swagger`,peers:[],description:`OpenAPI spec + Swagger UI + ReDoc`},db:{pkg:`@forinda/kickjs-db`,peers:[],description:`kick/db core — schema DSL, migrations, KickDbClient, customType`},"db-pg":{pkg:`@forinda/kickjs-db-pg`,peers:[`pg`],description:`kick/db PostgreSQL dialect + adapter (pgDialect, pgAdapter)`},drizzle:{pkg:`@forinda/kickjs-drizzle`,peers:[`drizzle-orm`],description:`Drizzle ORM adapter + query builder`},prisma:{pkg:`@forinda/kickjs-prisma`,peers:[`@prisma/client`],description:`Prisma adapter + query builder`},ws:{pkg:`@forinda/kickjs-ws`,peers:[`socket.io`],description:`WebSocket with @WsController decorators`},devtools:{pkg:`@forinda/kickjs-devtools`,peers:[],description:`Development dashboard — routes, DI, metrics, health`,dev:!0},auth:{pkg:`@forinda/kickjs-auth`,peers:[`jsonwebtoken`],description:`Authentication — JWT, API key, and custom strategies`},queue:{pkg:`@forinda/kickjs-queue`,peers:[],description:`Queue adapter (BullMQ/RabbitMQ/Kafka)`},"queue:bullmq":{pkg:`@forinda/kickjs-queue`,peers:[`bullmq`,`ioredis`],description:`Queue with BullMQ + Redis`},"queue:rabbitmq":{pkg:`@forinda/kickjs-queue`,peers:[`amqplib`],description:`Queue with RabbitMQ`},"queue:kafka":{pkg:`@forinda/kickjs-queue`,peers:[`kafkajs`],description:`Queue with Kafka`},mcp:{pkg:`@forinda/kickjs-mcp`,peers:[`@modelcontextprotocol/sdk`],description:`Model Context Protocol server — expose @Controller endpoints as AI tools`},testing:{pkg:`@forinda/kickjs-testing`,peers:[],description:`Test utilities and TestModule builder`,dev:!0}};function I(e,t=process.cwd()){let n=t;for(;;){if(h(C(n,e)))return n;let t=b(n);if(t===n)return null;n=t}}function Et(){return I(`pnpm-lock.yaml`)?`pnpm`:I(`yarn.lock`)?`yarn`:I(`bun.lockb`)||I(`bun.lock`)?`bun`:I(`package-lock.json`)?`npm`:null}function Dt(){let e=process.cwd();for(;e;){let t=C(e,`package.json`);if(h(t))try{let e=JSON.parse(_(t,`utf-8`)).packageManager;if(typeof e==`string`){let t=e.split(`@`)[0];if(i.includes(t))return t}}catch{}let n=b(e);if(n===e)return null;e=n}return null}async function Ot(e){if(e&&i.includes(e))return{pm:e,source:`flag`};let t=await r(process.cwd());if(t?.packageManager&&i.includes(t.packageManager))return{pm:t.packageManager,source:`config`};let n=Dt();if(n)return{pm:n,source:`package.json`};let a=Et();return a?{pm:a,source:`lockfile`}:{pm:`npm`,source:`default`}}async function kt(e){let{pm:t}=await Ot(e);return t}function At(e=!1){let t=Object.entries(Tt),n=Math.max(...t.map(([e])=>e.length)),r=t.filter(([,e])=>e.core),i=t.filter(([,e])=>!e.core),a=([e,t])=>{let r=e.padEnd(n+2),i=t.peers.length?` (+ ${t.peers.join(`, `)})`:``;return` ${r} ${t.description}${i}`};console.log(`
1188
1198
  Core packages (always installed by \`kick new\`):
1189
1199
  `);for(let e of r)console.log(a(e));if(e){console.log(`
1190
1200
  Optional packages (add as needed):
1191
1201
  `);for(let e of i)console.log(a(e))}else console.log(`\n Plus ${i.length} optional packages (auth, swagger, db, queue, …).`),console.log(" Run `kick add --list --all` for the full catalog.");console.log(`
1192
- Usage: kick add auth drizzle swagger`),console.log(` kick add queue:bullmq`),console.log()}function Ct(e){e.command(`list`).alias(`ls`).description(`List KickJS packages (core only; pair with --all for the full catalog)`).option(`--all`,`Include the full optional catalog`).action(e=>{St(!!e.all)})}function wt(e){e.command(`add [packages...]`).description(`Add KickJS packages with their required dependencies`).option(`--pm <manager>`,`Package manager override`).option(`-D, --dev`,`Install as dev dependency`).option(`--list`,`List packages (core only by default; pair with --all)`).option(`--all`,`When listing, include the full optional catalog`).action(async(e,t)=>{if(t.list||e.length===0){St(!!t.all);return}let{pm:n,source:r}=await bt(t.pm);console.log(`\n Using ${n} (resolved from ${r})`);let i=t.dev,a=new Set,o=new Set,s=[];for(let t of e){let e=_t[t];if(!e){s.push(t);continue}let n=i||e.dev?o:a;n.add(e.pkg);for(let t of e.peers)n.add(t)}if(!(s.length>0&&(console.log(`\n Unknown packages: ${s.join(`, `)}`),console.log(` Run "kick add --list" to see available packages.
1202
+ Usage: kick add auth drizzle swagger`),console.log(` kick add queue:bullmq`),console.log()}function jt(e){e.command(`list`).alias(`ls`).description(`List KickJS packages (core only; pair with --all for the full catalog)`).option(`--all`,`Include the full optional catalog`).action(e=>{At(!!e.all)})}function Mt(e){e.command(`add [packages...]`).description(`Add KickJS packages with their required dependencies`).option(`--pm <manager>`,`Package manager override`).option(`-D, --dev`,`Install as dev dependency`).option(`--list`,`List packages (core only by default; pair with --all)`).option(`--all`,`When listing, include the full optional catalog`).action(async(e,t)=>{if(t.list||e.length===0){At(!!t.all);return}let{pm:n,source:r}=await Ot(t.pm);console.log(`\n Using ${n} (resolved from ${r})`);let i=t.dev,a=new Set,o=new Set,s=[];for(let t of e){let e=Tt[t];if(!e){s.push(t);continue}let n=i||e.dev?o:a;n.add(e.pkg);for(let t of e.peers)n.add(t)}if(!(s.length>0&&(console.log(`\n Unknown packages: ${s.join(`, `)}`),console.log(` Run "kick add --list" to see available packages.
1193
1203
  `),a.size===0&&o.size===0))){if(a.size>0){let e=Array.from(a),t=`${n} add ${e.join(` `)}`;console.log(`\n Installing ${e.length} dependency(ies):`);for(let t of e)console.log(` + ${t}`);console.log();try{w(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}if(o.size>0){let e=Array.from(o),t=`${n} add -D ${e.join(` `)}`;console.log(`\n Installing ${e.length} dev dependency(ies):`);for(let t of e)console.log(` + ${t} (dev)`);console.log();try{w(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}console.log(` Done!
1194
- `)}})}const Tt=[{value:`auth`,label:`Auth`,hint:`JWT, OAuth, API keys`},{value:`swagger`,label:`Swagger`,hint:`OpenAPI docs`},{value:`ws`,label:`WebSocket`,hint:`rooms, heartbeat`},{value:`queue`,label:`Queue`,hint:`BullMQ/RabbitMQ/Kafka`},{value:`devtools`,label:`DevTools`,hint:`debug dashboard`}];function Et(e){e.command(`new [name]`).alias(`init`).description(`Create a new KickJS project (use "." for current directory)`).option(`-d, --directory <dir>`,`Target directory (defaults to project name)`).option(`--pm <manager>`,`Package manager: pnpm | npm | yarn | bun`).option(`--git`,`Initialize git repository`).option(`--no-git`,`Skip git initialization`).option(`--install`,`Install dependencies after scaffolding`).option(`--no-install`,`Skip dependency installation`).option(`-f, --force`,`Remove existing files without prompting`).option(`-t, --template <type>`,`Project template: rest | ddd | cqrs | minimal`).option(`-r, --repo <type>`,`Default repository: prisma | drizzle | inmemory | custom`).option(`--packages <packages>`,`Comma-separated packages to include (e.g. auth,swagger,ws,queue)`).option(`-y, --yes`,`Pick safe defaults for every prompt (template=minimal, repo=inmemory, no extras, git+install on)`).option(`--non-interactive`,`alias for --yes`).action(async(e,t)=>{dt(`KickJS — Create a new project`);let n=!!(t.yes||t.nonInteractive);e||=n?`my-api`:await pt({message:`Project name`,placeholder:`my-api`,defaultValue:`my-api`});let r;if(e===`.`?(r=S(`.`),e=ie(r)):r=S(t.directory||e),h(r)){let i=ee(r);if(i.length>0){if(t.force)I.warn(`Clearing existing files in ${r}`);else if(n){I.warn(`Directory "${e}" is not empty. Pass --force to clear it.`),P(`Aborted.`);return}else{I.warn(`Directory "${e}" is not empty:`);let t=i.slice(0,5);for(let e of t)I.message(` - ${e}`);if(i.length>5&&I.message(` ... and ${i.length-5} more`),!await F({message:O.red(`Remove all existing files and proceed?`),initialValue:!1})){P(`Aborted.`);return}}for(let e of i)te(S(r,e),{recursive:!0,force:!0})}}let i=t.template;i||=n?`minimal`:await mt({message:`Project template`,options:[{value:`rest`,label:`REST API`,hint:`Express + Swagger`},{value:`ddd`,label:`DDD`,hint:`Domain-Driven Design modules`},{value:`cqrs`,label:`CQRS`,hint:`Commands, Queries, Events + WS/Queue`},{value:`minimal`,label:`Minimal`,hint:`bare Express`}]});let a=t.pm;a||=n?await xt(void 0):await mt({message:`Package manager`,options:[{value:`pnpm`,label:`pnpm`},{value:`npm`,label:`npm`},{value:`yarn`,label:`yarn`},{value:`bun`,label:`bun`}]});let o=t.repo;o||(n?o=`inmemory`:(o=await mt({message:`Default repository/ORM`,options:[{value:`prisma`,label:`Prisma`},{value:`drizzle`,label:`Drizzle`},{value:`inmemory`,label:`In-Memory`},{value:`custom`,label:`Custom`,hint:`specify later`}]}),o===`custom`&&(o=await pt({message:`Custom repository name`,defaultValue:`custom`}))));let s;if(t.packages!==void 0){let e=t.packages.trim().toLowerCase();s=e===``||e===`none`||e===`false`?[]:t.packages.split(`,`).map(e=>e.trim()).filter(Boolean)}else s=n?[]:await ht({message:`Select packages to include`,options:[...Tt],required:!1});let c;c=t.git===void 0?n?!0:await F({message:`Initialize git repository?`,initialValue:!0}):t.git;let l;l=t.install===void 0?n?!0:await F({message:`Install dependencies?`,initialValue:!0}):t.install,await st({name:e,directory:r,packageManager:a,initGit:c,installDeps:l,template:i,defaultRepo:o,packages:s}),P(`Done! Next steps: ${O.cyan(`cd ${e} && ${a} dev`)}`)})}function R(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function z(e){let t=R(e);return t.charAt(0).toLowerCase()+t.slice(1)}function B(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function V(e){return he.plural(e)}function Dt(e){return he.plural(e)}function Ot(e){return B(e).replace(/-/g,`_`)}function kt(e){let t=e.cwd??process.cwd(),n=e.pluralize??!0,r=R(e.name),i=z(e.name),a=B(e.name),o=Ot(e.name),s={name:e.name,pascal:r,camel:i,kebab:a,snake:o,modulesDir:e.modulesDir??`src/modules`,cwd:t,args:e.args??[],flags:e.flags??{}};if(n){let e=V(a);s.pluralKebab=e,s.pluralPascal=R(e),s.pluralCamel=z(e)}return s}function At(e,t){return S(e.cwd,t)}async function jt(e){return import(C(e).href)}const Mt=new Map;async function Nt(e){let t=Mt.get(e);if(t)return t;let n=Pt(e);return Mt.set(e,n),n}async function Pt(e){let t=S(e,`package.json`);if(!h(t))return{generators:[],loaded:[],failed:[]};let n=Ft(JSON.parse(await T(t,`utf-8`))),r=p(S(e,`package.json`)),i=[],a=[],o=[];for(let e of n){let t;try{t=r.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await T(t,`utf-8`))}catch(t){o.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let s=n.kickjs.generators,c=S(y(t),s);if(!h(c)){o.push({source:e,reason:`kickjs.generators points to missing file: ${s}`});continue}let l;try{l=await jt(c)}catch(t){o.push({source:e,reason:`failed to import manifest: ${t}`});continue}let u=l.default;if(!Array.isArray(u)){o.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of u){if(!It(t)){o.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}i.push({source:e,spec:t})}a.push(e)}return{generators:i,loaded:a,failed:o}}function Ft(e){let t=new Set;for(let n of[e.dependencies,e.devDependencies,e.peerDependencies])if(n)for(let e of Object.keys(n))t.add(e);return Array.from(t)}function It(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function Lt(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return Bt(r.spec,r.source,e,n);let i=zt(await Nt(n),e.generatorName);return i?Bt(i.spec,i.source,e,n):null}async function Rt(e,t=[]){let n=await Nt(e),r=new Set(t.map(e=>e.spec.name)),i=n.generators.filter(e=>!r.has(e.spec.name));return{generators:[...t,...i],loaded:n.loaded,failed:n.failed}}function zt(e,t){return e.generators.find(e=>e.spec.name===t)}async function Bt(e,t,n,r){let i=kt({name:n.itemName,args:n.args,flags:n.flags,modulesDir:n.modulesDir,pluralize:n.pluralize,cwd:r}),a=await e.files(i),o=[];for(let e of a){let t=At(i,e.path);await A(t,e.content),o.push(t)}return{files:o,source:t}}const Vt={inmemory:`in-memory`,drizzle:`Drizzle`,prisma:`Prisma`};function Ht(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function Ut(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function Wt(e){return Vt[e]??Ht(e)}function Gt(e,t,n){let r={inmemory:`InMemory${e}Repository`,drizzle:`Drizzle${e}Repository`,prisma:`Prisma${e}Repository`},i={inmemory:`in-memory-${t}`,drizzle:`drizzle-${t}`,prisma:`prisma-${t}`};return{repoClass:r[n]??`${Ht(n)}${e}Repository`,repoFile:i[n]??`${Ut(n)}-${t}`}}function Kt(e){let{pascal:t,kebab:n,plural:r=``,repo:i}=e,{repoClass:a,repoFile:o}=Gt(t,n,i);return`/**
1204
+ `)}})}const Nt=[{value:`auth`,label:`Auth`,hint:`JWT, OAuth, API keys`},{value:`swagger`,label:`Swagger`,hint:`OpenAPI docs`},{value:`ws`,label:`WebSocket`,hint:`rooms, heartbeat`},{value:`queue`,label:`Queue`,hint:`BullMQ/RabbitMQ/Kafka`},{value:`devtools`,label:`DevTools`,hint:`debug dashboard`}];function Pt(e){e.command(`new [name]`).alias(`init`).description(`Create a new KickJS project (use "." for current directory)`).option(`-d, --directory <dir>`,`Target directory (defaults to project name)`).option(`--pm <manager>`,`Package manager: pnpm | npm | yarn | bun`).option(`--git`,`Initialize git repository`).option(`--no-git`,`Skip git initialization`).option(`--install`,`Install dependencies after scaffolding`).option(`--no-install`,`Skip dependency installation`).option(`-f, --force`,`Remove existing files without prompting`).option(`-t, --template <type>`,`Project template: rest | ddd | cqrs | minimal`).option(`-r, --repo <type>`,`Default repository: prisma | drizzle | inmemory | custom`).option(`--packages <packages>`,`Comma-separated packages to include (e.g. auth,swagger,ws,queue)`).option(`-y, --yes`,`Pick safe defaults for every prompt (template=minimal, repo=inmemory, no extras, git+install on)`).option(`--non-interactive`,`alias for --yes`).action(async(e,t)=>{yt(`KickJS — Create a new project`);let n=!!(t.yes||t.nonInteractive);e||=n?`my-api`:await xt({message:`Project name`,placeholder:`my-api`,defaultValue:`my-api`});let r;if(e===`.`?(r=C(`.`),e=re(r)):r=C(t.directory||e),h(r)){let i=ee(r);if(i.length>0){if(t.force)F.warn(`Clearing existing files in ${r}`);else if(n){F.warn(`Directory "${e}" is not empty. Pass --force to clear it.`),N(`Aborted.`);return}else{F.warn(`Directory "${e}" is not empty:`);let t=i.slice(0,5);for(let e of t)F.message(` - ${e}`);if(i.length>5&&F.message(` ... and ${i.length-5} more`),!await P({message:O.red(`Remove all existing files and proceed?`),initialValue:!1})){N(`Aborted.`);return}}for(let e of i)v(C(r,e),{recursive:!0,force:!0})}}let i=t.template;i||=n?`minimal`:await St({message:`Project template`,options:[{value:`rest`,label:`REST API`,hint:`Express + Swagger`},{value:`ddd`,label:`DDD`,hint:`Domain-Driven Design modules`},{value:`cqrs`,label:`CQRS`,hint:`Commands, Queries, Events + WS/Queue`},{value:`minimal`,label:`Minimal`,hint:`bare Express`}]});let a=t.pm;a||=n?await kt(void 0):await St({message:`Package manager`,options:[{value:`pnpm`,label:`pnpm`},{value:`npm`,label:`npm`},{value:`yarn`,label:`yarn`},{value:`bun`,label:`bun`}]});let o=t.repo;o||(n?o=`inmemory`:(o=await St({message:`Default repository/ORM`,options:[{value:`prisma`,label:`Prisma`},{value:`drizzle`,label:`Drizzle`},{value:`inmemory`,label:`In-Memory`},{value:`custom`,label:`Custom`,hint:`specify later`}]}),o===`custom`&&(o=await xt({message:`Custom repository name`,defaultValue:`custom`}))));let s;if(t.packages!==void 0){let e=t.packages.trim().toLowerCase();s=e===``||e===`none`||e===`false`?[]:t.packages.split(`,`).map(e=>e.trim()).filter(Boolean)}else s=n?[]:await Ct({message:`Select packages to include`,options:[...Nt],required:!1});let c;c=t.git===void 0?n?!0:await P({message:`Initialize git repository?`,initialValue:!0}):t.git;let l;l=t.install===void 0?n?!0:await P({message:`Install dependencies?`,initialValue:!0}):t.install,await ht({name:e,directory:r,packageManager:a,initGit:c,installDeps:l,template:i,defaultRepo:o,packages:s}),N(`Done! Next steps: ${O.cyan(`cd ${e} && ${a} dev`)}`)})}function L(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function R(e){let t=L(e);return t.charAt(0).toLowerCase()+t.slice(1)}function z(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function B(e){return ye.plural(e)}function Ft(e){return ye.plural(e)}function It(e){return z(e).replace(/-/g,`_`)}function Lt(e){let t=e.cwd??process.cwd(),n=e.pluralize??!0,r=L(e.name),i=R(e.name),a=z(e.name),o=It(e.name),s={name:e.name,pascal:r,camel:i,kebab:a,snake:o,modulesDir:e.modulesDir??`src/modules`,cwd:t,args:e.args??[],flags:e.flags??{}};if(n){let e=B(a);s.pluralKebab=e,s.pluralPascal=L(e),s.pluralCamel=R(e)}return s}function Rt(e,t){return C(e.cwd,t)}async function zt(e){return import(ce(e).href)}const Bt=new Map;async function Vt(e){let t=Bt.get(e);if(t)return t;let n=Ht(e);return Bt.set(e,n),n}async function Ht(e){let t=C(e,`package.json`);if(!h(t))return{generators:[],loaded:[],failed:[]};let n=Ut(JSON.parse(await T(t,`utf-8`))),r=p(C(e,`package.json`)),i=[],a=[],o=[];for(let e of n){let t;try{t=r.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await T(t,`utf-8`))}catch(t){o.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let s=n.kickjs.generators,c=C(b(t),s);if(!h(c)){o.push({source:e,reason:`kickjs.generators points to missing file: ${s}`});continue}let l;try{l=await zt(c)}catch(t){o.push({source:e,reason:`failed to import manifest: ${t}`});continue}let u=l.default;if(!Array.isArray(u)){o.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of u){if(!Wt(t)){o.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}i.push({source:e,spec:t})}a.push(e)}return{generators:i,loaded:a,failed:o}}function Ut(e){let t=new Set;for(let n of[e.dependencies,e.devDependencies,e.peerDependencies])if(n)for(let e of Object.keys(n))t.add(e);return Array.from(t)}function Wt(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function Gt(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return Jt(r.spec,r.source,e,n);let i=qt(await Vt(n),e.generatorName);return i?Jt(i.spec,i.source,e,n):null}async function Kt(e,t=[]){let n=await Vt(e),r=new Set(t.map(e=>e.spec.name)),i=n.generators.filter(e=>!r.has(e.spec.name));return{generators:[...t,...i],loaded:n.loaded,failed:n.failed}}function qt(e,t){return e.generators.find(e=>e.spec.name===t)}async function Jt(e,t,n,r){let i=Lt({name:n.itemName,args:n.args,flags:n.flags,modulesDir:n.modulesDir,pluralize:n.pluralize,cwd:r}),a=await e.files(i),o=[];for(let e of a){let t=Rt(i,e.path);await A(t,e.content),o.push(t)}return{files:o,source:t}}function V(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Yt={inmemory:`in-memory`,drizzle:`Drizzle`,prisma:`Prisma`};function Xt(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function Zt(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function Qt(e){return Yt[e]??Xt(e)}function $t(e,t,n){let r={inmemory:`InMemory${e}Repository`,drizzle:`Drizzle${e}Repository`,prisma:`Prisma${e}Repository`},i={inmemory:`in-memory-${t}`,drizzle:`drizzle-${t}`,prisma:`prisma-${t}`};return{repoClass:r[n]??`${Xt(n)}${e}Repository`,repoFile:i[n]??`${Zt(n)}-${t}`}}function en(e){return e??`define`}function tn(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=$t(t,n,i),c=en(a),l=`/**
1195
1205
  * ${t} Module
1196
1206
  *
1197
1207
  * Self-contained feature module following Domain-Driven Design (DDD).
@@ -1201,46 +1211,84 @@ description: Patterns to refuse outright when the user asks for them — they br
1201
1211
  * presentation/ — HTTP controllers (entry points)
1202
1212
  * application/ — Use cases (orchestration) and DTOs (validation)
1203
1213
  * domain/ — Entities, value objects, repository interfaces, domain services
1204
- * infrastructure/ — Repository implementations (currently ${Wt(i)})
1205
- */
1206
- import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1207
- import { buildRoutes } from '@forinda/kickjs'
1208
- import { ${t.toUpperCase()}_REPOSITORY } from './domain/repositories/${n}.repository'
1209
- import { ${a} } from './infrastructure/repositories/${o}.repository'
1214
+ * infrastructure/ — Repository implementations (currently ${Qt(i)})
1215
+ */`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './domain/repositories/${n}.repository'
1216
+ import { ${o} } from './infrastructure/repositories/${s}.repository'
1210
1217
  import { ${t}Controller } from './presentation/${n}.controller'
1211
1218
 
1212
1219
  // Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
1213
1220
  import.meta.glob(
1214
1221
  ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],
1215
1222
  { eager: true },
1216
- )
1223
+ )`,d=` /**
1224
+ * Declare HTTP routes for this module. Return value shape:
1225
+ *
1226
+ * - \`path\` — URL prefix for this route set, mounted under
1227
+ * \`/{apiPrefix}/v{version}{path}\`.
1228
+ * - \`controller\` — Controller class. Used both for the route
1229
+ * handler bindings and OpenAPI spec generation.
1230
+ * - \`version\` — Optional. Overrides the app-wide API version
1231
+ * for this route set only.
1232
+ *
1233
+ * Return an **array** to mount multiple route sets under the
1234
+ * same module (e.g. side-by-side v1 + v2 controllers):
1235
+ *
1236
+ * return [
1237
+ * { path: '/${r}', version: 1, controller: ${t}V1Controller },
1238
+ * { path: '/${r}', version: 2, controller: ${t}V2Controller },
1239
+ * ]
1240
+ */`;return c===`class`?`${l}
1241
+ import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1242
+ ${u}
1217
1243
 
1218
1244
  export class ${t}Module implements AppModule {
1219
1245
  /**
1220
1246
  * Register module dependencies in the DI container.
1221
1247
  * Bind repository interface tokens to their implementations here.
1222
- * Currently wired to ${Wt(i)}. To swap implementations, change the factory target.
1248
+ * Currently wired to ${Qt(i)}. To swap implementations, change the factory target.
1223
1249
  */
1224
1250
  register(container: Container): void {
1225
1251
  container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
1226
- container.resolve(${a}),
1252
+ container.resolve(${o}),
1227
1253
  )
1228
1254
  }
1229
1255
 
1230
- /**
1231
- * Declare HTTP routes for this module.
1232
- * The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${r}).
1233
- * Passing 'controller' enables automatic OpenAPI spec generation via SwaggerAdapter.
1234
- */
1256
+ ${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
1235
1257
  routes(): ModuleRoutes {
1236
1258
  return {
1237
1259
  path: '/${r}',
1238
- router: buildRoutes(${t}Controller),
1239
1260
  controller: ${t}Controller,
1240
1261
  }
1241
1262
  }
1242
1263
  }
1243
- `}function qt(e){let{pascal:t,kebab:n,plural:r=``,repo:i}=e,{repoClass:a,repoFile:o}=Gt(t,n,i);return`/**
1264
+ `:`${l}
1265
+ import { defineModule } from '@forinda/kickjs'
1266
+ ${u}
1267
+
1268
+ export const ${t}Module = defineModule({
1269
+ name: '${t}Module',
1270
+ build: () => ({
1271
+ /**
1272
+ * Register module dependencies in the DI container.
1273
+ * Bind repository interface tokens to their implementations here.
1274
+ * Currently wired to ${Qt(i)}. To swap implementations, change the factory target.
1275
+ */
1276
+ register(container) {
1277
+ container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
1278
+ container.resolve(${o}),
1279
+ )
1280
+ },
1281
+
1282
+ ${d}
1283
+ routes() {
1284
+ return {
1285
+ path: '/${r}',
1286
+ controller: ${t}Controller,
1287
+ }
1288
+ },
1289
+ }),
1290
+ })
1291
+ `}function nn(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=$t(t,n,i),c=en(a),l=`/**
1244
1292
  * ${t} Module
1245
1293
  *
1246
1294
  * REST module with a flat folder structure.
@@ -1250,47 +1298,109 @@ export class ${t}Module implements AppModule {
1250
1298
  * ${n}.controller.ts — HTTP routes (CRUD)
1251
1299
  * ${n}.service.ts — Business logic
1252
1300
  * ${n}.repository.ts — Repository interface
1253
- * ${o}.repository.ts — Repository implementation
1301
+ * ${s}.repository.ts — Repository implementation
1254
1302
  * dtos/ — Request/response schemas
1255
- */
1256
- import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1257
- import { buildRoutes } from '@forinda/kickjs'
1258
- import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
1259
- import { ${a} } from './${o}.repository'
1303
+ */`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
1304
+ import { ${o} } from './${s}.repository'
1260
1305
  import { ${t}Controller } from './${n}.controller'
1261
1306
 
1262
1307
  // Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
1263
- import.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })
1308
+ import.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })`,d=` /**
1309
+ * Declare HTTP routes for this module. Return value shape:
1310
+ *
1311
+ * - \`path\` — URL prefix for this route set.
1312
+ * - \`controller\` — Controller class (also drives OpenAPI).
1313
+ * - \`version\` — Optional. Overrides the app-wide API version.
1314
+ *
1315
+ * Return an **array** to mount multiple route sets — admin
1316
+ * surfaces, side-by-side v1 + v2 controllers, etc:
1317
+ *
1318
+ * return [
1319
+ * { path: '/${r}', version: 1, controller: ${t}V1Controller },
1320
+ * { path: '/${r}', version: 2, controller: ${t}V2Controller },
1321
+ * ]
1322
+ */`;return c===`class`?`${l}
1323
+ import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1324
+ ${u}
1264
1325
 
1265
1326
  export class ${t}Module implements AppModule {
1266
1327
  register(container: Container): void {
1267
1328
  container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
1268
- container.resolve(${a}),
1329
+ container.resolve(${o}),
1269
1330
  )
1270
1331
  }
1271
1332
 
1333
+ ${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
1272
1334
  routes(): ModuleRoutes {
1273
1335
  return {
1274
1336
  path: '/${r}',
1275
- router: buildRoutes(${t}Controller),
1276
1337
  controller: ${t}Controller,
1277
1338
  }
1278
1339
  }
1279
1340
  }
1280
- `}function Jt(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1281
- import { buildRoutes } from '@forinda/kickjs'
1341
+ `:`${l}
1342
+ import { defineModule } from '@forinda/kickjs'
1343
+ ${u}
1344
+
1345
+ export const ${t}Module = defineModule({
1346
+ name: '${t}Module',
1347
+ build: () => ({
1348
+ register(container) {
1349
+ container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
1350
+ container.resolve(${o}),
1351
+ )
1352
+ },
1353
+
1354
+ ${d}
1355
+ routes() {
1356
+ return {
1357
+ path: '/${r}',
1358
+ controller: ${t}Controller,
1359
+ }
1360
+ },
1361
+ }),
1362
+ })
1363
+ `}function rn(e){let{pascal:t,kebab:n,plural:r=``,style:i}=e,a=en(i),o=` /**
1364
+ * Declare HTTP routes. Return value shape:
1365
+ *
1366
+ * - \`path\` — URL prefix for this route set.
1367
+ * - \`controller\` — Controller class (also drives OpenAPI).
1368
+ * - \`version\` — Optional. Overrides the app-wide API version.
1369
+ *
1370
+ * Return an array to mount multiple route sets:
1371
+ *
1372
+ * return [
1373
+ * { path: '/${r}', version: 1, controller: ${t}V1Controller },
1374
+ * { path: '/${r}', version: 2, controller: ${t}V2Controller },
1375
+ * ]
1376
+ */`;return a===`class`?`import { type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1282
1377
  import { ${t}Controller } from './${n}.controller'
1283
1378
 
1284
1379
  export class ${t}Module implements AppModule {
1380
+ ${o.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
1285
1381
  routes(): ModuleRoutes {
1286
1382
  return {
1287
1383
  path: '/${r}',
1288
- router: buildRoutes(${t}Controller),
1289
1384
  controller: ${t}Controller,
1290
1385
  }
1291
1386
  }
1292
1387
  }
1293
- `}function Yt(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
1388
+ `:`import { defineModule } from '@forinda/kickjs'
1389
+ import { ${t}Controller } from './${n}.controller'
1390
+
1391
+ export const ${t}Module = defineModule({
1392
+ name: '${t}Module',
1393
+ build: () => ({
1394
+ ${o}
1395
+ routes() {
1396
+ return {
1397
+ path: '/${r}',
1398
+ controller: ${t}Controller,
1399
+ }
1400
+ },
1401
+ }),
1402
+ })
1403
+ `}function an(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
1294
1404
  import { ApiTags } from '@forinda/kickjs-swagger'
1295
1405
  import { Create${t}UseCase } from '../application/use-cases/create-${n}.use-case'
1296
1406
  import { Get${t}UseCase } from '../application/use-cases/get-${n}.use-case'
@@ -1353,7 +1463,7 @@ export class ${t}Controller {
1353
1463
  ctx.noContent()
1354
1464
  }
1355
1465
  }
1356
- `}function Xt(e){let{pascal:t,kebab:n}=e,r=t.charAt(0).toLowerCase()+t.slice(1);return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
1466
+ `}function on(e){let{pascal:t,kebab:n}=e,r=t.charAt(0).toLowerCase()+t.slice(1);return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
1357
1467
  import { ApiTags } from '@forinda/kickjs-swagger'
1358
1468
  import { ${t}Service } from './${n}.service'
1359
1469
  import { create${t}Schema } from './dtos/create-${n}.dto'
@@ -1408,14 +1518,14 @@ export class ${t}Controller {
1408
1518
  ctx.noContent()
1409
1519
  }
1410
1520
  }
1411
- `}function Zt(e){let{pascal:t}=e;return`import type { QueryParamsConfig } from '@forinda/kickjs'
1521
+ `}function sn(e){let{pascal:t}=e;return`import type { QueryParamsConfig } from '@forinda/kickjs'
1412
1522
 
1413
1523
  export const ${t.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {
1414
1524
  filterable: ['name'],
1415
1525
  sortable: ['name', 'createdAt'],
1416
1526
  searchable: ['name'],
1417
1527
  }
1418
- `}function Qt(e){let{pascal:t}=e;return`import { z } from 'zod'
1528
+ `}function cn(e){let{pascal:t}=e;return`import { z } from 'zod'
1419
1529
 
1420
1530
  /**
1421
1531
  * Create ${t} DTO — Zod schema for validating POST request bodies.
@@ -1431,20 +1541,20 @@ export const create${t}Schema = z.object({
1431
1541
  })
1432
1542
 
1433
1543
  export type Create${t}DTO = z.infer<typeof create${t}Schema>
1434
- `}function $t(e){let{pascal:t}=e;return`import { z } from 'zod'
1544
+ `}function ln(e){let{pascal:t}=e;return`import { z } from 'zod'
1435
1545
 
1436
1546
  export const update${t}Schema = z.object({
1437
1547
  name: z.string().min(1).max(200).optional(),
1438
1548
  })
1439
1549
 
1440
1550
  export type Update${t}DTO = z.infer<typeof update${t}Schema>
1441
- `}function en(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
1551
+ `}function un(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
1442
1552
  id: string
1443
1553
  name: string
1444
1554
  createdAt: string
1445
1555
  updatedAt: string
1446
1556
  }
1447
- `}function tn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`create-${n}.use-case.ts`,content:`/**
1557
+ `}function dn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`create-${n}.use-case.ts`,content:`/**
1448
1558
  * Create ${t} Use Case
1449
1559
  *
1450
1560
  * Application layer — orchestrates a single business operation.
@@ -1522,7 +1632,7 @@ export class Delete${t}UseCase {
1522
1632
  await this.repo.delete(id)
1523
1633
  }
1524
1634
  }
1525
- `}]}function nn(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
1635
+ `}]}function fn(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
1526
1636
  * ${t} Repository Interface
1527
1637
  *
1528
1638
  * Defines the contract for data access.
@@ -1617,7 +1727,7 @@ export class InMemory${t}Repository implements I${t}Repository {
1617
1727
  this.store.delete(id)
1618
1728
  }
1619
1729
  }
1620
- `}function rn(e){let{pascal:t,kebab:n,repoType:r=``,repoPrefix:i=`../../domain/repositories`,dtoPrefix:a=`../../application/dtos`}=e,o=r.charAt(0).toUpperCase()+r.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
1730
+ `}function pn(e){let{pascal:t,kebab:n,repoType:r=``,repoPrefix:i=`../../domain/repositories`,dtoPrefix:a=`../../application/dtos`}=e,o=r.charAt(0).toUpperCase()+r.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
1621
1731
  * ${o} ${t} Repository
1622
1732
  *
1623
1733
  * Stub implementation for a custom '${r}' repository.
@@ -1686,7 +1796,7 @@ export class ${o}${t}Repository implements I${t}Repository {
1686
1796
  this.store.delete(id)
1687
1797
  }
1688
1798
  }
1689
- `}function an(e){let{pascal:t,kebab:n}=e;return`/**
1799
+ `}function mn(e){let{pascal:t,kebab:n}=e;return`/**
1690
1800
  * ${t} Domain Service
1691
1801
  *
1692
1802
  * Domain layer — contains business rules that don't belong to a single entity.
@@ -1709,7 +1819,7 @@ export class ${t}DomainService {
1709
1819
  }
1710
1820
  }
1711
1821
  }
1712
- `}function on(e){let{pascal:t,kebab:n}=e;return`/**
1822
+ `}function hn(e){let{pascal:t,kebab:n}=e;return`/**
1713
1823
  * ${t} Entity
1714
1824
  *
1715
1825
  * Domain layer — the core business object.
@@ -1778,7 +1888,7 @@ export class ${t} {
1778
1888
  }
1779
1889
  }
1780
1890
  }
1781
- `}function sn(e){let{pascal:t}=e;return`/**
1891
+ `}function gn(e){let{pascal:t}=e;return`/**
1782
1892
  * ${t} ID Value Object
1783
1893
  *
1784
1894
  * Domain layer — wraps a primitive ID with type safety and validation.
@@ -1812,7 +1922,7 @@ export class ${t}Id {
1812
1922
  return this.value === other.value
1813
1923
  }
1814
1924
  }
1815
- `}function cn(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
1925
+ `}function _n(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
1816
1926
  import { Container } from '@forinda/kickjs'
1817
1927
 
1818
1928
  describe('${t}Controller', () => {
@@ -1864,7 +1974,7 @@ describe('${t}Controller', () => {
1864
1974
  })
1865
1975
  })
1866
1976
  })
1867
- `}function ln(e){let{pascal:t,kebab:n,plural:r=``,repoPrefix:i=`../infrastructure/repositories/in-memory-${n}.repository`}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
1977
+ `}function vn(e){let{pascal:t,kebab:n,plural:r=``,repoPrefix:i=`../infrastructure/repositories/in-memory-${n}.repository`}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
1868
1978
  import { InMemory${t}Repository } from '${i}'
1869
1979
 
1870
1980
  describe('InMemory${t}Repository', () => {
@@ -1926,7 +2036,7 @@ describe('InMemory${t}Repository', () => {
1926
2036
  expect(found).toBeNull()
1927
2037
  })
1928
2038
  })
1929
- `}function un(e){let{pascal:t,kebab:n}=e;return`import { Service, Inject, HttpException } from '@forinda/kickjs'
2039
+ `}function yn(e){let{pascal:t,kebab:n}=e;return`import { Service, Inject, HttpException } from '@forinda/kickjs'
1930
2040
  import type { ParsedQuery } from '@forinda/kickjs'
1931
2041
  import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from './${n}.repository'
1932
2042
  import type { ${t}ResponseDTO } from './dtos/${n}-response.dto'
@@ -1963,14 +2073,14 @@ export class ${t}Service {
1963
2073
  await this.repo.delete(id)
1964
2074
  }
1965
2075
  }
1966
- `}function dn(e){let{pascal:t}=e;return`import type { QueryFieldConfig } from '@forinda/kickjs'
2076
+ `}function bn(e){let{pascal:t}=e;return`import type { QueryFieldConfig } from '@forinda/kickjs'
1967
2077
 
1968
2078
  export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
1969
2079
  filterable: ['name'],
1970
2080
  sortable: ['name', 'createdAt'],
1971
2081
  searchable: ['name'],
1972
2082
  }
1973
- `}function fn(e){let{pascal:t,kebab:n,plural:r=``,repo:i}=e,a={inmemory:`InMemory${t}Repository`,drizzle:`Drizzle${t}Repository`,prisma:`Prisma${t}Repository`},o={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},s=a[i]??a.inmemory,c=o[i]??o.inmemory;return`/**
2083
+ `}function xn(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,o={inmemory:`InMemory${t}Repository`,drizzle:`Drizzle${t}Repository`,prisma:`Prisma${t}Repository`},s={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},c=o[i]??o.inmemory,l=s[i]??s.inmemory,u=a??`define`,d=`/**
1974
2084
  * ${t} Module — CQRS Pattern
1975
2085
  *
1976
2086
  * Separates read (queries) and write (commands) operations.
@@ -1982,11 +2092,8 @@ export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
1982
2092
  * queries/ — Read operations (get, list)
1983
2093
  * events/ — Domain events + handlers (WS broadcast, queue dispatch)
1984
2094
  * dtos/ — Request/response schemas
1985
- */
1986
- import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
1987
- import { buildRoutes } from '@forinda/kickjs'
1988
- import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
1989
- import { ${s} } from './${c}.repository'
2095
+ */`,f=`import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
2096
+ import { ${c} } from './${l}.repository'
1990
2097
  import { ${t}Controller } from './${n}.controller'
1991
2098
 
1992
2099
  // Eagerly load decorated classes
@@ -1998,24 +2105,61 @@ import.meta.glob(
1998
2105
  '!./**/*.test.ts',
1999
2106
  ],
2000
2107
  { eager: true },
2001
- )
2108
+ )`,p=` /**
2109
+ * Declare HTTP routes for this CQRS module. Return value shape:
2110
+ *
2111
+ * - \`path\` — URL prefix for this route set.
2112
+ * - \`controller\` — Controller class (also drives OpenAPI).
2113
+ * - \`version\` — Optional. Overrides the app-wide API version.
2114
+ *
2115
+ * Return an array to mount multiple route sets:
2116
+ *
2117
+ * return [
2118
+ * { path: '/${r}', version: 1, controller: ${t}V1Controller },
2119
+ * { path: '/${r}', version: 2, controller: ${t}V2Controller },
2120
+ * ]
2121
+ */`;return u===`class`?`${d}
2122
+ import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
2123
+ ${f}
2002
2124
 
2003
2125
  export class ${t}Module implements AppModule {
2004
2126
  register(container: Container): void {
2005
2127
  container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
2006
- container.resolve(${s}),
2128
+ container.resolve(${c}),
2007
2129
  )
2008
2130
  }
2009
2131
 
2132
+ ${p.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
2010
2133
  routes(): ModuleRoutes {
2011
2134
  return {
2012
2135
  path: '/${r}',
2013
- router: buildRoutes(${t}Controller),
2014
2136
  controller: ${t}Controller,
2015
2137
  }
2016
2138
  }
2017
2139
  }
2018
- `}function pn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
2140
+ `:`${d}
2141
+ import { defineModule } from '@forinda/kickjs'
2142
+ ${f}
2143
+
2144
+ export const ${t}Module = defineModule({
2145
+ name: '${t}Module',
2146
+ build: () => ({
2147
+ register(container) {
2148
+ container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
2149
+ container.resolve(${c}),
2150
+ )
2151
+ },
2152
+
2153
+ ${p}
2154
+ routes() {
2155
+ return {
2156
+ path: '/${r}',
2157
+ controller: ${t}Controller,
2158
+ }
2159
+ },
2160
+ }),
2161
+ })
2162
+ `}function Sn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
2019
2163
  import { ApiTags } from '@forinda/kickjs-swagger'
2020
2164
  import { Create${t}Command } from './commands/create-${n}.command'
2021
2165
  import { Update${t}Command } from './commands/update-${n}.command'
@@ -2078,7 +2222,7 @@ export class ${t}Controller {
2078
2222
  ctx.noContent()
2079
2223
  }
2080
2224
  }
2081
- `}function mn(e){let{pascal:t,kebab:n}=e;return[{file:`create-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
2225
+ `}function Cn(e){let{pascal:t,kebab:n}=e;return[{file:`create-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
2082
2226
  import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
2083
2227
  import type { Create${t}DTO } from '../dtos/create-${n}.dto'
2084
2228
  import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
@@ -2132,7 +2276,7 @@ export class Delete${t}Command {
2132
2276
  this.events.emit('${n}.deleted', { id })
2133
2277
  }
2134
2278
  }
2135
- `}]}function hn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`get-${n}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
2279
+ `}]}function wn(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`get-${n}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
2136
2280
  import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
2137
2281
  import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
2138
2282
 
@@ -2160,7 +2304,7 @@ export class List${i}Query {
2160
2304
  return this.repo.findPaginated(parsed)
2161
2305
  }
2162
2306
  }
2163
- `}]}function gn(e){let{pascal:t,kebab:n}=e;return[{file:`${n}.events.ts`,content:`import { Service } from '@forinda/kickjs'
2307
+ `}]}function Tn(e){let{pascal:t,kebab:n}=e;return[{file:`${n}.events.ts`,content:`import { Service } from '@forinda/kickjs'
2164
2308
  import { EventEmitter } from 'node:events'
2165
2309
  import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
2166
2310
 
@@ -2246,7 +2390,7 @@ export class On${t}ChangeHandler {
2246
2390
  })
2247
2391
  }
2248
2392
  }
2249
- `}]}function _n(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
2393
+ `}]}function En(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
2250
2394
  * Drizzle ${t} Repository
2251
2395
  *
2252
2396
  * Implements the repository interface using Drizzle ORM.
@@ -2328,7 +2472,7 @@ export class Drizzle${t}Repository implements I${t}Repository {
2328
2472
  throw new Error('Drizzle ${t} repository not yet implemented')
2329
2473
  }
2330
2474
  }
2331
- `}function vn(e){let{pascal:t,kebab:n}=e;return`import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
2475
+ `}function Dn(e){let{pascal:t,kebab:n}=e;return`import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
2332
2476
  // TODO: Import your schema table and reference actual columns for type safety
2333
2477
  // import { ${n}s } from '@/db/schema'
2334
2478
 
@@ -2346,7 +2490,7 @@ export const ${t.toUpperCase()}_QUERY_CONFIG: DrizzleQueryParamsConfig = {
2346
2490
  // ${n}s.name,
2347
2491
  ],
2348
2492
  }
2349
- `}function yn(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e,a=n.replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
2493
+ `}function On(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e,a=n.replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
2350
2494
  * Prisma ${t} Repository
2351
2495
  *
2352
2496
  * Implements the repository interface using Prisma Client.
@@ -2404,7 +2548,7 @@ export class Prisma${t}Repository implements I${t}Repository {
2404
2548
  await this.prisma.${a}.deleteMany({ where: { id } })
2405
2549
  }
2406
2550
  }
2407
- `}async function bn(e){let{pascal:t,kebab:n,plural:r,write:i}=e;await i(`${n}.module.ts`,Jt({pascal:t,kebab:n,plural:r})),await i(`${n}.controller.ts`,`import { Controller, Get, type Ctx } from '@forinda/kickjs'
2551
+ `}async function kn(e){let{pascal:t,kebab:n,plural:r,style:i,write:a}=e;await a(`${n}.module.ts`,rn({pascal:t,kebab:n,plural:r,style:i})),await a(`${n}.controller.ts`,`import { Controller, Get, type Ctx } from '@forinda/kickjs'
2408
2552
 
2409
2553
  // \`Ctx<KickRoutes.${t}Controller['<method>']>\` is generated by
2410
2554
  // \`kick typegen\` (auto-run on \`kick dev\`).
@@ -2416,14 +2560,19 @@ export class ${t}Controller {
2416
2560
  ctx.json({ message: '${t} list' })
2417
2561
  }
2418
2562
  }
2419
- `)}async function xn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,write:l}=e;await l(`${n}.module.ts`,qt({pascal:t,kebab:n,plural:r,repo:a})),await l(`${n}.constants.ts`,dn({pascal:t,kebab:n})),await l(`${n}.controller.ts`,Xt({pascal:t,kebab:n,plural:r,pluralPascal:i})),await l(`${n}.service.ts`,un({pascal:t,kebab:n})),await l(`dtos/create-${n}.dto.ts`,Qt({pascal:t,kebab:n})),await l(`dtos/update-${n}.dto.ts`,$t({pascal:t,kebab:n})),await l(`dtos/${n}-response.dto.ts`,en({pascal:t,kebab:n})),await l(`${n}.repository.ts`,nn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let u={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},d={inmemory:()=>H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>_n({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>yn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},f=u[a]??`${B(a)}-${n}`,p=d[a]??(()=>rn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await l(`${f}.repository.ts`,p()),o||(a!==`inmemory`&&await l(`in-memory-${n}.repository.ts`,H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await l(`__tests__/${n}.controller.test.ts`,cn({pascal:t,kebab:n,plural:r})),await l(`__tests__/${n}.repository.test.ts`,ln({pascal:t,kebab:n,plural:r,repoPrefix:`../${u.inmemory??`in-memory-${n}`}.repository`})))}async function Sn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,write:l}=e;await l(`${n}.module.ts`,fn({pascal:t,kebab:n,plural:r,repo:a})),await l(`${n}.constants.ts`,dn({pascal:t,kebab:n})),await l(`${n}.controller.ts`,pn({pascal:t,kebab:n,plural:r,pluralPascal:i})),await l(`dtos/create-${n}.dto.ts`,Qt({pascal:t,kebab:n})),await l(`dtos/update-${n}.dto.ts`,$t({pascal:t,kebab:n})),await l(`dtos/${n}-response.dto.ts`,en({pascal:t,kebab:n}));let u=mn({pascal:t,kebab:n});for(let e of u)await l(`commands/${e.file}`,e.content);let d=hn({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of d)await l(`queries/${e.file}`,e.content);let f=gn({pascal:t,kebab:n});for(let e of f)await l(`events/${e.file}`,e.content);await l(`${n}.repository.ts`,nn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let p={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},m={inmemory:()=>H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>_n({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>yn({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},h=p[a]??`${B(a)}-${n}`,g=m[a]??(()=>rn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await l(`${h}.repository.ts`,g()),o||(a!==`inmemory`&&await l(`in-memory-${n}.repository.ts`,H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await l(`__tests__/${n}.controller.test.ts`,cn({pascal:t,kebab:n,plural:r})),await l(`__tests__/${n}.repository.test.ts`,ln({pascal:t,kebab:n,plural:r,repoPrefix:`../${p.inmemory??`in-memory-${n}`}.repository`})))}async function Cn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noEntity:o,noTests:s,prismaClientPath:c,tokenScope:l,write:u}=e;await u(`${n}.module.ts`,Kt({pascal:t,kebab:n,plural:r,repo:a})),await u(`constants.ts`,a===`drizzle`?vn({pascal:t,kebab:n}):Zt({pascal:t,kebab:n})),await u(`presentation/${n}.controller.ts`,Yt({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`application/dtos/create-${n}.dto.ts`,Qt({pascal:t,kebab:n})),await u(`application/dtos/update-${n}.dto.ts`,$t({pascal:t,kebab:n})),await u(`application/dtos/${n}-response.dto.ts`,en({pascal:t,kebab:n}));let d=tn({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of d)await u(`application/use-cases/${e.file}`,e.content);await u(`domain/repositories/${n}.repository.ts`,nn({pascal:t,kebab:n,tokenScope:l})),await u(`domain/services/${n}-domain.service.ts`,an({pascal:t,kebab:n}));let f={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},p={inmemory:()=>H({pascal:t,kebab:n}),drizzle:()=>_n({pascal:t,kebab:n}),prisma:()=>yn({pascal:t,kebab:n,prismaClientPath:c})},m=f[a]??`${B(a)}-${n}`,h=p[a]??(()=>rn({pascal:t,kebab:n,repoType:a}));await u(`infrastructure/repositories/${m}.repository.ts`,h()),o||(await u(`domain/entities/${n}.entity.ts`,on({pascal:t,kebab:n})),await u(`domain/value-objects/${n}-id.vo.ts`,sn({pascal:t,kebab:n}))),s||(a!==`inmemory`&&await u(`infrastructure/repositories/in-memory-${n}.repository.ts`,H({pascal:t,kebab:n})),await u(`__tests__/${n}.controller.test.ts`,cn({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,ln({pascal:t,kebab:n,plural:r})))}function wn(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function Tn(e){let{name:t,modulesDir:n,noEntity:r,noTests:i,repo:a=`inmemory`,force:o,dryRun:s}=e,c=e.pluralize!==!1,l=e.pattern??`ddd`;e.minimal&&(l=`minimal`);let u=B(t),d=R(t),f=c?V(u):u,p=c?Dt(d):d,m=b(n,f),h=[],g=o??!1,_={kebab:u,pascal:d,plural:f,pluralPascal:p,moduleDir:m,repo:a,noEntity:r??!1,noTests:i??!1,prismaClientPath:e.prismaClientPath??`@prisma/client`,tokenScope:e.tokenScope??`app`,write:async(e,t)=>{let n=b(m,e);if(s){h.push(n);return}if(!g&&await N(n)&&!await F({message:`File exists: ${O.dim(e)}. Overwrite?`,initialValue:!1})){I.warn(`Skipped: ${e}`);return}await A(n,t),h.push(n)},files:h};switch(l){case`minimal`:await bn(_);break;case`rest`:await xn(_);break;case`cqrs`:await Sn(_);break;default:await Cn(_);break}return s||await En(n,d,f,u),h}async function En(e,t,n,r){let i=b(e,`index.ts`),a=await N(i),o=`./${n}/${r}.module`;if(!a){await A(i,`import type { AppModuleClass } from '@forinda/kickjs'
2420
- import { ${t}Module } from '${o}'
2563
+ `)}async function An(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,style:l,write:u}=e;await u(`${n}.module.ts`,nn({pascal:t,kebab:n,plural:r,repo:a,style:l})),await u(`${n}.constants.ts`,bn({pascal:t,kebab:n})),await u(`${n}.controller.ts`,on({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`${n}.service.ts`,yn({pascal:t,kebab:n})),await u(`dtos/create-${n}.dto.ts`,cn({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,ln({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,un({pascal:t,kebab:n})),await u(`${n}.repository.ts`,fn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let d={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},f={inmemory:()=>H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>En({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>On({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},p=d[a]??`${z(a)}-${n}`,m=f[a]??(()=>pn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await u(`${p}.repository.ts`,m()),o||(a!==`inmemory`&&await u(`in-memory-${n}.repository.ts`,H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await u(`__tests__/${n}.controller.test.ts`,_n({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,vn({pascal:t,kebab:n,plural:r,repoPrefix:`../${d.inmemory??`in-memory-${n}`}.repository`})))}async function jn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,style:l,write:u}=e;await u(`${n}.module.ts`,xn({pascal:t,kebab:n,plural:r,repo:a,style:l})),await u(`${n}.constants.ts`,bn({pascal:t,kebab:n})),await u(`${n}.controller.ts`,Sn({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`dtos/create-${n}.dto.ts`,cn({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,ln({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,un({pascal:t,kebab:n}));let d=Cn({pascal:t,kebab:n});for(let e of d)await u(`commands/${e.file}`,e.content);let f=wn({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of f)await u(`queries/${e.file}`,e.content);let p=Tn({pascal:t,kebab:n});for(let e of p)await u(`events/${e.file}`,e.content);await u(`${n}.repository.ts`,fn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let m={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},h={inmemory:()=>H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>En({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>On({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},g=m[a]??`${z(a)}-${n}`,_=h[a]??(()=>pn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await u(`${g}.repository.ts`,_()),o||(a!==`inmemory`&&await u(`in-memory-${n}.repository.ts`,H({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await u(`__tests__/${n}.controller.test.ts`,_n({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,vn({pascal:t,kebab:n,plural:r,repoPrefix:`../${m.inmemory??`in-memory-${n}`}.repository`})))}async function Mn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noEntity:o,noTests:s,prismaClientPath:c,tokenScope:l,style:u,write:d}=e;await d(`${n}.module.ts`,tn({pascal:t,kebab:n,plural:r,repo:a,style:u})),await d(`constants.ts`,a===`drizzle`?Dn({pascal:t,kebab:n}):sn({pascal:t,kebab:n})),await d(`presentation/${n}.controller.ts`,an({pascal:t,kebab:n,plural:r,pluralPascal:i})),await d(`application/dtos/create-${n}.dto.ts`,cn({pascal:t,kebab:n})),await d(`application/dtos/update-${n}.dto.ts`,ln({pascal:t,kebab:n})),await d(`application/dtos/${n}-response.dto.ts`,un({pascal:t,kebab:n}));let f=dn({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of f)await d(`application/use-cases/${e.file}`,e.content);await d(`domain/repositories/${n}.repository.ts`,fn({pascal:t,kebab:n,tokenScope:l})),await d(`domain/services/${n}-domain.service.ts`,mn({pascal:t,kebab:n}));let p={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},m={inmemory:()=>H({pascal:t,kebab:n}),drizzle:()=>En({pascal:t,kebab:n}),prisma:()=>On({pascal:t,kebab:n,prismaClientPath:c})},h=p[a]??`${z(a)}-${n}`,g=m[a]??(()=>pn({pascal:t,kebab:n,repoType:a}));await d(`infrastructure/repositories/${h}.repository.ts`,g()),o||(await d(`domain/entities/${n}.entity.ts`,hn({pascal:t,kebab:n})),await d(`domain/value-objects/${n}-id.vo.ts`,gn({pascal:t,kebab:n}))),s||(a!==`inmemory`&&await d(`infrastructure/repositories/in-memory-${n}.repository.ts`,H({pascal:t,kebab:n})),await d(`__tests__/${n}.controller.test.ts`,_n({pascal:t,kebab:n,plural:r})),await d(`__tests__/${n}.repository.test.ts`,vn({pascal:t,kebab:n,plural:r})))}function Nn(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function Pn(e){let{name:t,modulesDir:n,noEntity:r,noTests:i,repo:a=`inmemory`,force:o,dryRun:s}=e,c=e.pluralize!==!1,l=e.pattern??`ddd`;e.minimal&&(l=`minimal`);let u=z(t),d=L(t),f=c?B(u):u,p=c?Ft(d):d,m=x(n,f),h=[],g=o??!1,_={kebab:u,pascal:d,plural:f,pluralPascal:p,moduleDir:m,repo:a,noEntity:r??!1,noTests:i??!1,prismaClientPath:e.prismaClientPath??`@prisma/client`,tokenScope:e.tokenScope??`app`,style:e.style??`define`,write:async(e,t)=>{let n=x(m,e);if(s){h.push(n);return}if(!g&&await Be(n)&&!await P({message:`File exists: ${O.dim(e)}. Overwrite?`,initialValue:!1})){F.warn(`Skipped: ${e}`);return}await A(n,t),h.push(n)},files:h};switch(l){case`minimal`:await kn(_);break;case`rest`:await An(_);break;case`cqrs`:await jn(_);break;default:await Mn(_);break}return s||await Fn(n,d,f,u,_.style),h}async function Fn(e,t,n,r,i=`define`){let a=x(e,`index.ts`),o=await Be(a),s=`./${n}/${r}.module`,c=i===`class`?`${t}Module`:`${t}Module()`;if(!o){await A(a,i===`class`?`import type { AppModuleEntry } from '@forinda/kickjs'
2564
+ import { ${t}Module } from '${s}'
2565
+
2566
+ export const modules: AppModuleEntry[] = [${c}]
2567
+ `:`import { defineModules } from '@forinda/kickjs'
2568
+ import { ${t}Module } from '${s}'
2421
2569
 
2422
- export const modules: AppModuleClass[] = [${t}Module]
2423
- `);return}let s=await T(i,`utf-8`),c=`import { ${t}Module } from '${o}'`;if(!s.includes(`${t}Module`)){let e=s.lastIndexOf(`import `);if(e!==-1){let t=s.indexOf(`
2424
- `,e);s=s.slice(0,t+1)+c+`
2425
- `+s.slice(t+1)}else s=c+`
2426
- `+s;s=s.replace(/(=\s*\[)([\s\S]*?)(])/,(e,n,r,i)=>{let a=r.trim();if(!a)return`${n}${t}Module${i}`;let o=a.endsWith(`,`)?``:`,`;return`${n}${r.trimEnd()}${o} ${t}Module${i}`})}await E(i,s,`utf-8`)}async function Dn(e){let{name:t,outDir:n}=e,r=B(t),i=R(t),a=[],o=b(n,`${r}.adapter.ts`);return await A(o,`import {
2570
+ export const modules = defineModules().mount(${c})
2571
+ `);return}let l=await T(a,`utf-8`),u=`import { ${t}Module } from '${s}'`,d=V(s);if(!RegExp(`^import\\s*\\{[^}]*\\b${V(t)}Module\\b[^}]*\\}\\s*from\\s*['"]${d}['"]`,`m`).test(l)){let e=l.lastIndexOf(`import `);if(e!==-1){let t=l.indexOf(`
2572
+ `,e);l=l.slice(0,t+1)+u+`
2573
+ `+l.slice(t+1)}else l=u+`
2574
+ `+l}let f=Ln(l);if(f){let e=l.slice(f.rhsStart,f.rhsEnd+1);RegExp(`\\b${V(t)}Module\\b`).test(e)||(l=In(l,c))}else l=In(l,c);await E(a,l,`utf-8`)}function In(e,t){let n=Ln(e);if(!n)return e;if(n.shape===`array`){let r=e.slice(n.rhsStart+1,n.rhsEnd),i=r.trim(),a;if(!i)a=`[${t}]`;else{let e=i.endsWith(`,`)?``:`,`;a=`[${r.trimEnd()}${e} ${t}]`}return e.slice(0,n.rhsStart)+a+e.slice(n.rhsEnd+1)}return`${e.slice(0,n.chainEnd)}\n .mount(${t})${e.slice(n.chainEnd)}`}function Ln(e){let t=/export\s+const\s+modules\b[^=]*=/.exec(e);if(!t)return null;let n=t.index+t[0].length;for(;n<e.length&&/\s/.test(e[n]??``);)n++;if(e[n]===`[`){let t=Bn(e,n);return t===-1?null:{shape:`array`,rhsStart:n,rhsEnd:t}}if(e.slice(n,n+13)===`defineModules`){let t=Rn(e,n);return t===-1?null:{shape:`chain`,rhsStart:n,rhsEnd:t-1,chainEnd:t}}return null}function Rn(e,t=0){let n=/defineModules\s*\(/g;n.lastIndex=t;let r=n.exec(e);if(!r)return-1;let i=r.index+r[0].length-1;if(e[i]!==`(`||(i=Vn(e,i),i===-1))return-1;for(i++;;){let t=i;for(;t<e.length&&/\s/.test(e[t]??``);)t++;if(e[t]!==`.`||e.slice(t,t+6)!==`.mount`)break;for(t+=6;t<e.length&&/\s/.test(e[t]??``);)t++;if(e[t]!==`(`)break;let n=Vn(e,t);if(n===-1)break;i=n+1}return i}function zn(e,t){let n=e.slice(t,t+2);if(n===`//`){for(t+=2;t<e.length&&e[t]!==`
2575
+ `;)t++;return t}if(n===`/*`){for(t+=2;t+1<e.length&&!(e[t]===`*`&&e[t+1]===`/`);)t++;return t+2}return t}function Bn(e,t){if(e[t]!==`[`)return-1;let n=1,r=t+1;for(;r<e.length;){let t=e.slice(r,r+2);if(t===`//`||t===`/*`){r=zn(e,r);continue}let i=e[r]??``;if(i===`'`||i===`"`||i==="`"){let t=i;for(r++;r<e.length&&e[r]!==t;)e[r]===`\\`&&r++,r++;r<e.length&&r++;continue}if(i===`[`)n++;else if(i===`]`&&(n--,n===0))return r;r++}return-1}function Vn(e,t){if(e[t]!==`(`)return-1;let n=1,r=t+1;for(;r<e.length;){let t=e.slice(r,r+2);if(t===`//`||t===`/*`){r=zn(e,r);continue}let i=e[r]??``;if(i===`'`||i===`"`||i==="`"){let t=i;for(r++;r<e.length&&e[r]!==t;)e[r]===`\\`&&r++,r++;r<e.length&&r++;continue}if(i===`(`)n++;else if(i===`)`&&(n--,n===0))return r;r++}return-1}async function Hn(e){let{name:t,outDir:n}=e,r=z(t),i=L(t),a=[],o=x(n,`${r}.adapter.ts`);return await A(o,`import {
2427
2576
  defineAdapter,
2428
2577
  type AdapterContext,
2429
2578
  type AdapterMiddleware,
@@ -2592,10 +2741,10 @@ export const ${i}Adapter = defineAdapter<${i}AdapterConfig>({
2592
2741
  }
2593
2742
  },
2594
2743
  })
2595
- `),a.push(o),a}async function On(e){let{name:t,outDir:n}=e,r=B(t),i=R(t),a=[],o=b(n,`${r}.plugin.ts`);return await A(o,`import {
2744
+ `),a.push(o),a}async function Un(e){let{name:t,outDir:n}=e,r=z(t),i=L(t),a=[],o=x(n,`${r}.plugin.ts`);return await A(o,`import {
2596
2745
  definePlugin,
2597
2746
  type AppAdapter,
2598
- type AppModuleClass,
2747
+ type AppModuleEntry,
2599
2748
  type Container,
2600
2749
  type ContributorRegistrations,
2601
2750
  } from '@forinda/kickjs'
@@ -2661,13 +2810,17 @@ export const ${i}Plugin = definePlugin<${i}PluginConfig>({
2661
2810
  },
2662
2811
 
2663
2812
  /**
2664
- * Return module classes this plugin contributes to the app.
2665
- * These load before user modules, so plugin controllers and
2666
- * services are available for user code to \`@Autowired\`.
2813
+ * Return modules this plugin contributes to the app. These load
2814
+ * before user modules, so plugin controllers and services are
2815
+ * available for user code to \`@Autowired\`.
2816
+ *
2817
+ * Accepts both \`defineModule\`-style instances (call the factory:
2818
+ * \`ExampleModule()\`) and legacy \`class … implements AppModule\`
2819
+ * constructors.
2667
2820
  */
2668
- modules(): AppModuleClass[] {
2821
+ modules(): AppModuleEntry[] {
2669
2822
  return [
2670
- // ExampleModule,
2823
+ // ExampleModule(),
2671
2824
  ]
2672
2825
  },
2673
2826
 
@@ -2732,14 +2885,14 @@ export const ${i}Plugin = definePlugin<${i}PluginConfig>({
2732
2885
  },
2733
2886
  }),
2734
2887
  })
2735
- `),a.push(o),a}const kn={controller:`presentation`,service:`domain/services`,dto:`application/dtos`,guard:`presentation/guards`,middleware:`middleware`},An={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`},jn={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,command:`commands`,query:`queries`,event:`events`};function U(e){let{type:t,outDir:n,moduleName:r,modulesDir:i=`src/modules`,defaultDir:a,pattern:o=`ddd`,shouldPluralize:s=!0}=e;if(n)return S(n);if(r){let e=o===`ddd`?kn:o===`cqrs`?jn:An,n=B(r),a=s?V(n):n,c=e[t]??``,l=b(i,a);return S(c?b(l,c):l)}return S(a)}async function Mn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`middleware`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/middleware`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=z(t),c=[],l=b(a,`${o}.middleware.ts`);return await A(l,`import type { Request, Response, NextFunction } from 'express'
2888
+ `),a.push(o),a}const Wn={controller:`presentation`,service:`domain/services`,dto:`application/dtos`,guard:`presentation/guards`,middleware:`middleware`},Gn={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`},Kn={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,command:`commands`,query:`queries`,event:`events`};function U(e){let{type:t,outDir:n,moduleName:r,modulesDir:i=`src/modules`,defaultDir:a,pattern:o=`ddd`,shouldPluralize:s=!0}=e;if(n)return C(n);if(r){let e=o===`ddd`?Wn:o===`cqrs`?Kn:Gn,n=z(r),a=s?B(n):n,c=e[t]??``,l=x(i,a);return C(c?x(l,c):l)}return C(a)}async function qn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`middleware`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/middleware`,pattern:i,shouldPluralize:e.pluralize??!0}),o=z(t),s=R(t),c=[],l=x(a,`${o}.middleware.ts`);return await A(l,`import type { Request, Response, NextFunction } from 'express'
2736
2889
 
2737
- export interface ${R(t)}Options {
2890
+ export interface ${L(t)}Options {
2738
2891
  // Add configuration options here
2739
2892
  }
2740
2893
 
2741
2894
  /**
2742
- * ${R(t)} middleware.
2895
+ * ${L(t)} middleware.
2743
2896
  *
2744
2897
  * Usage in bootstrap:
2745
2898
  * middleware: [${s}()]
@@ -2750,13 +2903,13 @@ export interface ${R(t)}Options {
2750
2903
  * Usage with @Middleware decorator:
2751
2904
  * @Middleware(${s}())
2752
2905
  */
2753
- export function ${s}(options: ${R(t)}Options = {}) {
2906
+ export function ${s}(options: ${L(t)}Options = {}) {
2754
2907
  return (req: Request, res: Response, next: NextFunction) => {
2755
2908
  // Implement your middleware logic here
2756
2909
  next()
2757
2910
  }
2758
2911
  }
2759
- `),c.push(l),c}async function Nn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`guard`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/guards`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=z(t),c=R(t),l=[],u=b(a,`${o}.guard.ts`);return await A(u,`import { Container, HttpException } from '@forinda/kickjs'
2912
+ `),c.push(l),c}async function Jn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`guard`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/guards`,pattern:i,shouldPluralize:e.pluralize??!0}),o=z(t),s=R(t),c=L(t),l=[],u=x(a,`${o}.guard.ts`);return await A(u,`import { Container, HttpException } from '@forinda/kickjs'
2760
2913
  import type { RequestContext } from '@forinda/kickjs'
2761
2914
 
2762
2915
  /**
@@ -2792,7 +2945,7 @@ export async function ${s}Guard(ctx: RequestContext, next: () => void): Promise<
2792
2945
  ctx.res.status(401).json({ message: 'Invalid or expired token' })
2793
2946
  }
2794
2947
  }
2795
- `),l.push(u),l}async function Pn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`service`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/services`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=[],l=b(a,`${o}.service.ts`);return await A(l,`import { Service } from '@forinda/kickjs'
2948
+ `),l.push(u),l}async function Yn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`service`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/services`,pattern:i,shouldPluralize:e.pluralize??!0}),o=z(t),s=L(t),c=[],l=x(a,`${o}.service.ts`);return await A(l,`import { Service } from '@forinda/kickjs'
2796
2949
 
2797
2950
  @Service()
2798
2951
  export class ${s}Service {
@@ -2801,7 +2954,7 @@ export class ${s}Service {
2801
2954
  // @Inject(MY_REPO) private readonly repo: IMyRepository,
2802
2955
  // ) {}
2803
2956
  }
2804
- `),c.push(l),c}async function Fn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`controller`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/controllers`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=[],l=b(a,`${o}.controller.ts`);return await A(l,`import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
2957
+ `),c.push(l),c}async function Xn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`controller`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/controllers`,pattern:i,shouldPluralize:e.pluralize??!0}),o=z(t),s=L(t),c=[],l=x(a,`${o}.controller.ts`);return await A(l,`import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
2805
2958
 
2806
2959
  // \`Ctx<KickRoutes.${s}Controller['<method>']>\` is generated by
2807
2960
  // \`kick typegen\` (auto-run on \`kick dev\`). After the first run, your IDE
@@ -2822,7 +2975,7 @@ export class ${s}Controller {
2822
2975
  ctx.created({ message: '${s} created', data: ctx.body })
2823
2976
  }
2824
2977
  }
2825
- `),c.push(l),c}async function In(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`dto`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/dtos`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=z(t),l=[],u=b(a,`${o}.dto.ts`);return await A(u,`import { z } from 'zod'
2978
+ `),c.push(l),c}async function Zn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`dto`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/dtos`,pattern:i,shouldPluralize:e.pluralize??!0}),o=z(t),s=L(t),c=R(t),l=[],u=x(a,`${o}.dto.ts`);return await A(u,`import { z } from 'zod'
2826
2979
 
2827
2980
  export const ${c}Schema = z.object({
2828
2981
  // Define your schema fields here
@@ -2830,7 +2983,7 @@ export const ${c}Schema = z.object({
2830
2983
  })
2831
2984
 
2832
2985
  export type ${s}DTO = z.infer<typeof ${c}Schema>
2833
- `),l.push(u),l}async function Ln(e){let t=b(e.outDir,`kick.config.ts`),n=e.modulesDir??`src/modules`,r=e.defaultRepo??`inmemory`;return h(t)&&!e.force&&!await F({message:`kick.config.ts already exists. Overwrite?`,initialValue:!1})?(console.log(`
2986
+ `),l.push(u),l}async function Qn(e){let t=x(e.outDir,`kick.config.ts`),n=e.modulesDir??`src/modules`,r=e.defaultRepo??`inmemory`;return h(t)&&!e.force&&!await P({message:`kick.config.ts already exists. Overwrite?`,initialValue:!1})?(console.log(`
2834
2987
  Skipped — existing kick.config.ts preserved.`),[]):(await A(t,`import { defineConfig } from '@forinda/kickjs-cli'
2835
2988
 
2836
2989
  export default defineConfig({
@@ -2868,7 +3021,18 @@ export default defineConfig({
2868
3021
  },
2869
3022
  ],
2870
3023
  })
2871
- `),[t])}const Rn=new Set([`rest`,`ddd`,`cqrs`,`minimal`]);function zn(e,t){if(t)return t;try{let t=JSON.parse(_(b(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function Bn(e,t){if(t)return t;try{let t=JSON.parse(_(b(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function Vn(e,t){if(t)return t;try{let t=(await r(e))?.pattern;if(t&&Rn.has(t))return t}catch{}return`ddd`}async function Hn(e){let t=e.only??`all`,n=zn(e.outDir,e.name),r=Bn(e.outDir,e.pm),i=await Vn(e.outDir,e.template),a=t===`agents`||t===`both`||t===`all`,o=t===`claude`||t===`both`||t===`all`,s=t===`skills`||t===`all`,c=[];a&&c.push({file:b(e.outDir,`AGENTS.md`),render:()=>nt(n,i,r)}),o&&c.push({file:b(e.outDir,`CLAUDE.md`),render:()=>tt(n,i,r)}),s&&c.push({file:b(e.outDir,`kickjs-skills.md`),render:()=>rt(n,i,r)});let l=[];for(let{file:t,render:n}of c){if(h(t)&&!e.force&&!await F({message:`${t.replace(e.outDir+`/`,``)} already exists. Overwrite?`,initialValue:!1})){console.log(` Skipped — existing ${t.replace(e.outDir+`/`,``)} preserved.`);continue}await A(t,n()),l.push(t)}return l}async function Un(e={}){let t=e.strategy??`jwt`,n=e.outDir??`src/modules/auth`,r=b(n,`dto`),i=[],a=b(n,`auth.module.ts`);await A(a,`import { Module } from '@forinda/kickjs'
3024
+ `),[t])}const $n=new Set([`rest`,`ddd`,`cqrs`,`minimal`]);function er(e,t){if(t)return t;try{let t=JSON.parse(_(x(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function tr(e,t){if(t)return t;try{let t=JSON.parse(_(x(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function nr(e,t){if(t)return t;try{let t=(await r(e))?.pattern;if(t&&$n.has(t))return t}catch{}return`ddd`}async function rr(e){let t=e.only??`all`,n=er(e.outDir,e.name),r=tr(e.outDir,e.pm),i=await nr(e.outDir,e.template),a=t===`agents`||t===`both`||t===`all`,o=t===`claude`||t===`both`||t===`all`,s=t===`skills`||t===`all`,c=[];a&&c.push({file:x(e.outDir,`AGENTS.md`),render:()=>ct(n,i,r)}),o&&c.push({file:x(e.outDir,`CLAUDE.md`),render:()=>st(n,i,r)}),s&&c.push({file:x(e.outDir,`kickjs-skills.md`),render:()=>lt(n,i,r)});let l=[];for(let{file:t,render:n}of c){if(h(t)&&!e.force&&!await P({message:`${t.replace(e.outDir+`/`,``)} already exists. Overwrite?`,initialValue:!1})){console.log(` Skipped — existing ${t.replace(e.outDir+`/`,``)} preserved.`);continue}await A(t,n()),l.push(t)}return l}function ir(e,t){if(e[t]!==`{`)return-1;let n=1;for(let r=t+1;r<e.length;r++){let t=e[r];if(t===`{`)n++;else if(t===`}`&&(n--,n===0))return r}return-1}function W(e,t){let n=t.exec(e);if(!n)return null;let r=n.index+n[0].length-1,i=ir(e,r);return i===-1?null:e.slice(r+1,i)}function G(e,t,n){let r=` `.repeat(n);return e.split(`
3025
+ `).map(e=>{if(e.trim()===``)return e;let n=RegExp(`^ {0,${t}}`);return r+e.replace(n,``)}).join(`
3026
+ `)}function ar(e){return e.replaceAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*'@forinda\/kickjs'/g,(e,t)=>{let n=t.split(`,`).map(e=>e.trim()).filter(e=>e&&e!==`Container`&&e!==`type Container`&&e!==`type AppModule`&&e!==`AppModule`&&e!==`type ModuleRoutes`&&e!==`ModuleRoutes`);return n.includes(`defineModule`)||n.push(`defineModule`),`import { ${n.join(`, `)} } from '@forinda/kickjs'`})}function or(e,t){return e.replaceAll(/import\s*\{\s*([^}]+)\s*\}\s*from\s*'@forinda\/kickjs'/g,(e,n)=>{let r=n.split(`,`).map(e=>e.trim()).filter(e=>e&&e!==`defineModule`);return t.container&&!r.includes(`Container`)&&r.push(`Container`),t.appModule&&!r.some(e=>e===`AppModule`||e===`type AppModule`)&&r.push(`type AppModule`),t.moduleRoutes&&!r.some(e=>e===`ModuleRoutes`||e===`type ModuleRoutes`)&&r.push(`type ModuleRoutes`),t.contributorRegistrations&&!r.some(e=>e===`ContributorRegistrations`||e===`type ContributorRegistrations`)&&r.push(`type ContributorRegistrations`),`import { ${r.join(`, `)} } from '@forinda/kickjs'`})}function sr(e){if(/\bdefineModule\s*\(/.test(e))return{migrated:null,reason:`already in target form`};let t=[...e.matchAll(/export\s+class\s+(\w+Module)\s+implements\s+AppModule\s*\{/g)];if(t.length===0)return{migrated:null,reason:`no class form detected`};if(t.length>1)return{migrated:null,reason:`multiple module classes in one file — migrate manually`};let n=t[0],r=n[1],i=n.index+n[0].length-1,a=ir(e,i);if(a===-1)return{migrated:null,reason:`unbalanced class braces`};let o=e.slice(i+1,a),s=e.slice(0,n.index),c=e.slice(a+1),l=W(o,/register\s*\(([^)]*)\)\s*:\s*void\s*\{/),u=W(o,/contributors\s*\(\s*\)\s*:\s*ContributorRegistrations\s*\{/),d=W(o,/routes\s*\(\s*\)\s*:\s*[A-Za-z|[\]\s]+\{/);if(!d)return{migrated:null,reason:`routes() method missing or signature unrecognized`};let f=ar(s),p=``;return l&&(p+=` register(container) {${G(l,4,6)} },\n\n`),u&&(p+=` contributors() {${G(u,4,6)} },\n\n`),p+=` routes() {${G(d,4,6)} },`,{migrated:`${f}${`export const ${r} = defineModule({
3027
+ name: '${r}',
3028
+ build: () => ({
3029
+ ${p}
3030
+ }),
3031
+ })`}${c}`}}function cr(e){if(/export\s+class\s+\w+Module\s+implements\s+AppModule\s*\{/.test(e))return{migrated:null,reason:`already in target form`};let t=[...e.matchAll(/export\s+const\s+(\w+Module)\s*=\s*defineModule\s*\(\s*\{/g)];if(t.length===0)return{migrated:null,reason:`no defineModule form detected`};if(t.length>1)return{migrated:null,reason:`multiple defineModule blocks in one file — migrate manually`};let n=t[0],r=n[1],i=n.index+n[0].length-1,a=ir(e,i);if(a===-1)return{migrated:null,reason:`unbalanced defineModule braces`};let o=e.indexOf(`)`,a);if(o===-1)return{migrated:null,reason:`unbalanced defineModule call parens`};let s=e.slice(i+1,a),c=e.slice(0,n.index),l=o+1;for(;l<e.length&&(e[l]===`
3032
+ `||e[l]===`\r`);)l++;let u=e.slice(l),d=/build\s*:\s*\([^)]*\)\s*=>\s*\(\s*\{/g.exec(s);if(!d)return{migrated:null,reason:`build: () => ({...}) not found in defineModule`};let f=d.index+d[0].length-1,p=ir(s,f);if(p===-1)return{migrated:null,reason:`unbalanced build() braces`};let m=s.slice(f+1,p),h=W(m,/register\s*\(([^)]*)\)\s*\{/),g=W(m,/contributors\s*\(\s*\)\s*\{/),_=W(m,/routes\s*\(\s*\)\s*\{/);if(!_)return{migrated:null,reason:`routes() method missing inside build()`};let ee=or(c,{container:h!==null,appModule:!0,moduleRoutes:!0,contributorRegistrations:g!==null}),v=``;return h!==null&&(v+=` register(container: Container): void {${G(h,6,4)} }\n\n`),g!==null&&(v+=` contributors(): ContributorRegistrations {${G(g,6,4)} }\n\n`),v+=` routes(): ModuleRoutes {${G(_,6,4)} }`,{migrated:`${ee}${`export class ${r} implements AppModule {
3033
+ ${v}
3034
+ }
3035
+ `}${u}`}}function lr(e,t){return t===`class`?cr(e):sr(e)}function ur(e,t){let n=e,r=!1;if(t===`define`){/\bAppModuleClass\b/.test(n)&&(n=n.replaceAll(/\bAppModuleClass\b/g,`AppModuleEntry`),r=!0);let e=/(=\s*\[)([\s\S]*?)(])/,t=e.exec(n);if(t){let i=t[1],a=t[3],o=t[2],s=o.replaceAll(/(\b\w+Module)(?![(.])/g,`$1()`);s!==o&&(n=n.replace(e,`${i}${s}${a}`),r=!0)}}else{/\bAppModuleEntry\b/.test(n)&&(n=n.replaceAll(/\bAppModuleEntry\b/g,`AppModuleClass`),r=!0);let e=/(=\s*\[)([\s\S]*?)(])/,t=e.exec(n);if(t){let i=t[1],a=t[3],o=t[2],s=o.replaceAll(/(\b\w+Module)\s*\(\s*\)/g,`$1`);s!==o&&(n=n.replace(e,`${i}${s}${a}`),r=!0)}}return r?{migrated:n}:{migrated:null,reason:`no changes needed`}}async function dr(e){let t=[];return await n(C(e),0),t;async function n(e,r){let i;try{i=await ge(e)}catch{return}for(let a of i){if(a===`node_modules`||a===`dist`||a===`.kickjs`)continue;let i=x(e,a),o;try{o=await ve(i)}catch{continue}o.isDirectory()?await n(i,r+1):(a.endsWith(`.module.ts`)||a===`index.ts`&&r===1)&&t.push(i)}}}async function fr(e,t){let n=0;return await r(e,t),n;async function r(e,t){let i;try{i=await ge(e)}catch{return}await he(t,{recursive:!0});for(let a of i){if(a===`node_modules`||a===`dist`||a===`.kickjs`)continue;let i=x(e,a),o=x(t,a),s;try{s=await ve(i)}catch{continue}s.isDirectory()?await r(i,o):(await me(i,o),n++)}}}function pr(e){return x(e,`.kickjs`,`codemod-backups`,`${new Date().toISOString().replaceAll(/[:.]/g,`-`)}-modules`)}async function mr(e,t){let{dryRun:n=!1,cwd:r=process.cwd(),target:i}=t,a=t.backup??!n,o=await dr(e),s=await T(x(e,`index.ts`),`utf-8`).then(()=>!0,()=>!1),c=null;a&&(o.length>0||s)&&(c=pr(r),await fr(e,c));let l=[];for(let e of o){let t=lr(await T(e,`utf-8`),i);if(t.migrated==null){l.push({path:e,status:`skipped`,reason:t.reason});continue}n||await E(e,t.migrated,`utf-8`),l.push({path:e,status:`migrated`})}let u=x(e,`index.ts`),d=null;try{d=await T(u,`utf-8`)}catch{return{target:i,files:l,indexStatus:`not-found`,indexPath:u,backupDir:c}}let f=ur(d,i);return f.migrated==null?{target:i,files:l,indexStatus:`skipped`,indexPath:u,indexReason:f.reason,backupDir:c}:(n||await E(u,f.migrated,`utf-8`),{target:i,files:l,indexStatus:`migrated`,indexPath:u,backupDir:c})}async function hr(e,t){let n=await dr(e),r=[],i=t===`define`?/export\s+class\s+\w+Module\s+implements\s+AppModule\s*\{/:/export\s+const\s+\w+Module\s*=\s*defineModule\s*\(/;for(let e of n){let t=await T(e,`utf-8`);i.test(t)&&r.push(e)}return r}async function gr(e={}){let t=e.strategy??`jwt`,n=e.outDir??`src/modules/auth`,r=x(n,`dto`),i=[],a=x(n,`auth.module.ts`);await A(a,`import { Module } from '@forinda/kickjs'
2872
3036
  import { AuthController } from './auth.controller'
2873
3037
  import { AuthService } from './auth.service'
2874
3038
 
@@ -2877,7 +3041,7 @@ import { AuthService } from './auth.service'
2877
3041
  services: [AuthService],
2878
3042
  })
2879
3043
  export class AuthModule {}
2880
- `),i.push(a);let o=b(n,`auth.controller.ts`);await A(o,t===`jwt`?Wn():Kn()),i.push(o);let s=b(n,`auth.service.ts`);await A(s,t===`jwt`?Gn():qn()),i.push(s);let c=b(r,`register.dto.ts`);await A(c,`import { z } from 'zod'
3044
+ `),i.push(a);let o=x(n,`auth.controller.ts`);await A(o,t===`jwt`?_r():yr()),i.push(o);let s=x(n,`auth.service.ts`);await A(s,t===`jwt`?vr():br()),i.push(s);let c=x(r,`register.dto.ts`);await A(c,`import { z } from 'zod'
2881
3045
 
2882
3046
  export const RegisterDto = z.object({
2883
3047
  email: z.string().email(),
@@ -2886,7 +3050,7 @@ export const RegisterDto = z.object({
2886
3050
  })
2887
3051
 
2888
3052
  export type RegisterInput = z.infer<typeof RegisterDto>
2889
- `),i.push(c);let l=b(r,`login.dto.ts`);await A(l,`import { z } from 'zod'
3053
+ `),i.push(c);let l=x(r,`login.dto.ts`);await A(l,`import { z } from 'zod'
2890
3054
 
2891
3055
  export const LoginDto = z.object({
2892
3056
  email: z.string().email(),
@@ -2894,7 +3058,7 @@ export const LoginDto = z.object({
2894
3058
  })
2895
3059
 
2896
3060
  export type LoginInput = z.infer<typeof LoginDto>
2897
- `),i.push(l);let u=b(n,`auth.test.ts`);if(await A(u,`import { describe, it, expect } from 'vitest'
3061
+ `),i.push(l);let u=x(n,`auth.test.ts`);if(await A(u,`import { describe, it, expect } from 'vitest'
2898
3062
 
2899
3063
  describe('Auth Module', () => {
2900
3064
  it.todo('POST /register — creates a new user')
@@ -2903,7 +3067,7 @@ describe('Auth Module', () => {
2903
3067
  it.todo('POST /logout — invalidates session/token')
2904
3068
  it.todo('GET /me — returns authenticated user')
2905
3069
  })
2906
- `),i.push(u),e.roleGuards!==!1){let e=b(n,`auth.guard.ts`);await A(e,`import { Roles } from '@forinda/kickjs-auth'
3070
+ `),i.push(u),e.roleGuards!==!1){let e=x(n,`auth.guard.ts`);await A(e,`import { Roles } from '@forinda/kickjs-auth'
2907
3071
 
2908
3072
  /**
2909
3073
  * Role-based access guard.
@@ -2914,7 +3078,7 @@ describe('Auth Module', () => {
2914
3078
  */
2915
3079
  export const AdminOnly = Roles('admin')
2916
3080
  export const ManagerOnly = Roles('manager')
2917
- `),i.push(e)}return i}function Wn(){return`import { Controller, Post, Get } from '@forinda/kickjs'
3081
+ `),i.push(e)}return i}function _r(){return`import { Controller, Post, Get } from '@forinda/kickjs'
2918
3082
  import { Authenticated, Public } from '@forinda/kickjs-auth'
2919
3083
  import type { RequestContext } from '@forinda/kickjs'
2920
3084
  import { Autowired } from '@forinda/kickjs'
@@ -2950,7 +3114,7 @@ export class AuthController {
2950
3114
  return ctx.json({ user: ctx.user })
2951
3115
  }
2952
3116
  }
2953
- `}function Gn(){return`import { Service, Autowired } from '@forinda/kickjs'
3117
+ `}function vr(){return`import { Service, Autowired } from '@forinda/kickjs'
2954
3118
  import { PasswordService } from '@forinda/kickjs-auth'
2955
3119
  import type { RegisterInput } from './dto/register.dto'
2956
3120
  import type { LoginInput } from './dto/login.dto'
@@ -2989,7 +3153,7 @@ export class AuthService {
2989
3153
  return { user: { id: user.id, email: user.email, name: user.name } }
2990
3154
  }
2991
3155
  }
2992
- `}function Kn(){return`import { Controller, Post, Get } from '@forinda/kickjs'
3156
+ `}function yr(){return`import { Controller, Post, Get } from '@forinda/kickjs'
2993
3157
  import { Authenticated, Public } from '@forinda/kickjs-auth'
2994
3158
  import { sessionLogin, sessionLogout } from '@forinda/kickjs-auth'
2995
3159
  import type { RequestContext } from '@forinda/kickjs'
@@ -3028,7 +3192,7 @@ export class AuthController {
3028
3192
  return ctx.json({ user: ctx.user })
3029
3193
  }
3030
3194
  }
3031
- `}function qn(){return`import { Service, Autowired } from '@forinda/kickjs'
3195
+ `}function br(){return`import { Service, Autowired } from '@forinda/kickjs'
3032
3196
  import { PasswordService } from '@forinda/kickjs-auth'
3033
3197
  import type { RegisterInput } from './dto/register.dto'
3034
3198
  import type { LoginInput } from './dto/login.dto'
@@ -3065,7 +3229,7 @@ export class AuthService {
3065
3229
  return { id: user.id, email: user.email, name: user.name }
3066
3230
  }
3067
3231
  }
3068
- `}async function Jn(e){let{name:t,outDir:n}=e,r=R(t),i=B(t),a=z(t),o=e.queue??`${i}-queue`,s=[];return await(async(e,t)=>{let r=b(n,e);await A(r,t),s.push(r)})(`${i}.job.ts`,`import { Inject } from '@forinda/kickjs'
3232
+ `}async function xr(e){let{name:t,outDir:n}=e,r=L(t),i=z(t),a=R(t),o=e.queue??`${i}-queue`,s=[];return await(async(e,t)=>{let r=x(n,e);await A(r,t),s.push(r)})(`${i}.job.ts`,`import { Inject } from '@forinda/kickjs'
3069
3233
  import { Job, Process, QUEUE_MANAGER, type QueueService } from '@forinda/kickjs-queue'
3070
3234
 
3071
3235
  /**
@@ -3098,7 +3262,7 @@ export class ${r}Job {
3098
3262
  // Handle high-priority variant of this job
3099
3263
  }
3100
3264
  }
3101
- `),s}const Yn={string:{ts:`string`,zod:`z.string()`},text:{ts:`string`,zod:`z.string()`},number:{ts:`number`,zod:`z.number()`},int:{ts:`number`,zod:`z.number().int()`},float:{ts:`number`,zod:`z.number()`},boolean:{ts:`boolean`,zod:`z.boolean()`},date:{ts:`string`,zod:`z.string().datetime()`},email:{ts:`string`,zod:`z.string().email()`},url:{ts:`string`,zod:`z.string().url()`},uuid:{ts:`string`,zod:`z.string().uuid()`},json:{ts:`any`,zod:`z.any()`}};function Xn(e){return e.map(e=>{let t=e.indexOf(`:`);if(t===-1)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let n=e.slice(0,t),r=e.slice(t+1);if(!n||!r)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let i=!1;r.endsWith(`:optional`)&&(r=r.slice(0,-9),i=!0),n.endsWith(`?`)&&(n=n.slice(0,-1),i=!0),r.endsWith(`?`)&&(r=r.slice(0,-1),i=!0);let a=r;if(a.startsWith(`enum:`)){let e=a.slice(5).split(`,`);return{name:n,type:`enum`,tsType:e.map(e=>`'${e}'`).join(` | `),zodType:`z.enum([${e.map(e=>`'${e}'`).join(`, `)}])`,optional:i}}let o=Yn[a];if(!o){let e=[...Object.keys(Yn),`enum:a,b,c`].join(`, `);throw Error(`Unknown field type: "${a}". Valid types: ${e}`)}return{name:n,type:a,tsType:o.ts,zodType:o.zod,optional:i}})}async function Zn(e){let{name:t,fields:n,modulesDir:r,noEntity:i,noTests:a,repo:o=`inmemory`,tokenScope:s=`app`}=e,c=e.pluralize!==!1,l=B(t),u=R(t);z(t);let d=c?V(l):l,f=c?Dt(u):u,p=b(r,d),m=[],h=async(e,t)=>{let n=b(p,e);await A(n,t),m.push(n)};await h(`${l}.module.ts`,ar(u,l,d)),await h(`constants.ts`,tr(u,n)),await h(`presentation/${l}.controller.ts`,or(u,l,d,f)),await h(`application/dtos/create-${l}.dto.ts`,Qn(u,n)),await h(`application/dtos/update-${l}.dto.ts`,$n(u,n)),await h(`application/dtos/${l}-response.dto.ts`,er(u,n));let g=lr(u,l,d,f);for(let e of g)await h(`application/use-cases/${e.file}`,e.content);return await h(`domain/repositories/${l}.repository.ts`,sr(u,l,s)),await h(`domain/services/${l}-domain.service.ts`,cr(u,l)),o===`inmemory`&&await h(`infrastructure/repositories/in-memory-${l}.repository.ts`,nr(u,l,n)),i||(await h(`domain/entities/${l}.entity.ts`,rr(u,l,n)),await h(`domain/value-objects/${l}-id.vo.ts`,ir(u))),await ur(r,u,d,l),m}function Qn(e,t){return`import { z } from 'zod'
3265
+ `),s}const Sr={string:{ts:`string`,zod:`z.string()`},text:{ts:`string`,zod:`z.string()`},number:{ts:`number`,zod:`z.number()`},int:{ts:`number`,zod:`z.number().int()`},float:{ts:`number`,zod:`z.number()`},boolean:{ts:`boolean`,zod:`z.boolean()`},date:{ts:`string`,zod:`z.string().datetime()`},email:{ts:`string`,zod:`z.string().email()`},url:{ts:`string`,zod:`z.string().url()`},uuid:{ts:`string`,zod:`z.string().uuid()`},json:{ts:`any`,zod:`z.any()`}};function Cr(e){return e.map(e=>{let t=e.indexOf(`:`);if(t===-1)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let n=e.slice(0,t),r=e.slice(t+1);if(!n||!r)throw Error(`Invalid field: "${e}". Use format: name:type (e.g. title:string)`);let i=!1;r.endsWith(`:optional`)&&(r=r.slice(0,-9),i=!0),n.endsWith(`?`)&&(n=n.slice(0,-1),i=!0),r.endsWith(`?`)&&(r=r.slice(0,-1),i=!0);let a=r;if(a.startsWith(`enum:`)){let e=a.slice(5).split(`,`);return{name:n,type:`enum`,tsType:e.map(e=>`'${e}'`).join(` | `),zodType:`z.enum([${e.map(e=>`'${e}'`).join(`, `)}])`,optional:i}}let o=Sr[a];if(!o){let e=[...Object.keys(Sr),`enum:a,b,c`].join(`, `);throw Error(`Unknown field type: "${a}". Valid types: ${e}`)}return{name:n,type:a,tsType:o.ts,zodType:o.zod,optional:i}})}async function wr(e){let{name:t,fields:n,modulesDir:r,noEntity:i,noTests:a,repo:o=`inmemory`,tokenScope:s=`app`,style:c=`define`}=e,l=e.pluralize!==!1,u=z(t),d=L(t);R(t);let f=l?B(u):u,p=l?Ft(d):d,m=x(r,f),h=[],g=async(e,t)=>{let n=x(m,e);await A(n,t),h.push(n)};await g(`${u}.module.ts`,Mr(d,u,f,c)),await g(`constants.ts`,Or(d,n)),await g(`presentation/${u}.controller.ts`,Nr(d,u,f,p)),await g(`application/dtos/create-${u}.dto.ts`,Tr(d,n)),await g(`application/dtos/update-${u}.dto.ts`,Er(d,n)),await g(`application/dtos/${u}-response.dto.ts`,Dr(d,n));let _=Ir(d,u,f,p);for(let e of _)await g(`application/use-cases/${e.file}`,e.content);return await g(`domain/repositories/${u}.repository.ts`,Pr(d,u,s)),await g(`domain/services/${u}-domain.service.ts`,Fr(d,u)),o===`inmemory`&&await g(`infrastructure/repositories/in-memory-${u}.repository.ts`,kr(d,u,n)),i||(await g(`domain/entities/${u}.entity.ts`,Ar(d,u,n)),await g(`domain/value-objects/${u}-id.vo.ts`,jr(d))),await Fn(r,d,f,u,c),h}function Tr(e,t){return`import { z } from 'zod'
3102
3266
 
3103
3267
  export const create${e}Schema = z.object({
3104
3268
  ${t.map(e=>{let t=e.zodType;return` ${e.name}: ${t}${e.optional?`.optional()`:``},`}).join(`
@@ -3106,7 +3270,7 @@ ${t.map(e=>{let t=e.zodType;return` ${e.name}: ${t}${e.optional?`.optional()`:`
3106
3270
  })
3107
3271
 
3108
3272
  export type Create${e}DTO = z.infer<typeof create${e}Schema>
3109
- `}function $n(e,t){return`import { z } from 'zod'
3273
+ `}function Er(e,t){return`import { z } from 'zod'
3110
3274
 
3111
3275
  export const update${e}Schema = z.object({
3112
3276
  ${t.map(e=>` ${e.name}: ${e.zodType}.optional(),`).join(`
@@ -3114,21 +3278,21 @@ ${t.map(e=>` ${e.name}: ${e.zodType}.optional(),`).join(`
3114
3278
  })
3115
3279
 
3116
3280
  export type Update${e}DTO = z.infer<typeof update${e}Schema>
3117
- `}function er(e,t){return`export interface ${e}ResponseDTO {
3281
+ `}function Dr(e,t){return`export interface ${e}ResponseDTO {
3118
3282
  id: string
3119
3283
  ${t.map(e=>` ${e.name}${e.optional?`?`:``}: ${e.tsType}`).join(`
3120
3284
  `)}
3121
3285
  createdAt: string
3122
3286
  updatedAt: string
3123
3287
  }
3124
- `}function tr(e,t){let n=t.filter(e=>e.tsType===`string`).map(e=>`'${e.name}'`);t.filter(e=>e.tsType===`number`).map(e=>`'${e.name}'`);let r=t.map(e=>`'${e.name}'`),i=[...r].join(`, `),a=[...r,`'createdAt'`,`'updatedAt'`].join(`, `),o=n.length>0?n.join(`, `):`'name'`;return`import type { ApiQueryParamsConfig } from '@forinda/kickjs'
3288
+ `}function Or(e,t){let n=t.filter(e=>e.tsType===`string`).map(e=>`'${e.name}'`);t.filter(e=>e.tsType===`number`).map(e=>`'${e.name}'`);let r=t.map(e=>`'${e.name}'`),i=[...r].join(`, `),a=[...r,`'createdAt'`,`'updatedAt'`].join(`, `),o=n.length>0?n.join(`, `):`'name'`;return`import type { ApiQueryParamsConfig } from '@forinda/kickjs'
3125
3289
 
3126
3290
  export const ${e.toUpperCase()}_QUERY_CONFIG: ApiQueryParamsConfig = {
3127
3291
  filterable: [${i}],
3128
3292
  sortable: [${a}],
3129
3293
  searchable: [${o}],
3130
3294
  }
3131
- `}function nr(e,t,n){return`import { randomUUID } from 'node:crypto'
3295
+ `}function kr(e,t,n){return`import { randomUUID } from 'node:crypto'
3132
3296
  import { Repository, HttpException } from '@forinda/kickjs'
3133
3297
  import type { ParsedQuery } from '@forinda/kickjs'
3134
3298
  import type { I${e}Repository } from '../../domain/repositories/${t}.repository'
@@ -3180,7 +3344,7 @@ ${n.map(e=>` ${e.name}: dto.${e.name},`).join(`
3180
3344
  this.store.delete(id)
3181
3345
  }
3182
3346
  }
3183
- `}function rr(e,t,n){return`import { ${e}Id } from '../value-objects/${t}-id.vo'
3347
+ `}function Ar(e,t,n){return`import { ${e}Id } from '../value-objects/${t}-id.vo'
3184
3348
 
3185
3349
  interface ${e}Props {
3186
3350
  id: ${e}Id
@@ -3226,7 +3390,7 @@ ${n.map(e=>` ${e.name}: this.props.${e.name},`).join(`
3226
3390
  }
3227
3391
  }
3228
3392
  }
3229
- `}function ir(e){return`import { randomUUID } from 'node:crypto'
3393
+ `}function jr(e){return`import { randomUUID } from 'node:crypto'
3230
3394
 
3231
3395
  export class ${e}Id {
3232
3396
  private constructor(private readonly value: string) {}
@@ -3241,8 +3405,7 @@ export class ${e}Id {
3241
3405
  toString(): string { return this.value }
3242
3406
  equals(other: ${e}Id): boolean { return this.value === other.value }
3243
3407
  }
3244
- `}function ar(e,t,n){return`import { type AppModule, type ModuleRoutes, Container, buildRoutes } from '@forinda/kickjs'
3245
- import { ${e}Controller } from './presentation/${t}.controller'
3408
+ `}function Mr(e,t,n,r=`define`){let i=`import { ${e}Controller } from './presentation/${t}.controller'
3246
3409
  import { ${e.toUpperCase()}_REPOSITORY } from './domain/repositories/${t}.repository'
3247
3410
  import { InMemory${e}Repository } from './infrastructure/repositories/in-memory-${t}.repository'
3248
3411
 
@@ -3251,7 +3414,21 @@ import { InMemory${e}Repository } from './infrastructure/repositories/in-memory-
3251
3414
  import.meta.glob(
3252
3415
  ['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],
3253
3416
  { eager: true },
3254
- )
3417
+ )`,a=` /**
3418
+ * Declare HTTP routes for this module. Return value shape:
3419
+ *
3420
+ * - \`path\` — URL prefix for this route set.
3421
+ * - \`controller\` — Controller class (also drives OpenAPI).
3422
+ * - \`version\` — Optional. Overrides the app-wide API version.
3423
+ *
3424
+ * Return an array to mount multiple route sets:
3425
+ *
3426
+ * return [
3427
+ * { path: '/${n}', version: 1, controller: ${e}V1Controller },
3428
+ * { path: '/${n}', version: 2, controller: ${e}V2Controller },
3429
+ * ]
3430
+ */`;return r===`class`?`import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
3431
+ ${i}
3255
3432
 
3256
3433
  export class ${e}Module implements AppModule {
3257
3434
  /**
@@ -3266,15 +3443,42 @@ export class ${e}Module implements AppModule {
3266
3443
  )
3267
3444
  }
3268
3445
 
3446
+ ${a.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
3269
3447
  routes(): ModuleRoutes {
3270
3448
  return {
3271
3449
  path: '/${n}',
3272
- router: buildRoutes(${e}Controller),
3273
3450
  controller: ${e}Controller,
3274
3451
  }
3275
3452
  }
3276
3453
  }
3277
- `}function or(e,t,n,r){return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
3454
+ `:`import { defineModule } from '@forinda/kickjs'
3455
+ ${i}
3456
+
3457
+ export const ${e}Module = defineModule({
3458
+ name: '${e}Module',
3459
+ build: () => ({
3460
+ /**
3461
+ * Bind the repository token to its concrete implementation.
3462
+ * Decorator-managed classes (@Service, @Controller, @Repository) are
3463
+ * registered automatically — only token-to-impl bindings need to live here.
3464
+ */
3465
+ register(container) {
3466
+ container.registerFactory(
3467
+ ${e.toUpperCase()}_REPOSITORY,
3468
+ () => container.resolve(InMemory${e}Repository),
3469
+ )
3470
+ },
3471
+
3472
+ ${a}
3473
+ routes() {
3474
+ return {
3475
+ path: '/${n}',
3476
+ controller: ${e}Controller,
3477
+ }
3478
+ },
3479
+ }),
3480
+ })
3481
+ `}function Nr(e,t,n,r){return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
3278
3482
  import { ApiTags } from '@forinda/kickjs-swagger'
3279
3483
  import { Create${e}UseCase } from '../application/use-cases/create-${t}.use-case'
3280
3484
  import { Get${e}UseCase } from '../application/use-cases/get-${t}.use-case'
@@ -3337,7 +3541,7 @@ export class ${e}Controller {
3337
3541
  ctx.noContent()
3338
3542
  }
3339
3543
  }
3340
- `}function sr(e,t,n){return`import { createToken } from '@forinda/kickjs'
3544
+ `}function Pr(e,t,n){return`import { createToken } from '@forinda/kickjs'
3341
3545
  import type { ${e}ResponseDTO } from '../../application/dtos/${t}-response.dto'
3342
3546
  import type { Create${e}DTO } from '../../application/dtos/create-${t}.dto'
3343
3547
  import type { Update${e}DTO } from '../../application/dtos/update-${t}.dto'
@@ -3363,7 +3567,7 @@ export interface I${e}Repository {
3363
3567
  * adopters must NOT use the reserved \`'kick/'\` namespace.
3364
3568
  */
3365
3569
  export const ${e.toUpperCase()}_REPOSITORY = createToken<I${e}Repository>('${n}/${e}/repository')
3366
- `}function cr(e,t){return`import { Service, Inject, HttpException } from '@forinda/kickjs'
3570
+ `}function Fr(e,t){return`import { Service, Inject, HttpException } from '@forinda/kickjs'
3367
3571
  import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../repositories/${t}.repository'
3368
3572
 
3369
3573
  @Service()
@@ -3377,7 +3581,7 @@ export class ${e}DomainService {
3377
3581
  if (!entity) throw HttpException.notFound('${e} not found')
3378
3582
  }
3379
3583
  }
3380
- `}function lr(e,t,n,r){return[{file:`create-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
3584
+ `}function Ir(e,t,n,r){return[{file:`create-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
3381
3585
  import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
3382
3586
  import type { Create${e}DTO } from '../dtos/create-${t}.dto'
3383
3587
 
@@ -3420,10 +3624,7 @@ export class Delete${e}UseCase {
3420
3624
  constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
3421
3625
  async execute(id: string) { return this.repo.delete(id) }
3422
3626
  }
3423
- `}]}async function ur(e,t,n,r){let i=b(e,`index.ts`),a=await N(i),o=`./${n}/${r}.module`;if(!a){await A(i,`import type { AppModuleClass } from '@forinda/kickjs'\nimport { ${t}Module } from '${o}'\n\nexport const modules: AppModuleClass[] = [${t}Module]\n`);return}let s=await T(i,`utf-8`),c=`import { ${t}Module } from '${o}'`;if(!s.includes(`${t}Module`)){let e=s.lastIndexOf(`import `);if(e!==-1){let t=s.indexOf(`
3424
- `,e);s=s.slice(0,t+1)+c+`
3425
- `+s.slice(t+1)}else s=c+`
3426
- `+s;s=s.replace(/(=\s*\[)([\s\S]*?)(])/,(e,n,r,i)=>{let a=r.trim();if(!a)return`${n}${t}Module${i}`;let o=a.endsWith(`,`)?``:`,`;return`${n}${r.trimEnd()}${o} ${t}Module${i}`})}await E(i,s,`utf-8`)}async function dr(e){let{name:t,moduleName:n,modulesDir:r}=e,i=e.pluralize??!0,a=B(t),o=R(t),s=[],c;if(e.outDir)c=S(e.outDir);else if(n){let e=B(n),t=i?V(e):e;c=S(b(r??`src/modules`,t,`__tests__`))}else c=S(`src/__tests__`);let l=b(c,`${a}.test.ts`);return await A(l,`import { describe, it, expect, beforeEach } from 'vitest'
3627
+ `}]}async function Lr(e){let{name:t,moduleName:n,modulesDir:r}=e,i=e.pluralize??!0,a=z(t),o=L(t),s=[],c;if(e.outDir)c=C(e.outDir);else if(n){let e=z(n),t=i?B(e):e;c=C(x(r??`src/modules`,t,`__tests__`))}else c=C(`src/__tests__`);let l=x(c,`${a}.test.ts`);return await A(l,`import { describe, it, expect, beforeEach } from 'vitest'
3427
3628
  import { Container } from '@forinda/kickjs'
3428
3629
 
3429
3630
  describe('${o}', () => {
@@ -3446,59 +3647,59 @@ describe('${o}', () => {
3446
3647
  expect(true).toBe(true)
3447
3648
  })
3448
3649
  })
3449
- `),s.push(l),s}const fr=[`agents`,`claude`,`skills`,`both`,`all`];function W(e){return e.parent?.opts()?.dryRun??!1}function G(e,t=!1){let n=process.cwd();console.log(`\n ${t?`Would generate`:`Generated`} ${e.length} file${e.length===1?``:`s`}:`);for(let t of e)console.log(` ${t.replace(n+`/`,``)}`);t&&console.log(`
3450
- (dry run — no files were written)`),console.log()}async function pr(e){if(!e)try{let e=await r(process.cwd());await f({cwd:process.cwd(),allowDuplicates:!0,silent:!0,schemaValidator:e?.typegen?.schemaValidator??`zod`,envFile:e?.typegen?.envFile,srcDir:e?.typegen?.srcDir,outDir:e?.typegen?.outDir})}catch{}}const mr=[{name:`module <name>`,description:`Full DDD module (controller, DTOs, use-cases, repo)`},{name:`scaffold <name> <fields...>`,description:`CRUD module from field definitions`},{name:`controller <name>`,description:`@Controller() class [-m module]`},{name:`service <name>`,description:`@Service() singleton [-m module]`},{name:`middleware <name>`,description:`Express middleware function [-m module]`},{name:`guard <name>`,description:`Route guard (auth, roles, etc.) [-m module]`},{name:`dto <name>`,description:`Zod DTO schema [-m module]`},{name:`adapter <name>`,description:`AppAdapter with lifecycle hooks (app-level only)`},{name:`test <name>`,description:`Vitest test scaffold [-m module]`},{name:`job <name>`,description:`Queue @Job processor`},{name:`config`,description:`Generate kick.config.ts`},{name:`agents`,description:`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md from upstream templates`}];async function hr(){console.log(`
3650
+ `),s.push(l),s}const Rr=[`agents`,`claude`,`skills`,`both`,`all`];function K(e){return e.parent?.opts()?.dryRun??!1}function q(e,t=!1){let n=process.cwd();console.log(`\n ${t?`Would generate`:`Generated`} ${e.length} file${e.length===1?``:`s`}:`);for(let t of e)console.log(` ${t.replace(n+`/`,``)}`);t&&console.log(`
3651
+ (dry run — no files were written)`),console.log()}async function zr(e){if(!e)try{let e=await r(process.cwd());await f({cwd:process.cwd(),allowDuplicates:!0,silent:!0,schemaValidator:e?.typegen?.schemaValidator??`zod`,envFile:e?.typegen?.envFile,srcDir:e?.typegen?.srcDir,outDir:e?.typegen?.outDir})}catch{}}const Br=[{name:`module <name>`,description:`Full DDD module (controller, DTOs, use-cases, repo)`},{name:`scaffold <name> <fields...>`,description:`CRUD module from field definitions`},{name:`controller <name>`,description:`@Controller() class [-m module]`},{name:`service <name>`,description:`@Service() singleton [-m module]`},{name:`middleware <name>`,description:`Express middleware function [-m module]`},{name:`guard <name>`,description:`Route guard (auth, roles, etc.) [-m module]`},{name:`dto <name>`,description:`Zod DTO schema [-m module]`},{name:`adapter <name>`,description:`AppAdapter with lifecycle hooks (app-level only)`},{name:`test <name>`,description:`Vitest test scaffold [-m module]`},{name:`job <name>`,description:`Queue @Job processor`},{name:`config`,description:`Generate kick.config.ts`},{name:`agents`,description:`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md from upstream templates`}];async function Vr(){console.log(`
3451
3652
  Built-in generators:
3452
- `);let e=Math.max(...mr.map(e=>e.name.length));for(let t of mr)console.log(` kick g ${t.name.padEnd(e+2)} ${t.description}`);let t=await r(process.cwd()),n=a(t?.plugins??[],t?.commands??[]),i=await Rt(process.cwd(),n.generators);if(i.generators.length>0){console.log(`
3653
+ `);let e=Math.max(...Br.map(e=>e.name.length));for(let t of Br)console.log(` kick g ${t.name.padEnd(e+2)} ${t.description}`);let t=await r(process.cwd()),n=a(t?.plugins??[],t?.commands??[]),i=await Kt(process.cwd(),n.generators);if(i.generators.length>0){console.log(`
3453
3654
  Plugin generators:
3454
3655
  `);let e=Math.max(...i.generators.map(e=>`${e.spec.name} <name>`.length));for(let{source:t,spec:n}of i.generators){let r=`${n.name} <name>`;console.log(` kick g ${r.padEnd(e+2)} ${n.description} [${t}]`)}}if(i.failed.length>0){console.log(`
3455
3656
  Failed to load:
3456
- `);for(let{source:e,reason:t}of i.failed)console.log(` ${e} — ${t}`)}console.log()}async function gr(e,i,a){let o=await r(process.cwd()),s=n(o),c=i.modulesDir??s.dir??`src/modules`,l=i.repo??wn(s.repo),u=i.pattern??o?.pattern??`ddd`,d=i.pluralize===!1?!1:s.pluralize??!0,f=t(o,process.cwd()),p=[];for(let t of e){let e=await Tn({name:t,modulesDir:S(c),noEntity:i.entity===!1,noTests:i.tests===!1,repo:l,minimal:i.minimal,force:i.force,pattern:u,dryRun:a,pluralize:d,prismaClientPath:s.prismaClientPath,tokenScope:f});p.push(...e)}G(p,a),await pr(a)}function _r(e){let i=e.command(`generate [names...]`).alias(`g`).description("Generate code scaffolds — bare form `kick g <name>` is shorthand for `kick g module <name>`").option(`--list`,`List all available generators`).option(`--dry-run`,`Preview files that would be generated without writing them`).option(`--no-entity`,`Skip entity and value object generation (module shortcut)`).option(`--no-tests`,`Skip test file generation (module shortcut)`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{if(t.list){await hr();return}if(!e||e.length===0){i.help();return}let o=W(n);if(k(o),e.length>=2){let[n,i,...s]=e,c=await r(process.cwd()),l=a(c?.plugins??[],c?.commands??[]),u=await Lt({generatorName:n,itemName:i,args:s,flags:t,cwd:process.cwd()},l.generators);if(u){G(u.files,o);return}}await gr(e,t,o)});i.command(`module <names...>`).description(`Generate one or more modules (e.g. kick g module user task project)`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{let r=W(n);k(r),await gr(e,t,r)}),i.command(`adapter <name>`).description(`Generate an AppAdapter with lifecycle hooks and middleware support`).option(`-o, --out <dir>`,`Output directory`,`src/adapters`).action(async(e,t,n)=>{let r=W(n);k(r),G(await Dn({name:e,outDir:S(t.out)}),r)}),i.command(`plugin <name>`).description(`Generate a KickPlugin with DI, modules, adapters, middleware, and lifecycle hooks`).option(`-o, --out <dir>`,`Output directory`,`src/plugins`).action(async(e,t,n)=>{let r=W(n);k(r),G(await On({name:e,outDir:S(t.out)}),r)}),i.command(`middleware <name>`).description(`Generate an Express middleware function
3457
- Use -m to scope it to a module: kick g middleware auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await Mn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`guard <name>`).description(`Generate a route guard (auth, roles, etc.)
3458
- Use -m to scope it to a module: kick g guard admin -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await Nn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`service <name>`).description(`Generate a @Service() class
3459
- Use -m to scope it to a module: kick g service payment -m orders`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await Pn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`controller <name>`).description(`Generate a @Controller() class with basic routes
3460
- Use -m to scope it to a module: kick g controller auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await Fn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a),await pr(a)}),i.command(`dto <name>`).description(`Generate a Zod DTO schema
3461
- Use -m to scope it to a module: kick g dto create-user -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;G(await In({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`test <name>`).description(`Generate a Vitest test scaffold
3462
- Use -m to scope it to a module: kick g test user-service -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module's __tests__/ folder`).action(async(e,t,i)=>{let a=W(i);k(a);let o=n(await r(process.cwd())),s=o.dir??`src/modules`;G(await dr({name:e,outDir:t.out,moduleName:t.module,modulesDir:s,pluralize:o.pluralize??!0}),a)}),i.command(`job <name>`).description(`Generate a @Job queue processor with @Process handlers`).option(`-o, --out <dir>`,`Output directory`,`src/jobs`).option(`-q, --queue <name>`,`Queue name (default: <name>-queue)`).action(async(e,t,n)=>{let r=W(n);k(r),G(await Jn({name:e,outDir:S(t.out),queue:t.queue}),r)}),i.command(`scaffold <name> [fields...]`).description(`Generate a full CRUD module from field definitions
3657
+ `);for(let{source:e,reason:t}of i.failed)console.log(` ${e} — ${t}`)}console.log()}async function Hr(e,i,a){let o=await r(process.cwd()),s=n(o),c=i.modulesDir??s.dir??`src/modules`,l=i.repo??Nn(s.repo),u=i.pattern??o?.pattern??`ddd`,d=i.pluralize===!1?!1:s.pluralize??!0,f=t(o,process.cwd()),p=s.style??`define`;if(!a&&p===`define`){let e=await hr(C(c),`define`);if(e.length>0){console.error(`\n ${O.red(`Error:`)} ${e.length} module file(s) still use the legacy \`class … implements AppModule\` shape.\n ${O.dim(`Project setting:`)} modules.style: 'define' (default)\n\n ${O.bold(`Files needing migration:`)}`);for(let t of e.slice(0,5))console.error(` - ${t}`);e.length>5&&console.error(` … and ${e.length-5} more`),console.error(`\n ${O.bold(`Pick one:`)}\n 1. Migrate everything to defineModule:\n ${O.dim(`$`)} kick codemod modules --experimental --apply\n 2. Keep the class form — pin it in kick.config.ts:\n ${O.dim(`// kick.config.ts`)}\n ${O.dim(`export default defineConfig({ modules: { style: 'class' } })`)}\n`),process.exit(1)}}let m=[];for(let t of e){let e=await Pn({name:t,modulesDir:C(c),noEntity:i.entity===!1,noTests:i.tests===!1,repo:l,minimal:i.minimal,force:i.force,pattern:u,dryRun:a,pluralize:d,prismaClientPath:s.prismaClientPath,tokenScope:f,style:s.style});m.push(...e)}q(m,a),await zr(a)}function Ur(e){let i=e.command(`generate [names...]`).alias(`g`).description("Generate code scaffolds — bare form `kick g <name>` is shorthand for `kick g module <name>`").option(`--list`,`List all available generators`).option(`--dry-run`,`Preview files that would be generated without writing them`).option(`--no-entity`,`Skip entity and value object generation (module shortcut)`).option(`--no-tests`,`Skip test file generation (module shortcut)`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{if(t.list){await Vr();return}if(!e||e.length===0){i.help();return}let o=K(n);if(k(o),e.length>=2){let[n,i,...s]=e,c=await r(process.cwd()),l=a(c?.plugins??[],c?.commands??[]),u=await Gt({generatorName:n,itemName:i,args:s,flags:t,cwd:process.cwd()},l.generators);if(u){q(u.files,o);return}}await Hr(e,t,o)});i.command(`module <names...>`).description(`Generate one or more modules (e.g. kick g module user task project)`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--repo <type>`,`Repository implementation: inmemory | drizzle | prisma`).option(`--pattern <pattern>`,`Override project pattern: rest | ddd | cqrs | minimal`).option(`--minimal`,`Shorthand for --pattern minimal`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t,n)=>{let r=K(n);k(r),await Hr(e,{...n.optsWithGlobals(),...t},r)}),i.command(`adapter <name>`).description(`Generate an AppAdapter with lifecycle hooks and middleware support`).option(`-o, --out <dir>`,`Output directory`,`src/adapters`).action(async(e,t,n)=>{let r=K(n);k(r),q(await Hn({name:e,outDir:C(t.out)}),r)}),i.command(`plugin <name>`).description(`Generate a KickPlugin with DI, modules, adapters, middleware, and lifecycle hooks`).option(`-o, --out <dir>`,`Output directory`,`src/plugins`).action(async(e,t,n)=>{let r=K(n);k(r),q(await Un({name:e,outDir:C(t.out)}),r)}),i.command(`middleware <name>`).description(`Generate an Express middleware function
3658
+ Use -m to scope it to a module: kick g middleware auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await qn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`guard <name>`).description(`Generate a route guard (auth, roles, etc.)
3659
+ Use -m to scope it to a module: kick g guard admin -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await Jn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`service <name>`).description(`Generate a @Service() class
3660
+ Use -m to scope it to a module: kick g service payment -m orders`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await Yn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`controller <name>`).description(`Generate a @Controller() class with basic routes
3661
+ Use -m to scope it to a module: kick g controller auth -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await Xn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a),await zr(a)}),i.command(`dto <name>`).description(`Generate a Zod DTO schema
3662
+ Use -m to scope it to a module: kick g dto create-user -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).action(async(e,t,i)=>{let a=K(i);k(a);let o=await r(process.cwd()),s=n(o),c=s.dir??`src/modules`;q(await Zn({name:e,outDir:t.out,moduleName:t.module,modulesDir:c,pattern:o?.pattern,pluralize:s.pluralize??!0}),a)}),i.command(`test <name>`).description(`Generate a Vitest test scaffold
3663
+ Use -m to scope it to a module: kick g test user-service -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module's __tests__/ folder`).action(async(e,t,i)=>{let a=K(i);k(a);let o=n(await r(process.cwd())),s=o.dir??`src/modules`;q(await Lr({name:e,outDir:t.out,moduleName:t.module,modulesDir:s,pluralize:o.pluralize??!0}),a)}),i.command(`job <name>`).description(`Generate a @Job queue processor with @Process handlers`).option(`-o, --out <dir>`,`Output directory`,`src/jobs`).option(`-q, --queue <name>`,`Queue name (default: <name>-queue)`).action(async(e,t,n)=>{let r=K(n);k(r),q(await xr({name:e,outDir:C(t.out),queue:t.queue}),r)}),i.command(`scaffold <name> [fields...]`).description(`Generate a full CRUD module from field definitions
3463
3664
  Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
3464
3665
  Types: string, text, number, int, float, boolean, date, email, url, uuid, json, enum:a,b,c
3465
3666
  Optional: append :optional (shell-safe): description:text:optional
3466
- or use ? with quoting: "description:text?" or "description?:text"`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`--modules-dir <dir>`,`Modules directory`).action(async(e,i,a,o)=>{let s=W(o);k(s),i.length===0&&(console.error(`
3667
+ or use ? with quoting: "description:text?" or "description?:text"`).option(`--no-entity`,`Skip entity and value object generation`).option(`--no-tests`,`Skip test file generation`).option(`--no-pluralize`,`Use singular names (skip auto-pluralization)`).option(`--modules-dir <dir>`,`Modules directory`).action(async(e,i,a,o)=>{let s=K(o);k(s),i.length===0&&(console.error(`
3467
3668
  Error: At least one field is required.
3468
3669
  Usage: kick g scaffold <name> <field:type> [field:type...]
3469
3670
  Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
3470
3671
  Optional: append :optional (shell-safe, no quoting needed)
3471
- `),process.exit(1));let c=await r(process.cwd()),l=n(c),u=a.modulesDir??l.dir??`src/modules`,d=Xn(i),f=t(c,process.cwd()),p=await Zn({name:e,fields:d,modulesDir:S(u),noEntity:a.entity===!1,noTests:a.tests===!1,pluralize:a.pluralize===!1?!1:l.pluralize??!0,tokenScope:f});console.log(`\n Scaffolded ${e} with ${d.length} field(s):`);for(let e of d)console.log(` ${e.name}: ${e.type}${e.optional?` (optional)`:``}`);G(p,s),await pr(s)}),i.command(`auth-scaffold`).description(`Generate a complete auth module (register, login, logout, password hashing)
3472
- Includes controller, service, DTOs, and test stubs.`).option(`-s, --strategy <type>`,`Auth strategy: jwt | session`).option(`--role-guards`,`Generate role-based guards (default: true)`).option(`--no-role-guards`,`Skip role-based guard generation`).option(`-o, --out <dir>`,`Output directory`,`src/modules/auth`).action(async(e,t)=>{let n=W(t);k(n);let r=e.strategy;r||=await mt({message:`Auth strategy`,options:[{value:`jwt`,label:`JWT`,hint:`stateless token-based auth`},{value:`session`,label:`Session`,hint:`server-side session with cookies`}]});let i=e.roleGuards;i===void 0&&(i=await F({message:`Generate role-based guards?`,initialValue:!0})),G(await Un({strategy:r,outDir:e.out,roleGuards:i}),n)}),i.command(`config`).description(`Generate a kick.config.ts at the project root`).option(`--modules-dir <dir>`,`Modules directory path`,`src/modules`).option(`--repo <type>`,`Default repository type: inmemory | drizzle | prisma`,`inmemory`).option(`-f, --force`,`Overwrite existing kick.config.ts without prompting`).action(async(e,t)=>{let n=W(t);k(n),G(await Ln({outDir:S(`.`),modulesDir:e.modulesDir,defaultRepo:e.repo,force:e.force}),n)}),i.command(`agents`).alias(`agent-docs`).alias(`ai-docs`).description(`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md (sync after framework upgrades)`).option(`--only <which>`,`Limit scope: agents | claude | skills | both (agents+claude) | all (default: all)`,`all`).option(`--name <name>`,`Project name (defaults to package.json name)`).option(`--pm <pm>`,`Package manager (defaults to package.json packageManager)`).option(`--template <template>`,`Template: rest | ddd | cqrs | minimal`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t)=>{let n=W(t);k(n);let r=e.only??`all`;if(!fr.includes(r)){console.error(` Invalid --only value: ${r}. Expected: ${fr.join(` | `)}`),process.exitCode=1;return}G(await Hn({outDir:S(`.`),only:r,name:e.name,pm:e.pm,template:e.template,force:e.force}),n)})}async function vr(e){let t=v.resolve(e.cwd,`.kickjs/types`);await pe(t,{recursive:!0});let n=new Map,r=e.scan??d,i={cwd:e.cwd,config:e.config,async importTs(e){return await import(C(e).href)},async writeFile(t,n){let r=v.resolve(e.cwd,t);await pe(v.dirname(r),{recursive:!0}),await E(r,n,`utf8`)},getScanResult:e=>{let t=yr(e),i=n.get(t);return i||(i=r(e),n.set(t,i)),i},log:console},a=[];for(let n of e.plugins){let r=await n.generate(i);if(r===null){a.push({id:n.id,status:`skipped`});continue}let o=n.outExtension??`.d.ts`,s=v.join(t,`${n.id.replace(/\//g,`__`)}${o}`),c=`/* AUTO-GENERATED by kick typegen — do not edit. Plugin: ${n.id} */\n\n`+r+`
3473
- `,l=``;if(h(s)&&(l=await T(s,`utf8`)),l===c){a.push({id:n.id,status:`unchanged`,outFile:s});continue}if(e.check)throw Error(`kick typegen --check: drift detected for ${n.id} (${s})`);await E(s,c,`utf8`),a.push({id:n.id,status:`written`,outFile:s})}return a}function yr(e){let t=(e.extensions??[]).slice().toSorted().join(`,`),n=(e.exclude??[]).slice().toSorted().join(`,`);return[`root=${e.root}`,`cwd=${e.cwd}`,`extensions=${t}`,`exclude=${n}`,`envFile=${e.envFile??``}`].join(`|`)}function br(e,t){let n=new Set(t),r=[],i=[],a=new Set;for(let t of e)n.has(t.id)?(i.push(t),a.add(t.id)):r.push(t);return{enabled:r,skipped:i,unknown:[...n].filter(e=>!a.has(e))}}var xr=e({applyDisableFilter:()=>br,runAllPluginTypegens:()=>Sr});async function Sr(e){let{enabled:t,skipped:n,unknown:r}=br(a([...Zi,...e.config?.plugins??[]],e.config?.commands??[]).typegens,e.config?.typegen?.disable??[]);if(!e.silent&&n.length>0)for(let e of n)console.log(` ${e.id}: disabled (typegen.disable)`);if(!e.silent&&r.length>0&&console.warn(` kick typegen: disable list references unknown id(s): ${r.map(e=>`'${e}'`).join(`, `)}. Run \`kick typegen --list\` to see registered ids.`),t.length===0)return[];try{let n=await vr({cwd:e.cwd,config:e.config??{},plugins:t,check:e.check});if(!e.silent)for(let e of n)console.log(` ${e.id}: ${e.status}`);return n}catch(t){if(!e.silent){let e=t instanceof Error?t.message:String(t);console.warn(` kick typegen plugins: skipped (${e})`)}return[]}}async function Cr(e,t){let{cwd:n,silent:r=!1}=t,i=t.distDir??e?.build?.outDir??`dist`,a=e?.assetMap;if(!a||Object.keys(a).length===0)return null;let o=r?()=>{}:console.log,s=S(n,i);g(s,{recursive:!0});let c=[],l={};for(let[e,t]of Object.entries(a)){let r=await wr(e,t,n,s);c.push(r.entrySummary),Object.assign(l,r.manifestSlice),o(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let u={version:1,entries:l},d=b(s,`.kickjs-assets.json`);return re(d,JSON.stringify(u,null,2)+`
3474
- `,`utf-8`),o(` ✓ wrote manifest → ${x(n,d)} (${Object.keys(l).length} entries)`),{manifestPath:d,entries:c,manifest:u}}async function wr(e,t,n,r){let i=S(n,t.src),a=t.dest?S(n,t.dest):b(r,e);if(Er(a,n))return console.warn(` ⚠ assetMap.${e}.dest ('${t.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:t.src,dest:x(n,a),filesCopied:0},manifestSlice:{}};if(!h(i)||!Dr(i))return{entrySummary:{namespace:e,src:t.src,dest:x(n,a),filesCopied:0},manifestSlice:{}};let o=await ge(t.glob??`**/*`,{cwd:i,nodir:!0,dot:!1,posix:!0});g(a,{recursive:!0});let s={},{pairs:c,collisionGroupsResolved:l}=_e(e,[...o].toSorted(),{strategy:t.keys??`auto`});for(let{rel:e,key:t}of c){let n=b(i,e),o=b(a,e);g(y(o),{recursive:!0}),m(n,o),s[t]=Tr(r,o)}return l>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${l} basename collision(s) by keeping extensions (set 'keys: "strip"' to opt back into legacy last-write-wins behaviour, or 'keys: "with-extension"' to keep all keys verbose).`),{entrySummary:{namespace:e,src:t.src,dest:x(n,a),filesCopied:o.length},manifestSlice:s}}function Tr(e,t){return x(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function Er(e,t){let n=x(t,e);return n===``?!1:n.startsWith(`..`)||oe(n)}function Dr(e){try{return ne(e).isDirectory()}catch{return!1}}function Or(e){if(typeof e==`boolean`)return e;let t=process.env.KICKJS_WATCH_POLLING;return t===`1`||t===`true`}async function kr(e,t,n={}){t&&(process.env.PORT=t);let i=Or(n.polling),a=process.cwd(),o=await r(a),s=o?.typegen?.schemaValidator??`zod`,c=o?.typegen?.envFile;try{await f({cwd:a,allowDuplicates:!0,schemaValidator:s,envFile:c,srcDir:o?.typegen?.srcDir,outDir:o?.typegen?.outDir,assetMap:o?.assetMap,runPlugins:!1})}catch(e){console.warn(` kick typegen: skipped (${e?.message??e})`)}await Sr({cwd:a,config:o});let{createRequire:l}=await import(`node:module`),{createServer:u}=await import(C(l(S(`package.json`)).resolve(`vite`)).href),d=await u({configFile:S(`vite.config.ts`),server:{port:t?parseInt(t,10):void 0,...i?{watch:{usePolling:!0,interval:100}}:{}}}),p=o?.assetMap?Object.values(o.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>S(a,e)):[],m=e=>p.some(t=>e===t||e.startsWith(`${t}/`)),h=null,g=e=>{if(e.includes(`.kickjs`)||e.endsWith(`.d.ts`))return;let t=/\.(ts|tsx|mts|cts)$/.test(e),n=m(e);!t&&!n||(h&&clearTimeout(h),h=setTimeout(()=>{f({cwd:a,silent:!0,allowDuplicates:!0,schemaValidator:s,envFile:c,srcDir:o?.typegen?.srcDir,outDir:o?.typegen?.outDir,assetMap:o?.assetMap,runPlugins:!1}).catch(()=>{}),Sr({cwd:a,config:o,silent:!0}).catch(()=>{})},100))};d.watcher.on(`add`,g),d.watcher.on(`unlink`,g),d.watcher.on(`change`,g),p.length>0&&d.watcher.add(p),await d.listen(),d.printUrls(),console.log(`
3672
+ `),process.exit(1));let c=await r(process.cwd()),l=n(c),u=a.modulesDir??l.dir??`src/modules`,d=Cr(i),f=t(c,process.cwd()),p=c?.pattern??`ddd`;p!==`ddd`&&(console.error(`\n Error: 'kick g scaffold' currently only supports the DDD pattern.\n Detected project pattern: '${p}'.\n Workarounds:\n - Run 'kick g module ${e}' for the ${p} layout (no fields), then add fields manually.\n - Override the pattern for this scaffold by setting kick.config.ts pattern: 'ddd'.\n`),process.exit(1));let m=await wr({name:e,fields:d,modulesDir:C(u),noEntity:a.entity===!1,noTests:a.tests===!1,pluralize:a.pluralize===!1?!1:l.pluralize??!0,tokenScope:f,style:l.style});console.log(`\n Scaffolded ${e} with ${d.length} field(s):`);for(let e of d)console.log(` ${e.name}: ${e.type}${e.optional?` (optional)`:``}`);q(m,s),await zr(s)}),i.command(`auth-scaffold`).description(`Generate a complete auth module (register, login, logout, password hashing)
3673
+ Includes controller, service, DTOs, and test stubs.`).option(`-s, --strategy <type>`,`Auth strategy: jwt | session`).option(`--role-guards`,`Generate role-based guards (default: true)`).option(`--no-role-guards`,`Skip role-based guard generation`).option(`-o, --out <dir>`,`Output directory`,`src/modules/auth`).action(async(e,t)=>{let n=K(t);k(n);let r=e.strategy;r||=await St({message:`Auth strategy`,options:[{value:`jwt`,label:`JWT`,hint:`stateless token-based auth`},{value:`session`,label:`Session`,hint:`server-side session with cookies`}]});let i=e.roleGuards;i===void 0&&(i=await P({message:`Generate role-based guards?`,initialValue:!0})),q(await gr({strategy:r,outDir:e.out,roleGuards:i}),n)}),i.command(`config`).description(`Generate a kick.config.ts at the project root`).option(`--modules-dir <dir>`,`Modules directory path`,`src/modules`).option(`--repo <type>`,`Default repository type: inmemory | drizzle | prisma`,`inmemory`).option(`-f, --force`,`Overwrite existing kick.config.ts without prompting`).action(async(e,t)=>{let n=K(t);k(n),q(await Qn({outDir:C(`.`),modulesDir:e.modulesDir,defaultRepo:e.repo,force:e.force}),n)}),i.command(`agents`).alias(`agent-docs`).alias(`ai-docs`).description(`Regenerate AGENTS.md + CLAUDE.md + kickjs-skills.md (sync after framework upgrades)`).option(`--only <which>`,`Limit scope: agents | claude | skills | both (agents+claude) | all (default: all)`,`all`).option(`--name <name>`,`Project name (defaults to package.json name)`).option(`--pm <pm>`,`Package manager (defaults to package.json packageManager)`).option(`--template <template>`,`Template: rest | ddd | cqrs | minimal`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t)=>{let n=K(t);k(n);let r=e.only??`all`;if(!Rr.includes(r)){console.error(` Invalid --only value: ${r}. Expected: ${Rr.join(` | `)}`),process.exitCode=1;return}q(await rr({outDir:C(`.`),only:r,name:e.name,pm:e.pm,template:e.template,force:e.force}),n)})}async function Wr(e){let t=y.resolve(e.cwd,`.kickjs/types`);await he(t,{recursive:!0});let n=new Map,r=e.scan??d,i={cwd:e.cwd,config:e.config,async importTs(e){return await import(ce(e).href)},async writeFile(t,n){let r=y.resolve(e.cwd,t);await he(y.dirname(r),{recursive:!0}),await E(r,n,`utf8`)},getScanResult:e=>{let t=Gr(e),i=n.get(t);return i||(i=r(e),n.set(t,i)),i},log:console},a=[];for(let n of e.plugins){let r=await n.generate(i);if(r===null){a.push({id:n.id,status:`skipped`});continue}let o=n.outExtension??`.d.ts`,s=y.join(t,`${n.id.replace(/\//g,`__`)}${o}`),c=`/* AUTO-GENERATED by kick typegen — do not edit. Plugin: ${n.id} */\n\n`+r+`
3674
+ `,l=``;if(h(s)&&(l=await T(s,`utf8`)),l===c){a.push({id:n.id,status:`unchanged`,outFile:s});continue}if(e.check)throw Error(`kick typegen --check: drift detected for ${n.id} (${s})`);await E(s,c,`utf8`),a.push({id:n.id,status:`written`,outFile:s})}return a}function Gr(e){let t=(e.extensions??[]).slice().toSorted().join(`,`),n=(e.exclude??[]).slice().toSorted().join(`,`);return[`root=${e.root}`,`cwd=${e.cwd}`,`extensions=${t}`,`exclude=${n}`,`envFile=${e.envFile??``}`].join(`|`)}function Kr(e,t){let n=new Set(t),r=[],i=[],a=new Set;for(let t of e)n.has(t.id)?(i.push(t),a.add(t.id)):r.push(t);return{enabled:r,skipped:i,unknown:[...n].filter(e=>!a.has(e))}}var qr=e({applyDisableFilter:()=>Kr,runAllPluginTypegens:()=>Jr});async function Jr(e){let{enabled:t,skipped:n,unknown:r}=Kr(a([...ka,...e.config?.plugins??[]],e.config?.commands??[]).typegens,e.config?.typegen?.disable??[]);if(!e.silent&&n.length>0)for(let e of n)console.log(` ${e.id}: disabled (typegen.disable)`);if(!e.silent&&r.length>0&&console.warn(` kick typegen: disable list references unknown id(s): ${r.map(e=>`'${e}'`).join(`, `)}. Run \`kick typegen --list\` to see registered ids.`),t.length===0)return[];try{let n=await Wr({cwd:e.cwd,config:e.config??{},plugins:t,check:e.check});if(!e.silent)for(let e of n)console.log(` ${e.id}: ${e.status}`);return n}catch(t){if(!e.silent){let e=t instanceof Error?t.message:String(t);console.warn(` kick typegen plugins: skipped (${e})`)}return[]}}async function Yr(e,t){let{cwd:n,silent:r=!1}=t,i=t.distDir??e?.build?.outDir??`dist`,a=e?.assetMap;if(!a||Object.keys(a).length===0)return null;let o=r?()=>{}:console.log,s=C(n,i);g(s,{recursive:!0});let c=[],l={};for(let[e,t]of Object.entries(a)){let r=await Xr(e,t,n,s);c.push(r.entrySummary),Object.assign(l,r.manifestSlice),o(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let u={version:1,entries:l},d=x(s,`.kickjs-assets.json`);return ne(d,JSON.stringify(u,null,2)+`
3675
+ `,`utf-8`),o(` ✓ wrote manifest → ${S(n,d)} (${Object.keys(l).length} entries)`),{manifestPath:d,entries:c,manifest:u}}async function Xr(e,t,n,r){let i=C(n,t.src),a=t.dest?C(n,t.dest):x(r,e);if(Qr(a,n))return console.warn(` ⚠ assetMap.${e}.dest ('${t.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:t.src,dest:S(n,a),filesCopied:0},manifestSlice:{}};if(!h(i)||!$r(i))return{entrySummary:{namespace:e,src:t.src,dest:S(n,a),filesCopied:0},manifestSlice:{}};let o=await be(t.glob??`**/*`,{cwd:i,nodir:!0,dot:!1,posix:!0});g(a,{recursive:!0});let s={},{pairs:c,collisionGroupsResolved:l}=xe(e,[...o].toSorted(),{strategy:t.keys??`auto`});for(let{rel:e,key:t}of c){let n=x(i,e),o=x(a,e);g(b(o),{recursive:!0}),m(n,o),s[t]=Zr(r,o)}return l>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${l} basename collision(s) by keeping extensions (set 'keys: "strip"' to opt back into legacy last-write-wins behaviour, or 'keys: "with-extension"' to keep all keys verbose).`),{entrySummary:{namespace:e,src:t.src,dest:S(n,a),filesCopied:o.length},manifestSlice:s}}function Zr(e,t){return S(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function Qr(e,t){let n=S(t,e);return n===``?!1:n.startsWith(`..`)||ae(n)}function $r(e){try{return te(e).isDirectory()}catch{return!1}}function ei(e){if(typeof e==`boolean`)return e;let t=process.env.KICKJS_WATCH_POLLING;return t===`1`||t===`true`}async function ti(e,t,n={}){t&&(process.env.PORT=t);let i=ei(n.polling),a=process.cwd(),o=await r(a),s=o?.typegen?.schemaValidator??`zod`,c=o?.typegen?.envFile;try{await f({cwd:a,allowDuplicates:!0,schemaValidator:s,envFile:c,srcDir:o?.typegen?.srcDir,outDir:o?.typegen?.outDir,assetMap:o?.assetMap,runPlugins:!1})}catch(e){console.warn(` kick typegen: skipped (${e?.message??e})`)}await Jr({cwd:a,config:o});let{createRequire:l}=await import(`node:module`),{createServer:u}=await import(ce(l(C(`package.json`)).resolve(`vite`)).href),d=await u({configFile:C(`vite.config.ts`),server:{port:t?parseInt(t,10):void 0,...i?{watch:{usePolling:!0,interval:100}}:{}}}),p=o?.assetMap?Object.values(o.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>C(a,e)):[],m=e=>p.some(t=>e===t||e.startsWith(`${t}/`)),h=null,g=e=>{if(e.includes(`.kickjs`)||e.endsWith(`.d.ts`))return;let t=/\.(ts|tsx|mts|cts)$/.test(e),n=m(e);!t&&!n||(h&&clearTimeout(h),h=setTimeout(()=>{f({cwd:a,silent:!0,allowDuplicates:!0,schemaValidator:s,envFile:c,srcDir:o?.typegen?.srcDir,outDir:o?.typegen?.outDir,assetMap:o?.assetMap,runPlugins:!1}).catch(()=>{}),Jr({cwd:a,config:o,silent:!0}).catch(()=>{})},100))};d.watcher.on(`add`,g),d.watcher.on(`unlink`,g),d.watcher.on(`change`,g),p.length>0&&d.watcher.add(p),await d.listen(),d.printUrls(),console.log(`
3475
3676
  KickJS dev server running (Vite + @forinda/kickjs-vite)
3476
- `);let _=async()=>{h&&clearTimeout(h),await d.close(),process.exit(0)};process.on(`SIGINT`,_),process.on(`SIGTERM`,_)}function Ar(e){e.command(`dev`).description(`Start development server with Vite HMR (zero-downtime reload)`).option(`-e, --entry <file>`,`Entry file`,`src/index.ts`).option(`-p, --port <port>`,`Port number`).option(`--polling`,`Force chokidar to poll for file changes (Docker / WSL / NFS / older kernels)`).action(async e=>{try{await kr(e.entry,e.port,{polling:e.polling})}catch(e){e.code===`ERR_MODULE_NOT_FOUND`&&e.message?.includes(`vite`)?console.error(`
3677
+ `);let _=async()=>{h&&clearTimeout(h),await d.close(),process.exit(0)};process.on(`SIGINT`,_),process.on(`SIGTERM`,_)}function ni(e){e.command(`dev`).description(`Start development server with Vite HMR (zero-downtime reload)`).option(`-e, --entry <file>`,`Entry file`,`src/index.ts`).option(`-p, --port <port>`,`Port number`).option(`--polling`,`Force chokidar to poll for file changes (Docker / WSL / NFS / older kernels)`).action(async e=>{try{await ti(e.entry,e.port,{polling:e.polling})}catch(e){e.code===`ERR_MODULE_NOT_FOUND`&&e.message?.includes(`vite`)?console.error(`
3477
3678
  Error: vite is not installed.
3478
3679
  Run: pnpm add -D vite unplugin-swc
3479
3680
  `):console.error(`
3480
3681
  Dev server failed:`,e.message??e),process.exit(1)}}),e.command(`build`).description(`Build for production via Vite`).action(async()=>{console.log(`
3481
3682
  Building for production...
3482
- `);let{createRequire:e}=await import(`node:module`),{build:t}=await import(C(e(S(`package.json`)).resolve(`vite`)).href);await t({configFile:S(`vite.config.ts`)});let n=await r(process.cwd()),i=n?.copyDirs??[];if(i.length>0){console.log(`
3483
- Copying directories to dist...`);for(let e of i){let t=typeof e==`string`?e:e.src,n=typeof e==`string`?b(`dist`,e):e.dest??b(`dist`,t),r=S(t),i=S(n);if(!h(r)){console.log(` ⚠ Skipped ${t} (not found)`);continue}g(i,{recursive:!0}),m(r,i,{recursive:!0}),console.log(` ✓ ${t} → ${n}`)}}if(n?.assetMap&&Object.keys(n.assetMap).length>0){console.log(`
3484
- Building asset map...`);try{await Cr(n,{cwd:process.cwd()})}catch(e){console.error(` ✗ asset build failed: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}console.log(`
3683
+ `);let{createRequire:e}=await import(`node:module`),{build:t}=await import(ce(e(C(`package.json`)).resolve(`vite`)).href);await t({configFile:C(`vite.config.ts`)});let n=await r(process.cwd()),i=n?.copyDirs??[];if(i.length>0){console.log(`
3684
+ Copying directories to dist...`);for(let e of i){let t=typeof e==`string`?e:e.src,n=typeof e==`string`?x(`dist`,e):e.dest??x(`dist`,t),r=C(t),i=C(n);if(!h(r)){console.log(` ⚠ Skipped ${t} (not found)`);continue}g(i,{recursive:!0}),m(r,i,{recursive:!0}),console.log(` ✓ ${t} → ${n}`)}}if(n?.assetMap&&Object.keys(n.assetMap).length>0){console.log(`
3685
+ Building asset map...`);try{await Yr(n,{cwd:process.cwd()})}catch(e){console.error(` ✗ asset build failed: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}console.log(`
3485
3686
  Build complete.
3486
3687
  `)}),e.command(`build:assets`).description(`Rebuild the .kickjs-assets.json manifest under the configured outDir (no JS rebuild)`).action(async()=>{let e=await r(process.cwd());if(!e?.assetMap||Object.keys(e.assetMap).length===0){console.log(` No assetMap entries — nothing to build.`);return}console.log(`
3487
- Building asset map...`);try{await Cr(e,{cwd:process.cwd()}),console.log(`
3688
+ Building asset map...`);try{await Yr(e,{cwd:process.cwd()}),console.log(`
3488
3689
  Asset build complete.
3489
- `)}catch(e){console.error(` ✗ ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}),e.command(`start`).description(`Start production server`).option(`-e, --entry <file>`,`Entry file`,`dist/index.js`).option(`-p, --port <port>`,`Port number`).action(e=>{let t={NODE_ENV:`production`};e.port&&(t.PORT=String(e.port)),Ae(e.entry,t)}),e.command(`dev:debug`).description(`Start dev server with Node.js inspector attached`).option(`-e, --entry <file>`,`Entry file`,`src/index.ts`).option(`-p, --port <port>`,`Port number`).option(`--inspect-port <port>`,`Inspector port`,`9229`).action(async e=>{let t=e.inspectPort??`9229`;process.env.NODE_OPTIONS=`--inspect=0.0.0.0:${t}`,console.log(` Debugger: ws://0.0.0.0:${t}`);try{await kr(e.entry,e.port)}catch(e){console.error(`
3490
- Dev server (debug) failed:`,e.message??e),process.exit(1)}})}function jr(e){e.command(`info`).description(`Print system and framework info`).action(()=>{console.log(`
3690
+ `)}catch(e){console.error(` ✗ ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}),e.command(`start`).description(`Start production server`).option(`-e, --entry <file>`,`Entry file`,`dist/index.js`).option(`-p, --port <port>`,`Port number`).action(e=>{let t={NODE_ENV:`production`};e.port&&(t.PORT=String(e.port)),Pe(e.entry,t)}),e.command(`dev:debug`).description(`Start dev server with Node.js inspector attached`).option(`-e, --entry <file>`,`Entry file`,`src/index.ts`).option(`-p, --port <port>`,`Port number`).option(`--inspect-port <port>`,`Inspector port`,`9229`).action(async e=>{let t=e.inspectPort??`9229`;process.env.NODE_OPTIONS=`--inspect=0.0.0.0:${t}`,console.log(` Debugger: ws://0.0.0.0:${t}`);try{await ti(e.entry,e.port)}catch(e){console.error(`
3691
+ Dev server (debug) failed:`,e.message??e),process.exit(1)}})}function ri(e){e.command(`info`).description(`Print system and framework info`).action(()=>{console.log(`
3491
3692
  KickJS CLI
3492
3693
 
3493
3694
  System:
3494
- OS: ${ye()} ${be()} (${ve()})
3695
+ OS: ${Ce()} ${we()} (${Se()})
3495
3696
  Node: ${process.version}
3496
3697
 
3497
3698
  Packages:
3498
3699
  @forinda/kickjs workspace
3499
3700
  @forinda/kickjs-vite workspace
3500
3701
  @forinda/kickjs-cli workspace
3501
- `)})}const{bold:K,dim:q,green:Mr,red:Nr,yellow:Pr,blue:Fr}=O;function Ir(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),i=e%60,a=[];return t&&a.push(`${t}d`),n&&a.push(`${n}h`),r&&a.push(`${r}m`),a.push(`${i}s`),a.join(` `)}async function Lr(e){let t=await fetch(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)throw Error(`${t.status} ${t.statusText}`);return t.json()}async function J(e,t){try{return await Lr(`${e}${t}`)}catch{return null}}async function Rr(e){let[t,n,r,i,a]=await Promise.all([J(e,`/health`),J(e,`/metrics`),J(e,`/routes`),J(e,`/container`),J(e,`/ws`)]);return{health:t,metrics:n,routes:r,container:i,ws:a}}function zr(e,t){let{health:n,metrics:r,routes:i,container:a,ws:o}=t,s=q(`─`.repeat(60));if(console.log(),console.log(K(` KickJS Inspector`)+q(` → ${e}`)),console.log(s),n){let e=n.status===`healthy`?Mr(`● healthy`):Nr(`● `+n.status);console.log(` ${K(`Health:`)} ${e}`)}else console.log(` ${K(`Health:`)} ${Nr(`● unreachable`)}`);if(r){let e=((r.errorRate??0)*100).toFixed(1),t=r.errorRate>.1?Nr:r.errorRate>0?Pr:Mr;console.log(` ${K(`Uptime:`)} ${Ir(r.uptimeSeconds)}`),console.log(` ${K(`Requests:`)} ${r.requests}`),console.log(` ${K(`Errors:`)} ${r.serverErrors} server, ${r.clientErrors??0} client ${q(`(`)}${t(e+`%`)}${q(`)`)}`)}if(a&&console.log(` ${K(`DI:`)} ${a.count} bindings`),o&&o.enabled&&console.log(` ${K(`WS:`)} ${o.connections??0} connections, ${o.namespaces??0} namespaces`),i?.routes?.length){console.log(),console.log(K(` Routes`)),console.log(s),console.log(` ${q(`METHOD`)} ${q(`PATH`.padEnd(36))} ${q(`CONTROLLER`)}`);for(let e of i.routes){let t=e.path.length>36?e.path.slice(0,33)+`...`:e.path.padEnd(36);console.log(` ${lt(e.method)} ${t} ${Fr(e.controller)}.${q(e.handler)}`)}}console.log(s),console.log()}function Br(e){e.command(`inspect [url]`).description(`Connect to a running KickJS app and display debug info`).option(`-p, --port <port>`,`Override port`).option(`-w, --watch`,`Poll every 5 seconds`).option(`-j, --json`,`Output raw JSON`).action(async(e,t)=>{let n=e??`http://localhost:3000`;if(t.port)try{let e=new URL(n);e.port=t.port,n=e.origin}catch{n=`http://localhost:${t.port}`}let r=`${n.replace(/\/$/,``)}/_debug`,i=async()=>{try{let e=await Rr(r);t.json?console.log(JSON.stringify(e,null,2)):zr(n,e)}catch(e){t.json?console.log(JSON.stringify({error:String(e)})):(console.error(Nr(` ✖ Could not connect to ${n}`)),console.error(q(` ${e instanceof Error?e.message:String(e)}`))),t.watch||(process.exitCode=1)}};if(t.watch){let e=async()=>{process.stdout.write(`\x1B[2J\x1B[H`),await i()};await e(),setInterval(e,5e3)}else await i()})}function Vr(e,t){let n=e.toLowerCase();return t.every(e=>n.includes(e.toLowerCase()))}function Y(e,t){let n=e.toLowerCase();return t.some(e=>n.includes(e.toLowerCase()))}const Hr=[{match(e,t){let n=Vr(e,[`config`,`get`])&&Y(e,[`undefined`,`null`]),r=e.includes(`@Value`)&&Y(e,[`undefined`,`is not defined`]);return!n&&!r?null:{confidence:n&&r?90:75,diagnosis:{id:`env-schema-not-registered`,title:`ConfigService.get() returns undefined for user-defined keys`,explanation:`Your src/index.ts is missing \`import "./config"\`. That side-effect import
3702
+ `)})}const{bold:J,dim:Y,green:ii,red:ai,yellow:oi,blue:si}=O;function ci(e){let t=Math.floor(e/86400),n=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),i=e%60,a=[];return t&&a.push(`${t}d`),n&&a.push(`${n}h`),r&&a.push(`${r}m`),a.push(`${i}s`),a.join(` `)}async function li(e){let t=await fetch(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)throw Error(`${t.status} ${t.statusText}`);return t.json()}async function X(e,t){try{return await li(`${e}${t}`)}catch{return null}}async function ui(e){let[t,n,r,i,a]=await Promise.all([X(e,`/health`),X(e,`/metrics`),X(e,`/routes`),X(e,`/container`),X(e,`/ws`)]);return{health:t,metrics:n,routes:r,container:i,ws:a}}function di(e,t){let{health:n,metrics:r,routes:i,container:a,ws:o}=t,s=Y(`─`.repeat(60));if(console.log(),console.log(J(` KickJS Inspector`)+Y(` → ${e}`)),console.log(s),n){let e=n.status===`healthy`?ii(`● healthy`):ai(`● `+n.status);console.log(` ${J(`Health:`)} ${e}`)}else console.log(` ${J(`Health:`)} ${ai(`● unreachable`)}`);if(r){let e=((r.errorRate??0)*100).toFixed(1),t=r.errorRate>.1?ai:r.errorRate>0?oi:ii;console.log(` ${J(`Uptime:`)} ${ci(r.uptimeSeconds)}`),console.log(` ${J(`Requests:`)} ${r.requests}`),console.log(` ${J(`Errors:`)} ${r.serverErrors} server, ${r.clientErrors??0} client ${Y(`(`)}${t(e+`%`)}${Y(`)`)}`)}if(a&&console.log(` ${J(`DI:`)} ${a.count} bindings`),o&&o.enabled&&console.log(` ${J(`WS:`)} ${o.connections??0} connections, ${o.namespaces??0} namespaces`),i?.routes?.length){console.log(),console.log(J(` Routes`)),console.log(s),console.log(` ${Y(`METHOD`)} ${Y(`PATH`.padEnd(36))} ${Y(`CONTROLLER`)}`);for(let e of i.routes){let t=e.path.length>36?e.path.slice(0,33)+`...`:e.path.padEnd(36);console.log(` ${_t(e.method)} ${t} ${si(e.controller)}.${Y(e.handler)}`)}}console.log(s),console.log()}function fi(e){e.command(`inspect [url]`).description(`Connect to a running KickJS app and display debug info`).option(`-p, --port <port>`,`Override port`).option(`-w, --watch`,`Poll every 5 seconds`).option(`-j, --json`,`Output raw JSON`).action(async(e,t)=>{let n=e??`http://localhost:3000`;if(t.port)try{let e=new URL(n);e.port=t.port,n=e.origin}catch{n=`http://localhost:${t.port}`}let r=`${n.replace(/\/$/,``)}/_debug`,i=async()=>{try{let e=await ui(r);t.json?console.log(JSON.stringify(e,null,2)):di(n,e)}catch(e){t.json?console.log(JSON.stringify({error:String(e)})):(console.error(ai(` ✖ Could not connect to ${n}`)),console.error(Y(` ${e instanceof Error?e.message:String(e)}`))),t.watch||(process.exitCode=1)}};if(t.watch){let e=async()=>{process.stdout.write(`\x1B[2J\x1B[H`),await i()};await e(),setInterval(e,5e3)}else await i()})}function pi(e,t){let n=e.toLowerCase();return t.every(e=>n.includes(e.toLowerCase()))}function Z(e,t){let n=e.toLowerCase();return t.some(e=>n.includes(e.toLowerCase()))}const mi=[{match(e,t){let n=pi(e,[`config`,`get`])&&Z(e,[`undefined`,`null`]),r=e.includes(`@Value`)&&Z(e,[`undefined`,`is not defined`]);return!n&&!r?null:{confidence:n&&r?90:75,diagnosis:{id:`env-schema-not-registered`,title:`ConfigService.get() returns undefined for user-defined keys`,explanation:`Your src/index.ts is missing \`import "./config"\`. That side-effect import
3502
3703
  registers the env schema with kickjs at module-load time. Without it,
3503
3704
  ConfigService falls back to the base schema (PORT/NODE_ENV/LOG_LEVEL only)
3504
3705
  and every user-defined key reads as undefined. @Value() may *appear* to
@@ -3510,7 +3711,7 @@ import { modules } from './modules'
3510
3711
  import './config' // ← add this — registers env schema
3511
3712
  import { bootstrap } from '@forinda/kickjs'
3512
3713
  import { modules } from './modules'
3513
- `,docs:`https://forinda.github.io/kick-js/guide/configuration.html#wiring-the-schema-at-startup`}}}},{match(e,t){let n=Y(e,[`vitest`,`test`,`spec`,`__tests__`,`.test.`]);return Y(e,[`already registered`,`already exists`,`duplicate`,`has been registered`])?{confidence:n?85:60,diagnosis:{id:`container-not-reset-in-tests`,title:`DI container leaks between test cases`,explanation:`KickJS decorators register classes on the global Container at import time.
3714
+ `,docs:`https://forinda.github.io/kick-js/guide/configuration.html#wiring-the-schema-at-startup`}}}},{match(e,t){let n=Z(e,[`vitest`,`test`,`spec`,`__tests__`,`.test.`]);return Z(e,[`already registered`,`already exists`,`duplicate`,`has been registered`])?{confidence:n?85:60,diagnosis:{id:`container-not-reset-in-tests`,title:`DI container leaks between test cases`,explanation:`KickJS decorators register classes on the global Container at import time.
3514
3715
  When vitest re-imports your modules across tests, the same class can be
3515
3716
  registered twice and the container throws. The fix is to wipe the
3516
3717
  container between tests so each case starts fresh.`,fix:`Add Container.reset() to a beforeEach hook in the failing test file:`,codeAfter:`import { describe, it, beforeEach } from 'vitest'
@@ -3520,7 +3721,7 @@ describe('UserController', () => {
3520
3721
  beforeEach(() => Container.reset())
3521
3722
 
3522
3723
  it('does the thing', async () => { /* ... */ })
3523
- })`,docs:`https://forinda.github.io/kick-js/guide/testing.html`}}:null}},{match(e,t){return e.includes(`@Module`)||Vr(e,[`Module`,`is not a function`])||Vr(e,[`Module`,`no exported member`])?{confidence:80,diagnosis:{id:`module-decorator-not-found`,title:`KickJS does not have a @Module decorator (different pattern from NestJS)`,explanation:`NestJS uses @Module({ controllers, providers }). KickJS uses an interface
3724
+ })`,docs:`https://forinda.github.io/kick-js/guide/testing.html`}}:null}},{match(e,t){return e.includes(`@Module`)||pi(e,[`Module`,`is not a function`])||pi(e,[`Module`,`no exported member`])?{confidence:80,diagnosis:{id:`module-decorator-not-found`,title:`KickJS does not have a @Module decorator (different pattern from NestJS)`,explanation:`NestJS uses @Module({ controllers, providers }). KickJS uses an interface
3524
3725
  pattern instead: a class implements AppModule and exposes routes() that
3525
3726
  returns the controller wiring. This was a deliberate choice — modules
3526
3727
  become explicit values rather than metadata, which makes them easier to
@@ -3547,7 +3748,7 @@ KickRoutes["POST /users"]. The new form is per-controller, per-method,
3547
3748
  and matches the actual class names so refactors propagate via
3548
3749
  rename-symbol instead of grep.`,fix:`Update the Ctx<...> type parameter to use the namespace form:`,codeBefore:`@Post('/', { body: createUserSchema })
3549
3750
  create(ctx: Ctx<KickRoutes['POST /users']>) { /* ... */ }`,codeAfter:`@Post('/', { body: createUserSchema, name: 'CreateUser' })
3550
- create(ctx: Ctx<KickRoutes.UserController['create']>) { /* ... */ }`,docs:`https://forinda.github.io/kick-js/guide/typegen.html`}}:null}},{match(e,t){let n=Y(e,[`cluster`,`workers`,`two ports`,`duplicate server`]),r=Y(e,[`kick dev`,`vite`,`eaddrinuse`,`5173`,`5174`,`two servers`]);return!n||!r?null:{confidence:85,diagnosis:{id:`cluster-in-vite-dev`,title:"Cluster mode is incompatible with `kick dev` (Vite owns the server)",explanation:`In dev mode, Vite owns the HTTP server. If your bootstrap passes
3751
+ create(ctx: Ctx<KickRoutes.UserController['create']>) { /* ... */ }`,docs:`https://forinda.github.io/kick-js/guide/typegen.html`}}:null}},{match(e,t){let n=Z(e,[`cluster`,`workers`,`two ports`,`duplicate server`]),r=Z(e,[`kick dev`,`vite`,`eaddrinuse`,`5173`,`5174`,`two servers`]);return!n||!r?null:{confidence:85,diagnosis:{id:`cluster-in-vite-dev`,title:"Cluster mode is incompatible with `kick dev` (Vite owns the server)",explanation:`In dev mode, Vite owns the HTTP server. If your bootstrap passes
3551
3752
  cluster: { workers: N }, the framework forks N workers, each of which
3552
3753
  spins up its own Vite instance on a separate port. The fix landed in
3553
3754
  v2.2.5: McpAdapter (and bootstrap()) now detects Vite dev mode and
@@ -3555,7 +3756,7 @@ silently skips cluster, with a warning. If you see this on an older
3555
3756
  version, upgrade or guard the cluster option behind NODE_ENV.`,fix:`Either upgrade to v2.2.5+ or gate cluster mode on production:`,codeAfter:`export const app = await bootstrap({
3556
3757
  modules,
3557
3758
  cluster: process.env.NODE_ENV === 'production' ? { workers: 4 } : false,
3558
- })`,docs:`https://forinda.github.io/kick-js/guide/cluster.html`}}}},{match(e,t){return Y(e,[`reflect-metadata`,`Reflect.getMetadata is not a function`,`Reflect.defineMetadata`,`design:type`,`design:paramtypes`])?{confidence:90,diagnosis:{id:`reflect-metadata-missing`,title:`reflect-metadata is not loaded — DI cannot read decorator types`,explanation:`The DI container reads constructor parameter types via the
3759
+ })`,docs:`https://forinda.github.io/kick-js/guide/cluster.html`}}}},{match(e,t){return Z(e,[`reflect-metadata`,`Reflect.getMetadata is not a function`,`Reflect.defineMetadata`,`design:type`,`design:paramtypes`])?{confidence:90,diagnosis:{id:`reflect-metadata-missing`,title:`reflect-metadata is not loaded — DI cannot read decorator types`,explanation:`The DI container reads constructor parameter types via the
3559
3760
  reflect-metadata polyfill. The polyfill must be imported once,
3560
3761
  before any decorator runs. Most projects do this at the top of
3561
3762
  src/index.ts; missing the import causes obscure "design:paramtypes"
@@ -3564,32 +3765,32 @@ import './config'
3564
3765
  import { bootstrap } from '@forinda/kickjs'
3565
3766
  import { modules } from './modules'
3566
3767
 
3567
- export const app = await bootstrap({ modules })`,docs:`https://forinda.github.io/kick-js/guide/dependency-injection.html`}}:null}},{match(e,t){return Y(e,[`404`,`cannot get`,`cannot post`,`no route`])?{confidence:50,diagnosis:{id:`module-not-registered`,title:`A 404 may indicate a module is not in the modules array`,explanation:`KickJS only mounts modules listed in \`src/modules/index.ts\`. If you
3768
+ export const app = await bootstrap({ modules })`,docs:`https://forinda.github.io/kick-js/guide/dependency-injection.html`}}:null}},{match(e,t){return Z(e,[`404`,`cannot get`,`cannot post`,`no route`])?{confidence:50,diagnosis:{id:`module-not-registered`,title:`A 404 may indicate a module is not in the modules array`,explanation:`KickJS only mounts modules listed in \`src/modules/index.ts\`. If you
3568
3769
  generated a module via \`kick g module foo\` but the routes don't appear,
3569
3770
  the most likely cause is that the module is missing from the exported
3570
3771
  array. The CLI usually wires this automatically, but a hand-edit can
3571
- drop the entry.`,fix:`Open src/modules/index.ts and verify the module is in the array:`,codeAfter:`import type { AppModuleClass } from '@forinda/kickjs'
3772
+ drop the entry.`,fix:`Open src/modules/index.ts and verify the module is in the array:`,codeAfter:`import type { AppModuleEntry } from '@forinda/kickjs'
3572
3773
  import { UserModule } from './users/user.module'
3573
3774
  import { TaskModule } from './tasks/task.module' // ← was this missing?
3574
3775
 
3575
- export const modules: AppModuleClass[] = [UserModule, TaskModule]`,docs:`https://forinda.github.io/kick-js/guide/project-structure.html`}}:null}}];function Ur(e,t){let n=null;for(let r of Hr){let i=null;try{i=r.match(e,t)}catch{continue}!i||i.confidence<40||(!n||i.confidence>n.confidence)&&(n=i)}return n}async function Wr(e){let t=e.provider??`openai`,n=process.env.OPENAI_API_KEY;if(t===`openai`&&!n)return{kind:`unavailable`,reason:`OPENAI_API_KEY environment variable is not set`,suggestion:`Set OPENAI_API_KEY in your shell, e.g.
3776
+ export const modules: AppModuleEntry[] = [UserModule(), TaskModule()]`,docs:`https://forinda.github.io/kick-js/guide/project-structure.html`}}:null}}];function hi(e,t){let n=null;for(let r of mi){let i=null;try{i=r.match(e,t)}catch{continue}!i||i.confidence<40||(!n||i.confidence>n.confidence)&&(n=i)}return n}async function gi(e){let t=e.provider??`openai`,n=process.env.OPENAI_API_KEY;if(t===`openai`&&!n)return{kind:`unavailable`,reason:`OPENAI_API_KEY environment variable is not set`,suggestion:`Set OPENAI_API_KEY in your shell, e.g.
3576
3777
  export OPENAI_API_KEY="sk-..."
3577
3778
 
3578
3779
  Then re-run \`kick explain --ai "<your error>"\`.`};let r;try{r=await import(`@forinda/kickjs-ai`)}catch{return{kind:`unavailable`,reason:`@forinda/kickjs-ai is not installed`,suggestion:`Install the AI package to enable the LLM fallback:
3579
3780
  kick add ai
3580
3781
 
3581
3782
  Or manually:
3582
- pnpm add @forinda/kickjs-ai`}}let{OpenAIProvider:i}=r,a=new i({apiKey:n,defaultChatModel:e.model??`gpt-4o-mini`}),o=Gr(e.cwd),s=`Error or stack trace:\n\n${e.input.trim()}`;try{let e=Kr((await a.chat({messages:[{role:`system`,content:o},{role:`user`,content:s}]})).content);return e?{kind:`ok`,diagnosis:e}:{kind:`error`,message:`The LLM responded but the payload was not valid JSON in the expected shape. Try again, or file an issue with the error text.`}}catch(e){return{kind:`error`,message:`LLM request failed: ${e instanceof Error?e.message:String(e)}`}}}function Gr(e){return[`You are a diagnostic assistant for KickJS, a decorator-driven Node.js`,`framework built on Express 5 and TypeScript. KickJS projects use:`,` - @Controller, @Get, @Post, @Autowired, @Service, @Value decorators`,` - An AppModule interface with a routes() method (NOT a @Module decorator)`,` - Zod schemas as both runtime validators and OpenAPI sources`,` - Ctx<KickRoutes.ControllerName['method']> for typed request context`,` - src/config/index.ts with defineEnv/loadEnv for env schema`,' - A side-effect `import "./config"` in src/index.ts to register the schema',` - Container.reset() in beforeEach for DI test isolation`,``,`When the user gives you an error message or stack trace, produce a`,`structured diagnosis that helps them fix the bug. You MUST respond`,`with a single JSON object (no surrounding prose, no markdown fences)`,`matching this shape:`,``,`{`,` "id": "<kebab-case-identifier>",`,` "title": "<one-line problem summary>",`,` "explanation": "<multi-line explanation of what is wrong>",`,` "fix": "<multi-line instructions for fixing the problem>",`,` "codeBefore": "<optional: broken code snippet>",`,` "codeAfter": "<optional: corrected code snippet>",`,` "docs": "<optional: KickJS doc URL that discusses this topic>"`,`}`,``,`The KickJS docs live at https://forinda.github.io/kick-js/ — prefer`,`that domain for any doc links you suggest.`,e?`The project is located at ${e}.`:``].filter(e=>e.length>0).join(`
3583
- `)}function Kr(e){let t=[e,qr(e),Jr(e)].filter(e=>e!==null);for(let e of t)try{let t=JSON.parse(e);if(Yr(t))return t}catch{continue}return null}function qr(e){let t=e.match(/```(?:json)?\s*\n([\s\S]*?)```/);return t?t[1]?.trim()??null:null}function Jr(e){let t=e.indexOf(`{`);if(t===-1)return null;let n=0,r=!1,i=!1;for(let a=t;a<e.length;a++){let o=e[a];if(i){i=!1;continue}if(o===`\\`&&r){i=!0;continue}if(o===`"`){r=!r;continue}if(!r&&(o===`{`&&n++,o===`}`&&(n--,n===0)))return e.slice(t,a+1)}return null}function Yr(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.id==`string`&&typeof t.title==`string`&&typeof t.explanation==`string`&&typeof t.fix==`string`}function Xr(e){e.command(`explain [message]`).description(`Explain a KickJS error and suggest a fix`).option(`-m, --message <text>`,`Error message to explain (alternative to positional arg)`).option(`--ai`,`Fall back to LLM if no known-issue matches (requires @forinda/kickjs-ai)`).option(`--model <name>`,`Model name for the --ai fallback`,`gpt-4o-mini`).option(`--json`,`Output the diagnosis as JSON for tooling integration`).action(async(e,t)=>{let n=await $r(e,t.message);(!n||n.trim().length===0)&&(process.stderr.write(`Error: no input provided.
3783
+ pnpm add @forinda/kickjs-ai`}}let{OpenAIProvider:i}=r,a=new i({apiKey:n,defaultChatModel:e.model??`gpt-4o-mini`}),o=_i(e.cwd),s=`Error or stack trace:\n\n${e.input.trim()}`;try{let e=vi((await a.chat({messages:[{role:`system`,content:o},{role:`user`,content:s}]})).content);return e?{kind:`ok`,diagnosis:e}:{kind:`error`,message:`The LLM responded but the payload was not valid JSON in the expected shape. Try again, or file an issue with the error text.`}}catch(e){return{kind:`error`,message:`LLM request failed: ${e instanceof Error?e.message:String(e)}`}}}function _i(e){return[`You are a diagnostic assistant for KickJS, a decorator-driven Node.js`,`framework built on Express 5 and TypeScript. KickJS projects use:`,` - @Controller, @Get, @Post, @Autowired, @Service, @Value decorators`,` - An AppModule interface with a routes() method (NOT a @Module decorator)`,` - Zod schemas as both runtime validators and OpenAPI sources`,` - Ctx<KickRoutes.ControllerName['method']> for typed request context`,` - src/config/index.ts with defineEnv/loadEnv for env schema`,' - A side-effect `import "./config"` in src/index.ts to register the schema',` - Container.reset() in beforeEach for DI test isolation`,``,`When the user gives you an error message or stack trace, produce a`,`structured diagnosis that helps them fix the bug. You MUST respond`,`with a single JSON object (no surrounding prose, no markdown fences)`,`matching this shape:`,``,`{`,` "id": "<kebab-case-identifier>",`,` "title": "<one-line problem summary>",`,` "explanation": "<multi-line explanation of what is wrong>",`,` "fix": "<multi-line instructions for fixing the problem>",`,` "codeBefore": "<optional: broken code snippet>",`,` "codeAfter": "<optional: corrected code snippet>",`,` "docs": "<optional: KickJS doc URL that discusses this topic>"`,`}`,``,`The KickJS docs live at https://forinda.github.io/kick-js/ — prefer`,`that domain for any doc links you suggest.`,e?`The project is located at ${e}.`:``].filter(e=>e.length>0).join(`
3784
+ `)}function vi(e){let t=[e,yi(e),bi(e)].filter(e=>e!==null);for(let e of t)try{let t=JSON.parse(e);if(xi(t))return t}catch{continue}return null}function yi(e){let t=e.match(/```(?:json)?\s*\n([\s\S]*?)```/);return t?t[1]?.trim()??null:null}function bi(e){let t=e.indexOf(`{`);if(t===-1)return null;let n=0,r=!1,i=!1;for(let a=t;a<e.length;a++){let o=e[a];if(i){i=!1;continue}if(o===`\\`&&r){i=!0;continue}if(o===`"`){r=!r;continue}if(!r&&(o===`{`&&n++,o===`}`&&(n--,n===0)))return e.slice(t,a+1)}return null}function xi(e){if(typeof e!=`object`||!e)return!1;let t=e;return typeof t.id==`string`&&typeof t.title==`string`&&typeof t.explanation==`string`&&typeof t.fix==`string`}function Si(e){e.command(`explain [message]`).description(`Explain a KickJS error and suggest a fix`).option(`-m, --message <text>`,`Error message to explain (alternative to positional arg)`).option(`--ai`,`Fall back to LLM if no known-issue matches (requires @forinda/kickjs-ai)`).option(`--model <name>`,`Model name for the --ai fallback`,`gpt-4o-mini`).option(`--json`,`Output the diagnosis as JSON for tooling integration`).action(async(e,t)=>{let n=await Ti(e,t.message);(!n||n.trim().length===0)&&(process.stderr.write(`Error: no input provided.
3584
3785
 
3585
3786
  Pass a message as a positional arg, --message flag, or pipe via stdin:
3586
3787
  kick explain "config.get returned undefined"
3587
3788
  pnpm test 2>&1 | kick explain
3588
- `),process.exit(1));let r=ti(),i=Ur(n,r);if(t.json&&i){process.stdout.write(JSON.stringify({matched:!0,...i},null,2)+`
3589
- `);return}if(i){ni(n,i.diagnosis,i.confidence);return}t.ai||(t.json&&(process.stdout.write(JSON.stringify({matched:!1},null,2)+`
3590
- `),process.exit(2)),ri(n,!1),process.exit(2));let a=await Wr({input:n,model:t.model,cwd:r.cwd});t.json&&(process.stdout.write(JSON.stringify(Zr(a),null,2)+`
3591
- `),process.exit(a.kind===`ok`?0:2)),Qr(n,a),process.exit(a.kind===`ok`?0:2)})}function Zr(e){return e.kind===`ok`?{matched:!0,source:`ai`,diagnosis:e.diagnosis}:e.kind===`unavailable`?{matched:!1,aiUnavailable:!0,reason:e.reason}:{matched:!1,aiError:!0,error:e.message}}function Qr(e,t){if(t.kind===`ok`){ni(e,t.diagnosis,-1,!0);return}if(t.kind===`unavailable`){process.stdout.write(`\n Explaining: ${Z(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback unavailable: ${t.reason}\n\n`),process.stdout.write(`${X(t.suggestion,` `)}\n\n`);return}process.stdout.write(`\n Explaining: ${Z(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback error: ${t.message}\n\n`)}async function $r(e,t){return e&&e.trim().length>0?e:t&&t.trim().length>0?t:process.stdin.isTTY?``:ei()}function ei(){return new Promise((e,t)=>{let n=``;process.stdin.setEncoding(`utf8`),process.stdin.on(`data`,e=>{n+=e}),process.stdin.on(`end`,()=>e(n)),process.stdin.on(`error`,t)})}function ti(){let e=process.cwd();return{cwd:e,hasFile:t=>h(S(e,t))}}function ni(e,t,n,r=!1){let i=Z(e.trim(),200),a=r?`AI-generated — verify before applying`:ii(n);process.stdout.write(`\n Explaining: ${i}\n`),process.stdout.write(`\n Match: ${t.id} (${a})\n`),process.stdout.write(` Title: ${t.title}\n`),process.stdout.write(`\n Diagnosis:\n${X(t.explanation,` `)}\n`),process.stdout.write(`\n Fix:\n${X(t.fix,` `)}\n`),t.codeBefore&&process.stdout.write(`\n Before:\n${X(t.codeBefore,` `)}\n`),t.codeAfter&&process.stdout.write(`\n After:\n${X(t.codeAfter,` `)}\n`),t.docs&&process.stdout.write(`\n Docs: ${t.docs}\n`),process.stdout.write(`
3592
- `)}function ri(e,t){let n=Z(e.trim(),200);process.stdout.write(`\n Explaining: ${n}\n\n`),t?process.stdout.write(` No known-issue matched, and --ai fallback is not yet wired.
3789
+ `),process.exit(1));let r=Di(),i=hi(n,r);if(t.json&&i){process.stdout.write(JSON.stringify({matched:!0,...i},null,2)+`
3790
+ `);return}if(i){Oi(n,i.diagnosis,i.confidence);return}t.ai||(t.json&&(process.stdout.write(JSON.stringify({matched:!1},null,2)+`
3791
+ `),process.exit(2)),ki(n,!1),process.exit(2));let a=await gi({input:n,model:t.model,cwd:r.cwd});t.json&&(process.stdout.write(JSON.stringify(Ci(a),null,2)+`
3792
+ `),process.exit(a.kind===`ok`?0:2)),wi(n,a),process.exit(a.kind===`ok`?0:2)})}function Ci(e){return e.kind===`ok`?{matched:!0,source:`ai`,diagnosis:e.diagnosis}:e.kind===`unavailable`?{matched:!1,aiUnavailable:!0,reason:e.reason}:{matched:!1,aiError:!0,error:e.message}}function wi(e,t){if(t.kind===`ok`){Oi(e,t.diagnosis,-1,!0);return}if(t.kind===`unavailable`){process.stdout.write(`\n Explaining: ${ji(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback unavailable: ${t.reason}\n\n`),process.stdout.write(`${Ai(t.suggestion,` `)}\n\n`);return}process.stdout.write(`\n Explaining: ${ji(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback error: ${t.message}\n\n`)}async function Ti(e,t){return e&&e.trim().length>0?e:t&&t.trim().length>0?t:process.stdin.isTTY?``:Ei()}function Ei(){return new Promise((e,t)=>{let n=``;process.stdin.setEncoding(`utf8`),process.stdin.on(`data`,e=>{n+=e}),process.stdin.on(`end`,()=>e(n)),process.stdin.on(`error`,t)})}function Di(){let e=process.cwd();return{cwd:e,hasFile:t=>h(C(e,t))}}function Oi(e,t,n,r=!1){let i=ji(e.trim(),200),a=r?`AI-generated — verify before applying`:Mi(n);process.stdout.write(`\n Explaining: ${i}\n`),process.stdout.write(`\n Match: ${t.id} (${a})\n`),process.stdout.write(` Title: ${t.title}\n`),process.stdout.write(`\n Diagnosis:\n${Ai(t.explanation,` `)}\n`),process.stdout.write(`\n Fix:\n${Ai(t.fix,` `)}\n`),t.codeBefore&&process.stdout.write(`\n Before:\n${Ai(t.codeBefore,` `)}\n`),t.codeAfter&&process.stdout.write(`\n After:\n${Ai(t.codeAfter,` `)}\n`),t.docs&&process.stdout.write(`\n Docs: ${t.docs}\n`),process.stdout.write(`
3793
+ `)}function ki(e,t){let n=ji(e.trim(),200);process.stdout.write(`\n Explaining: ${n}\n\n`),t?process.stdout.write(` No known-issue matched, and --ai fallback is not yet wired.
3593
3794
  When @forinda/kickjs-ai ships its provider implementations,
3594
3795
  this command will call the configured LLM with the error +
3595
3796
  project context and return a structured fix.
@@ -3606,12 +3807,12 @@ Pass a message as a positional arg, --message flag, or pipe via stdin:
3606
3807
  3. File an issue with the error text:
3607
3808
  https://github.com/forinda/kick-js/issues/new
3608
3809
 
3609
- `)}function X(e,t){return e.split(`
3810
+ `)}function Ai(e,t){return e.split(`
3610
3811
  `).map(e=>`${t}${e}`).join(`
3611
- `)}function Z(e,t){return e.length<=t?e:e.slice(0,t-1)+`…`}function ii(e){return e>=90?`high confidence`:e>=70?`good match`:e>=50?`medium confidence`:`low confidence — verify manually`}function ai(e){let t=e.command(`mcp`).description(`Model Context Protocol commands (start | init)`);t.command(`start`,{isDefault:!0}).description(`Run the built application as an MCP server over stdio`).option(`-e, --entry <file>`,`Entry file`,`dist/index.js`).option(`--node-arg <arg...>`,`Extra arguments to pass to node`).action(oi),t.command(`init`).description(`Generate .mcp.json for Claude Code / Cursor / Zed`).option(`-n, --name <name>`,`Server name (defaults to package.json name)`).option(`-o, --out <file>`,`Output file`,`.mcp.json`).option(`-f, --force`,`Overwrite an existing entry without prompting`).option(`--global`,`Write to ~/.mcp.json instead of the project root`).action(si)}function oi(e){let t=S(e.entry);h(t)||(process.stderr.write(`Error: entry file not found: ${t}\n\nBuild the app first with \`kick build\`, or pass a custom entry:\n kick mcp -e dist/server.js\n`),process.exit(1));let n=[...e.nodeArg??[],t],r=ue(process.execPath,n,{stdio:`inherit`,env:{...process.env,KICK_MCP_STDIO:`1`,NODE_ENV:process.env.NODE_ENV??`production`}});r.on(`error`,e=>{process.stderr.write(`Failed to start MCP server: ${e.message}\n`),process.exit(1)}),r.on(`exit`,(e,t)=>{if(t){process.kill(process.pid,t);return}process.exit(e??0)});let i=e=>{r.killed||r.kill(e)};process.on(`SIGINT`,()=>i(`SIGINT`)),process.on(`SIGTERM`,()=>i(`SIGTERM`))}function si(e){let t=process.cwd(),n=ci(t)??ie(t),r=e.name??n,i=e.global?S(process.env.HOME??`.`,`.mcp.json`):S(t,e.out),a={command:`kick`,args:[`mcp`],cwd:t},o={mcpServers:{}};if(h(i))try{let e=_(i,`utf8`),t=JSON.parse(e);t&&typeof t==`object`&&t.mcpServers&&(o={mcpServers:{...t.mcpServers}})}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`Error: existing ${i} is not valid JSON (${t}).\nFix the file or pass --force to overwrite the entry.\n`),process.exit(1)}o.mcpServers[r]&&!e.force&&(process.stderr.write(`Error: an entry for "${r}" already exists in ${i}.\nPass --force to overwrite it, or use --name to pick a different key.\n`),process.exit(1)),o.mcpServers[r]=a,re(i,JSON.stringify(o,null,2)+`
3612
- `,`utf8`),process.stdout.write(`\n ✓ Wrote MCP server entry "${r}" to ${i}\n\n To activate it:\n 1. Build your app: kick build\n 2. Restart your MCP client (Claude Code, Cursor, Zed)\n 3. The server should appear in the client's tool picker\n\n`)}function ci(e){let t=S(e,`package.json`);if(!h(t))return null;try{let e=_(t,`utf8`),n=JSON.parse(e);return typeof n.name==`string`?n.name:null}catch{return null}}function li(e){e.command(`tinker`).description(`Interactive REPL with DI container and services loaded`).option(`-e, --entry <file>`,`Entry file to load`,`src/index.ts`).action(async e=>{let t=process.cwd(),n=S(t,e.entry);h(n)||(console.error(`\n Error: ${e.entry} not found.\n`),process.exit(1));let r=di(t,`tsx`);r||(console.error(`
3812
+ `)}function ji(e,t){return e.length<=t?e:e.slice(0,t-1)+`…`}function Mi(e){return e>=90?`high confidence`:e>=70?`good match`:e>=50?`medium confidence`:`low confidence — verify manually`}function Ni(e){let t=e.command(`mcp`).description(`Model Context Protocol commands (start | init)`);t.command(`start`,{isDefault:!0}).description(`Run the built application as an MCP server over stdio`).option(`-e, --entry <file>`,`Entry file`,`dist/index.js`).option(`--node-arg <arg...>`,`Extra arguments to pass to node`).action(Pi),t.command(`init`).description(`Generate .mcp.json for Claude Code / Cursor / Zed`).option(`-n, --name <name>`,`Server name (defaults to package.json name)`).option(`-o, --out <file>`,`Output file`,`.mcp.json`).option(`-f, --force`,`Overwrite an existing entry without prompting`).option(`--global`,`Write to ~/.mcp.json instead of the project root`).action(Fi)}function Pi(e){let t=C(e.entry);h(t)||(process.stderr.write(`Error: entry file not found: ${t}\n\nBuild the app first with \`kick build\`, or pass a custom entry:\n kick mcp -e dist/server.js\n`),process.exit(1));let n=[...e.nodeArg??[],t],r=de(process.execPath,n,{stdio:`inherit`,env:{...process.env,KICK_MCP_STDIO:`1`,NODE_ENV:process.env.NODE_ENV??`production`}});r.on(`error`,e=>{process.stderr.write(`Failed to start MCP server: ${e.message}\n`),process.exit(1)}),r.on(`exit`,(e,t)=>{if(t){process.kill(process.pid,t);return}process.exit(e??0)});let i=e=>{r.killed||r.kill(e)};process.on(`SIGINT`,()=>i(`SIGINT`)),process.on(`SIGTERM`,()=>i(`SIGTERM`))}function Fi(e){let t=process.cwd(),n=Ii(t)??re(t),r=e.name??n,i=e.global?C(process.env.HOME??`.`,`.mcp.json`):C(t,e.out),a={command:`kick`,args:[`mcp`],cwd:t},o={mcpServers:{}};if(h(i))try{let e=_(i,`utf8`),t=JSON.parse(e);t&&typeof t==`object`&&t.mcpServers&&(o={mcpServers:{...t.mcpServers}})}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`Error: existing ${i} is not valid JSON (${t}).\nFix the file or pass --force to overwrite the entry.\n`),process.exit(1)}o.mcpServers[r]&&!e.force&&(process.stderr.write(`Error: an entry for "${r}" already exists in ${i}.\nPass --force to overwrite it, or use --name to pick a different key.\n`),process.exit(1)),o.mcpServers[r]=a,ne(i,JSON.stringify(o,null,2)+`
3813
+ `,`utf8`),process.stdout.write(`\n ✓ Wrote MCP server entry "${r}" to ${i}\n\n To activate it:\n 1. Build your app: kick build\n 2. Restart your MCP client (Claude Code, Cursor, Zed)\n 3. The server should appear in the client's tool picker\n\n`)}function Ii(e){let t=C(e,`package.json`);if(!h(t))return null;try{let e=_(t,`utf8`),n=JSON.parse(e);return typeof n.name==`string`?n.name:null}catch{return null}}function Li(e){e.command(`tinker`).description(`Interactive REPL with DI container and services loaded`).option(`-e, --entry <file>`,`Entry file to load`,`src/index.ts`).action(async e=>{let t=process.cwd(),n=C(t,e.entry);h(n)||(console.error(`\n Error: ${e.entry} not found.\n`),process.exit(1));let r=zi(t,`tsx`);r||(console.error(`
3613
3814
  Error: tsx not found. Install it: pnpm add -D tsx
3614
- `),process.exit(1));let i=ui(n,e.entry),a=b(t,`.kick-tinker.mjs`),{writeFileSync:o,unlinkSync:s}=await import(`node:fs`);o(a,i,`utf-8`);try{let e=le(a,[],{cwd:t,execPath:r,stdio:`inherit`});await new Promise(t=>{e.on(`exit`,()=>t())})}finally{try{s(a)}catch{}}})}function ui(e,t){return`
3815
+ `),process.exit(1));let i=Ri(n,e.entry),a=x(t,`.kick-tinker.mjs`),{writeFileSync:o,unlinkSync:s}=await import(`node:fs`);o(a,i,`utf-8`);try{let e=ue(a,[],{cwd:t,execPath:r,stdio:`inherit`});await new Promise(t=>{e.on(`exit`,()=>t())})}finally{try{s(a)}catch{}}})}function Ri(e,t){return`
3615
3816
  import 'reflect-metadata'
3616
3817
 
3617
3818
  // Prevent bootstrap() from starting the HTTP server
@@ -3636,7 +3837,7 @@ try {
3636
3837
 
3637
3838
  // Load entry to trigger decorator registration
3638
3839
  try {
3639
- await import('${C(e).href}')
3840
+ await import('${ce(e).href}')
3640
3841
  } catch (err) {
3641
3842
  console.warn(' Warning: ' + err.message)
3642
3843
  console.warn(' Container may be partially initialized.\\n')
@@ -3665,21 +3866,32 @@ server.on('exit', () => {
3665
3866
  console.log('\\n Goodbye!\\n')
3666
3867
  process.exit(0)
3667
3868
  })
3668
- `}function di(e,t){let n=e;for(;;){let e=b(n,`node_modules`,`.bin`,t);if(h(e))return e;let r=S(n,`..`);if(r===n)break;n=r}return null}async function fi(e){let{name:t,modulesDir:n,force:r}=e,i=e.pluralize!==!1,a=B(t),o=R(t),s=i?V(a):a,c=b(n,s);if(!await N(c)){console.log(`\n Module not found: ${c}\n`);return}if(!r&&!await F({message:O.red(`Delete module '${s}' at ${c}? This cannot be undone.`),initialValue:!1})){console.log(`
3869
+ `}function zi(e,t){let n=e;for(;;){let e=x(n,`node_modules`,`.bin`,t);if(h(e))return e;let r=C(n,`..`);if(r===n)break;n=r}return null}function Bi(e,t){let n=RegExp(`^\\s*${V(t)}Module\\b`),r=!1,i=0,a=e;for(;;){let e=a.indexOf(`.mount(`,i);if(e===-1)break;let t=e+7,o=1,s=t;for(;s<a.length&&o>0;){let e=a.slice(s,s+2);if(e===`//`||e===`/*`){if(e===`//`)for(s+=2;s<a.length&&a[s]!==`
3870
+ `;)s++;else{for(s+=2;s+1<a.length&&!(a[s]===`*`&&a[s+1]===`/`);)s++;s+=2}continue}let t=a[s]??``;if(t===`'`||t===`"`||t==="`"){let e=t;for(s++;s<a.length&&a[s]!==e;)a[s]===`\\`&&s++,s++}else if(t===`(`)o++;else if(t===`)`&&(o--,o===0))break;s++}if(o!==0)break;let c=a.slice(t,s);if(n.test(c)){let t=e;for(;t>0&&(a[t-1]===` `||a[t-1]===` `||a[t-1]===`
3871
+ `);)t--;a=a.slice(0,t)+a.slice(s+1),r=!0,i=t;continue}i=s+1}return{content:a,changed:r}}function Vi(e,t){let n=Ln(e);if(!n)return e;let r=n.rhsStart,i=n.rhsEnd+1,a=e.slice(r,i);return a=Bi(a,t).content,a=a.replace(RegExp(`\\s*,?\\s*${V(t)}Module\\b(?:\\s*\\(\\s*\\))?\\s*,?`,`g`),e=>{let t=e.trimStart().startsWith(`,`),n=e.trimEnd().endsWith(`,`);return t&&n?`,`:``}),a=a.replace(/,(\s*])/,`$1`),e.slice(0,r)+a+e.slice(i)}async function Hi(e){let{name:t,modulesDir:n,force:r}=e,i=e.pluralize!==!1,a=z(t),o=L(t),s=i?B(a):a,c=x(n,s);if(!await Be(c)){console.log(`\n Module not found: ${c}\n`);return}if(!r&&!await P({message:O.red(`Delete module '${s}' at ${c}? This cannot be undone.`),initialValue:!1})){console.log(`
3669
3872
  Cancelled.
3670
- `);return}await me(c,{recursive:!0,force:!0}),console.log(` Deleted: ${c}`);let l=b(n,`index.ts`);if(await N(l)){let e=await T(l,`utf-8`),t=e,n=RegExp(`^import\\s*\\{\\s*${o}Module\\s*\\}\\s*from\\s*['"][^'"]*${s}(?:/[^'"]*)?['"].*\\n?`,`gm`);e=e.replace(n,``),e=e.replace(RegExp(`\\s*,?\\s*${o}Module\\s*,?`,`g`),e=>{let t=e.trimStart().startsWith(`,`),n=e.trimEnd().endsWith(`,`);return t&&n?`,`:``}),e=e.replace(/,(\s*])/,`$1`),e=e.replace(/\n{3,}/g,`
3873
+ `);return}await _e(c,{recursive:!0,force:!0}),console.log(` Deleted: ${c}`);let l=x(n,`index.ts`);if(await Be(l)){let e=await T(l,`utf-8`),t=e,n=RegExp(`^import\\s*\\{\\s*${V(o)}Module\\s*\\}\\s*from\\s*['"][^'"]*${V(s)}(?:/[^'"]*)?['"].*\\n?`,`gm`);e=e.replace(n,``),e=Vi(e,o),e=e.replace(/\n{3,}/g,`
3671
3874
 
3672
- `),e!==t&&(await E(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function pi(e){e.command(`remove`).alias(`rm`).description(`Remove generated code`).command(`module <names...>`).description(`Remove one or more modules (e.g. kick rm module user task)`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular module name`).option(`-f, --force`,`Skip confirmation prompt`).action(async(e,t)=>{let i=n(await r(process.cwd())),a=t.modulesDir??i.dir??`src/modules`,o=t.pluralize===!1?!1:i.pluralize??!0;for(let n of e)await fi({name:n,modulesDir:S(a),force:t.force,pluralize:o})})}function mi(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;console.warn(` kick typegen: unknown --schema-validator '${e}' (only 'zod' and 'false' are supported). Falling back to project config.`)}}function hi(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function gi(e){e.command(`typegen`).description(`Generate type-safe DI registry and module types into .kickjs/types/`).option(`-w, --watch`,`Watch source files and regenerate on change`).option(`-s, --src <dir>`,`Source directory to scan`,`src`).option(`-o, --out <dir>`,`Output directory`,`.kickjs/types`).option(`--silent`,`Suppress output`).option(`--allow-duplicates`,`Auto-namespace duplicate class names instead of failing (use with caution)`).option(`--schema-validator <name>`,`Schema validator for body/query/params typing (currently 'zod' or 'false')`).option(`--env-file <path>`,`Path to env schema file for KickEnv typing (default 'src/env.ts'; pass 'false' to disable)`).option(`--check`,`CI gate: fail on plugin-typegen drift instead of writing`).option(`--list`,"List every registered typegen plugin id (use to populate `typegen.disable`)").action(async e=>{let t=process.cwd(),n=await r(t);if(e.list){let{mergeCliPlugins:e}=await import(`./plugin-B56zClEX.mjs`).then(e=>e.t),{builtinCliPlugins:t}=await Promise.resolve().then(()=>Xi),r=e([...t,...n?.plugins??[]],n?.commands??[]),i=new Set(n?.typegen?.disable??[]);if(r.typegens.length===0){console.log(` No typegen plugins registered.`);return}let a=Math.max(...r.typegens.map(e=>e.id.length));console.log(`
3875
+ `),e!==t&&(await E(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function Ui(e){e.command(`remove`).alias(`rm`).description(`Remove generated code`).command(`module <names...>`).description(`Remove one or more modules (e.g. kick rm module user task)`).option(`--modules-dir <dir>`,`Modules directory`).option(`--no-pluralize`,`Use singular module name`).option(`-f, --force`,`Skip confirmation prompt`).action(async(e,t)=>{let i=n(await r(process.cwd())),a=t.modulesDir??i.dir??`src/modules`,o=t.pluralize===!1?!1:i.pluralize??!0;for(let n of e)await Hi({name:n,modulesDir:C(a),force:t.force,pluralize:o})})}function Wi(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;console.warn(` kick typegen: unknown --schema-validator '${e}' (only 'zod' and 'false' are supported). Falling back to project config.`)}}function Gi(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function Ki(e){e.command(`typegen`).description(`Generate type-safe DI registry and module types into .kickjs/types/`).option(`-w, --watch`,`Watch source files and regenerate on change`).option(`-s, --src <dir>`,`Source directory to scan`,`src`).option(`-o, --out <dir>`,`Output directory`,`.kickjs/types`).option(`--silent`,`Suppress output`).option(`--allow-duplicates`,`Auto-namespace duplicate class names instead of failing (use with caution)`).option(`--schema-validator <name>`,`Schema validator for body/query/params typing (currently 'zod' or 'false')`).option(`--env-file <path>`,`Path to env schema file for KickEnv typing (default 'src/env.ts'; pass 'false' to disable)`).option(`--check`,`CI gate: fail on plugin-typegen drift instead of writing`).option(`--list`,"List every registered typegen plugin id (use to populate `typegen.disable`)").action(async e=>{let t=process.cwd(),n=await r(t);if(e.list){let{mergeCliPlugins:e}=await import(`./plugin-D6nfJWY0.mjs`).then(e=>e.t),{builtinCliPlugins:t}=await Promise.resolve().then(()=>Oa),r=e([...t,...n?.plugins??[]],n?.commands??[]),i=new Set(n?.typegen?.disable??[]);if(r.typegens.length===0){console.log(` No typegen plugins registered.`);return}let a=Math.max(...r.typegens.map(e=>e.id.length));console.log(`
3673
3876
  Registered typegen plugins:
3674
- `);for(let e of r.typegens){let t=i.has(e.id)?` (disabled)`:``;console.log(` ${e.id.padEnd(a+2)}inputs: ${e.inputs.join(`, `)||`(none)`}${t}`)}console.log();return}let i=mi(e.schemaValidator)??n?.typegen?.schemaValidator??`zod`,a=hi(e.envFile)??n?.typegen?.envFile,o={cwd:t,srcDir:e.src??n?.typegen?.srcDir,outDir:e.out??n?.typegen?.outDir,silent:e.silent,allowDuplicates:e.allowDuplicates,schemaValidator:i,envFile:a,assetMap:n?.assetMap,runPlugins:!1};try{if(e.watch){let t=await u(o);e.silent||console.log(` kick typegen: watching for changes (Ctrl-C to exit)`);let n=()=>{t(),process.exit(0)};process.on(`SIGINT`,n),process.on(`SIGTERM`,n),await new Promise(()=>{})}else{await f(o);let r=await Sr({cwd:t,config:n??null,silent:e.silent,check:e.check});e.check&&r.some(e=>e.status===`written`)&&process.exit(1)}}catch(e){e instanceof c?console.error(`
3877
+ `);for(let e of r.typegens){let t=i.has(e.id)?` (disabled)`:``;console.log(` ${e.id.padEnd(a+2)}inputs: ${e.inputs.join(`, `)||`(none)`}${t}`)}console.log();return}let i=Wi(e.schemaValidator)??n?.typegen?.schemaValidator??`zod`,a=Gi(e.envFile)??n?.typegen?.envFile,o={cwd:t,srcDir:e.src??n?.typegen?.srcDir,outDir:e.out??n?.typegen?.outDir,silent:e.silent,allowDuplicates:e.allowDuplicates,schemaValidator:i,envFile:a,assetMap:n?.assetMap,runPlugins:!1};try{if(e.watch){let t=await u(o);e.silent||console.log(` kick typegen: watching for changes (Ctrl-C to exit)`);let n=()=>{t(),process.exit(0)};process.on(`SIGINT`,n),process.on(`SIGTERM`,n),await new Promise(()=>{})}else{await f(o);let r=await Jr({cwd:t,config:n??null,silent:e.silent,check:e.check});e.check&&r.some(e=>e.status===`written`)&&process.exit(1)}}catch(e){e instanceof c?console.error(`
3675
3878
  `+e.message+`
3676
- `):e instanceof Error?console.error(`\n kick typegen failed: ${e.message}`):console.error(`\n kick typegen failed: ${JSON.stringify(e)}`),process.exit(1)}})}function _i(e){let t=[];if(!h(e))return t;let n=ee(e,{withFileTypes:!0});for(let r of n){let n=b(e,r.name);if(r.isDirectory()){if([`node_modules`,`dist`,`.kickjs`,`.git`].includes(r.name))continue;t.push(..._i(n))}else r.isFile()&&/\.tsx?$/.test(r.name)&&!r.name.endsWith(`.d.ts`)&&t.push(n)}return t}function vi(e){try{return _(e,`utf-8`)}catch{return``}}const yi=new Set([`secret`,`changeme`,`password`,`test`,`default`,``]);function bi(e,t){let n=vi(b(e,`.env`));if(n){let e=n.match(/^JWT_SECRET\s*=\s*['"]?([^'"\n]*)['"]?/m);if(e){let t=e[1].trim();if(yi.has(t.toLowerCase())||t.length<32)return{severity:`CRITICAL`,message:`JWT_SECRET appears to be a default value or too short (< 32 chars) — change it`}}}for(let e of t)for(let t of[/JWT_SECRET['"]?\s*[:=]\s*['"]?(secret|changeme|password|test|default)['"]?/i,/secret\s*[:=]\s*['"]?(secret|changeme|password|test|default)['"]?/i])if(t.test(e))return{severity:`CRITICAL`,message:`JWT_SECRET appears to be a default value in source code — use an environment variable`};return null}function xi(e){for(let t of e)if(/cors\s*\(/.test(t)&&/origin\s*:\s*['"]\*['"]/.test(t))return{severity:`CRITICAL`,message:`CORS origin is '*' — restrict to your domains`};return null}function Si(e){for(let t of e)if(/rateLimit/i.test(t)||/@RateLimit/i.test(t))return null;return{severity:`WARNING`,message:`No rate limiting detected — add rateLimit() middleware or @RateLimit decorator`}}function Ci(){return process.env.NODE_ENV===`production`?null:{severity:`WARNING`,message:`NODE_ENV is '${process.env.NODE_ENV??`undefined`}', not 'production'`}}function wi(e){let t=!1,n=!1;for(let r of e)/tokenStore/i.test(r)&&(t=!0),/MemoryTokenStore/i.test(r)&&(n=!0);return n?{severity:`WARNING`,message:`MemoryTokenStore detected — use a persistent store (Redis, DB) for production deployments`}:t?null:{severity:`WARNING`,message:`No token revocation store detected — consider adding one for auth token management`}}function Ti(e){for(let t of e)if(/helmet\s*\(/.test(t))return/security\s*\.\s*helmet\s*.*false/.test(t)?{severity:`WARNING`,message:`Helmet security headers are disabled — enable them for production`}:{severity:`INFO`,message:`Helmet security headers active`};return{severity:`WARNING`,message:`Helmet not detected — add helmet() middleware for security headers`}}function Ei(e){for(let t of e)if(/AuthAdapter/i.test(t))return{severity:`INFO`,message:`AuthAdapter configured`};return{severity:`INFO`,message:`No AuthAdapter detected — add one if your app requires authentication`}}function Di(e){let t=_i(b(e,`src`)).map(e=>vi(e)),n=[],r=bi(e,t);r&&n.push(r);let i=xi(t);i&&n.push(i);let a=Si(t);a&&n.push(a);let o=Ci();o&&n.push(o);let s=wi(t);return s&&n.push(s),n.push(Ti(t)),n.push(Ei(t)),n}function Oi(e){e.command(`check`).description(`Audit project for common issues`).option(`--deploy`,`Run production readiness checks`).action(e=>{if(!e.deploy){console.log(`
3879
+ `):e instanceof Error?console.error(`\n kick typegen failed: ${e.message}`):console.error(`\n kick typegen failed: ${JSON.stringify(e)}`),process.exit(1)}})}function qi(e){let t=[];if(!h(e))return t;let n=ee(e,{withFileTypes:!0});for(let r of n){let n=x(e,r.name);if(r.isDirectory()){if([`node_modules`,`dist`,`.kickjs`,`.git`].includes(r.name))continue;t.push(...qi(n))}else r.isFile()&&/\.tsx?$/.test(r.name)&&!r.name.endsWith(`.d.ts`)&&t.push(n)}return t}function Ji(e){try{return _(e,`utf-8`)}catch{return``}}const Yi=new Set([`secret`,`changeme`,`password`,`test`,`default`,``]);function Xi(e,t){let n=Ji(x(e,`.env`));if(n){let e=n.match(/^JWT_SECRET\s*=\s*['"]?([^'"\n]*)['"]?/m);if(e){let t=e[1].trim();if(Yi.has(t.toLowerCase())||t.length<32)return{severity:`CRITICAL`,message:`JWT_SECRET appears to be a default value or too short (< 32 chars) — change it`}}}for(let e of t)for(let t of[/JWT_SECRET['"]?\s*[:=]\s*['"]?(secret|changeme|password|test|default)['"]?/i,/secret\s*[:=]\s*['"]?(secret|changeme|password|test|default)['"]?/i])if(t.test(e))return{severity:`CRITICAL`,message:`JWT_SECRET appears to be a default value in source code — use an environment variable`};return null}function Zi(e){for(let t of e)if(/cors\s*\(/.test(t)&&/origin\s*:\s*['"]\*['"]/.test(t))return{severity:`CRITICAL`,message:`CORS origin is '*' — restrict to your domains`};return null}function Qi(e){for(let t of e)if(/rateLimit/i.test(t)||/@RateLimit/i.test(t))return null;return{severity:`WARNING`,message:`No rate limiting detected — add rateLimit() middleware or @RateLimit decorator`}}function $i(){return process.env.NODE_ENV===`production`?null:{severity:`WARNING`,message:`NODE_ENV is '${process.env.NODE_ENV??`undefined`}', not 'production'`}}function ea(e){let t=!1,n=!1;for(let r of e)/tokenStore/i.test(r)&&(t=!0),/MemoryTokenStore/i.test(r)&&(n=!0);return n?{severity:`WARNING`,message:`MemoryTokenStore detected — use a persistent store (Redis, DB) for production deployments`}:t?null:{severity:`WARNING`,message:`No token revocation store detected — consider adding one for auth token management`}}function ta(e){for(let t of e)if(/helmet\s*\(/.test(t))return/security\s*\.\s*helmet\s*.*false/.test(t)?{severity:`WARNING`,message:`Helmet security headers are disabled — enable them for production`}:{severity:`INFO`,message:`Helmet security headers active`};return{severity:`WARNING`,message:`Helmet not detected — add helmet() middleware for security headers`}}function na(e){for(let t of e)if(/AuthAdapter/i.test(t))return{severity:`INFO`,message:`AuthAdapter configured`};return{severity:`INFO`,message:`No AuthAdapter detected — add one if your app requires authentication`}}function ra(e){let t=qi(x(e,`src`)).map(e=>Ji(e)),n=[],r=Xi(e,t);r&&n.push(r);let i=Zi(t);i&&n.push(i);let a=Qi(t);a&&n.push(a);let o=$i();o&&n.push(o);let s=ea(t);return s&&n.push(s),n.push(ta(t)),n.push(na(t)),n}function ia(e){e.command(`check`).description(`Audit project for common issues`).option(`--deploy`,`Run production readiness checks`).action(e=>{if(!e.deploy){console.log(`
3677
3880
  Usage: kick check --deploy
3678
3881
 
3679
3882
  Available checks:
3680
3883
  --deploy Audit for production readiness (security, config, best practices)
3681
- `);return}let t=process.cwd();dt(`KickJS Deploy Check`);let n=gt();n.start(`Scanning project...`);let r=Di(t);n.stop(`Scan complete`);let i={CRITICAL:0,WARNING:1,INFO:2};r.sort((e,t)=>i[e.severity]-i[t.severity]);for(let e of r)I.message(`${ut(e.severity)} ${e.message}`);let a=r.filter(e=>e.severity===`CRITICAL`).length,o=r.filter(e=>e.severity===`WARNING`).length,s=r.filter(e=>e.severity===`INFO`).length,c=o===1?`warning`:`warnings`,l=[a>0?O.red(`${a} critical`):`${a} critical`,o>0?O.yellow(`${o} ${c}`):`${o} ${c}`,`${s} info`].join(`, `);a>0?(P(O.red(`${l} — fix critical issues before deploying`)),process.exit(1)):P(O.green(`${l} — looking good!`))})}async function Q(e){return Oe({configPath:v.resolve(process.cwd(),e.config)})}async function $(e){if(e.adapter){let t=await e.adapter();return{adapter:t,cleanup:async()=>t.close()}}if(!e.connectionString)throw Error(`kickjs-db: no adapter resolved — set db.connectionString (or DATABASE_URL) in kick.config.ts, or supply db.adapter() factory`);let t=e.dialect??`postgres`;if(t!==`postgres`)throw Error(`kickjs-db: built-in CLI adapter only supports postgres in M1 (dialect=${t}); use db.adapter() factory for other dialects`);let[{pgAdapter:n},r]=await Promise.all([import(`@forinda/kickjs-db-pg`),import(`pg`)]),i=new r.default.Pool({connectionString:e.connectionString}),a=n({pool:i});return{adapter:a,cleanup:async()=>{await a.close(),await i.end()}}}function ki(e){if(e.length===0){console.log(`No migrations.`);return}console.table(e.map(e=>({id:e.id,state:e.state,batch:e.batch??`-`,reviewed:e.reviewed,applied:e.appliedAt??`-`})))}function Ai(e){let t=e.command(`db`).description(`Database commands (kickjs-db)`);t.command(`generate <name>`).description(`Generate a new migration from schema diff`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`-e, --empty`,`Skip schema diff and create an empty migration shell (data migration, seed, freeform SQL)`).action(async(e,t)=>{let n=process.cwd(),r=await xe({name:e,config:await Q(t),cwd:n,empty:t.empty});if(r.status===`no-changes`){console.log(`No schema changes detected.`);return}if(r.empty){console.log(`Created empty migration ${r.migrationDir} (author up.sql + down.sql).`);return}let i=r.changeCount===1?``:`s`;console.log(`Created migration ${r.migrationDir} (${r.changeCount} change${i}).`)});let n=t.command(`migrate`).description(`Migration runner subcommands`);n.command(`latest`).description(`Apply all pending migrations in a new batch`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await Ce({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied batch ${r.batch}: ${r.applied.join(`, `)}`)}finally{await r()}}),n.command(`up`).description(`Apply the next single pending migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await Ee({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied ${r.applied[0]} (batch ${r.batch})`)}finally{await r()}}),n.command(`down`).description(`Reverse the most recent applied migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await Se({adapter:n,migrationsDir:t.migrationsDir});e.reversed?console.log(`Reversed ${e.reversed}.`):console.log(`Nothing to reverse.`)}finally{await r()}}),n.command(`rollback`).description(`Reverse the entire last batch as a single unit`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await we({adapter:n,migrationsDir:t.migrationsDir});e.reversed.length===0?console.log(`Nothing to roll back.`):console.log(`Rolled back batch ${e.batch}: ${e.reversed.join(`, `)}`)}finally{await r()}}),n.command(`status`).description(`Print applied + pending migrations`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{ki(await Te({adapter:n,migrationsDir:t.migrationsDir}))}finally{await r()}}),t.command(`introspect`).description(`Generate a TypeScript schema file from a live database`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--out <path>`,`Output file (defaults to db.schemaPath from config)`).option(`--json`,`Print the raw SchemaSnapshot JSON to stdout instead of writing TS source`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await n.introspect();if(e.json){console.log(JSON.stringify(r,null,2));return}let i=e.out??t.schemaPath;await E(i,De(r),`utf8`);let a=Object.keys(r.tables).length;console.log(`Wrote ${i} (${a} table${a===1?``:`s`}).`)}finally{await r()}})}const ji=[`src/db/schema.ts`,`src/db/schema/index.ts`,`src/db/schema`],Mi=()=>({id:`kick/db`,inputs:[`src/db/schema.ts`,`src/db/schema/**/*.ts`],async generate(e){let t=Ni(e.cwd);if(!t)return null;let n=v.resolve(e.cwd,`.kickjs/types`);return[`import type { SchemaToTypes, SchemaToRelationsRegister, KickDbClient } from '@forinda/kickjs-db'`,`import type * as appSchema from '${Pi(v.relative(n,t)).replace(/\.ts$/,``).replace(/\/index$/,``)}'`,``,`declare global {`,` interface KickDbSchema extends SchemaToTypes<typeof appSchema> {}`,`}`,``,`declare module '@forinda/kickjs-db' {`,` interface KickDbRegister {`,` db: KickDbClient<KickDbSchema>`,` }`,``,` interface KickDbRelationsRegister {`,` db: SchemaToRelationsRegister<typeof appSchema>`,` }`,`}`].join(`
3682
- `)}});function Ni(e){for(let t of ji){let n=v.resolve(e,t);if(t.endsWith(`.ts`)){if(h(n))return n}else{let e=v.join(n,`index.ts`);if(h(e))return e}}return null}function Pi(e){return e.replace(/\\/g,`/`)}const Fi=()=>({id:`kick/assets`,inputs:[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`],async generate(e){if(!h(v.resolve(e.cwd,`kick.config.ts`)))return null;let t=await r(e.cwd);if(!t?.assetMap)return null;let n=s(t.assetMap,e.cwd);return n.count===0?null:l(n)}}),Ii="/* eslint-disable */\n// AUTO-GENERATED by `kick typegen`. DO NOT EDIT.\n// Re-run with `kick typegen` or rely on `kick dev` to refresh.\n";function Li(e,t,n){if(e.length===0)return`${Ii}
3884
+ `);return}let t=process.cwd();yt(`KickJS Deploy Check`);let n=wt();n.start(`Scanning project...`);let r=ra(t);n.stop(`Scan complete`);let i={CRITICAL:0,WARNING:1,INFO:2};r.sort((e,t)=>i[e.severity]-i[t.severity]);for(let e of r)F.message(`${vt(e.severity)} ${e.message}`);let a=r.filter(e=>e.severity===`CRITICAL`).length,o=r.filter(e=>e.severity===`WARNING`).length,s=r.filter(e=>e.severity===`INFO`).length,c=o===1?`warning`:`warnings`,l=[a>0?O.red(`${a} critical`):`${a} critical`,o>0?O.yellow(`${o} ${c}`):`${o} ${c}`,`${s} info`].join(`, `);a>0?(N(O.red(`${l} — fix critical issues before deploying`)),process.exit(1)):N(O.green(`${l} — looking good!`))})}async function Q(e){return Me({configPath:y.resolve(process.cwd(),e.config)})}async function $(e){if(e.adapter){let t=await e.adapter();return{adapter:t,cleanup:async()=>t.close()}}if(!e.connectionString)throw Error(`kickjs-db: no adapter resolved — set db.connectionString (or DATABASE_URL) in kick.config.ts, or supply db.adapter() factory`);let t=e.dialect??`postgres`;if(t!==`postgres`)throw Error(`kickjs-db: built-in CLI adapter only supports postgres in M1 (dialect=${t}); use db.adapter() factory for other dialects`);let[{pgAdapter:n},r]=await Promise.all([import(`@forinda/kickjs-db-pg`),import(`pg`)]),i=new r.default.Pool({connectionString:e.connectionString}),a=n({pool:i});return{adapter:a,cleanup:async()=>{await a.close(),await i.end()}}}function aa(e){if(e.length===0){console.log(`No migrations.`);return}console.table(e.map(e=>({id:e.id,state:e.state,batch:e.batch??`-`,reviewed:e.reviewed,applied:e.appliedAt??`-`})))}function oa(e){let t=e.command(`db`).description(`Database commands (kickjs-db)`);t.command(`generate <name>`).description(`Generate a new migration from schema diff`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`-e, --empty`,`Skip schema diff and create an empty migration shell (data migration, seed, freeform SQL)`).action(async(e,t)=>{let n=process.cwd(),r=await Te({name:e,config:await Q(t),cwd:n,empty:t.empty});if(r.status===`no-changes`){console.log(`No schema changes detected.`);return}if(r.empty){console.log(`Created empty migration ${r.migrationDir} (author up.sql + down.sql).`);return}let i=r.changeCount===1?``:`s`;console.log(`Created migration ${r.migrationDir} (${r.changeCount} change${i}).`)});let n=t.command(`migrate`).description(`Migration runner subcommands`);n.command(`latest`).description(`Apply all pending migrations in a new batch`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await De({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied batch ${r.batch}: ${r.applied.join(`, `)}`)}finally{await r()}}),n.command(`up`).description(`Apply the next single pending migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--confirm-enum-drop`,"Allow migrations carrying the `-- KICK ENUM REMOVE` header to apply",!1).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await Ae({adapter:n,migrationsDir:t.migrationsDir,confirmEnumDrop:e.confirmEnumDrop});r.applied.length===0?console.log(`No pending migrations.`):console.log(`Applied ${r.applied[0]} (batch ${r.batch})`)}finally{await r()}}),n.command(`down`).description(`Reverse the most recent applied migration`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await Ee({adapter:n,migrationsDir:t.migrationsDir});e.reversed?console.log(`Reversed ${e.reversed}.`):console.log(`Nothing to reverse.`)}finally{await r()}}),n.command(`rollback`).description(`Reverse the entire last batch as a single unit`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let e=await Oe({adapter:n,migrationsDir:t.migrationsDir});e.reversed.length===0?console.log(`Nothing to roll back.`):console.log(`Rolled back batch ${e.batch}: ${e.reversed.join(`, `)}`)}finally{await r()}}),n.command(`status`).description(`Print applied + pending migrations`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{aa(await ke({adapter:n,migrationsDir:t.migrationsDir}))}finally{await r()}}),t.command(`introspect`).description(`Generate a TypeScript schema file from a live database`).option(`-c, --config <path>`,`Path to kick.config.ts`,`kick.config.ts`).option(`--out <path>`,`Output file (defaults to db.schemaPath from config)`).option(`--json`,`Print the raw SchemaSnapshot JSON to stdout instead of writing TS source`).action(async e=>{let t=await Q(e),{adapter:n,cleanup:r}=await $(t);try{let r=await n.introspect();if(e.json){console.log(JSON.stringify(r,null,2));return}let i=e.out??t.schemaPath;await E(i,je(r),`utf8`);let a=Object.keys(r.tables).length;console.log(`Wrote ${i} (${a} table${a===1?``:`s`}).`)}finally{await r()}})}function sa(e){return e.optsWithGlobals().dryRun??!1}function ca(e){e.command(`codemod`).description(`Codebase migration commands (AST-style rewrites distinct from db migrate)`).command(`modules`).description(`Rewrite module declarations between class form and the defineModule factory.
3885
+ Direction defaults to \`modules.style\` from kick.config (or "define").
3886
+ --target define|class Override the migration direction.
3887
+ --apply Apply the changes (default: dry-run preview).
3888
+ --experimental Acknowledge that AST migration is experimental.`).option(`--modules-dir <dir>`,`Modules directory (default: src/modules from kick.config)`).option(`--apply`,`Apply the migration to disk (default: dry-run)`).option(`--experimental`,`Acknowledge that this command is experimental`).option(`--target <style>`,`Migration direction — 'define' or 'class'`).option(`--no-backup`,`Skip the .kickjs/codemod-backups/ snapshot (default: backup on)`).action(async(e,t)=>{let i=sa(t)||!e.apply;k(i),e.experimental||(console.error(`
3889
+ `+O.red(`Error:`)+` kick codemod modules is experimental — pass --experimental to acknowledge.
3890
+ The regex-based rewrite handles the shapes our templates produce.
3891
+ Hand-rolled modules with non-standard structures may be skipped.
3892
+ Always commit before running with --apply.
3893
+ `),process.exit(1));let a=n(await r(process.cwd())),o=C(e.modulesDir??a.dir??`src/modules`),s;e.target===`define`||e.target===`class`?s=e.target:e.target===void 0?s=a.style??`define`:(console.error(`\n ${O.red(`Error:`)} --target must be 'define' or 'class' (got '${e.target}').\n`),process.exit(1));let c=O.dim(`→ ${s}`),l=i?O.dim(`(dry-run)`):O.bold(`(applying)`);console.log(`\n ${O.bold(`kick codemod modules`)} ${c} ${l}`),console.log(` modulesDir: ${O.dim(o)}\n`);let u=e.backup!==!1&&!i,d=await mr(o,{dryRun:i,target:s,backup:u});if(d.backupDir){let e=d.backupDir;console.log(` ${O.green(`✓`)} backup: ${O.dim(e)}\n ${O.dim(`(restore: rm -rf <modulesDir> && mv "<backup>" <modulesDir>)`)}\n`)}else !i&&e.backup===!1&&console.log(` ${O.dim(`(--no-backup — skipping snapshot)`)}\n`);let f=0,p=0;for(let e of d.files)if(e.status===`migrated`)f++,console.log(` ${O.green(`✓`)} ${e.path}`);else{p++;let t=O.dim(`(${e.reason??`skipped`})`);console.log(` ${O.dim(`-`)} ${e.path} ${t}`)}if(console.log(),d.indexStatus===`migrated`)console.log(` ${O.green(`✓`)} ${d.indexPath}`);else if(d.indexStatus===`skipped`){let e=O.dim(`(${d.indexReason??`skipped`})`);console.log(` ${O.dim(`-`)} ${d.indexPath} ${e}`)}else console.log(` ${O.dim(`-`)} ${d.indexPath} ${O.dim(`(not found)`)}`);let m=i?O.dim(` (dry-run — pass --apply to write)`):``;console.log(`\n ${O.bold(String(f))} migrated, ${O.bold(String(p))} skipped${m}\n`)})}const la=[`src/db/schema.ts`,`src/db/schema/index.ts`,`src/db/schema`],ua=()=>({id:`kick/db`,inputs:[`src/db/schema.ts`,`src/db/schema/**/*.ts`],async generate(e){let t=da(e.cwd);if(!t)return null;let n=y.resolve(e.cwd,`.kickjs/types`);return[`import type { SchemaToTypes, SchemaToRelationsRegister, KickDbClient } from '@forinda/kickjs-db'`,`import type * as appSchema from '${fa(y.relative(n,t)).replace(/\.ts$/,``).replace(/\/index$/,``)}'`,``,`declare global {`,` interface KickDbSchema extends SchemaToTypes<typeof appSchema> {}`,`}`,``,`declare module '@forinda/kickjs-db' {`,` interface KickDbRegister {`,` db: KickDbClient<KickDbSchema>`,` }`,``,` interface KickDbRelationsRegister {`,` db: SchemaToRelationsRegister<typeof appSchema>`,` }`,`}`].join(`
3894
+ `)}});function da(e){for(let t of la){let n=y.resolve(e,t);if(t.endsWith(`.ts`)){if(h(n))return n}else{let e=y.join(n,`index.ts`);if(h(e))return e}}return null}function fa(e){return e.replace(/\\/g,`/`)}const pa=()=>({id:`kick/assets`,inputs:[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`],async generate(e){if(!h(y.resolve(e.cwd,`kick.config.ts`)))return null;let t=await r(e.cwd);if(!t?.assetMap)return null;let n=s(t.assetMap,e.cwd);return n.count===0?null:l(n)}}),ma="/* eslint-disable */\n// AUTO-GENERATED by `kick typegen`. DO NOT EDIT.\n// Re-run with `kick typegen` or rely on `kick dev` to refresh.\n";function ha(e,t,n){if(e.length===0)return`${ma}
3683
3895
  // (no routes discovered yet — annotate a controller method with
3684
3896
  // @Get/@Post/@Put/@Delete/@Patch and re-run \`kick typegen\`)
3685
3897
  declare global {
@@ -3688,8 +3900,8 @@ declare global {
3688
3900
  }
3689
3901
 
3690
3902
  export {}
3691
- `;let r=new Map;for(let t of e){let e=r.get(t.controller)??[];e.push(t),r.set(t.controller,e)}let i=new Map,a=(e,r)=>{let a=Bi(e,r,t,n,i);return a?`import('zod').infer<typeof ${a}>`:null},o=[];for(let[e,t]of r){let n=[` interface ${e} {`];for(let e of t){let t=e.pathParams.length>0?`{ ${e.pathParams.map(e=>`${e}: string`).join(`; `)} }`:`{}`,r=a(e.bodySchema,e.filePath),i=a(e.querySchema,e.filePath),o=a(e.paramsSchema,e.filePath)??t,s=r??`unknown`,c=i??Ri(e),l=zi(e);n.push(` /**`,` * ${e.httpMethod} ${e.path}`,...l.map(e=>` * ${e}`),` */`,` ${e.method}: {`,` params: ${o}`,` body: ${s}`,` query: ${c}`,` response: unknown`,` }`)}n.push(` }`),o.push(n.join(`
3692
- `))}return`${Ii}${Vi(i)}
3903
+ `;let r=new Map;for(let t of e){let e=r.get(t.controller)??[];e.push(t),r.set(t.controller,e)}let i=new Map,a=(e,r)=>{let a=va(e,r,t,n,i);return a?`import('zod').infer<typeof ${a}>`:null},o=[];for(let[e,t]of r){let n=[` interface ${e} {`];for(let e of t){let t=e.pathParams.length>0?`{ ${e.pathParams.map(e=>`${e}: string`).join(`; `)} }`:`{}`,r=a(e.bodySchema,e.filePath),i=a(e.querySchema,e.filePath),o=a(e.paramsSchema,e.filePath)??t,s=r??`unknown`,c=i??ga(e),l=_a(e);n.push(` /**`,` * ${e.httpMethod} ${e.path}`,...l.map(e=>` * ${e}`),` */`,` ${e.method}: {`,` params: ${o}`,` body: ${s}`,` query: ${c}`,` response: unknown`,` }`)}n.push(` }`),o.push(n.join(`
3904
+ `))}return`${ma}${ya(i)}
3693
3905
  declare global {
3694
3906
  // eslint-disable-next-line @typescript-eslint/no-namespace
3695
3907
  namespace KickRoutes {
@@ -3699,9 +3911,9 @@ ${o.join(`
3699
3911
  }
3700
3912
 
3701
3913
  export {}
3702
- `}function Ri(e){if(e.queryFilterable===null)return`unknown`;let t=e.querySortable??[];return`{ filter?: string | string[]; sort?: ${t.length>0?t.flatMap(e=>[`'${e}'`,`'-${e}'`]).join(` | `):`string`}; q?: string; page?: string; limit?: string }`}function zi(e){let t=[];return e.queryFilterable&&e.queryFilterable.length>0&&t.push(`Filterable: ${e.queryFilterable.join(`, `)}`),e.querySortable&&e.querySortable.length>0&&t.push(`Sortable: ${e.querySortable.join(`, `)}`),e.querySearchable&&e.querySearchable.length>0&&t.push(`Searchable: ${e.querySearchable.join(`, `)}`),t}function Bi(e,t,n,r,i){if(!e||r!==`zod`||e.source===null)return null;let a=Hi(e.source,t,n);if(a===`unknown`)return null;let o=`${a}::${e.identifier}`,s=i.get(o)?.specifier;return s?s=i.get(o).specifier:(s=`_S${i.size}`,i.set(o,{identifier:e.identifier,specifier:s})),s}function Vi(e){if(e.size===0)return``;let t=[];for(let[n,r]of e){let[e]=n.split(`::`);t.push(`import type { ${r.identifier} as ${r.specifier} } from '${e}'`)}return t.join(`
3914
+ `}function ga(e){if(e.queryFilterable===null)return`unknown`;let t=e.querySortable??[];return`{ filter?: string | string[]; sort?: ${t.length>0?t.flatMap(e=>[`'${e}'`,`'-${e}'`]).join(` | `):`string`}; q?: string; page?: string; limit?: string }`}function _a(e){let t=[];return e.queryFilterable&&e.queryFilterable.length>0&&t.push(`Filterable: ${e.queryFilterable.join(`, `)}`),e.querySortable&&e.querySortable.length>0&&t.push(`Sortable: ${e.querySortable.join(`, `)}`),e.querySearchable&&e.querySearchable.length>0&&t.push(`Searchable: ${e.querySearchable.join(`, `)}`),t}function va(e,t,n,r,i){if(!e||r!==`zod`||e.source===null)return null;let a=ba(e.source,t,n);if(a===`unknown`)return null;let o=`${a}::${e.identifier}`,s=i.get(o)?.specifier;return s?s=i.get(o).specifier:(s=`_S${i.size}`,i.set(o,{identifier:e.identifier,specifier:s})),s}function ya(e){if(e.size===0)return``;let t=[];for(let[n,r]of e){let[e]=n.split(`::`);t.push(`import type { ${r.identifier} as ${r.specifier} } from '${e}'`)}return t.join(`
3703
3915
  `)+`
3704
- `}function Hi(e,t,n){if(e===null)return`unknown`;let r=y(n);if(e===``){let e=x(r,t).split(se).join(`/`);return e=e.replace(/\.(ts|tsx|mts|cts)$/i,``),e.startsWith(`.`)||(e=`./`+e),e}if(!e.startsWith(`.`)&&!e.startsWith(`/`))return e;let i=x(r,S(y(t),e)).split(se).join(`/`);return i=i.replace(/\.(ts|tsx|mts|cts)$/i,``),i.startsWith(`.`)||(i=`./`+i),i}const Ui=()=>({id:`kick/routes`,outExtension:`.ts`,inputs:[`src/**/*.controller.ts`,`src/**/*.module.ts`],async generate(e){let t=await e.getScanResult({root:Wi(e),cwd:e.cwd,envFile:Gi(e)}),n=e.config?.typegen?.schemaValidator??`zod`,r=v.resolve(e.cwd,`.kickjs/types/kick__routes.ts`);return Li(t.routes,r,n)}});function Wi(e){return v.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Gi(e){let t=e.config?.typegen?.envFile;if(t!==!1)return t}function Ki(e,t){if(!e)return null;let n=x(y(t),e.filePath).split(se).join(`/`);return n=n.replace(/\.(ts|tsx|mts|cts)$/i,``),n.startsWith(`.`)||(n=`./`+n),`/* eslint-disable */
3916
+ `}function ba(e,t,n){if(e===null)return`unknown`;let r=b(n);if(e===``){let e=S(r,t).split(oe).join(`/`);return e=e.replace(/\.(ts|tsx|mts|cts)$/i,``),e.startsWith(`.`)||(e=`./`+e),e}if(!e.startsWith(`.`)&&!e.startsWith(`/`))return e;let i=S(r,C(b(t),e)).split(oe).join(`/`);return i=i.replace(/\.(ts|tsx|mts|cts)$/i,``),i.startsWith(`.`)||(i=`./`+i),i}const xa=()=>({id:`kick/routes`,outExtension:`.ts`,inputs:[`src/**/*.controller.ts`,`src/**/*.module.ts`],async generate(e){let t=await e.getScanResult({root:Sa(e),cwd:e.cwd,envFile:Ca(e)}),n=e.config?.typegen?.schemaValidator??`zod`,r=y.resolve(e.cwd,`.kickjs/types/kick__routes.ts`);return ha(t.routes,r,n)}});function Sa(e){return y.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Ca(e){let t=e.config?.typegen?.envFile;if(t!==!1)return t}function wa(e,t){if(!e)return null;let n=S(b(t),e.filePath).split(oe).join(`/`);return n=n.replace(/\.(ts|tsx|mts|cts)$/i,``),n.startsWith(`.`)||(n=`./`+n),`/* eslint-disable */
3705
3917
  // AUTO-GENERATED by \`kick typegen\`. DO NOT EDIT.
3706
3918
  // Re-run with \`kick typegen\` or rely on \`kick dev\` to refresh.
3707
3919
 
@@ -3737,4 +3949,4 @@ declare global {
3737
3949
  }
3738
3950
 
3739
3951
  export {}
3740
- `}const qi=()=>({id:`kick/env`,outExtension:`.ts`,inputs:[`src/env.ts`,`src/**/env.ts`,`src/**/*.env.ts`],async generate(e){let t=Yi(e);if(t===!1)return null;let n=await e.getScanResult({root:Ji(e),cwd:e.cwd,envFile:t});if(!n.env)return null;let r=v.resolve(e.cwd,`.kickjs/types/kick__env.ts`);return Ki(n.env,r)}});function Ji(e){return v.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Yi(e){return e.config?.typegen?.envFile}var Xi=e({builtinCliPlugins:()=>Zi});const Zi=[o({name:`kick/init`,register:Et}),o({name:`kick/generate`,register:_r}),o({name:`kick/run`,register:Ar}),o({name:`kick/info`,register:jr}),o({name:`kick/inspect`,register:Br}),o({name:`kick/add`,register:wt}),o({name:`kick/list`,register:Ct}),o({name:`kick/explain`,register:Xr}),o({name:`kick/mcp`,register:ai}),o({name:`kick/tinker`,register:li}),o({name:`kick/remove`,register:pi}),o({name:`kick/typegen`,register:gi}),o({name:`kick/check`,register:Oi}),o({name:`kick/db`,register:Ai,typegens:[Mi()]}),o({name:`kick/assets`,typegens:[Fi()]}),o({name:`kick/routes`,typegens:[Ui()]}),o({name:`kick/env`,typegens:[qi()]})];export{ke as i,Xi as n,xr as r,Zi as t};
3952
+ `}const Ta=()=>({id:`kick/env`,outExtension:`.ts`,inputs:[`src/env.ts`,`src/**/env.ts`,`src/**/*.env.ts`],async generate(e){let t=Da(e);if(t===!1)return null;let n=await e.getScanResult({root:Ea(e),cwd:e.cwd,envFile:t});if(!n.env)return null;let r=y.resolve(e.cwd,`.kickjs/types/kick__env.ts`);return wa(n.env,r)}});function Ea(e){return y.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function Da(e){return e.config?.typegen?.envFile}var Oa=e({builtinCliPlugins:()=>ka});const ka=[o({name:`kick/init`,register:Pt}),o({name:`kick/generate`,register:Ur}),o({name:`kick/run`,register:ni}),o({name:`kick/info`,register:ri}),o({name:`kick/inspect`,register:fi}),o({name:`kick/add`,register:Mt}),o({name:`kick/list`,register:jt}),o({name:`kick/explain`,register:Si}),o({name:`kick/mcp`,register:Ni}),o({name:`kick/tinker`,register:Li}),o({name:`kick/remove`,register:Ui}),o({name:`kick/typegen`,register:Ki}),o({name:`kick/check`,register:ia}),o({name:`kick/db`,register:oa,typegens:[ua()]}),o({name:`kick/codemod`,register:ca}),o({name:`kick/assets`,typegens:[pa()]}),o({name:`kick/routes`,typegens:[xa()]}),o({name:`kick/env`,typegens:[Ta()]})];export{Ne as i,Oa as n,qr as r,ka as t};