@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.
- package/dist/{builtins-CnQ6lxcV.mjs → builtins-BCRm8cUp.mjs} +163 -100
- package/dist/builtins-BCRm8cUp.mjs.map +1 -0
- package/dist/{builtins-BxGfcEP6.mjs → builtins-BbJqgLmS.mjs} +432 -220
- package/dist/cli.mjs +2 -2
- package/dist/config-cyBKEKWL.mjs +12 -0
- package/dist/config-cyBKEKWL.mjs.map +1 -0
- package/dist/config-q_jBP6Ln.mjs +11 -0
- package/dist/{generator-extension-BNgcYlom.mjs → generator-extension-CsT2e6Fj.mjs} +274 -125
- package/dist/generator-extension-CsT2e6Fj.mjs.map +1 -0
- package/dist/index.d.mts +45 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{plugin-Ccvf-gn6.mjs → plugin-8R67gfJG.mjs} +3 -3
- package/dist/{plugin-Ccvf-gn6.mjs.map → plugin-8R67gfJG.mjs.map} +1 -1
- package/dist/{plugin-B56zClEX.mjs → plugin-D6nfJWY0.mjs} +2 -2
- package/dist/{rolldown-runtime-CP9PNXAB.mjs → rolldown-runtime-B9Lxtctb.mjs} +1 -1
- package/dist/{run-plugins-sjeIm8hS.mjs → run-plugins-CYRt77yP.mjs} +2 -2
- package/dist/{typegen-CFW1vIgv.mjs → typegen-CgWQM1FH.mjs} +3 -3
- package/dist/{typegen-CYA1y8NJ.mjs → typegen-SyGEEyKv.mjs} +4 -4
- package/dist/{typegen-CYA1y8NJ.mjs.map → typegen-SyGEEyKv.mjs.map} +1 -1
- package/dist/{types-2ICiQzlQ.mjs → types-dPuCyHXz.mjs} +2 -2
- package/dist/{types-2ICiQzlQ.mjs.map → types-dPuCyHXz.mjs.map} +1 -1
- package/package.json +2 -2
- package/dist/builtins-CnQ6lxcV.mjs.map +0 -1
- package/dist/config-B5g_GsV2.mjs +0 -12
- package/dist/config-B5g_GsV2.mjs.map +0 -1
- package/dist/config-DE9Vo6LN.mjs +0 -11
- package/dist/generator-extension-BNgcYlom.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v5.
|
|
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-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
84
|
+
`}function Xe(){return`PORT=3000
|
|
85
85
|
NODE_ENV=development
|
|
86
|
-
`}function
|
|
86
|
+
`}function Ze(){return`PORT=3000
|
|
87
87
|
NODE_ENV=development
|
|
88
|
-
`}function
|
|
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
|
|
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
|
|
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
|
-
|
|
174
|
-
|
|
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
|
|
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
|
|
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
|
|
245
|
+
`}function it(){return`import { defineModule } from '@forinda/kickjs'
|
|
244
246
|
import { HelloController } from './hello.controller'
|
|
245
247
|
|
|
246
|
-
export
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
|
657
|
-
- [ ] Register module in \`src/modules/index.ts\` (\`
|
|
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
|
-
| \`
|
|
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 | \`
|
|
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
|
|
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
|
|
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
|
|
1186
|
-
Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${r} install failed. Run it manually.`)}}try{let{runTypegen:e}=await import(`./typegen-
|
|
1187
|
-
Project scaffolded successfully!`),console.log();let
|
|
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
|
|
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
|
|
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 ${
|
|
1205
|
-
|
|
1206
|
-
import {
|
|
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 ${
|
|
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(${
|
|
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
|
-
|
|
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
|
-
* ${
|
|
1301
|
+
* ${s}.repository.ts — Repository implementation
|
|
1254
1302
|
* dtos/ — Request/response schemas
|
|
1255
|
-
|
|
1256
|
-
import {
|
|
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(${
|
|
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
|
-
|
|
1281
|
-
import {
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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(${
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2420
|
-
import { ${t}Module } from '${
|
|
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
|
|
2423
|
-
`);return}let
|
|
2424
|
-
`,e);
|
|
2425
|
-
`+
|
|
2426
|
-
`+
|
|
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
|
|
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
|
|
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
|
|
2665
|
-
*
|
|
2666
|
-
*
|
|
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():
|
|
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
|
|
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 ${
|
|
2890
|
+
export interface ${L(t)}Options {
|
|
2738
2891
|
// Add configuration options here
|
|
2739
2892
|
}
|
|
2740
2893
|
|
|
2741
2894
|
/**
|
|
2742
|
-
* ${
|
|
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: ${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3450
|
-
(dry run — no files were written)`),console.log()}async function
|
|
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(...
|
|
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
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
3474
|
-
`,`utf-8`),o(` ✓ wrote manifest → ${
|
|
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
|
|
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(
|
|
3483
|
-
Copying directories to dist...`);for(let e of i){let t=typeof e==`string`?e:e.src,n=typeof e==`string`?
|
|
3484
|
-
Building asset map...`);try{await
|
|
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
|
|
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)),
|
|
3490
|
-
Dev server (debug) failed:`,e.message??e),process.exit(1)}})}function
|
|
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: ${
|
|
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:
|
|
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=
|
|
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`)||
|
|
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=
|
|
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
|
|
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
|
|
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 {
|
|
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:
|
|
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=
|
|
3583
|
-
`)}function
|
|
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=
|
|
3589
|
-
`);return}if(i){
|
|
3590
|
-
`),process.exit(2)),
|
|
3591
|
-
`),process.exit(a.kind===`ok`?0:2)),
|
|
3592
|
-
`)}function
|
|
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
|
|
3810
|
+
`)}function Ai(e,t){return e.split(`
|
|
3610
3811
|
`).map(e=>`${t}${e}`).join(`
|
|
3611
|
-
`)}function
|
|
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
|
|
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=
|
|
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('${
|
|
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
|
|
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
|
|
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
|
|
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=
|
|
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
|
|
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();
|
|
3682
|
-
|
|
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=
|
|
3692
|
-
`))}return`${
|
|
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
|
|
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
|
|
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
|
|
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};
|