@forinda/kickjs-cli 5.11.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-docs-Dmku65k2.mjs +12 -0
- package/dist/agent-docs-Dmku65k2.mjs.map +1 -0
- package/dist/{builtins-BL1BhYEv.mjs → builtins-G49e_Qsj.mjs} +2 -2
- package/dist/cli.mjs +151 -1376
- package/dist/config-DdtRfl33.mjs +13 -0
- package/dist/config-DdtRfl33.mjs.map +1 -0
- package/dist/doctor-SUUDEI1J.mjs +1221 -0
- package/dist/doctor-SUUDEI1J.mjs.map +1 -0
- package/dist/index.d.mts +663 -853
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{plugin-C4hfxiPw.mjs → plugin-Dg0Lk2Lp.mjs} +3 -3
- package/dist/{plugin-C4hfxiPw.mjs.map → plugin-Dg0Lk2Lp.mjs.map} +1 -1
- package/dist/{project-docs-CfB-KVN5.mjs → project-docs-C-dA6-TO.mjs} +6 -36
- package/dist/project-docs-C-dA6-TO.mjs.map +1 -0
- package/dist/{project-root-CDYKLnfG.mjs → project-root-so4F5DRN.mjs} +3 -3
- package/dist/{project-root-CDYKLnfG.mjs.map → project-root-so4F5DRN.mjs.map} +1 -1
- package/dist/{rolldown-runtime-CeWwRE8g.mjs → rolldown-runtime-DrKbExWn.mjs} +1 -1
- package/dist/run-plugins-B2_AT35s.mjs +636 -0
- package/dist/run-plugins-B2_AT35s.mjs.map +1 -0
- package/dist/typegen-5MX2F5iL.mjs +114 -0
- package/dist/typegen-5MX2F5iL.mjs.map +1 -0
- package/dist/types-C5PH0h7Z.mjs +11 -0
- package/package.json +13 -13
- package/dist/agent-docs-CXqrGZLl.mjs +0 -12
- package/dist/agent-docs-CXqrGZLl.mjs.map +0 -1
- package/dist/config-Cf8GU8CG.mjs +0 -13
- package/dist/config-Cf8GU8CG.mjs.map +0 -1
- package/dist/doctor-Dl709LzL.mjs +0 -2076
- package/dist/doctor-Dl709LzL.mjs.map +0 -1
- package/dist/project-docs-CfB-KVN5.mjs.map +0 -1
- package/dist/run-plugins-M_WVt-7a.mjs +0 -976
- package/dist/run-plugins-M_WVt-7a.mjs.map +0 -1
- package/dist/typegen-CezcLjMb.mjs +0 -114
- package/dist/typegen-CezcLjMb.mjs.map +0 -1
- package/dist/types-DvYczI2m.mjs +0 -12
- package/dist/types-DvYczI2m.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli
|
|
2
|
+
* @forinda/kickjs-cli v6.0.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{createRequire as e}from"node:module";import{Command as t}from"commander";import{cpSync as n,existsSync as r,mkdirSync as i,readFileSync as a,readdirSync as o,rmSync as s,statSync as c,writeFileSync as l}from"node:fs";import u,{basename as d,dirname as f,extname as p,isAbsolute as m,join as h,parse as g,relative as _,resolve as v,sep as y}from"node:path";import{fileURLToPath as b,pathToFileURL as x}from"node:url";import{execFileSync as
|
|
12
|
-
`,`utf-8`)}catch{}}function
|
|
11
|
+
import{createRequire as e}from"node:module";import{Command as t}from"commander";import{cpSync as n,existsSync as r,mkdirSync as i,readFileSync as a,readdirSync as o,rmSync as s,statSync as c,writeFileSync as l}from"node:fs";import u,{basename as d,dirname as f,extname as p,isAbsolute as m,join as h,parse as g,relative as _,resolve as v,sep as y}from"node:path";import{fileURLToPath as b,pathToFileURL as x}from"node:url";import{execFileSync as S,execSync as C,fork as ee,spawn as te,spawnSync as ne}from"node:child_process";import{access as re,copyFile as ie,mkdir as w,readFile as T,readdir as ae,rm as oe,stat as se,unlink as ce,writeFile as E}from"node:fs/promises";import{KickPluginConflictError as le,defineCliPlugin as D}from"@forinda/kickjs-cli-kit";import*as O from"@clack/prompts";import k from"picocolors";import ue from"pluralize";import{glob as de,globSync as fe}from"glob";import{groupAssetKeys as pe}from"@forinda/kickjs";import{arch as me,platform as he,release as ge}from"node:os";var _e=Object.defineProperty,A=(e,t)=>{let n={};for(var r in e)_e(n,r,{get:e[r],enumerable:!0});return t||_e(n,Symbol.toStringTag,{value:`Module`}),n};function ve(e,t,n){C(e,{cwd:t,stdio:`inherit`,env:n?{...process.env,...n}:process.env})}function ye(e,t,n){let r=ne(process.execPath,[e],{cwd:n,stdio:`inherit`,env:{...process.env,...t}});r.status!==0&&process.exit(r.status??1)}function be(e,t){if(!t?.commands?.length)return;let n=new Set(e.commands.map(e=>e.name()));for(let r of t.commands){if(n.has(r.name)){console.warn(` Warning: custom command '${r.name}' skipped — conflicts with a built-in command`);continue}xe(e,r)}}function xe(e,t){let n=e.command(t.name).description(t.description);if(t.aliases)for(let e of t.aliases)n.alias(e);n.allowUnknownOption(!0),n.argument(`[args...]`,`Additional arguments passed to the command`),n.action(e=>{let n=e.join(` `),r=Array.isArray(t.steps)?t.steps:[t.steps];for(let e of r){let r=n?`${e} ${n}`:e;console.log(` $ ${r}`);try{ve(r)}catch{console.error(` Command failed: ${t.name}`),process.exitCode=1;return}}})}var Se=A({BUILTIN_REPO_TYPES:()=>we,DEPRECATED_REPO_TYPES:()=>Te,PACKAGE_MANAGERS:()=>Ce,defineConfig:()=>De,loadKickConfig:()=>M,resolveModuleConfig:()=>j,resolveTokenScope:()=>Oe,validateAssetMap:()=>Me,warnIfDeprecatedRepo:()=>Ee,writeAssetConfigSnapshot:()=>je});const Ce=[`pnpm`,`npm`,`yarn`,`bun`],we=[`inmemory`],Te=[`prisma`,`drizzle`];function Ee(e){return Te.includes(e)?(console.warn(` Note: the '${e}' repository preset is deprecated. Generating a generic custom repository named '${e}' instead — wire it to your DB by hand. Pass any name via \`--repo <name>\` or \`modules.repo: { name: '<name>' }\`.`),!0):!1}function De(e){return e}function Oe(e,t){if(e?.tokenScope&&typeof e.tokenScope==`string`&&e.tokenScope.length>0){let t=ke(e.tokenScope);if(t.length>0)return t}try{let e=h(t,`package.json`);if(r(e)){let t=JSON.parse(a(e,`utf-8`));if(typeof t.name==`string`&&t.name.length>0){let e=t.name.match(/^@([^/]+)\//),n=ke(e?e[1]:t.name);if(n.length>0)return n}}}catch{}return`app`}function ke(e){return e.toLowerCase().replace(/[^a-z0-9-]/g,`-`).replace(/^-+|-+$/g,``).replace(/-{2,}/g,`-`)}function j(e){if(!e)return{};let t={dir:e.modules?.dir,repo:e.modules?.repo,schemaDir:e.modules?.schemaDir,pluralize:e.modules?.pluralize,prismaClientPath:e.modules?.prismaClientPath,style:e.modules?.style};return t.style!==void 0&&t.style!==`define`&&t.style!==`class`&&(console.warn(` Warning: modules.style '${t.style}' is not a valid value (expected 'define' or 'class'). Falling back to 'define'.`),t.style=`define`),t.repo&&typeof t.repo==`string`&&!we.includes(t.repo)&&(Ee(t.repo)||console.warn(` Warning: modules.repo '${t.repo}' is not a built-in type (${we.join(`, `)}). It will generate a stub repository. Use { name: '${t.repo}' } to silence this warning.`)),t}const Ae=[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`,`kick.config.json`];async function M(e){let{findProjectRoot:t}=await Promise.resolve().then(()=>Ut),n=t(e);for(let e of Ae){let t=h(n,e);try{await re(t)}catch{continue}if(e.endsWith(`.json`)){let e=await T(t,`utf-8`);return JSON.parse(e)}if(e.endsWith(`.ts`)){let r;try{r=await import(`jiti`)}catch(t){let n=t instanceof Error?t.message:String(t);n.includes(`Cannot find package 'jiti'`)||n.includes(`ERR_MODULE_NOT_FOUND`)?console.warn(`Warning: Failed to load ${e} — 'jiti' is required for TypeScript configs. Run \`pnpm add -D jiti\` (or your package manager's equivalent), or rename the file to kick.config.js / kick.config.mjs / kick.config.json.`):console.warn(`Warning: Failed to initialize jiti for ${e}: ${n}`);continue}try{let e=await r.createJiti(n,{interopDefault:!0,fsCache:!1}).import(t,{default:!0}),i=Me(e,n);for(let e of i)console.warn(` Warning: ${e}`);return je(n,e),e}catch(t){let n=t instanceof Error?t.message:String(t);console.warn(`Warning: Failed to load ${e}: ${n}`);continue}}try{let{pathToFileURL:e}=await import(`node:url`),r=await import(e(t).href),i=r.default??r,a=Me(i,n);for(let e of a)console.warn(` Warning: ${e}`);return je(n,i),i}catch(t){let n=t instanceof Error?t.message:String(t);console.warn(`Warning: Failed to load ${e}: ${n}`);continue}}return null}function je(e,t){if(!(!t?.assetMap||Object.keys(t.assetMap).length===0))try{let n=h(e,`.kickjs`);i(n,{recursive:!0});let r={version:1,assetMap:t.assetMap,...t.build?.outDir?{build:{outDir:t.build.outDir}}:{}};l(h(n,`kick.config.json`),JSON.stringify(r,null,2)+`
|
|
12
|
+
`,`utf-8`)}catch{}}function Me(e,t){let n=[];if(!e?.assetMap)return n;let i=v(t);for(let[a,o]of Object.entries(e.assetMap)){if(!a||a.includes(`/`)){n.push(`assetMap key '${a}' is invalid — must be a non-empty string without '/'`);continue}if(typeof o?.src!=`string`||o.src.length===0){n.push(`assetMap.${a} is missing a non-empty 'src' field`);continue}r(v(t,o.src))||n.push(`assetMap.${a}.src ('${o.src}') does not exist — typegen + build will fail`),o.dest&&Ne(v(t,o.dest),i)&&n.push(`assetMap.${a}.dest ('${o.dest}') resolves outside the project root — refusing to copy`)}return n}function Ne(e,t){let n=_(t,e);return n===``?!1:n.startsWith(`..`)||m(n)}function Pe(e,t=[]){let n=new Map;for(let t of e){let e=(n.get(t.name)??0)+1;if(n.set(t.name,e),e===2)throw new le(`plugin`,t.name,[t.name,t.name])}let r=new Map,i=[];for(let t of e)for(let e of t.commands??[]){let n=r.get(e.name);if(n)throw new le(`command`,e.name,[n,t.name]);r.set(e.name,t.name),i.push(e)}let a=new Set(t.map(e=>e.name)),o=[...i.filter(e=>!a.has(e.name)),...t],s=new Map,c=[];for(let t of e)for(let e of t.typegens??[]){let n=s.get(e.id);if(n)throw new le(`typegen`,e.id,[n,t.name]);s.set(e.id,t.name),c.push(e)}let l=new Map,u=[];for(let t of e)for(let e of t.generators??[]){let n=l.get(e.name);if(n)throw new le(`generator`,e.name,[n,t.name]);l.set(e.name,t.name),u.push({source:t.name,spec:e})}return{commands:o,typegens:c,generators:u,register:async(t,n)=>{let r;if(n)r={generators:u,...n};else{let{findProjectRoot:e}=await Promise.resolve().then(()=>Ut),t=process.cwd();r={cwd:t,projectRoot:e(t),config:null,log:()=>{},generators:u}}for(let n of e)n.register&&await n.register(t,r)}}}var Fe=A({mergeCliPlugins:()=>Pe});let Ie=!1;function N(e){Ie=e}const Le=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function P(e,t){Ie||(await w(f(e),{recursive:!0}),await E(e,t,`utf-8`),Le.has(p(e))&&await Be(e,t).catch(()=>{}))}let Re;async function ze(t){if(Re!==void 0)return Re;try{Re=await import(e(h(t,`package.json`)).resolve(`oxfmt`))}catch{Re=null}return Re}async function Be(e,t){let n=await ze(process.cwd());if(!n)return;let r=await He(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await E(e,i.code,`utf-8`)}const Ve=new Map;async function He(e){let t=f(e),n=t;if(Ve.has(n))return Ve.get(n);for(;;){let e=h(t,`.oxfmtrc.json`);if(r(e))try{let t=await T(e,`utf-8`),r=JSON.parse(t);return delete r.$schema,delete r.ignorePatterns,Ve.set(n,r),r}catch{return Ve.set(n,null),null}let i=f(t);if(i===t)return Ve.set(n,null),null;t=i}}async function Ue(e){try{return await re(e),!0}catch{return!1}}const We={swagger:`@forinda/kickjs-swagger`,ws:`@forinda/kickjs-ws`,queue:`@forinda/kickjs-queue`,devtools:`@forinda/kickjs-devtools`},Ge={zod:{name:`zod`,range:`^4.3.6`},valibot:{name:`valibot`,range:`^1.4.1`},yup:{name:`yup`,range:`^1.7.1`}};function Ke(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 qe(e,t,n,r=[],i=`zod`){let a=Ge[i],o={"@forinda/kickjs":Ke(n,`@forinda/kickjs`),"@forinda/kickjs-schema":Ke(n,`@forinda/kickjs-schema`),dotenv:`^17.3.1`,express:`^5.1.0`,"reflect-metadata":`^0.2.2`,[a.name]:a.range};for(let e of r){let t=We[e];t&&!o[t]&&(o[t]=Ke(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:o,devDependencies:{"@forinda/kickjs-cli":Ke(n,`@forinda/kickjs-cli`),"@forinda/kickjs-vite":Ke(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 Je(){return`import { defineConfig } from 'vite'
|
|
13
13
|
import { resolve } from 'node:path'
|
|
14
14
|
import swc from 'unplugin-swc'
|
|
15
15
|
import { kickjsVitePlugin, envWatchPlugin } from '@forinda/kickjs-vite'
|
|
@@ -39,7 +39,7 @@ export default defineConfig({
|
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
41
|
})
|
|
42
|
-
`}function
|
|
42
|
+
`}function Ye(){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 Xe(){return JSON.stringify({semi:!1,singleQuote:!0,trailingComma:`all`,printWidth:100,tabWidth:2},null,2)}function Ze(){return`# https://editorconfig.org
|
|
43
43
|
root = true
|
|
44
44
|
|
|
45
45
|
[*]
|
|
@@ -52,14 +52,14 @@ insert_final_newline = true
|
|
|
52
52
|
|
|
53
53
|
[*.md]
|
|
54
54
|
trim_trailing_whitespace = false
|
|
55
|
-
`}function
|
|
55
|
+
`}function Qe(){return`node_modules/
|
|
56
56
|
dist/
|
|
57
57
|
.env
|
|
58
58
|
coverage/
|
|
59
59
|
.DS_Store
|
|
60
60
|
*.tsbuildinfo
|
|
61
61
|
.kickjs/
|
|
62
|
-
`}function
|
|
62
|
+
`}function $e(){return`# Auto-detect text files and normalise line endings to LF
|
|
63
63
|
* text=auto eol=lf
|
|
64
64
|
|
|
65
65
|
# Explicitly mark generated / binary files
|
|
@@ -77,11 +77,11 @@ coverage/
|
|
|
77
77
|
pnpm-lock.yaml -diff linguist-generated
|
|
78
78
|
yarn.lock -diff linguist-generated
|
|
79
79
|
package-lock.json -diff linguist-generated
|
|
80
|
-
`}function
|
|
80
|
+
`}function et(){return`PORT=3000
|
|
81
81
|
NODE_ENV=development
|
|
82
|
-
`}function
|
|
82
|
+
`}function tt(){return`PORT=3000
|
|
83
83
|
NODE_ENV=development
|
|
84
|
-
`}function
|
|
84
|
+
`}function nt(){return`import { defineConfig } from 'vitest/config'
|
|
85
85
|
import swc from 'unplugin-swc'
|
|
86
86
|
|
|
87
87
|
export default defineConfig({
|
|
@@ -92,33 +92,7 @@ export default defineConfig({
|
|
|
92
92
|
include: ['src/**/*.test.ts'],
|
|
93
93
|
},
|
|
94
94
|
})
|
|
95
|
-
`}function
|
|
96
|
-
// Side-effect import — registers the extended env schema with kickjs
|
|
97
|
-
// **before** any controller / service / @Value gets resolved. Without
|
|
98
|
-
// this line ConfigService.get('YOUR_KEY') returns undefined because the
|
|
99
|
-
// cached schema would still be the base shape. See guide/configuration.
|
|
100
|
-
import './config'
|
|
101
|
-
import { bootstrap } from '@forinda/kickjs'
|
|
102
|
-
// import { WsAdapter } from '@forinda/kickjs-ws'
|
|
103
|
-
// import { QueueAdapter, BullMQProvider } from '@forinda/kickjs-queue'
|
|
104
|
-
${t.length?t.join(`
|
|
105
|
-
`)+`
|
|
106
|
-
`:``}import { modules } from './modules'
|
|
107
|
-
|
|
108
|
-
// Export the app for the Vite plugin (dev mode)
|
|
109
|
-
export const app = await bootstrap({
|
|
110
|
-
modules,${t.length?`\n adapters: [\n${i.join(`
|
|
111
|
-
`)}\n // Uncomment for WebSocket support:\n // WsAdapter(),\n // Uncomment when Redis is available:\n // QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],`:`
|
|
112
|
-
adapters: [
|
|
113
|
-
// Uncomment for WebSocket support:
|
|
114
|
-
// WsAdapter(),
|
|
115
|
-
// Uncomment when Redis is available:
|
|
116
|
-
// QueueAdapter({
|
|
117
|
-
// provider: new BullMQProvider({ host: 'localhost', port: 6379 }),
|
|
118
|
-
// }),
|
|
119
|
-
],`}
|
|
120
|
-
})
|
|
121
|
-
`}case`minimal`:{let t=[],i=[];return r.includes(`swagger`)&&(t.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`),i.push(` SwaggerAdapter({ info: { title: '${e}', version: '${n}' } }),`)),r.includes(`devtools`)&&(t.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`),i.push(` DevToolsAdapter(),`)),`import 'reflect-metadata'
|
|
95
|
+
`}function rt(e,t,n,r=[]){switch(t){case`minimal`:{let t=[],i=[];return r.includes(`swagger`)&&(t.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`),i.push(` SwaggerAdapter({ info: { title: '${e}', version: '${n}' } }),`)),r.includes(`devtools`)&&(t.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`),i.push(` DevToolsAdapter(),`)),`import 'reflect-metadata'
|
|
122
96
|
// Side-effect import — registers the extended env schema with kickjs
|
|
123
97
|
// **before** any controller / service / @Value gets resolved. Without
|
|
124
98
|
// this line ConfigService.get('YOUR_KEY') returns undefined because the
|
|
@@ -162,14 +136,14 @@ export const app = await bootstrap({
|
|
|
162
136
|
express.json(),
|
|
163
137
|
],
|
|
164
138
|
})
|
|
165
|
-
`}}}function
|
|
139
|
+
`}}}function it(){return`import { defineModules } from '@forinda/kickjs'
|
|
166
140
|
import { HelloModule } from './hello/hello.module'
|
|
167
141
|
|
|
168
142
|
// Remove HelloModule and run: kick g module <name>
|
|
169
143
|
// \`defineModules()\` returns a chainable list — \`kick g module\` appends
|
|
170
144
|
// \`.mount(NewModule())\` to the chain on every generation.
|
|
171
145
|
export const modules = defineModules().mount(HelloModule())
|
|
172
|
-
`}function
|
|
146
|
+
`}function at(e=`zod`){return e===`valibot`?`import { loadEnvFromSchema } from '@forinda/kickjs/config'
|
|
173
147
|
import { fromValibot } from '@forinda/kickjs-schema/valibot'
|
|
174
148
|
import * as v from 'valibot'
|
|
175
149
|
|
|
@@ -297,7 +271,7 @@ const envSchema = fromZod(
|
|
|
297
271
|
export const env = loadEnvFromSchema(envSchema)
|
|
298
272
|
|
|
299
273
|
export default envSchema
|
|
300
|
-
`}function
|
|
274
|
+
`}function ot(){return`import { Service } from '@forinda/kickjs'
|
|
301
275
|
|
|
302
276
|
@Service()
|
|
303
277
|
export class HelloService {
|
|
@@ -309,7 +283,7 @@ export class HelloService {
|
|
|
309
283
|
return { status: 'ok', uptime: process.uptime() }
|
|
310
284
|
}
|
|
311
285
|
}
|
|
312
|
-
`}function
|
|
286
|
+
`}function st(){return`import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'
|
|
313
287
|
import { HelloService } from './hello.service'
|
|
314
288
|
|
|
315
289
|
// \`Ctx<KickRoutes.HelloController['<method>']>\` is generated by
|
|
@@ -331,7 +305,7 @@ export class HelloController {
|
|
|
331
305
|
ctx.json(this.helloService.healthCheck())
|
|
332
306
|
}
|
|
333
307
|
}
|
|
334
|
-
`}function
|
|
308
|
+
`}function ct(){return`import { defineModule } from '@forinda/kickjs'
|
|
335
309
|
import { HelloController } from './hello.controller'
|
|
336
310
|
|
|
337
311
|
export const HelloModule = defineModule({
|
|
@@ -352,7 +326,7 @@ export const HelloModule = defineModule({
|
|
|
352
326
|
},
|
|
353
327
|
}),
|
|
354
328
|
})
|
|
355
|
-
`}function
|
|
329
|
+
`}function lt(e,t=`inmemory`,n=`pnpm`){return`import { defineConfig } from '@forinda/kickjs-cli'
|
|
356
330
|
|
|
357
331
|
export default defineConfig({
|
|
358
332
|
pattern: '${e}',
|
|
@@ -361,7 +335,7 @@ export default defineConfig({
|
|
|
361
335
|
packageManager: '${n}',
|
|
362
336
|
modules: {
|
|
363
337
|
dir: 'src/modules',
|
|
364
|
-
repo: ${
|
|
338
|
+
repo: ${t===`inmemory`?`'inmemory'`:`{ name: '${t}' }`},
|
|
365
339
|
pluralize: true,
|
|
366
340
|
},
|
|
367
341
|
|
|
@@ -399,7 +373,7 @@ export default defineConfig({
|
|
|
399
373
|
},
|
|
400
374
|
],
|
|
401
375
|
})
|
|
402
|
-
`}function
|
|
376
|
+
`}function ut(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`),`# ${e}
|
|
403
377
|
|
|
404
378
|
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.
|
|
405
379
|
|
|
@@ -462,7 +436,7 @@ Copy \`.env.example\` to \`.env\` and configure:
|
|
|
462
436
|
|
|
463
437
|
- [KickJS Documentation](https://forinda.github.io/kick-js/)
|
|
464
438
|
- [CLI Reference](https://forinda.github.io/kick-js/api/cli.html)
|
|
465
|
-
`}function
|
|
439
|
+
`}function dt(e,t,n){return`# CLAUDE.md — ${e}
|
|
466
440
|
|
|
467
441
|
**Read \`./.agents/AGENTS.md\` first.** It is the canonical, multi-agent
|
|
468
442
|
reference for this project (Claude, Copilot, Codex, Gemini, etc.) —
|
|
@@ -536,7 +510,7 @@ When generating or modifying code in this project, stay aligned with the v4 conv
|
|
|
536
510
|
- **Refresh these files**: \`kick g agents -f\` regenerates \`CLAUDE.md\` at the project root and \`.agents/AGENTS.md\` + \`.agents/GEMINI.md\` + \`.agents/COPILOT.md\` + every \`.agents/skills/<name>/SKILL.md\` from the latest CLI templates. Hand-edited content is overwritten — keep customisation in \`.agents/AGENTS.local.md\` or per-skill \`SKILL.local.md\` files alongside.
|
|
537
511
|
|
|
538
512
|
For everything else (controllers, services, modules, RequestContext API, generators, CLI commands, package additions, env wiring, troubleshooting) → \`.agents/AGENTS.md\`.
|
|
539
|
-
`}function
|
|
513
|
+
`}function ft(e,t,n){return`# AGENTS.md — AI Agent Guide for ${e}
|
|
540
514
|
|
|
541
515
|
This guide is the **canonical, multi-agent reference** for this KickJS
|
|
542
516
|
application — Claude, Copilot, Codex, Gemini, etc. all read it first.
|
|
@@ -700,34 +674,12 @@ package additions, env access patterns, troubleshooting) is detailed below.
|
|
|
700
674
|
|
|
701
675
|
Each module in \`src/modules/<name>/\` typically contains:
|
|
702
676
|
|
|
703
|
-
${t===`
|
|
677
|
+
${t===`rest`?`\`\`\`
|
|
704
678
|
<name>/
|
|
705
679
|
├── <name>.controller.ts # HTTP routes (@Controller)
|
|
706
680
|
├── <name>.service.ts # Business logic (@Service)
|
|
707
681
|
├── <name>.repository.ts # Data access (@Repository)
|
|
708
|
-
├──
|
|
709
|
-
├── <name>.entity.ts # Domain entity (optional)
|
|
710
|
-
└── <name>.module.ts # Module definition (defineModule factory)
|
|
711
|
-
\`\`\`
|
|
712
|
-
`:t===`cqrs`?`\`\`\`
|
|
713
|
-
<name>/
|
|
714
|
-
├── commands/ # Write operations
|
|
715
|
-
│ ├── create-<name>.command.ts
|
|
716
|
-
│ └── create-<name>.handler.ts
|
|
717
|
-
├── queries/ # Read operations
|
|
718
|
-
│ ├── get-<name>.query.ts
|
|
719
|
-
│ └── get-<name>.handler.ts
|
|
720
|
-
├── events/ # Domain events
|
|
721
|
-
│ └── <name>-created.event.ts
|
|
722
|
-
├── <name>.controller.ts # HTTP routes
|
|
723
|
-
├── <name>.repository.ts # Data access
|
|
724
|
-
└── <name>.module.ts # Module definition (defineModule factory)
|
|
725
|
-
\`\`\`
|
|
726
|
-
`:t===`rest`?`\`\`\`
|
|
727
|
-
<name>/
|
|
728
|
-
├── <name>.controller.ts # HTTP routes (@Controller)
|
|
729
|
-
├── <name>.service.ts # Business logic (@Service)
|
|
730
|
-
├── <name>.dto.ts # Request/response schemas (Zod)
|
|
682
|
+
├── dtos/ # Request/response schemas (Zod)
|
|
731
683
|
└── <name>.module.ts # Module definition (defineModule factory)
|
|
732
684
|
\`\`\`
|
|
733
685
|
`:"```\nsrc/\n├── index.ts # Add routes here\n└── ... # Custom structure\n```\n"}
|
|
@@ -992,15 +944,7 @@ fast). The \`onError\` hook is async-permitted.
|
|
|
992
944
|
|
|
993
945
|
Full guide: <https://forinda.github.io/kick-js/guide/context-decorators>.
|
|
994
946
|
|
|
995
|
-
|
|
996
|
-
| Decorator | Purpose |
|
|
997
|
-
|-----------|---------|
|
|
998
|
-
| \`@Job('name')\` | Queue job handler |
|
|
999
|
-
| \`@Process('queue')\` | Queue processor |
|
|
1000
|
-
| \`@Cron('0 * * * *')\` | Cron schedule |
|
|
1001
|
-
| \`@WsController()\` | WebSocket controller |
|
|
1002
|
-
|
|
1003
|
-
`:``}## Common Pitfalls
|
|
947
|
+
## Common Pitfalls
|
|
1004
948
|
|
|
1005
949
|
1. **Forgot to register module** — Add to \`src/modules/index.ts\` exports array
|
|
1006
950
|
2. **DI not working** — Ensure \`reflect-metadata\` is imported in \`src/index.ts\`
|
|
@@ -1043,7 +987,7 @@ ${t===`cqrs`?`### Background Jobs
|
|
|
1043
987
|
- [Decorators Guide](https://forinda.github.io/kick-js/guide/decorators.html)
|
|
1044
988
|
- [DI System](https://forinda.github.io/kick-js/guide/dependency-injection.html)
|
|
1045
989
|
- [Testing](https://forinda.github.io/kick-js/api/testing.html)
|
|
1046
|
-
`}function
|
|
990
|
+
`}function pt(e,t,n){let r=`<!-- Generated by \`kick g agents\` for ${e}. Edits are overwritten on the next refresh; keep customisation in a SKILL.local.md alongside. -->`;return[{slug:`add-module`,frontmatterName:`kickjs-add-module`,description:`Use when the user asks to add a new feature module (controller + service + repo + DTOs).`,body:`**Trigger phrases**: "add a users module", "scaffold tasks", "new feature for X".
|
|
1047
991
|
|
|
1048
992
|
**Steps**:
|
|
1049
993
|
1. Run \`kick g module <name>\` (use plural form if the project pluralizes — check \`kick.config.ts\`).
|
|
@@ -1178,7 +1122,7 @@ description: ${e.description}
|
|
|
1178
1122
|
${r}
|
|
1179
1123
|
|
|
1180
1124
|
${e.body}
|
|
1181
|
-
`}))}function
|
|
1125
|
+
`}))}function mt(e,t,n){return`# GEMINI.md — ${e}
|
|
1182
1126
|
|
|
1183
1127
|
**Read \`./AGENTS.md\` first.** It is the canonical, multi-agent
|
|
1184
1128
|
reference for this project — every convention, structure, decorator
|
|
@@ -1212,7 +1156,7 @@ without us copy-pasting.
|
|
|
1212
1156
|
\`kick g agents --only gemini -f\` regenerates this file from the
|
|
1213
1157
|
CLI template. Hand-edited content is overwritten — keep customisation
|
|
1214
1158
|
in \`.agents/GEMINI.local.md\`.
|
|
1215
|
-
`}function
|
|
1159
|
+
`}function ht(e,t,n){return`# COPILOT.md — ${e}
|
|
1216
1160
|
|
|
1217
1161
|
**Read \`./AGENTS.md\` first.** It is the canonical, multi-agent
|
|
1218
1162
|
reference for this project — every convention, structure, decorator
|
|
@@ -1245,103 +1189,16 @@ Codex / Cursor / Gemini / Claude Code without copy-pasting.
|
|
|
1245
1189
|
\`kick g agents --only copilot -f\` regenerates this file from the
|
|
1246
1190
|
CLI template. Hand-edited content is overwritten — keep customisation
|
|
1247
1191
|
in \`.agents/COPILOT.local.md\`.
|
|
1248
|
-
`}const
|
|
1249
|
-
Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${r} install failed. Run it manually.`)}}try{let{runTypegen:e}=await Promise.resolve().then(()=>
|
|
1250
|
-
Project scaffolded successfully!`),console.log();let f=c!==process.cwd();l(`Next steps:`),f&&l(` cd ${t}`),e.installDeps||l(` ${r} install`);let p={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`};l(` ${p[i]??p.rest}`),l(` kick dev`),l(``),l(`Commands:`),l(` kick dev Start dev server with Vite HMR`),l(` kick build Production build via Vite`),l(` kick start Run production build`),l(``),l(`Generators:`),l(` kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)`),l(` kick g scaffold <n> <f..> CRUD module from field definitions`),l(` kick g controller <name> Standalone controller`),l(` kick g service <name> @Service() class`),l(` kick g middleware <name> Express middleware`),l(` kick g guard <name> Route guard (auth, roles, etc.)`),l(` kick g adapter <name> AppAdapter with lifecycle hooks`),l(` kick g dto <name> Zod DTO schema`),
|
|
1192
|
+
`}const gt=f(b(import.meta.url)),_t=JSON.parse(a(h(gt,`..`,`package.json`),`utf-8`)),vt=`^${_t.version}`,yt=[`@forinda/kickjs`,`@forinda/kickjs-cli`,`@forinda/kickjs-schema`,`@forinda/kickjs-vite`,`@forinda/kickjs-swagger`,`@forinda/kickjs-ws`,`@forinda/kickjs-queue`,`@forinda/kickjs-devtools`,`@forinda/kickjs-testing`];async function bt(){let e=await Promise.all(yt.map(async e=>{try{let t=S(`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,vt]}));return Object.fromEntries(e)}async function xt(e){let{name:t,directory:n,packageManager:r=`pnpm`,template:i=`rest`,defaultRepo:a=`inmemory`,packages:o=[],schemaLib:s=`zod`}=e,c=n,l=e=>console.log(` ${e}`);console.log(`\n Creating KickJS project: ${t}\n`),l(`Resolving package versions...`);let u=await bt();await P(h(c,`package.json`),qe(t,i,u,o,s)),await P(h(c,`vite.config.ts`),Je()),await P(h(c,`tsconfig.json`),Ye()),await P(h(c,`.prettierrc`),Xe()),await P(h(c,`.editorconfig`),Ze()),await P(h(c,`.gitignore`),Qe()),await P(h(c,`.gitattributes`),$e()),await P(h(c,`.env`),et()),await P(h(c,`.env.example`),tt()),await P(h(c,`src/config/index.ts`),at(s)),await P(h(c,`src/index.ts`),rt(t,i,_t.version,o)),await P(h(c,`src/modules/index.ts`),it()),await P(h(c,`src/modules/hello/hello.service.ts`),ot()),await P(h(c,`src/modules/hello/hello.controller.ts`),st()),await P(h(c,`src/modules/hello/hello.module.ts`),ct()),await P(h(c,`kick.config.ts`),lt(i,a,r)),await P(h(c,`vitest.config.ts`),nt()),await P(h(c,`README.md`),ut(t,i,r));let{generateAgentDocs:d}=await Promise.resolve().then(()=>Kn);if(await d({outDir:c,name:t,pm:r,template:i,only:`all`,force:!0}),e.installDeps){console.log(`\n Installing dependencies with ${r}...\n`);try{C(`${r} install`,{cwd:c,stdio:`inherit`}),console.log(`
|
|
1193
|
+
Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${r} install failed. Run it manually.`)}}try{let{runTypegen:e}=await Promise.resolve().then(()=>Ui);await e({cwd:c,allowDuplicates:!0,silent:!0})}catch{}if(e.initGit)try{C(`git init`,{cwd:c,stdio:`pipe`}),C(`git branch -M main`,{cwd:c,stdio:`pipe`}),C(`git add -A`,{cwd:c,stdio:`pipe`}),C(`git commit -m "chore: initial commit from kick new"`,{cwd:c,stdio:`pipe`}),l(`Git repository initialized`)}catch{l(`Warning: git init failed (git may not be installed)`)}console.log(`
|
|
1194
|
+
Project scaffolded successfully!`),console.log();let f=c!==process.cwd();l(`Next steps:`),f&&l(` cd ${t}`),e.installDeps||l(` ${r} install`);let p={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`};l(` ${p[i]??p.rest}`),l(` kick dev`),l(``),l(`Commands:`),l(` kick dev Start dev server with Vite HMR`),l(` kick build Production build via Vite`),l(` kick start Run production build`),l(``),l(`Generators:`),l(` kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)`),l(` kick g scaffold <n> <f..> CRUD module from field definitions`),l(` kick g controller <name> Standalone controller`),l(` kick g service <name> @Service() class`),l(` kick g middleware <name> Express middleware`),l(` kick g guard <name> Route guard (auth, roles, etc.)`),l(` kick g adapter <name> AppAdapter with lifecycle hooks`),l(` kick g dto <name> Zod DTO schema`),l(` kick g config Generate kick.config.ts`),l(``),l(`Add packages:`),l(` kick add <pkg> Install a KickJS package + peers`),l(` kick add --list Show all available packages`),l(``),l(`Available: auth, swagger, drizzle, prisma, ws, queue, devtools, mcp, testing`),l(``)}const St={GET:k.green,POST:k.cyan,PUT:k.yellow,PATCH:k.magenta,DELETE:k.red};function Ct(e){return(St[e]??k.dim)(e.padEnd(7))}function wt(e){let t=`[${e}]`.padEnd(10);switch(e){case`CRITICAL`:return k.red(t);case`WARNING`:return k.yellow(t);case`INFO`:return k.blue(k.dim(t));default:return t}}k.green(`✓`),k.red(`✖`),k.yellow(`⚠`),k.blue(`ℹ`);function Tt(e){O.intro(k.bgCyan(k.black(` ${e} `)))}function F(e){O.outro(e)}function Et(e){O.isCancel(e)&&(O.cancel(`Operation cancelled.`),process.exit(0))}async function Dt(e){let t=await O.text(e);return Et(t),t}async function Ot(e){let t=await O.select(e);return Et(t),t}async function kt(e){let t=await O.multiselect(e);return Et(t),t}async function I(e){let t=await O.confirm(e);return Et(t),t}function At(){return O.spinner()}const L=O.log,jt={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},zod:{pkg:`zod`,peers:[],description:`Zod schema validation (env, DTOs, OpenAPI) — wrap with fromZod()`},valibot:{pkg:`valibot`,peers:[],description:`Valibot schema validation — wrap with fromValibot()`},yup:{pkg:`yup`,peers:[],description:`Yup schema validation — wrap with fromYup()`},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`},pg:{pkg:`@forinda/kickjs-db`,peers:[`pg`],description:`kick/db + PostgreSQL driver (use @forinda/kickjs-db/pg)`},sqlite:{pkg:`@forinda/kickjs-db`,peers:[`better-sqlite3`],description:`kick/db + SQLite driver (use @forinda/kickjs-db/sqlite)`},mysql:{pkg:`@forinda/kickjs-db`,peers:[`mysql2`],description:`kick/db + MySQL driver (use @forinda/kickjs-db/mysql)`},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},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`},"queue:redis-pubsub":{pkg:`@forinda/kickjs-queue`,peers:[`ioredis`],description:`Lightweight pub/sub via Redis (no persistence)`},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 Mt(e,t=process.cwd()){let n=t;for(;;){if(r(v(n,e)))return n;let t=f(n);if(t===n)return null;n=t}}function Nt(){return Mt(`pnpm-lock.yaml`)?`pnpm`:Mt(`yarn.lock`)?`yarn`:Mt(`bun.lockb`)||Mt(`bun.lock`)?`bun`:Mt(`package-lock.json`)?`npm`:null}function Pt(){let e=process.cwd();for(;e;){let t=v(e,`package.json`);if(r(t))try{let e=JSON.parse(a(t,`utf-8`)).packageManager;if(typeof e==`string`){let t=e.split(`@`)[0];if(Ce.includes(t))return t}}catch{}let n=f(e);if(n===e)return null;e=n}return null}async function Ft(e){if(e&&Ce.includes(e))return{pm:e,source:`flag`};let t=await M(process.cwd());if(t?.packageManager&&Ce.includes(t.packageManager))return{pm:t.packageManager,source:`config`};let n=Pt();if(n)return{pm:n,source:`package.json`};let r=Nt();return r?{pm:r,source:`lockfile`}:{pm:`npm`,source:`default`}}async function It(e){let{pm:t}=await Ft(e);return t}function Lt(e=!1){let t=Object.entries(jt),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(`
|
|
1251
1195
|
Core packages (always installed by \`kick new\`):
|
|
1252
1196
|
`);for(let e of r)console.log(a(e));if(e){console.log(`
|
|
1253
1197
|
Optional packages (add as needed):
|
|
1254
1198
|
`);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(`
|
|
1255
|
-
Usage: kick add auth drizzle swagger`),console.log(` kick add queue:bullmq`),console.log()}function
|
|
1256
|
-
`),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{
|
|
1257
|
-
`)}})}const
|
|
1258
|
-
* ${t} Module
|
|
1259
|
-
*
|
|
1260
|
-
* Self-contained feature module following Domain-Driven Design (DDD).
|
|
1261
|
-
* Registers dependencies in the DI container and declares HTTP routes.
|
|
1262
|
-
*
|
|
1263
|
-
* Structure:
|
|
1264
|
-
* presentation/ — HTTP controllers (entry points)
|
|
1265
|
-
* application/ — Use cases (orchestration) and DTOs (validation)
|
|
1266
|
-
* domain/ — Entities, value objects, repository interfaces, domain services
|
|
1267
|
-
* infrastructure/ — Repository implementations (currently ${vn(i)})
|
|
1268
|
-
*/`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './domain/repositories/${n}.repository'
|
|
1269
|
-
import { ${o} } from './infrastructure/repositories/${s}.repository'
|
|
1270
|
-
import { ${t}Controller } from './presentation/${n}.controller'
|
|
1271
|
-
|
|
1272
|
-
// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
|
|
1273
|
-
import.meta.glob(
|
|
1274
|
-
['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],
|
|
1275
|
-
{ eager: true },
|
|
1276
|
-
)`,d=` /**
|
|
1277
|
-
* Declare HTTP routes for this module. Return value shape:
|
|
1278
|
-
*
|
|
1279
|
-
* - \`path\` — URL prefix for this route set, mounted under
|
|
1280
|
-
* \`/{apiPrefix}/v{version}{path}\`.
|
|
1281
|
-
* - \`controller\` — Controller class. Used both for the route
|
|
1282
|
-
* handler bindings and OpenAPI spec generation.
|
|
1283
|
-
* - \`version\` — Optional. Overrides the app-wide API version
|
|
1284
|
-
* for this route set only.
|
|
1285
|
-
*
|
|
1286
|
-
* Return an **array** to mount multiple route sets under the
|
|
1287
|
-
* same module (e.g. side-by-side v1 + v2 controllers):
|
|
1288
|
-
*
|
|
1289
|
-
* return [
|
|
1290
|
-
* { path: '/${r}', version: 1, controller: ${t}V1Controller },
|
|
1291
|
-
* { path: '/${r}', version: 2, controller: ${t}V2Controller },
|
|
1292
|
-
* ]
|
|
1293
|
-
*/`;return c===`class`?`${l}
|
|
1294
|
-
import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
|
|
1295
|
-
${u}
|
|
1296
|
-
|
|
1297
|
-
export class ${t}Module implements AppModule {
|
|
1298
|
-
/**
|
|
1299
|
-
* Register module dependencies in the DI container.
|
|
1300
|
-
* Bind repository interface tokens to their implementations here.
|
|
1301
|
-
* Currently wired to ${vn(i)}. To swap implementations, change the factory target.
|
|
1302
|
-
*/
|
|
1303
|
-
register(container: Container): void {
|
|
1304
|
-
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
1305
|
-
container.resolve(${o}),
|
|
1306
|
-
)
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
|
|
1310
|
-
routes(): ModuleRoutes {
|
|
1311
|
-
return {
|
|
1312
|
-
path: '/${r}',
|
|
1313
|
-
controller: ${t}Controller,
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
`:`${l}
|
|
1318
|
-
import { defineModule } from '@forinda/kickjs'
|
|
1319
|
-
${u}
|
|
1320
|
-
|
|
1321
|
-
export const ${t}Module = defineModule({
|
|
1322
|
-
name: '${t}Module',
|
|
1323
|
-
build: () => ({
|
|
1324
|
-
/**
|
|
1325
|
-
* Register module dependencies in the DI container.
|
|
1326
|
-
* Bind repository interface tokens to their implementations here.
|
|
1327
|
-
* Currently wired to ${vn(i)}. To swap implementations, change the factory target.
|
|
1328
|
-
*/
|
|
1329
|
-
register(container) {
|
|
1330
|
-
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
1331
|
-
container.resolve(${o}),
|
|
1332
|
-
)
|
|
1333
|
-
},
|
|
1334
|
-
|
|
1335
|
-
${d}
|
|
1336
|
-
routes() {
|
|
1337
|
-
return {
|
|
1338
|
-
path: '/${r}',
|
|
1339
|
-
controller: ${t}Controller,
|
|
1340
|
-
}
|
|
1341
|
-
},
|
|
1342
|
-
}),
|
|
1343
|
-
})
|
|
1344
|
-
`}function Sn(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=yn(t,n,i),c=bn(a),l=`/**
|
|
1199
|
+
Usage: kick add auth drizzle swagger`),console.log(` kick add queue:bullmq`),console.log()}function Rt(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=>{Lt(!!e.all)})}function zt(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){Lt(!!t.all);return}let{pm:n,source:r}=await Ft(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=jt[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.
|
|
1200
|
+
`),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{C(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{C(t,{stdio:`inherit`})}catch{console.log(`\n Installation failed. Run manually:\n ${t}\n`)}}console.log(` Done!
|
|
1201
|
+
`)}})}const Bt=[{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 Vt(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 | minimal`).option(`-r, --repo <type>`,`Repository name (inmemory, or any DB name e.g. postgres)`).option(`-s, --schema <lib>`,`Schema library for env / DTOs: zod | valibot | yup (default: zod)`).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)=>{Tt(`KickJS — Create a new project`);let n=!!(t.yes||t.nonInteractive);e||=n?`my-api`:await Dt({message:`Project name`,placeholder:`my-api`,defaultValue:`my-api`});let i;if(e===`.`?(i=v(`.`),e=d(i)):i=v(t.directory||e),r(i)){let r=o(i);if(r.length>0){if(t.force)L.warn(`Clearing existing files in ${i}`);else if(n){L.warn(`Directory "${e}" is not empty. Pass --force to clear it.`),F(`Aborted.`);return}else{L.warn(`Directory "${e}" is not empty:`);let t=r.slice(0,5);for(let e of t)L.message(` - ${e}`);if(r.length>5&&L.message(` ... and ${r.length-5} more`),!await I({message:k.red(`Remove all existing files and proceed?`),initialValue:!1})){F(`Aborted.`);return}}for(let e of r)s(v(i,e),{recursive:!0,force:!0})}}let a=t.template;a||=n?`minimal`:await Ot({message:`Project template`,options:[{value:`rest`,label:`REST API`,hint:`Express + Swagger`},{value:`minimal`,label:`Minimal`,hint:`bare Express`}]});let c=t.pm;c||=n?await It(void 0):await Ot({message:`Package manager`,options:[{value:`pnpm`,label:`pnpm`},{value:`npm`,label:`npm`},{value:`yarn`,label:`yarn`},{value:`bun`,label:`bun`}]});let l=t.repo;l||=n?`inmemory`:await Dt({message:`Repository name`,placeholder:`inmemory (or a DB name, e.g. postgres)`,defaultValue:`inmemory`}),Ee(l);let u=t.schema;u||=n?`zod`:await Ot({message:`Schema library (env + DTO validation)`,options:[{value:`zod`,label:`Zod`,hint:`default — broad ecosystem`},{value:`valibot`,label:`Valibot`,hint:`smaller bundle`},{value:`yup`,label:`Yup`,hint:`classic API`}]}),[`zod`,`valibot`,`yup`].includes(u)||(L.warn(`Unknown --schema "${u}", falling back to zod.`),u=`zod`);let f;if(t.packages!==void 0){let e=t.packages.trim().toLowerCase();f=e===``||e===`none`||e===`false`?[]:t.packages.split(`,`).map(e=>e.trim()).filter(Boolean)}else f=n?[]:await kt({message:`Select packages to include`,options:[...Bt],required:!1});let p;p=t.git===void 0?n?!0:await I({message:`Initialize git repository?`,initialValue:!0}):t.git;let m;m=t.install===void 0?n?!0:await I({message:`Install dependencies?`,initialValue:!0}):t.install,await xt({name:e,directory:i,packageManager:c,initGit:p,installDeps:m,template:a,defaultRepo:l,packages:f,schemaLib:u}),F(`Done! Next steps: ${k.cyan(`cd ${e} && ${c} dev`)}`)})}function R(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function z(e){let t=R(e);return t.charAt(0).toLowerCase()+t.slice(1)}function B(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function V(e){return ue.plural(e)}function Ht(e){return ue.plural(e)}var Ut=A({findProjectRoot:()=>Gt});const Wt=[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`,`kick.config.json`];function Gt(e=process.cwd()){let t=v(e),{root:n}=g(t),i=null,a=t;for(;;){for(let e of Wt)if(r(v(a,e)))return a;if(i===null&&r(v(a,`package.json`))&&(i=a),a===n)break;let e=f(a);if(e===a)break;a=e}return i??t}function Kt(e){return B(e).replace(/-/g,`_`)}function qt(e){let t=e.cwd??process.cwd(),n=e.projectRoot??Gt(t),r=e.pluralize??!0,i=R(e.name),a=z(e.name),o=B(e.name),s=Kt(e.name),c={name:e.name,pascal:i,camel:a,kebab:o,snake:s,modulesDir:e.modulesDir??`src/modules`,cwd:t,projectRoot:n,args:e.args??[],flags:e.flags??{}};if(r){let e=V(o);c.pluralKebab=e,c.pluralPascal=R(e),c.pluralCamel=z(e)}return c}function Jt(e,t){return v(e.cwd,t)}async function Yt(e){return import(x(e).href)}const Xt=new Map;async function Zt(e){let t=Xt.get(e);if(t)return t;let n=Qt(e);return Xt.set(e,n),n}async function Qt(t){let n=v(t,`package.json`);if(!r(n))return{generators:[],loaded:[],failed:[]};let i=$t(JSON.parse(await T(n,`utf-8`))),a=e(v(t,`package.json`)),o=[],s=[],c=[];for(let e of i){let t;try{t=a.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await T(t,`utf-8`))}catch(t){c.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let i=n.kickjs.generators,l=v(f(t),i);if(!r(l)){c.push({source:e,reason:`kickjs.generators points to missing file: ${i}`});continue}let u;try{u=await Yt(l)}catch(t){c.push({source:e,reason:`failed to import manifest: ${t}`});continue}let d=u.default;if(!Array.isArray(d)){c.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of d){if(!en(t)){c.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}o.push({source:e,spec:t})}s.push(e)}return{generators:o,loaded:s,failed:c}}function $t(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 en(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function tn(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return an(r.spec,r.source,e,n);let i=rn(await Zt(n),e.generatorName);return i?an(i.spec,i.source,e,n):null}async function nn(e,t=[]){let n=await Zt(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 rn(e,t){return e.generators.find(e=>e.spec.name===t)}async function an(e,t,n,r){let i=qt({name:n.itemName,args:n.args,flags:n.flags,modulesDir:n.modulesDir,pluralize:n.pluralize,cwd:r,projectRoot:n.projectRoot}),a=await e.files(i),o=[];for(let e of a){let t=Jt(i,e.path);await P(t,e.content),o.push(t)}return{files:o,source:t}}function H(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function on(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function sn(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function cn(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]??`${on(n)}${e}Repository`,repoFile:i[n]??`${sn(n)}-${t}`}}function ln(e){return e??`define`}function un(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=cn(t,n,i),c=ln(a),l=`/**
|
|
1345
1202
|
* ${t} Module
|
|
1346
1203
|
*
|
|
1347
1204
|
* REST module with a flat folder structure.
|
|
@@ -1413,7 +1270,7 @@ ${d}
|
|
|
1413
1270
|
},
|
|
1414
1271
|
}),
|
|
1415
1272
|
})
|
|
1416
|
-
`}function
|
|
1273
|
+
`}function dn(e){let{pascal:t,kebab:n,plural:r=``,style:i}=e,a=ln(i),o=` /**
|
|
1417
1274
|
* Declare HTTP routes. Return value shape:
|
|
1418
1275
|
*
|
|
1419
1276
|
* - \`path\` — URL prefix for this route set.
|
|
@@ -1453,70 +1310,7 @@ ${o}
|
|
|
1453
1310
|
},
|
|
1454
1311
|
}),
|
|
1455
1312
|
})
|
|
1456
|
-
`}function
|
|
1457
|
-
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
1458
|
-
import { Create${t}UseCase } from '../application/use-cases/create-${n}.use-case'
|
|
1459
|
-
import { Get${t}UseCase } from '../application/use-cases/get-${n}.use-case'
|
|
1460
|
-
import { List${i}UseCase } from '../application/use-cases/list-${r}.use-case'
|
|
1461
|
-
import { Update${t}UseCase } from '../application/use-cases/update-${n}.use-case'
|
|
1462
|
-
import { Delete${t}UseCase } from '../application/use-cases/delete-${n}.use-case'
|
|
1463
|
-
import { create${t}Schema } from '../application/dtos/create-${n}.dto'
|
|
1464
|
-
import { update${t}Schema } from '../application/dtos/update-${n}.dto'
|
|
1465
|
-
import { ${t.toUpperCase()}_QUERY_CONFIG } from '../constants'
|
|
1466
|
-
|
|
1467
|
-
// Each handler annotates its \`ctx\` with \`Ctx<KickRoutes.${t}Controller['<method>']>\`
|
|
1468
|
-
// so \`ctx.params\`, \`ctx.body\`, and \`ctx.query\` are typed end-to-end.
|
|
1469
|
-
// The \`KickRoutes\` namespace is generated by \`kick typegen\` (auto-run on
|
|
1470
|
-
// \`kick dev\`) — see https://forinda.github.io/kick-js/guide/typegen.
|
|
1471
|
-
|
|
1472
|
-
@Controller()
|
|
1473
|
-
export class ${t}Controller {
|
|
1474
|
-
@Autowired() private readonly create${t}UseCase!: Create${t}UseCase
|
|
1475
|
-
@Autowired() private readonly get${t}UseCase!: Get${t}UseCase
|
|
1476
|
-
@Autowired() private readonly list${i}UseCase!: List${i}UseCase
|
|
1477
|
-
@Autowired() private readonly update${t}UseCase!: Update${t}UseCase
|
|
1478
|
-
@Autowired() private readonly delete${t}UseCase!: Delete${t}UseCase
|
|
1479
|
-
|
|
1480
|
-
@Get('/')
|
|
1481
|
-
@ApiTags('${t}')
|
|
1482
|
-
@ApiQueryParams(${t.toUpperCase()}_QUERY_CONFIG)
|
|
1483
|
-
async list(ctx: Ctx<KickRoutes.${t}Controller['list']>) {
|
|
1484
|
-
return ctx.paginate(
|
|
1485
|
-
(parsed) => this.list${i}UseCase.execute(parsed),
|
|
1486
|
-
${t.toUpperCase()}_QUERY_CONFIG,
|
|
1487
|
-
)
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
@Get('/:id')
|
|
1491
|
-
@ApiTags('${t}')
|
|
1492
|
-
async getById(ctx: Ctx<KickRoutes.${t}Controller['getById']>) {
|
|
1493
|
-
const result = await this.get${t}UseCase.execute(ctx.params.id)
|
|
1494
|
-
if (!result) return ctx.notFound('${t} not found')
|
|
1495
|
-
ctx.json(result)
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
@Post('/', { body: create${t}Schema, name: 'Create${t}' })
|
|
1499
|
-
@ApiTags('${t}')
|
|
1500
|
-
async create(ctx: Ctx<KickRoutes.${t}Controller['create']>) {
|
|
1501
|
-
const result = await this.create${t}UseCase.execute(ctx.body)
|
|
1502
|
-
ctx.created(result)
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
@Put('/:id', { body: update${t}Schema, name: 'Update${t}' })
|
|
1506
|
-
@ApiTags('${t}')
|
|
1507
|
-
async update(ctx: Ctx<KickRoutes.${t}Controller['update']>) {
|
|
1508
|
-
const result = await this.update${t}UseCase.execute(ctx.params.id, ctx.body)
|
|
1509
|
-
ctx.json(result)
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
@Delete('/:id')
|
|
1513
|
-
@ApiTags('${t}')
|
|
1514
|
-
async remove(ctx: Ctx<KickRoutes.${t}Controller['remove']>) {
|
|
1515
|
-
await this.delete${t}UseCase.execute(ctx.params.id)
|
|
1516
|
-
ctx.noContent()
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
`}function Tn(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'
|
|
1313
|
+
`}function fn(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'
|
|
1520
1314
|
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
1521
1315
|
import { ${t}Service } from './${n}.service'
|
|
1522
1316
|
import { create${t}Schema } from './dtos/create-${n}.dto'
|
|
@@ -1571,14 +1365,7 @@ export class ${t}Controller {
|
|
|
1571
1365
|
ctx.noContent()
|
|
1572
1366
|
}
|
|
1573
1367
|
}
|
|
1574
|
-
`}function
|
|
1575
|
-
|
|
1576
|
-
export const ${t.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {
|
|
1577
|
-
filterable: ['name'],
|
|
1578
|
-
sortable: ['name', 'createdAt'],
|
|
1579
|
-
searchable: ['name'],
|
|
1580
|
-
}
|
|
1581
|
-
`}function Dn(e){let{pascal:t}=e;return`import { z } from 'zod'
|
|
1368
|
+
`}function pn(e){let{pascal:t}=e;return`import { z } from 'zod'
|
|
1582
1369
|
|
|
1583
1370
|
/**
|
|
1584
1371
|
* Create ${t} DTO — Zod schema for validating POST request bodies.
|
|
@@ -1594,98 +1381,20 @@ export const create${t}Schema = z.object({
|
|
|
1594
1381
|
})
|
|
1595
1382
|
|
|
1596
1383
|
export type Create${t}DTO = z.infer<typeof create${t}Schema>
|
|
1597
|
-
`}function
|
|
1384
|
+
`}function mn(e){let{pascal:t}=e;return`import { z } from 'zod'
|
|
1598
1385
|
|
|
1599
1386
|
export const update${t}Schema = z.object({
|
|
1600
1387
|
name: z.string().min(1).max(200).optional(),
|
|
1601
1388
|
})
|
|
1602
1389
|
|
|
1603
1390
|
export type Update${t}DTO = z.infer<typeof update${t}Schema>
|
|
1604
|
-
`}function
|
|
1391
|
+
`}function hn(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
|
|
1605
1392
|
id: string
|
|
1606
1393
|
name: string
|
|
1607
1394
|
createdAt: string
|
|
1608
1395
|
updatedAt: string
|
|
1609
1396
|
}
|
|
1610
|
-
`}function
|
|
1611
|
-
* Create ${t} Use Case
|
|
1612
|
-
*
|
|
1613
|
-
* Application layer — orchestrates a single business operation.
|
|
1614
|
-
* Use cases are thin: validate input (via DTO), call domain/repo, return response.
|
|
1615
|
-
* Keep business rules in the domain service, not here.
|
|
1616
|
-
*/
|
|
1617
|
-
import { Service, Inject } from '@forinda/kickjs'
|
|
1618
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../../domain/repositories/${n}.repository'
|
|
1619
|
-
import type { Create${t}DTO } from '../dtos/create-${n}.dto'
|
|
1620
|
-
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
1621
|
-
|
|
1622
|
-
@Service()
|
|
1623
|
-
export class Create${t}UseCase {
|
|
1624
|
-
constructor(
|
|
1625
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
1626
|
-
) {}
|
|
1627
|
-
|
|
1628
|
-
async execute(dto: Create${t}DTO): Promise<${t}ResponseDTO> {
|
|
1629
|
-
return this.repo.create(dto)
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
`},{file:`get-${n}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
1633
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../../domain/repositories/${n}.repository'
|
|
1634
|
-
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
1635
|
-
|
|
1636
|
-
@Service()
|
|
1637
|
-
export class Get${t}UseCase {
|
|
1638
|
-
constructor(
|
|
1639
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
1640
|
-
) {}
|
|
1641
|
-
|
|
1642
|
-
async execute(id: string): Promise<${t}ResponseDTO | null> {
|
|
1643
|
-
return this.repo.findById(id)
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
`},{file:`list-${r}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
1647
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../../domain/repositories/${n}.repository'
|
|
1648
|
-
import type { ParsedQuery } from '@forinda/kickjs'
|
|
1649
|
-
|
|
1650
|
-
@Service()
|
|
1651
|
-
export class List${i}UseCase {
|
|
1652
|
-
constructor(
|
|
1653
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
1654
|
-
) {}
|
|
1655
|
-
|
|
1656
|
-
async execute(parsed: ParsedQuery) {
|
|
1657
|
-
return this.repo.findPaginated(parsed)
|
|
1658
|
-
}
|
|
1659
|
-
}
|
|
1660
|
-
`},{file:`update-${n}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
1661
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../../domain/repositories/${n}.repository'
|
|
1662
|
-
import type { Update${t}DTO } from '../dtos/update-${n}.dto'
|
|
1663
|
-
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
1664
|
-
|
|
1665
|
-
@Service()
|
|
1666
|
-
export class Update${t}UseCase {
|
|
1667
|
-
constructor(
|
|
1668
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
1669
|
-
) {}
|
|
1670
|
-
|
|
1671
|
-
async execute(id: string, dto: Update${t}DTO): Promise<${t}ResponseDTO> {
|
|
1672
|
-
return this.repo.update(id, dto)
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
`},{file:`delete-${n}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
1676
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../../domain/repositories/${n}.repository'
|
|
1677
|
-
|
|
1678
|
-
@Service()
|
|
1679
|
-
export class Delete${t}UseCase {
|
|
1680
|
-
constructor(
|
|
1681
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
1682
|
-
) {}
|
|
1683
|
-
|
|
1684
|
-
async execute(id: string): Promise<void> {
|
|
1685
|
-
await this.repo.delete(id)
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
`}]}function jn(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
|
|
1397
|
+
`}function gn(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
|
|
1689
1398
|
* ${t} Repository Interface
|
|
1690
1399
|
*
|
|
1691
1400
|
* Defines the contract for data access.
|
|
@@ -1720,7 +1429,7 @@ export interface I${t}Repository {
|
|
|
1720
1429
|
* adopters must NOT use the reserved \`'kick/'\` namespace.
|
|
1721
1430
|
*/
|
|
1722
1431
|
export const ${t.toUpperCase()}_REPOSITORY = createToken<I${t}Repository>('${i}/${t}/repository')
|
|
1723
|
-
`}function
|
|
1432
|
+
`}function _n(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
|
|
1724
1433
|
* In-Memory ${t} Repository
|
|
1725
1434
|
*
|
|
1726
1435
|
* Implements the repository interface using a Map.
|
|
@@ -1759,7 +1468,7 @@ export class InMemory${t}Repository implements I${t}Repository {
|
|
|
1759
1468
|
const now = new Date().toISOString()
|
|
1760
1469
|
const entity: ${t}ResponseDTO = {
|
|
1761
1470
|
id: randomUUID(),
|
|
1762
|
-
|
|
1471
|
+
...dto,
|
|
1763
1472
|
createdAt: now,
|
|
1764
1473
|
updatedAt: now,
|
|
1765
1474
|
}
|
|
@@ -1780,7 +1489,7 @@ export class InMemory${t}Repository implements I${t}Repository {
|
|
|
1780
1489
|
this.store.delete(id)
|
|
1781
1490
|
}
|
|
1782
1491
|
}
|
|
1783
|
-
`}function
|
|
1492
|
+
`}function vn(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`/**
|
|
1784
1493
|
* ${o} ${t} Repository
|
|
1785
1494
|
*
|
|
1786
1495
|
* Stub implementation for a custom '${r}' repository.
|
|
@@ -1826,7 +1535,7 @@ export class ${o}${t}Repository implements I${t}Repository {
|
|
|
1826
1535
|
const now = new Date().toISOString()
|
|
1827
1536
|
const entity: ${t}ResponseDTO = {
|
|
1828
1537
|
id: randomUUID(),
|
|
1829
|
-
|
|
1538
|
+
...dto,
|
|
1830
1539
|
createdAt: now,
|
|
1831
1540
|
updatedAt: now,
|
|
1832
1541
|
}
|
|
@@ -1849,133 +1558,7 @@ export class ${o}${t}Repository implements I${t}Repository {
|
|
|
1849
1558
|
this.store.delete(id)
|
|
1850
1559
|
}
|
|
1851
1560
|
}
|
|
1852
|
-
`}function
|
|
1853
|
-
* ${t} Domain Service
|
|
1854
|
-
*
|
|
1855
|
-
* Domain layer — contains business rules that don't belong to a single entity.
|
|
1856
|
-
* Use this for cross-entity logic, validation rules, and domain invariants.
|
|
1857
|
-
* Keep it free of HTTP/framework concerns.
|
|
1858
|
-
*/
|
|
1859
|
-
import { Service, Inject, HttpException } from '@forinda/kickjs'
|
|
1860
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../repositories/${n}.repository'
|
|
1861
|
-
|
|
1862
|
-
@Service()
|
|
1863
|
-
export class ${t}DomainService {
|
|
1864
|
-
constructor(
|
|
1865
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
1866
|
-
) {}
|
|
1867
|
-
|
|
1868
|
-
async ensureExists(id: string): Promise<void> {
|
|
1869
|
-
const entity = await this.repo.findById(id)
|
|
1870
|
-
if (!entity) {
|
|
1871
|
-
throw HttpException.notFound('${t} not found')
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
`}function Pn(e){let{pascal:t,kebab:n}=e;return`/**
|
|
1876
|
-
* ${t} Entity
|
|
1877
|
-
*
|
|
1878
|
-
* Domain layer — the core business object.
|
|
1879
|
-
* Uses a private constructor with static factory methods (create, reconstitute)
|
|
1880
|
-
* to enforce invariants. Properties are accessed via getters to maintain encapsulation.
|
|
1881
|
-
*
|
|
1882
|
-
* Patterns used:
|
|
1883
|
-
* - Private constructor: prevents direct instantiation
|
|
1884
|
-
* - create(): factory for new entities (generates ID, sets timestamps)
|
|
1885
|
-
* - reconstitute(): factory for rebuilding from persistence (no side effects)
|
|
1886
|
-
* - changeName(): mutation method that enforces business rules
|
|
1887
|
-
*/
|
|
1888
|
-
import { ${t}Id } from '../value-objects/${n}-id.vo'
|
|
1889
|
-
|
|
1890
|
-
interface ${t}Props {
|
|
1891
|
-
id: ${t}Id
|
|
1892
|
-
name: string
|
|
1893
|
-
createdAt: Date
|
|
1894
|
-
updatedAt: Date
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
export class ${t} {
|
|
1898
|
-
private constructor(private props: ${t}Props) {}
|
|
1899
|
-
|
|
1900
|
-
static create(params: { name: string }): ${t} {
|
|
1901
|
-
const now = new Date()
|
|
1902
|
-
return new ${t}({
|
|
1903
|
-
id: ${t}Id.create(),
|
|
1904
|
-
name: params.name,
|
|
1905
|
-
createdAt: now,
|
|
1906
|
-
updatedAt: now,
|
|
1907
|
-
})
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
|
-
static reconstitute(props: ${t}Props): ${t} {
|
|
1911
|
-
return new ${t}(props)
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
get id(): ${t}Id {
|
|
1915
|
-
return this.props.id
|
|
1916
|
-
}
|
|
1917
|
-
get name(): string {
|
|
1918
|
-
return this.props.name
|
|
1919
|
-
}
|
|
1920
|
-
get createdAt(): Date {
|
|
1921
|
-
return this.props.createdAt
|
|
1922
|
-
}
|
|
1923
|
-
get updatedAt(): Date {
|
|
1924
|
-
return this.props.updatedAt
|
|
1925
|
-
}
|
|
1926
|
-
|
|
1927
|
-
changeName(name: string): void {
|
|
1928
|
-
if (!name || name.trim().length === 0) {
|
|
1929
|
-
throw new Error('Name cannot be empty')
|
|
1930
|
-
}
|
|
1931
|
-
this.props.name = name.trim()
|
|
1932
|
-
this.props.updatedAt = new Date()
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
toJSON() {
|
|
1936
|
-
return {
|
|
1937
|
-
id: this.props.id.toString(),
|
|
1938
|
-
name: this.props.name,
|
|
1939
|
-
createdAt: this.props.createdAt.toISOString(),
|
|
1940
|
-
updatedAt: this.props.updatedAt.toISOString(),
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
1944
|
-
`}function Fn(e){let{pascal:t}=e;return`/**
|
|
1945
|
-
* ${t} ID Value Object
|
|
1946
|
-
*
|
|
1947
|
-
* Domain layer — wraps a primitive ID with type safety and validation.
|
|
1948
|
-
* Value objects are immutable and compared by value, not reference.
|
|
1949
|
-
*
|
|
1950
|
-
* ${t}Id.create() — generate a new UUID
|
|
1951
|
-
* ${t}Id.from(id) — wrap an existing ID string (validates non-empty)
|
|
1952
|
-
* id.equals(other) — compare two IDs by value
|
|
1953
|
-
*/
|
|
1954
|
-
import { randomUUID } from 'node:crypto'
|
|
1955
|
-
|
|
1956
|
-
export class ${t}Id {
|
|
1957
|
-
private constructor(private readonly value: string) {}
|
|
1958
|
-
|
|
1959
|
-
static create(): ${t}Id {
|
|
1960
|
-
return new ${t}Id(randomUUID())
|
|
1961
|
-
}
|
|
1962
|
-
|
|
1963
|
-
static from(id: string): ${t}Id {
|
|
1964
|
-
if (!id || id.trim().length === 0) {
|
|
1965
|
-
throw new Error('${t}Id cannot be empty')
|
|
1966
|
-
}
|
|
1967
|
-
return new ${t}Id(id)
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
toString(): string {
|
|
1971
|
-
return this.value
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
|
-
equals(other: ${t}Id): boolean {
|
|
1975
|
-
return this.value === other.value
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
|
-
`}function In(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
|
|
1561
|
+
`}function yn(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
|
|
1979
1562
|
import { Container } from '@forinda/kickjs'
|
|
1980
1563
|
|
|
1981
1564
|
describe('${t}Controller', () => {
|
|
@@ -2027,7 +1610,7 @@ describe('${t}Controller', () => {
|
|
|
2027
1610
|
})
|
|
2028
1611
|
})
|
|
2029
1612
|
})
|
|
2030
|
-
`}function
|
|
1613
|
+
`}function bn(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'
|
|
2031
1614
|
import { InMemory${t}Repository } from '${i}'
|
|
2032
1615
|
|
|
2033
1616
|
describe('InMemory${t}Repository', () => {
|
|
@@ -2089,7 +1672,7 @@ describe('InMemory${t}Repository', () => {
|
|
|
2089
1672
|
expect(found).toBeNull()
|
|
2090
1673
|
})
|
|
2091
1674
|
})
|
|
2092
|
-
`}function
|
|
1675
|
+
`}function xn(e){let{pascal:t,kebab:n}=e;return`import { Service, Inject, HttpException } from '@forinda/kickjs'
|
|
2093
1676
|
import type { ParsedQuery } from '@forinda/kickjs'
|
|
2094
1677
|
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from './${n}.repository'
|
|
2095
1678
|
import type { ${t}ResponseDTO } from './dtos/${n}-response.dto'
|
|
@@ -2126,485 +1709,17 @@ export class ${t}Service {
|
|
|
2126
1709
|
await this.repo.delete(id)
|
|
2127
1710
|
}
|
|
2128
1711
|
}
|
|
2129
|
-
`}function
|
|
1712
|
+
`}function Sn(e){let{pascal:t}=e;return`import type { QueryFieldConfig } from '@forinda/kickjs'
|
|
2130
1713
|
|
|
2131
1714
|
export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
|
|
2132
1715
|
filterable: ['name'],
|
|
2133
1716
|
sortable: ['name', 'createdAt'],
|
|
2134
1717
|
searchable: ['name'],
|
|
2135
1718
|
}
|
|
2136
|
-
`}function
|
|
2137
|
-
* ${t} Module — CQRS Pattern
|
|
2138
|
-
*
|
|
2139
|
-
* Separates read (queries) and write (commands) operations.
|
|
2140
|
-
* Events are emitted after state changes and can be handled via
|
|
2141
|
-
* WebSocket broadcasts, queue jobs, or ETL pipelines.
|
|
2142
|
-
*
|
|
2143
|
-
* Structure:
|
|
2144
|
-
* commands/ — Write operations (create, update, delete)
|
|
2145
|
-
* queries/ — Read operations (get, list)
|
|
2146
|
-
* events/ — Domain events + handlers (WS broadcast, queue dispatch)
|
|
2147
|
-
* dtos/ — Request/response schemas
|
|
2148
|
-
*/`,f=`import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
|
|
2149
|
-
import { ${c} } from './${l}.repository'
|
|
2150
|
-
import { ${t}Controller } from './${n}.controller'
|
|
1719
|
+
`}async function Cn(e){let{pascal:t,kebab:n,plural:r,style:i,write:a}=e;await a(`${n}.module.ts`,dn({pascal:t,kebab:n,plural:r,style:i})),await a(`${n}.controller.ts`,`import { Controller, Get, type Ctx } from '@forinda/kickjs'
|
|
2151
1720
|
|
|
2152
|
-
//
|
|
2153
|
-
|
|
2154
|
-
[
|
|
2155
|
-
'./commands/**/*.ts',
|
|
2156
|
-
'./queries/**/*.ts',
|
|
2157
|
-
'./events/**/*.ts',
|
|
2158
|
-
'!./**/*.test.ts',
|
|
2159
|
-
],
|
|
2160
|
-
{ eager: true },
|
|
2161
|
-
)`,p=` /**
|
|
2162
|
-
* Declare HTTP routes for this CQRS module. Return value shape:
|
|
2163
|
-
*
|
|
2164
|
-
* - \`path\` — URL prefix for this route set.
|
|
2165
|
-
* - \`controller\` — Controller class (also drives OpenAPI).
|
|
2166
|
-
* - \`version\` — Optional. Overrides the app-wide API version.
|
|
2167
|
-
*
|
|
2168
|
-
* Return an array to mount multiple route sets:
|
|
2169
|
-
*
|
|
2170
|
-
* return [
|
|
2171
|
-
* { path: '/${r}', version: 1, controller: ${t}V1Controller },
|
|
2172
|
-
* { path: '/${r}', version: 2, controller: ${t}V2Controller },
|
|
2173
|
-
* ]
|
|
2174
|
-
*/`;return u===`class`?`${d}
|
|
2175
|
-
import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
|
|
2176
|
-
${f}
|
|
2177
|
-
|
|
2178
|
-
export class ${t}Module implements AppModule {
|
|
2179
|
-
register(container: Container): void {
|
|
2180
|
-
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
2181
|
-
container.resolve(${c}),
|
|
2182
|
-
)
|
|
2183
|
-
}
|
|
2184
|
-
|
|
2185
|
-
${p.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
|
|
2186
|
-
routes(): ModuleRoutes {
|
|
2187
|
-
return {
|
|
2188
|
-
path: '/${r}',
|
|
2189
|
-
controller: ${t}Controller,
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
`:`${d}
|
|
2194
|
-
import { defineModule } from '@forinda/kickjs'
|
|
2195
|
-
${f}
|
|
2196
|
-
|
|
2197
|
-
export const ${t}Module = defineModule({
|
|
2198
|
-
name: '${t}Module',
|
|
2199
|
-
build: () => ({
|
|
2200
|
-
register(container) {
|
|
2201
|
-
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
2202
|
-
container.resolve(${c}),
|
|
2203
|
-
)
|
|
2204
|
-
},
|
|
2205
|
-
|
|
2206
|
-
${p}
|
|
2207
|
-
routes() {
|
|
2208
|
-
return {
|
|
2209
|
-
path: '/${r}',
|
|
2210
|
-
controller: ${t}Controller,
|
|
2211
|
-
}
|
|
2212
|
-
},
|
|
2213
|
-
}),
|
|
2214
|
-
})
|
|
2215
|
-
`}function Vn(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'
|
|
2216
|
-
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
2217
|
-
import { Create${t}Command } from './commands/create-${n}.command'
|
|
2218
|
-
import { Update${t}Command } from './commands/update-${n}.command'
|
|
2219
|
-
import { Delete${t}Command } from './commands/delete-${n}.command'
|
|
2220
|
-
import { Get${t}Query } from './queries/get-${n}.query'
|
|
2221
|
-
import { List${i}Query } from './queries/list-${r}.query'
|
|
2222
|
-
import { create${t}Schema } from './dtos/create-${n}.dto'
|
|
2223
|
-
import { update${t}Schema } from './dtos/update-${n}.dto'
|
|
2224
|
-
import { ${t.toUpperCase()}_QUERY_CONFIG } from './${n}.constants'
|
|
2225
|
-
|
|
2226
|
-
// Each handler annotates its \`ctx\` with \`Ctx<KickRoutes.${t}Controller['<method>']>\`
|
|
2227
|
-
// so \`ctx.params\`, \`ctx.body\`, and \`ctx.query\` are typed end-to-end.
|
|
2228
|
-
// The \`KickRoutes\` namespace is generated by \`kick typegen\` (auto-run on
|
|
2229
|
-
// \`kick dev\`) — see https://forinda.github.io/kick-js/guide/typegen.
|
|
2230
|
-
|
|
2231
|
-
@Controller()
|
|
2232
|
-
export class ${t}Controller {
|
|
2233
|
-
@Autowired() private readonly create${t}Command!: Create${t}Command
|
|
2234
|
-
@Autowired() private readonly update${t}Command!: Update${t}Command
|
|
2235
|
-
@Autowired() private readonly delete${t}Command!: Delete${t}Command
|
|
2236
|
-
@Autowired() private readonly get${t}Query!: Get${t}Query
|
|
2237
|
-
@Autowired() private readonly list${i}Query!: List${i}Query
|
|
2238
|
-
|
|
2239
|
-
@Get('/')
|
|
2240
|
-
@ApiTags('${t}')
|
|
2241
|
-
@ApiQueryParams(${t.toUpperCase()}_QUERY_CONFIG)
|
|
2242
|
-
async list(ctx: Ctx<KickRoutes.${t}Controller['list']>) {
|
|
2243
|
-
return ctx.paginate(
|
|
2244
|
-
(parsed) => this.list${i}Query.execute(parsed),
|
|
2245
|
-
${t.toUpperCase()}_QUERY_CONFIG,
|
|
2246
|
-
)
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
|
-
@Get('/:id')
|
|
2250
|
-
@ApiTags('${t}')
|
|
2251
|
-
async getById(ctx: Ctx<KickRoutes.${t}Controller['getById']>) {
|
|
2252
|
-
const result = await this.get${t}Query.execute(ctx.params.id)
|
|
2253
|
-
if (!result) return ctx.notFound('${t} not found')
|
|
2254
|
-
ctx.json(result)
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
@Post('/', { body: create${t}Schema, name: 'Create${t}' })
|
|
2258
|
-
@ApiTags('${t}')
|
|
2259
|
-
async create(ctx: Ctx<KickRoutes.${t}Controller['create']>) {
|
|
2260
|
-
const result = await this.create${t}Command.execute(ctx.body)
|
|
2261
|
-
ctx.created(result)
|
|
2262
|
-
}
|
|
2263
|
-
|
|
2264
|
-
@Put('/:id', { body: update${t}Schema, name: 'Update${t}' })
|
|
2265
|
-
@ApiTags('${t}')
|
|
2266
|
-
async update(ctx: Ctx<KickRoutes.${t}Controller['update']>) {
|
|
2267
|
-
const result = await this.update${t}Command.execute(ctx.params.id, ctx.body)
|
|
2268
|
-
ctx.json(result)
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
@Delete('/:id')
|
|
2272
|
-
@ApiTags('${t}')
|
|
2273
|
-
async remove(ctx: Ctx<KickRoutes.${t}Controller['remove']>) {
|
|
2274
|
-
await this.delete${t}Command.execute(ctx.params.id)
|
|
2275
|
-
ctx.noContent()
|
|
2276
|
-
}
|
|
2277
|
-
}
|
|
2278
|
-
`}function Hn(e){let{pascal:t,kebab:n}=e;return[{file:`create-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
2279
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
2280
|
-
import type { Create${t}DTO } from '../dtos/create-${n}.dto'
|
|
2281
|
-
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
2282
|
-
import { ${t}Events } from '../events/${n}.events'
|
|
2283
|
-
|
|
2284
|
-
@Service()
|
|
2285
|
-
export class Create${t}Command {
|
|
2286
|
-
constructor(
|
|
2287
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
2288
|
-
@Inject(${t}Events) private readonly events: ${t}Events,
|
|
2289
|
-
) {}
|
|
2290
|
-
|
|
2291
|
-
async execute(dto: Create${t}DTO): Promise<${t}ResponseDTO> {
|
|
2292
|
-
const result = await this.repo.create(dto)
|
|
2293
|
-
this.events.emit('${n}.created', result)
|
|
2294
|
-
return result
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
`},{file:`update-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
2298
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
2299
|
-
import type { Update${t}DTO } from '../dtos/update-${n}.dto'
|
|
2300
|
-
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
2301
|
-
import { ${t}Events } from '../events/${n}.events'
|
|
2302
|
-
|
|
2303
|
-
@Service()
|
|
2304
|
-
export class Update${t}Command {
|
|
2305
|
-
constructor(
|
|
2306
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
2307
|
-
@Inject(${t}Events) private readonly events: ${t}Events,
|
|
2308
|
-
) {}
|
|
2309
|
-
|
|
2310
|
-
async execute(id: string, dto: Update${t}DTO): Promise<${t}ResponseDTO> {
|
|
2311
|
-
const result = await this.repo.update(id, dto)
|
|
2312
|
-
this.events.emit('${n}.updated', result)
|
|
2313
|
-
return result
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
`},{file:`delete-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
2317
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
2318
|
-
import { ${t}Events } from '../events/${n}.events'
|
|
2319
|
-
|
|
2320
|
-
@Service()
|
|
2321
|
-
export class Delete${t}Command {
|
|
2322
|
-
constructor(
|
|
2323
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
2324
|
-
@Inject(${t}Events) private readonly events: ${t}Events,
|
|
2325
|
-
) {}
|
|
2326
|
-
|
|
2327
|
-
async execute(id: string): Promise<void> {
|
|
2328
|
-
await this.repo.delete(id)
|
|
2329
|
-
this.events.emit('${n}.deleted', { id })
|
|
2330
|
-
}
|
|
2331
|
-
}
|
|
2332
|
-
`}]}function Un(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`get-${n}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
2333
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
2334
|
-
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
2335
|
-
|
|
2336
|
-
@Service()
|
|
2337
|
-
export class Get${t}Query {
|
|
2338
|
-
constructor(
|
|
2339
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
2340
|
-
) {}
|
|
2341
|
-
|
|
2342
|
-
async execute(id: string): Promise<${t}ResponseDTO | null> {
|
|
2343
|
-
return this.repo.findById(id)
|
|
2344
|
-
}
|
|
2345
|
-
}
|
|
2346
|
-
`},{file:`list-${r}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
2347
|
-
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
2348
|
-
import type { ParsedQuery } from '@forinda/kickjs'
|
|
2349
|
-
|
|
2350
|
-
@Service()
|
|
2351
|
-
export class List${i}Query {
|
|
2352
|
-
constructor(
|
|
2353
|
-
@Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
|
|
2354
|
-
) {}
|
|
2355
|
-
|
|
2356
|
-
async execute(parsed: ParsedQuery) {
|
|
2357
|
-
return this.repo.findPaginated(parsed)
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
`}]}function Wn(e){let{pascal:t,kebab:n}=e;return[{file:`${n}.events.ts`,content:`import { Service } from '@forinda/kickjs'
|
|
2361
|
-
import { EventEmitter } from 'node:events'
|
|
2362
|
-
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
2363
|
-
|
|
2364
|
-
/**
|
|
2365
|
-
* ${t} domain event types.
|
|
2366
|
-
*
|
|
2367
|
-
* These events are emitted by commands after state changes.
|
|
2368
|
-
* Subscribe to them in event handlers for side effects:
|
|
2369
|
-
* - WebSocket broadcasts (real-time UI updates)
|
|
2370
|
-
* - Queue jobs (async processing, ETL pipelines)
|
|
2371
|
-
* - Audit logging
|
|
2372
|
-
* - Cache invalidation
|
|
2373
|
-
*/
|
|
2374
|
-
export interface ${t}EventMap {
|
|
2375
|
-
'${n}.created': ${t}ResponseDTO
|
|
2376
|
-
'${n}.updated': ${t}ResponseDTO
|
|
2377
|
-
'${n}.deleted': { id: string }
|
|
2378
|
-
}
|
|
2379
|
-
|
|
2380
|
-
@Service()
|
|
2381
|
-
export class ${t}Events {
|
|
2382
|
-
private emitter = new EventEmitter()
|
|
2383
|
-
|
|
2384
|
-
emit<K extends keyof ${t}EventMap>(event: K, data: ${t}EventMap[K]): void {
|
|
2385
|
-
this.emitter.emit(event, data)
|
|
2386
|
-
}
|
|
2387
|
-
|
|
2388
|
-
on<K extends keyof ${t}EventMap>(event: K, handler: (data: ${t}EventMap[K]) => void): void {
|
|
2389
|
-
this.emitter.on(event, handler)
|
|
2390
|
-
}
|
|
2391
|
-
|
|
2392
|
-
off<K extends keyof ${t}EventMap>(event: K, handler: (data: ${t}EventMap[K]) => void): void {
|
|
2393
|
-
this.emitter.off(event, handler)
|
|
2394
|
-
}
|
|
2395
|
-
}
|
|
2396
|
-
`},{file:`on-${n}-change.handler.ts`,content:`import { Service, Autowired } from '@forinda/kickjs'
|
|
2397
|
-
import { ${t}Events } from './${n}.events'
|
|
2398
|
-
|
|
2399
|
-
/**
|
|
2400
|
-
* ${t} Change Event Handler
|
|
2401
|
-
*
|
|
2402
|
-
* Reacts to domain events emitted by commands.
|
|
2403
|
-
* Wire up side effects here:
|
|
2404
|
-
*
|
|
2405
|
-
* 1. WebSocket broadcast — notify connected clients in real-time
|
|
2406
|
-
* import { WsGateway } from '@forinda/kickjs-ws'
|
|
2407
|
-
* this.ws.broadcast('${n}-channel', { event, data })
|
|
2408
|
-
*
|
|
2409
|
-
* 2. Queue dispatch — offload heavy processing to background workers
|
|
2410
|
-
* import { QueueService } from '@forinda/kickjs-queue'
|
|
2411
|
-
* this.queue.add('${n}-etl', { action: event, payload: data })
|
|
2412
|
-
*
|
|
2413
|
-
* 3. ETL pipeline — transform and load data to external systems
|
|
2414
|
-
* await this.etlPipeline.process(data)
|
|
2415
|
-
*/
|
|
2416
|
-
@Service()
|
|
2417
|
-
export class On${t}ChangeHandler {
|
|
2418
|
-
@Autowired() private events!: ${t}Events
|
|
2419
|
-
|
|
2420
|
-
// Uncomment to inject WebSocket and Queue services:
|
|
2421
|
-
// @Autowired() private ws!: WsGateway
|
|
2422
|
-
// @Autowired() private queue!: QueueService
|
|
2423
|
-
|
|
2424
|
-
onInit(): void {
|
|
2425
|
-
this.events.on('${n}.created', (data) => {
|
|
2426
|
-
console.log('[${t}] Created:', data.id)
|
|
2427
|
-
// TODO: Broadcast via WebSocket
|
|
2428
|
-
// this.ws.broadcast('${n}-channel', { event: '${n}.created', data })
|
|
2429
|
-
// TODO: Dispatch to queue for async processing / ETL
|
|
2430
|
-
// this.queue.add('${n}-etl', { action: 'create', payload: data })
|
|
2431
|
-
})
|
|
2432
|
-
|
|
2433
|
-
this.events.on('${n}.updated', (data) => {
|
|
2434
|
-
console.log('[${t}] Updated:', data.id)
|
|
2435
|
-
// TODO: Broadcast via WebSocket
|
|
2436
|
-
// this.ws.broadcast('${n}-channel', { event: '${n}.updated', data })
|
|
2437
|
-
})
|
|
2438
|
-
|
|
2439
|
-
this.events.on('${n}.deleted', (data) => {
|
|
2440
|
-
console.log('[${t}] Deleted:', data.id)
|
|
2441
|
-
// TODO: Broadcast via WebSocket
|
|
2442
|
-
// this.ws.broadcast('${n}-channel', { event: '${n}.deleted', data })
|
|
2443
|
-
})
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
`}]}function Gn(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
|
|
2447
|
-
* Drizzle ${t} Repository
|
|
2448
|
-
*
|
|
2449
|
-
* Implements the repository interface using Drizzle ORM.
|
|
2450
|
-
* Uses buildFromColumns() with Column objects for type-safe query building.
|
|
2451
|
-
*
|
|
2452
|
-
* TODO: Update the schema import to match your Drizzle schema file.
|
|
2453
|
-
* TODO: Replace DRIZZLE_DB injection token with your actual database token.
|
|
2454
|
-
*
|
|
2455
|
-
* @Repository() registers this class in the DI container as a singleton.
|
|
2456
|
-
*/
|
|
2457
|
-
import { eq, ne, gt, gte, lt, lte, ilike, inArray, between, and, or, asc, desc, count, sql } from 'drizzle-orm'
|
|
2458
|
-
import { Repository, HttpException, Inject } from '@forinda/kickjs'
|
|
2459
|
-
import { DRIZZLE_DB, DrizzleQueryAdapter } from '@forinda/kickjs-drizzle'
|
|
2460
|
-
import type { ParsedQuery } from '@forinda/kickjs'
|
|
2461
|
-
import type { I${t}Repository } from '${r}/${n}.repository'
|
|
2462
|
-
import type { ${t}ResponseDTO } from '${i}/${n}-response.dto'
|
|
2463
|
-
import type { Create${t}DTO } from '${i}/create-${n}.dto'
|
|
2464
|
-
import type { Update${t}DTO } from '${i}/update-${n}.dto'
|
|
2465
|
-
import { ${t.toUpperCase()}_QUERY_CONFIG } from '../../constants'
|
|
2466
|
-
|
|
2467
|
-
// TODO: Import your Drizzle schema table — e.g.:
|
|
2468
|
-
// import { ${n}s } from '@/db/schema'
|
|
2469
|
-
|
|
2470
|
-
const queryAdapter = new DrizzleQueryAdapter({
|
|
2471
|
-
eq, ne, gt, gte, lt, lte, ilike, inArray, between, and, or, asc, desc,
|
|
2472
|
-
})
|
|
2473
|
-
|
|
2474
|
-
@Repository()
|
|
2475
|
-
export class Drizzle${t}Repository implements I${t}Repository {
|
|
2476
|
-
constructor(@Inject(DRIZZLE_DB) private db: any) {}
|
|
2477
|
-
|
|
2478
|
-
async findById(id: string): Promise<${t}ResponseDTO | null> {
|
|
2479
|
-
// TODO: Implement with Drizzle
|
|
2480
|
-
// const row = this.db.select().from(${n}s).where(eq(${n}s.id, id)).get()
|
|
2481
|
-
// return row ?? null
|
|
2482
|
-
throw new Error('Drizzle ${t} repository not yet implemented — update schema imports and queries')
|
|
2483
|
-
}
|
|
2484
|
-
|
|
2485
|
-
async findAll(): Promise<${t}ResponseDTO[]> {
|
|
2486
|
-
// TODO: Implement with Drizzle
|
|
2487
|
-
// return this.db.select().from(${n}s).all()
|
|
2488
|
-
throw new Error('Drizzle ${t} repository not yet implemented')
|
|
2489
|
-
}
|
|
2490
|
-
|
|
2491
|
-
async findPaginated(parsed: ParsedQuery): Promise<{ data: ${t}ResponseDTO[]; total: number }> {
|
|
2492
|
-
// TODO: Use buildFromColumns() with your query config for type-safe filtering
|
|
2493
|
-
// const query = queryAdapter.buildFromColumns(parsed, ${t.toUpperCase()}_QUERY_CONFIG)
|
|
2494
|
-
//
|
|
2495
|
-
// const data = this.db
|
|
2496
|
-
// .select().from(${n}s).$dynamic()
|
|
2497
|
-
// .where(query.where).orderBy(...query.orderBy)
|
|
2498
|
-
// .limit(query.limit).offset(query.offset).all()
|
|
2499
|
-
//
|
|
2500
|
-
// const totalResult = this.db
|
|
2501
|
-
// .select({ count: count() }).from(${n}s)
|
|
2502
|
-
// .$dynamic().where(query.where).get()
|
|
2503
|
-
//
|
|
2504
|
-
// return { data, total: totalResult?.count ?? 0 }
|
|
2505
|
-
throw new Error('Drizzle ${t} repository not yet implemented')
|
|
2506
|
-
}
|
|
2507
|
-
|
|
2508
|
-
async create(dto: Create${t}DTO): Promise<${t}ResponseDTO> {
|
|
2509
|
-
// TODO: Implement with Drizzle
|
|
2510
|
-
// return this.db.insert(${n}s).values(dto).returning().get()
|
|
2511
|
-
throw new Error('Drizzle ${t} repository not yet implemented')
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
async update(id: string, dto: Update${t}DTO): Promise<${t}ResponseDTO> {
|
|
2515
|
-
// TODO: Implement with Drizzle
|
|
2516
|
-
// const row = this.db.update(${n}s).set(dto).where(eq(${n}s.id, id)).returning().get()
|
|
2517
|
-
// if (!row) throw HttpException.notFound('${t} not found')
|
|
2518
|
-
// return row
|
|
2519
|
-
throw new Error('Drizzle ${t} repository not yet implemented')
|
|
2520
|
-
}
|
|
2521
|
-
|
|
2522
|
-
async delete(id: string): Promise<void> {
|
|
2523
|
-
// TODO: Implement with Drizzle
|
|
2524
|
-
// this.db.delete(${n}s).where(eq(${n}s.id, id)).run()
|
|
2525
|
-
throw new Error('Drizzle ${t} repository not yet implemented')
|
|
2526
|
-
}
|
|
2527
|
-
}
|
|
2528
|
-
`}function Kn(e){let{pascal:t,kebab:n}=e;return`import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
|
|
2529
|
-
// TODO: Import your schema table and reference actual columns for type safety
|
|
2530
|
-
// import { ${n}s } from '@/db/schema'
|
|
2531
|
-
|
|
2532
|
-
export const ${t.toUpperCase()}_QUERY_CONFIG: DrizzleQueryParamsConfig = {
|
|
2533
|
-
columns: {
|
|
2534
|
-
// Replace with actual Drizzle Column references for type-safe filtering:
|
|
2535
|
-
// name: ${n}s.name,
|
|
2536
|
-
// status: ${n}s.status,
|
|
2537
|
-
},
|
|
2538
|
-
sortable: {
|
|
2539
|
-
// name: ${n}s.name,
|
|
2540
|
-
// createdAt: ${n}s.createdAt,
|
|
2541
|
-
},
|
|
2542
|
-
searchColumns: [
|
|
2543
|
-
// ${n}s.name,
|
|
2544
|
-
],
|
|
2545
|
-
}
|
|
2546
|
-
`}function qn(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`/**
|
|
2547
|
-
* Prisma ${t} Repository
|
|
2548
|
-
*
|
|
2549
|
-
* Implements the repository interface using Prisma Client.
|
|
2550
|
-
* Requires a PrismaClient instance injected via the DI container.
|
|
2551
|
-
*
|
|
2552
|
-
* Ensure your Prisma schema has a '${t}' model defined.
|
|
2553
|
-
*
|
|
2554
|
-
* For full Prisma field-level type safety, replace PrismaModelDelegate with your PrismaClient:
|
|
2555
|
-
* @Inject(PRISMA_CLIENT) private prisma!: PrismaClient
|
|
2556
|
-
*
|
|
2557
|
-
* @Repository() registers this class in the DI container as a singleton.
|
|
2558
|
-
*/
|
|
2559
|
-
import { Repository, HttpException, Inject } from '@forinda/kickjs'
|
|
2560
|
-
import { PRISMA_CLIENT, type PrismaModelDelegate } from '@forinda/kickjs-prisma'
|
|
2561
|
-
import type { ParsedQuery } from '@forinda/kickjs'
|
|
2562
|
-
import type { I${t}Repository } from '${r}/${n}.repository'
|
|
2563
|
-
import type { ${t}ResponseDTO } from '${i}/${n}-response.dto'
|
|
2564
|
-
import type { Create${t}DTO } from '${i}/create-${n}.dto'
|
|
2565
|
-
import type { Update${t}DTO } from '${i}/update-${n}.dto'
|
|
2566
|
-
|
|
2567
|
-
@Repository()
|
|
2568
|
-
export class Prisma${t}Repository implements I${t}Repository {
|
|
2569
|
-
@Inject(PRISMA_CLIENT) private prisma!: { ${a}: PrismaModelDelegate }
|
|
2570
|
-
|
|
2571
|
-
async findById(id: string): Promise<${t}ResponseDTO | null> {
|
|
2572
|
-
return this.prisma.${a}.findUnique({ where: { id } }) as Promise<${t}ResponseDTO | null>
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
|
-
async findAll(): Promise<${t}ResponseDTO[]> {
|
|
2576
|
-
return this.prisma.${a}.findMany() as Promise<${t}ResponseDTO[]>
|
|
2577
|
-
}
|
|
2578
|
-
|
|
2579
|
-
async findPaginated(parsed: ParsedQuery): Promise<{ data: ${t}ResponseDTO[]; total: number }> {
|
|
2580
|
-
const [data, total] = await Promise.all([
|
|
2581
|
-
this.prisma.${a}.findMany({
|
|
2582
|
-
skip: parsed.pagination.offset,
|
|
2583
|
-
take: parsed.pagination.limit,
|
|
2584
|
-
}) as Promise<${t}ResponseDTO[]>,
|
|
2585
|
-
this.prisma.${a}.count(),
|
|
2586
|
-
])
|
|
2587
|
-
return { data, total }
|
|
2588
|
-
}
|
|
2589
|
-
|
|
2590
|
-
async create(dto: Create${t}DTO): Promise<${t}ResponseDTO> {
|
|
2591
|
-
return this.prisma.${a}.create({ data: dto as Record<string, unknown> }) as Promise<${t}ResponseDTO>
|
|
2592
|
-
}
|
|
2593
|
-
|
|
2594
|
-
async update(id: string, dto: Update${t}DTO): Promise<${t}ResponseDTO> {
|
|
2595
|
-
const existing = await this.prisma.${a}.findUnique({ where: { id } })
|
|
2596
|
-
if (!existing) throw HttpException.notFound('${t} not found')
|
|
2597
|
-
return this.prisma.${a}.update({ where: { id }, data: dto as Record<string, unknown> }) as Promise<${t}ResponseDTO>
|
|
2598
|
-
}
|
|
2599
|
-
|
|
2600
|
-
async delete(id: string): Promise<void> {
|
|
2601
|
-
await this.prisma.${a}.deleteMany({ where: { id } })
|
|
2602
|
-
}
|
|
2603
|
-
}
|
|
2604
|
-
`}async function Jn(e){let{pascal:t,kebab:n,plural:r,style:i,write:a}=e;await a(`${n}.module.ts`,Cn({pascal:t,kebab:n,plural:r,style:i})),await a(`${n}.controller.ts`,`import { Controller, Get, type Ctx } from '@forinda/kickjs'
|
|
2605
|
-
|
|
2606
|
-
// \`Ctx<KickRoutes.${t}Controller['<method>']>\` is generated by
|
|
2607
|
-
// \`kick typegen\` (auto-run on \`kick dev\`).
|
|
1721
|
+
// \`Ctx<KickRoutes.${t}Controller['<method>']>\` is generated by
|
|
1722
|
+
// \`kick typegen\` (auto-run on \`kick dev\`).
|
|
2608
1723
|
|
|
2609
1724
|
@Controller()
|
|
2610
1725
|
export class ${t}Controller {
|
|
@@ -2613,7 +1728,7 @@ export class ${t}Controller {
|
|
|
2613
1728
|
ctx.json({ message: '${t} list' })
|
|
2614
1729
|
}
|
|
2615
1730
|
}
|
|
2616
|
-
`)}async function
|
|
1731
|
+
`)}async function wn(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,tokenScope:s,style:c,write:l}=e;await l(`${n}.module.ts`,un({pascal:t,kebab:n,plural:r,repo:a,style:c})),await l(`${n}.constants.ts`,Sn({pascal:t,kebab:n})),await l(`${n}.controller.ts`,fn({pascal:t,kebab:n,plural:r,pluralPascal:i})),await l(`${n}.service.ts`,xn({pascal:t,kebab:n})),await l(`dtos/create-${n}.dto.ts`,pn({pascal:t,kebab:n})),await l(`dtos/update-${n}.dto.ts`,mn({pascal:t,kebab:n})),await l(`dtos/${n}-response.dto.ts`,hn({pascal:t,kebab:n})),await l(`${n}.repository.ts`,gn({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:s}));let u=a===`inmemory`,d=u?`in-memory-${n}`:`${B(a)}-${n}`,f=u?_n({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}):vn({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`});await l(`${d}.repository.ts`,f),o||(a!==`inmemory`&&await l(`in-memory-${n}.repository.ts`,_n({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await l(`__tests__/${n}.controller.test.ts`,yn({pascal:t,kebab:n,plural:r})),await l(`__tests__/${n}.repository.test.ts`,bn({pascal:t,kebab:n,plural:r,repoPrefix:`../in-memory-${n}.repository`})))}function Tn(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function En(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??`rest`;e.minimal&&(l=`minimal`);let u=B(t),d=R(t),f=c?V(u):u,p=c?Ht(d):d,m=h(n,f),g=[],_=o??!1,v={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=h(m,e);if(s){g.push(n);return}if(!_&&await Ue(n)&&!await I({message:`File exists: ${k.dim(e)}. Overwrite?`,initialValue:!1})){L.warn(`Skipped: ${e}`);return}await P(n,t),g.push(n)},files:g};switch(l){case`minimal`:await Cn(v);break;default:await wn(v);break}return s||await Dn(n,d,f,u,v.style),g}async function Dn(e,t,n,r,i=`define`){let a=h(e,`index.ts`),o=await Ue(a),s=`./${n}/${r}.module`,c=i===`class`?`${t}Module`:`${t}Module()`;if(!o){await P(a,i===`class`?`import type { AppModuleEntry } from '@forinda/kickjs'
|
|
2617
1732
|
import { ${t}Module } from '${s}'
|
|
2618
1733
|
|
|
2619
1734
|
export const modules: AppModuleEntry[] = [${c}]
|
|
@@ -2621,11 +1736,11 @@ export const modules: AppModuleEntry[] = [${c}]
|
|
|
2621
1736
|
import { ${t}Module } from '${s}'
|
|
2622
1737
|
|
|
2623
1738
|
export const modules = defineModules().mount(${c})
|
|
2624
|
-
`);return}let l=await
|
|
1739
|
+
`);return}let l=await T(a,`utf-8`),u=`import { ${t}Module } from '${s}'`,d=H(s);if(!RegExp(`^import\\s*\\{[^}]*\\b${H(t)}Module\\b[^}]*\\}\\s*from\\s*['"]${d}['"]`,`m`).test(l)){let e=l.lastIndexOf(`import `);if(e!==-1){let t=l.indexOf(`
|
|
2625
1740
|
`,e);l=l.slice(0,t+1)+u+`
|
|
2626
1741
|
`+l.slice(t+1)}else l=u+`
|
|
2627
|
-
`+l}let f=
|
|
2628
|
-
`;)t++;return t}if(n===`/*`){for(t+=2;t+1<e.length&&!(e[t]===`*`&&e[t+1]===`/`);)t++;return t+2}return t}function
|
|
1742
|
+
`+l}let f=kn(l);if(f){let e=l.slice(f.rhsStart,f.rhsEnd+1);RegExp(`\\b${H(t)}Module\\b`).test(e)||(l=On(l,c))}else l=On(l,c);await E(a,l,`utf-8`)}function On(e,t){let n=kn(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 kn(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=Mn(e,n);return t===-1?null:{shape:`array`,rhsStart:n,rhsEnd:t}}if(e.slice(n,n+13)===`defineModules`){let t=An(e,n);return t===-1?null:{shape:`chain`,rhsStart:n,rhsEnd:t-1,chainEnd:t}}return null}function An(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=Nn(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=Nn(e,t);if(n===-1)break;i=n+1}return i}function jn(e,t){let n=e.slice(t,t+2);if(n===`//`){for(t+=2;t<e.length&&e[t]!==`
|
|
1743
|
+
`;)t++;return t}if(n===`/*`){for(t+=2;t+1<e.length&&!(e[t]===`*`&&e[t+1]===`/`);)t++;return t+2}return t}function Mn(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=jn(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 Nn(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=jn(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 Pn(e){let{name:t,outDir:n}=e,r=B(t),i=R(t),a=[],o=h(n,`${r}.adapter.ts`);return await P(o,`import {
|
|
2629
1744
|
defineAdapter,
|
|
2630
1745
|
type AdapterContext,
|
|
2631
1746
|
type AdapterMiddleware,
|
|
@@ -2794,7 +1909,7 @@ export const ${i}Adapter = defineAdapter<${i}AdapterConfig>({
|
|
|
2794
1909
|
}
|
|
2795
1910
|
},
|
|
2796
1911
|
})
|
|
2797
|
-
`),a.push(o),a}async function
|
|
1912
|
+
`),a.push(o),a}async function Fn(e){let{name:t,outDir:n}=e,r=B(t),i=R(t),a=[],o=h(n,`${r}.plugin.ts`);return await P(o,`import {
|
|
2798
1913
|
definePlugin,
|
|
2799
1914
|
type AppAdapter,
|
|
2800
1915
|
type AppModuleEntry,
|
|
@@ -2938,9 +2053,9 @@ export const ${i}Plugin = definePlugin<${i}PluginConfig>({
|
|
|
2938
2053
|
},
|
|
2939
2054
|
}),
|
|
2940
2055
|
})
|
|
2941
|
-
`),a.push(o),a}const
|
|
2056
|
+
`),a.push(o),a}const In={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,contributor:`contributors`};function U(e){let{type:t,outDir:n,moduleName:r,modulesDir:i=`src/modules`,defaultDir:a,shouldPluralize:o=!0}=e;if(n)return v(n);if(r){let e=In,n=B(r),a=o?V(n):n,s=e[t]??``,c=h(i,a);return v(s?h(c,s):c)}return v(a)}async function Ln(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`middleware`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/middleware`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=z(t),c=[],l=h(a,`${o}.middleware.ts`);return await P(l,`import type { Request, Response, NextFunction } from 'express'
|
|
2942
2057
|
|
|
2943
|
-
export interface ${
|
|
2058
|
+
export interface ${R(t)}Options {
|
|
2944
2059
|
// Add configuration options here. The factory below closes over the
|
|
2945
2060
|
// resolved options object; pass them at the call site —
|
|
2946
2061
|
// \`${s}({ foo: 'bar' })\` — and the closure preserves them across
|
|
@@ -2948,7 +2063,7 @@ export interface ${I(t)}Options {
|
|
|
2948
2063
|
}
|
|
2949
2064
|
|
|
2950
2065
|
/**
|
|
2951
|
-
* ${
|
|
2066
|
+
* ${R(t)} middleware.
|
|
2952
2067
|
*
|
|
2953
2068
|
* Usage in bootstrap (fires on every request):
|
|
2954
2069
|
* middleware: [${s}()]
|
|
@@ -2980,7 +2095,7 @@ export interface ${I(t)}Options {
|
|
|
2980
2095
|
* Usage with @Middleware decorator:
|
|
2981
2096
|
* @Middleware(${s}())
|
|
2982
2097
|
*/
|
|
2983
|
-
export function ${s}(options: ${
|
|
2098
|
+
export function ${s}(options: ${R(t)}Options = {}) {
|
|
2984
2099
|
return (req: Request, res: Response, next: NextFunction) => {
|
|
2985
2100
|
// Implement your middleware logic here. \`options\` is captured by
|
|
2986
2101
|
// closure — log or read it anywhere in this handler body.
|
|
@@ -2988,7 +2103,7 @@ export function ${s}(options: ${I(t)}Options = {}) {
|
|
|
2988
2103
|
next()
|
|
2989
2104
|
}
|
|
2990
2105
|
}
|
|
2991
|
-
`),c.push(l),c}async function
|
|
2106
|
+
`),c.push(l),c}async function Rn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`guard`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/guards`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=z(t),c=R(t),l=[],u=h(a,`${o}.guard.ts`);return await P(u,`import { Container, HttpException } from '@forinda/kickjs'
|
|
2992
2107
|
import type { RequestContext } from '@forinda/kickjs'
|
|
2993
2108
|
|
|
2994
2109
|
/**
|
|
@@ -3024,8 +2139,8 @@ export async function ${s}Guard(ctx: RequestContext, next: () => void): Promise<
|
|
|
3024
2139
|
ctx.res.status(401).json({ message: 'Invalid or expired token' })
|
|
3025
2140
|
}
|
|
3026
2141
|
}
|
|
3027
|
-
`),l.push(u),l}function
|
|
3028
|
-
`)}\n}\n`:``,m=l.length>0?`${d}.withParams<${s}Params>()({`:`${d}({`,g=l.map(e=>({name:e.name,def:
|
|
2142
|
+
`),l.push(u),l}function zn(e){return e?e.split(`,`).map(e=>e.trim()).filter(Boolean).map(e=>{let[t,n]=e.split(`:`).map(e=>e.trim());return{name:t,type:n||`string`}}).filter(e=>e.name.length>0):[]}function Bn(e){switch(e){case`string`:return`''`;case`number`:return`0`;case`boolean`:return`false`;default:return null}}async function Vn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=e.type??`http`,o=B(t),s=R(t),c=e.key??z(t),l=Array.isArray(e.params)?e.params:zn(e.params),u=U({type:`contributor`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/contributors`,pattern:i,shouldPluralize:e.pluralize??!0}),d=a===`http`?`defineHttpContextDecorator`:`defineContextDecorator`,f=a===`http`?`RequestContext`:`ExecutionContext`,p=l.length>0?`\nexport type ${s}Params = {\n${l.map(e=>` ${e.name}: ${e.type}`).join(`
|
|
2143
|
+
`)}\n}\n`:``,m=l.length>0?`${d}.withParams<${s}Params>()({`:`${d}({`,g=l.map(e=>({name:e.name,def:Bn(e.type)})).filter(e=>e.def!==null).map(e=>` ${e.name}: ${e.def},`),_=l.length>0?` paramDefaults: {\n${g.join(`
|
|
3029
2144
|
`)}\n },\n`:``,v=l.length>0?`(ctx, _deps, params)`:`(ctx)`,y=l.length>0?` // \`params\` is typed as ${s}Params (call-site overrides merged onto paramDefaults).`:` // \`ctx\` is a ${f} — read ctx.req / ctx.headers / ctx.params (http) or ctx.get (bare).`,b=`import { ${d} } from '@forinda/kickjs'
|
|
3030
2145
|
import type { ${f} } from '@forinda/kickjs'
|
|
3031
2146
|
|
|
@@ -3064,7 +2179,7 @@ ${y}
|
|
|
3064
2179
|
throw new Error("${s} contributor: resolve() not implemented")
|
|
3065
2180
|
},
|
|
3066
2181
|
})
|
|
3067
|
-
`,x=h(u,`${o}.contributor.ts`);return await
|
|
2182
|
+
`,x=h(u,`${o}.contributor.ts`);return await P(x,b),[x]}async function Hn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`service`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/services`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=[],l=h(a,`${o}.service.ts`);return await P(l,`import { Service } from '@forinda/kickjs'
|
|
3068
2183
|
|
|
3069
2184
|
@Service()
|
|
3070
2185
|
export class ${s}Service {
|
|
@@ -3073,7 +2188,7 @@ export class ${s}Service {
|
|
|
3073
2188
|
// @Inject(MY_REPO) private readonly repo: IMyRepository,
|
|
3074
2189
|
// ) {}
|
|
3075
2190
|
}
|
|
3076
|
-
`),c.push(l),c}async function
|
|
2191
|
+
`),c.push(l),c}async function Un(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`controller`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/controllers`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=[],l=h(a,`${o}.controller.ts`);return await P(l,`import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
|
|
3077
2192
|
|
|
3078
2193
|
// \`Ctx<KickRoutes.${s}Controller['<method>']>\` is generated by
|
|
3079
2194
|
// \`kick typegen\` (auto-run on \`kick dev\`). After the first run, your IDE
|
|
@@ -3094,7 +2209,7 @@ export class ${s}Controller {
|
|
|
3094
2209
|
ctx.created({ message: '${s} created', data: ctx.body })
|
|
3095
2210
|
}
|
|
3096
2211
|
}
|
|
3097
|
-
`),c.push(l),c}async function
|
|
2212
|
+
`),c.push(l),c}async function Wn(e){let{name:t,moduleName:n,modulesDir:r,pattern:i}=e,a=U({type:`dto`,outDir:e.outDir,moduleName:n,modulesDir:r,defaultDir:`src/dtos`,pattern:i,shouldPluralize:e.pluralize??!0}),o=B(t),s=R(t),c=z(t),l=[],u=h(a,`${o}.dto.ts`);return await P(u,`import { z } from 'zod'
|
|
3098
2213
|
|
|
3099
2214
|
export const ${c}Schema = z.object({
|
|
3100
2215
|
// Define your schema fields here
|
|
@@ -3102,8 +2217,8 @@ export const ${c}Schema = z.object({
|
|
|
3102
2217
|
})
|
|
3103
2218
|
|
|
3104
2219
|
export type ${s}DTO = z.infer<typeof ${c}Schema>
|
|
3105
|
-
`),l.push(u),l}async function
|
|
3106
|
-
Skipped — existing kick.config.ts preserved.`),[]):(await
|
|
2220
|
+
`),l.push(u),l}async function Gn(e){let t=h(e.outDir,`kick.config.ts`),n=e.modulesDir??`src/modules`,i=e.defaultRepo??`inmemory`;return r(t)&&!e.force&&!await I({message:`kick.config.ts already exists. Overwrite?`,initialValue:!1})?(console.log(`
|
|
2221
|
+
Skipped — existing kick.config.ts preserved.`),[]):(await P(t,`import { defineConfig } from '@forinda/kickjs-cli'
|
|
3107
2222
|
|
|
3108
2223
|
export default defineConfig({
|
|
3109
2224
|
modules: {
|
|
@@ -3140,18 +2255,18 @@ export default defineConfig({
|
|
|
3140
2255
|
},
|
|
3141
2256
|
],
|
|
3142
2257
|
})
|
|
3143
|
-
`),[t])}var
|
|
2258
|
+
`),[t])}var Kn=A({generateAgentDocs:()=>Qn});const qn=`.agents`,Jn=new Set([`rest`,`minimal`]);function Yn(e,t){if(t)return t;try{let t=JSON.parse(a(h(e,`package.json`),`utf-8`));if(t.name)return t.name.replace(/^@[^/]+\//,``)}catch{}return e.split(`/`).findLast(Boolean)??`app`}function Xn(e,t){if(t)return t;try{let t=JSON.parse(a(h(e,`package.json`),`utf-8`));if(t.packageManager)return t.packageManager.split(`@`)[0]}catch{}return`pnpm`}async function Zn(e,t){if(t)return t;try{let t=(await M(e))?.pattern;if(t&&Jn.has(t))return t}catch{}return`rest`}async function Qn(e){let t=e.only??`all`,n=Yn(e.outDir,e.name),i=Xn(e.outDir,e.pm),a=await Zn(e.outDir,e.template),o=t===`agents`||t===`both`||t===`all`,s=t===`claude`||t===`both`||t===`all`,c=t===`skills`||t===`all`,l=t===`gemini`||t===`all`,u=t===`copilot`||t===`all`,d=[];if(o&&d.push({file:h(e.outDir,qn,`AGENTS.md`),render:()=>ft(n,a,i)}),s&&d.push({file:h(e.outDir,`CLAUDE.md`),render:()=>dt(n,a,i)}),c)for(let t of pt(n,a,i))d.push({file:h(e.outDir,qn,`skills`,t.slug,`SKILL.md`),render:()=>t.content});l&&d.push({file:h(e.outDir,qn,`GEMINI.md`),render:()=>mt(n,a,i)}),u&&d.push({file:h(e.outDir,qn,`COPILOT.md`),render:()=>ht(n,a,i)});let f=[];for(let{file:t,render:n}of d){if(r(t)&&!e.force&&!await I({message:`${t.replace(e.outDir+`/`,``)} already exists. Overwrite?`,initialValue:!1})){console.log(` Skipped — existing ${t.replace(e.outDir+`/`,``)} preserved.`);continue}await P(t,n()),f.push(t)}return f}function $n(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=$n(e,r);return i===-1?null:e.slice(r+1,i)}function G(e,t,n){let r=` `.repeat(n);return e.split(`
|
|
3144
2259
|
`).map(e=>{if(e.trim()===``)return e;let n=RegExp(`^ {0,${t}}`);return r+e.replace(n,``)}).join(`
|
|
3145
|
-
`)}function
|
|
2260
|
+
`)}function er(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 tr(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 nr(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=$n(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=er(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({
|
|
3146
2261
|
name: '${r}',
|
|
3147
2262
|
build: () => ({
|
|
3148
2263
|
${p}
|
|
3149
2264
|
}),
|
|
3150
|
-
})`}${c}`}}function
|
|
3151
|
-
`||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
|
|
2265
|
+
})`}${c}`}}function rr(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=$n(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]===`
|
|
2266
|
+
`||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=$n(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 v=tr(c,{container:h!==null,appModule:!0,moduleRoutes:!0,contributorRegistrations:g!==null}),y=``;return h!==null&&(y+=` register(container: Container): void {${G(h,6,4)} }\n\n`),g!==null&&(y+=` contributors(): ContributorRegistrations {${G(g,6,4)} }\n\n`),y+=` routes(): ModuleRoutes {${G(_,6,4)} }`,{migrated:`${v}${`export class ${r} implements AppModule {
|
|
3152
2267
|
${y}
|
|
3153
2268
|
}
|
|
3154
|
-
`}${u}`}}function
|
|
2269
|
+
`}${u}`}}function ir(e,t){return t===`class`?rr(e):nr(e)}function ar(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 or(e){let t=[];return await n(v(e),0),t;async function n(e,r){let i;try{i=await ae(e)}catch{return}for(let a of i){if(a===`node_modules`||a===`dist`||a===`.kickjs`)continue;let i=h(e,a),o;try{o=await se(i)}catch{continue}o.isDirectory()?await n(i,r+1):(a.endsWith(`.module.ts`)||a===`index.ts`&&r===1)&&t.push(i)}}}async function sr(e,t){let n=0;return await r(e,t),n;async function r(e,t){let i;try{i=await ae(e)}catch{return}await w(t,{recursive:!0});for(let a of i){if(a===`node_modules`||a===`dist`||a===`.kickjs`)continue;let i=h(e,a),o=h(t,a),s;try{s=await se(i)}catch{continue}s.isDirectory()?await r(i,o):(await ie(i,o),n++)}}}function cr(e){return h(e,`.kickjs`,`codemod-backups`,`${new Date().toISOString().replaceAll(/[:.]/g,`-`)}-modules`)}async function lr(e,t){let{dryRun:n=!1,cwd:r=process.cwd(),target:i}=t,a=t.backup??!n,o=await or(e),s=await T(h(e,`index.ts`),`utf-8`).then(()=>!0,()=>!1),c=null;a&&(o.length>0||s)&&(c=cr(r),await sr(e,c));let l=[];for(let e of o){let t=ir(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=h(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=ar(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 ur(e,t){let n=await or(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 dr(e){let{name:t,outDir:n}=e,r=R(t),i=B(t),a=z(t),o=e.queue??`${i}-queue`,s=[];return await(async(e,t)=>{let r=h(n,e);await P(r,t),s.push(r)})(`${i}.job.ts`,`import { Inject } from '@forinda/kickjs'
|
|
3155
2270
|
import { Job, Process, QUEUE_MANAGER, type QueueService } from '@forinda/kickjs-queue'
|
|
3156
2271
|
|
|
3157
2272
|
/**
|
|
@@ -3184,7 +2299,7 @@ export class ${r}Job {
|
|
|
3184
2299
|
// Handle high-priority variant of this job
|
|
3185
2300
|
}
|
|
3186
2301
|
}
|
|
3187
|
-
`),s}const
|
|
2302
|
+
`),s}const fr={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 pr(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=fr[a];if(!o){let e=[...Object.keys(fr),`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 mr(e){let{name:t,fields:n,modulesDir:r,repo:i=`inmemory`,tokenScope:a=`app`,style:o=`define`}=e,s=e.pluralize!==!1,c=B(t),l=R(t),u=s?V(c):c,d=s?Ht(l):l,f=h(r,u),p=[],m=async(e,t)=>{let n=h(f,e);await P(n,t),p.push(n)};await m(`${c}.module.ts`,un({pascal:l,kebab:c,plural:u,repo:i,style:o})),await m(`${c}.constants.ts`,Sn({pascal:l,kebab:c})),await m(`${c}.controller.ts`,fn({pascal:l,kebab:c,plural:u,pluralPascal:d})),await m(`${c}.service.ts`,xn({pascal:l,kebab:c})),await m(`dtos/create-${c}.dto.ts`,hr(l,n)),await m(`dtos/update-${c}.dto.ts`,gr(l,n)),await m(`dtos/${c}-response.dto.ts`,_r(l,n)),await m(`${c}.repository.ts`,gn({pascal:l,kebab:c,dtoPrefix:`./dtos`,tokenScope:a}));let g=i===`inmemory`,_=g?`in-memory-${c}`:`${B(i)}-${c}`,v=g?_n({pascal:l,kebab:c,repoPrefix:`.`,dtoPrefix:`./dtos`}):vn({pascal:l,kebab:c,repoType:i,repoPrefix:`.`,dtoPrefix:`./dtos`});return await m(`${_}.repository.ts`,v),await Dn(r,l,u,c,o),p}function hr(e,t){return`import { z } from 'zod'
|
|
3188
2303
|
|
|
3189
2304
|
export const create${e}Schema = z.object({
|
|
3190
2305
|
${t.map(e=>{let t=e.zodType;return` ${e.name}: ${t}${e.optional?`.optional()`:``},`}).join(`
|
|
@@ -3192,7 +2307,7 @@ ${t.map(e=>{let t=e.zodType;return` ${e.name}: ${t}${e.optional?`.optional()`:`
|
|
|
3192
2307
|
})
|
|
3193
2308
|
|
|
3194
2309
|
export type Create${e}DTO = z.infer<typeof create${e}Schema>
|
|
3195
|
-
`}function
|
|
2310
|
+
`}function gr(e,t){return`import { z } from 'zod'
|
|
3196
2311
|
|
|
3197
2312
|
export const update${e}Schema = z.object({
|
|
3198
2313
|
${t.map(e=>` ${e.name}: ${e.zodType}.optional(),`).join(`
|
|
@@ -3200,353 +2315,14 @@ ${t.map(e=>` ${e.name}: ${e.zodType}.optional(),`).join(`
|
|
|
3200
2315
|
})
|
|
3201
2316
|
|
|
3202
2317
|
export type Update${e}DTO = z.infer<typeof update${e}Schema>
|
|
3203
|
-
`}function
|
|
2318
|
+
`}function _r(e,t){return`export interface ${e}ResponseDTO {
|
|
3204
2319
|
id: string
|
|
3205
2320
|
${t.map(e=>` ${e.name}${e.optional?`?`:``}: ${e.tsType}`).join(`
|
|
3206
2321
|
`)}
|
|
3207
2322
|
createdAt: string
|
|
3208
2323
|
updatedAt: string
|
|
3209
2324
|
}
|
|
3210
|
-
`}function
|
|
3211
|
-
|
|
3212
|
-
export const ${e.toUpperCase()}_QUERY_CONFIG: ApiQueryParamsConfig = {
|
|
3213
|
-
filterable: [${i}],
|
|
3214
|
-
sortable: [${a}],
|
|
3215
|
-
searchable: [${o}],
|
|
3216
|
-
}
|
|
3217
|
-
`}function Jr(e,t,n){return`import { randomUUID } from 'node:crypto'
|
|
3218
|
-
import { Repository, HttpException } from '@forinda/kickjs'
|
|
3219
|
-
import type { ParsedQuery } from '@forinda/kickjs'
|
|
3220
|
-
import type { I${e}Repository } from '../../domain/repositories/${t}.repository'
|
|
3221
|
-
import type { ${e}ResponseDTO } from '../../application/dtos/${t}-response.dto'
|
|
3222
|
-
import type { Create${e}DTO } from '../../application/dtos/create-${t}.dto'
|
|
3223
|
-
import type { Update${e}DTO } from '../../application/dtos/update-${t}.dto'
|
|
3224
|
-
|
|
3225
|
-
@Repository()
|
|
3226
|
-
export class InMemory${e}Repository implements I${e}Repository {
|
|
3227
|
-
private store = new Map<string, ${e}ResponseDTO>()
|
|
3228
|
-
|
|
3229
|
-
async findById(id: string): Promise<${e}ResponseDTO | null> {
|
|
3230
|
-
return this.store.get(id) ?? null
|
|
3231
|
-
}
|
|
3232
|
-
|
|
3233
|
-
async findAll(): Promise<${e}ResponseDTO[]> {
|
|
3234
|
-
return Array.from(this.store.values())
|
|
3235
|
-
}
|
|
3236
|
-
|
|
3237
|
-
async findPaginated(parsed: ParsedQuery): Promise<{ data: ${e}ResponseDTO[]; total: number }> {
|
|
3238
|
-
const all = Array.from(this.store.values())
|
|
3239
|
-
const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)
|
|
3240
|
-
return { data, total: all.length }
|
|
3241
|
-
}
|
|
3242
|
-
|
|
3243
|
-
async create(dto: Create${e}DTO): Promise<${e}ResponseDTO> {
|
|
3244
|
-
const now = new Date().toISOString()
|
|
3245
|
-
const entity: ${e}ResponseDTO = {
|
|
3246
|
-
id: randomUUID(),
|
|
3247
|
-
${n.map(e=>` ${e.name}: dto.${e.name},`).join(`
|
|
3248
|
-
`)}
|
|
3249
|
-
createdAt: now,
|
|
3250
|
-
updatedAt: now,
|
|
3251
|
-
}
|
|
3252
|
-
this.store.set(entity.id, entity)
|
|
3253
|
-
return entity
|
|
3254
|
-
}
|
|
3255
|
-
|
|
3256
|
-
async update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO> {
|
|
3257
|
-
const existing = this.store.get(id)
|
|
3258
|
-
if (!existing) throw HttpException.notFound('${e} not found')
|
|
3259
|
-
const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }
|
|
3260
|
-
this.store.set(id, updated)
|
|
3261
|
-
return updated
|
|
3262
|
-
}
|
|
3263
|
-
|
|
3264
|
-
async delete(id: string): Promise<void> {
|
|
3265
|
-
if (!this.store.has(id)) throw HttpException.notFound('${e} not found')
|
|
3266
|
-
this.store.delete(id)
|
|
3267
|
-
}
|
|
3268
|
-
}
|
|
3269
|
-
`}function Yr(e,t,n){return`import { ${e}Id } from '../value-objects/${t}-id.vo'
|
|
3270
|
-
|
|
3271
|
-
interface ${e}Props {
|
|
3272
|
-
id: ${e}Id
|
|
3273
|
-
${n.map(e=>` ${e.name}${e.optional?`?`:``}: ${e.tsType}`).join(`
|
|
3274
|
-
`)}
|
|
3275
|
-
createdAt: Date
|
|
3276
|
-
updatedAt: Date
|
|
3277
|
-
}
|
|
3278
|
-
|
|
3279
|
-
export class ${e} {
|
|
3280
|
-
private constructor(private props: ${e}Props) {}
|
|
3281
|
-
|
|
3282
|
-
static create(params: { ${n.filter(e=>!e.optional).map(e=>`${e.name}: ${e.tsType}`).join(`; `)} }): ${e} {
|
|
3283
|
-
const now = new Date()
|
|
3284
|
-
return new ${e}({
|
|
3285
|
-
id: ${e}Id.create(),
|
|
3286
|
-
${n.filter(e=>!e.optional).map(e=>` ${e.name}: params.${e.name},`).join(`
|
|
3287
|
-
`)}
|
|
3288
|
-
createdAt: now,
|
|
3289
|
-
updatedAt: now,
|
|
3290
|
-
})
|
|
3291
|
-
}
|
|
3292
|
-
|
|
3293
|
-
static reconstitute(props: ${e}Props): ${e} {
|
|
3294
|
-
return new ${e}(props)
|
|
3295
|
-
}
|
|
3296
|
-
|
|
3297
|
-
get id(): ${e}Id { return this.props.id }
|
|
3298
|
-
${n.map(e=>` get ${e.name}(): ${e.tsType}${e.optional?` | undefined`:``} {
|
|
3299
|
-
return this.props.${e.name}
|
|
3300
|
-
}`).join(`
|
|
3301
|
-
`)}
|
|
3302
|
-
get createdAt(): Date { return this.props.createdAt }
|
|
3303
|
-
get updatedAt(): Date { return this.props.updatedAt }
|
|
3304
|
-
|
|
3305
|
-
toJSON() {
|
|
3306
|
-
return {
|
|
3307
|
-
id: this.props.id.toString(),
|
|
3308
|
-
${n.map(e=>` ${e.name}: this.props.${e.name},`).join(`
|
|
3309
|
-
`)}
|
|
3310
|
-
createdAt: this.props.createdAt.toISOString(),
|
|
3311
|
-
updatedAt: this.props.updatedAt.toISOString(),
|
|
3312
|
-
}
|
|
3313
|
-
}
|
|
3314
|
-
}
|
|
3315
|
-
`}function Xr(e){return`import { randomUUID } from 'node:crypto'
|
|
3316
|
-
|
|
3317
|
-
export class ${e}Id {
|
|
3318
|
-
private constructor(private readonly value: string) {}
|
|
3319
|
-
|
|
3320
|
-
static create(): ${e}Id { return new ${e}Id(randomUUID()) }
|
|
3321
|
-
|
|
3322
|
-
static from(id: string): ${e}Id {
|
|
3323
|
-
if (!id || id.trim().length === 0) throw new Error('${e}Id cannot be empty')
|
|
3324
|
-
return new ${e}Id(id)
|
|
3325
|
-
}
|
|
3326
|
-
|
|
3327
|
-
toString(): string { return this.value }
|
|
3328
|
-
equals(other: ${e}Id): boolean { return this.value === other.value }
|
|
3329
|
-
}
|
|
3330
|
-
`}function Zr(e,t,n,r=`define`){let i=`import { ${e}Controller } from './presentation/${t}.controller'
|
|
3331
|
-
import { ${e.toUpperCase()}_REPOSITORY } from './domain/repositories/${t}.repository'
|
|
3332
|
-
import { InMemory${e}Repository } from './infrastructure/repositories/in-memory-${t}.repository'
|
|
3333
|
-
|
|
3334
|
-
// Eagerly load decorated classes so @Service()/@Repository() decorators
|
|
3335
|
-
// register in the DI container before the application bootstraps.
|
|
3336
|
-
import.meta.glob(
|
|
3337
|
-
['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],
|
|
3338
|
-
{ eager: true },
|
|
3339
|
-
)`,a=` /**
|
|
3340
|
-
* Declare HTTP routes for this module. Return value shape:
|
|
3341
|
-
*
|
|
3342
|
-
* - \`path\` — URL prefix for this route set.
|
|
3343
|
-
* - \`controller\` — Controller class (also drives OpenAPI).
|
|
3344
|
-
* - \`version\` — Optional. Overrides the app-wide API version.
|
|
3345
|
-
*
|
|
3346
|
-
* Return an array to mount multiple route sets:
|
|
3347
|
-
*
|
|
3348
|
-
* return [
|
|
3349
|
-
* { path: '/${n}', version: 1, controller: ${e}V1Controller },
|
|
3350
|
-
* { path: '/${n}', version: 2, controller: ${e}V2Controller },
|
|
3351
|
-
* ]
|
|
3352
|
-
*/`;return r===`class`?`import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
|
|
3353
|
-
${i}
|
|
3354
|
-
|
|
3355
|
-
export class ${e}Module implements AppModule {
|
|
3356
|
-
/**
|
|
3357
|
-
* Bind the repository token to its concrete implementation.
|
|
3358
|
-
* Decorator-managed classes (@Service, @Controller, @Repository) are
|
|
3359
|
-
* registered automatically — only token-to-impl bindings need to live here.
|
|
3360
|
-
*/
|
|
3361
|
-
register(container: Container): void {
|
|
3362
|
-
container.registerFactory(
|
|
3363
|
-
${e.toUpperCase()}_REPOSITORY,
|
|
3364
|
-
() => container.resolve(InMemory${e}Repository),
|
|
3365
|
-
)
|
|
3366
|
-
}
|
|
3367
|
-
|
|
3368
|
-
${a.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
|
|
3369
|
-
routes(): ModuleRoutes {
|
|
3370
|
-
return {
|
|
3371
|
-
path: '/${n}',
|
|
3372
|
-
controller: ${e}Controller,
|
|
3373
|
-
}
|
|
3374
|
-
}
|
|
3375
|
-
}
|
|
3376
|
-
`:`import { defineModule } from '@forinda/kickjs'
|
|
3377
|
-
${i}
|
|
3378
|
-
|
|
3379
|
-
export const ${e}Module = defineModule({
|
|
3380
|
-
name: '${e}Module',
|
|
3381
|
-
build: () => ({
|
|
3382
|
-
/**
|
|
3383
|
-
* Bind the repository token to its concrete implementation.
|
|
3384
|
-
* Decorator-managed classes (@Service, @Controller, @Repository) are
|
|
3385
|
-
* registered automatically — only token-to-impl bindings need to live here.
|
|
3386
|
-
*/
|
|
3387
|
-
register(container) {
|
|
3388
|
-
container.registerFactory(
|
|
3389
|
-
${e.toUpperCase()}_REPOSITORY,
|
|
3390
|
-
() => container.resolve(InMemory${e}Repository),
|
|
3391
|
-
)
|
|
3392
|
-
},
|
|
3393
|
-
|
|
3394
|
-
${a}
|
|
3395
|
-
routes() {
|
|
3396
|
-
return {
|
|
3397
|
-
path: '/${n}',
|
|
3398
|
-
controller: ${e}Controller,
|
|
3399
|
-
}
|
|
3400
|
-
},
|
|
3401
|
-
}),
|
|
3402
|
-
})
|
|
3403
|
-
`}function Qr(e,t,n,r){return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams, type Ctx } from '@forinda/kickjs'
|
|
3404
|
-
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
3405
|
-
import { Create${e}UseCase } from '../application/use-cases/create-${t}.use-case'
|
|
3406
|
-
import { Get${e}UseCase } from '../application/use-cases/get-${t}.use-case'
|
|
3407
|
-
import { List${r}UseCase } from '../application/use-cases/list-${n}.use-case'
|
|
3408
|
-
import { Update${e}UseCase } from '../application/use-cases/update-${t}.use-case'
|
|
3409
|
-
import { Delete${e}UseCase } from '../application/use-cases/delete-${t}.use-case'
|
|
3410
|
-
import { create${e}Schema } from '../application/dtos/create-${t}.dto'
|
|
3411
|
-
import { update${e}Schema } from '../application/dtos/update-${t}.dto'
|
|
3412
|
-
import { ${e.toUpperCase()}_QUERY_CONFIG } from '../constants'
|
|
3413
|
-
|
|
3414
|
-
// Each handler annotates its \`ctx\` with \`Ctx<KickRoutes.${e}Controller['<method>']>\`
|
|
3415
|
-
// so \`ctx.params\`, \`ctx.body\`, and \`ctx.query\` are typed end-to-end.
|
|
3416
|
-
// The \`KickRoutes\` namespace is generated by \`kick typegen\` (auto-run on
|
|
3417
|
-
// \`kick dev\`) — see https://forinda.github.io/kick-js/guide/typegen.
|
|
3418
|
-
|
|
3419
|
-
@Controller()
|
|
3420
|
-
export class ${e}Controller {
|
|
3421
|
-
@Autowired() private readonly create${e}UseCase!: Create${e}UseCase
|
|
3422
|
-
@Autowired() private readonly get${e}UseCase!: Get${e}UseCase
|
|
3423
|
-
@Autowired() private readonly list${r}UseCase!: List${r}UseCase
|
|
3424
|
-
@Autowired() private readonly update${e}UseCase!: Update${e}UseCase
|
|
3425
|
-
@Autowired() private readonly delete${e}UseCase!: Delete${e}UseCase
|
|
3426
|
-
|
|
3427
|
-
@Get('/')
|
|
3428
|
-
@ApiTags('${e}')
|
|
3429
|
-
@ApiQueryParams(${e.toUpperCase()}_QUERY_CONFIG)
|
|
3430
|
-
async list(ctx: Ctx<KickRoutes.${e}Controller['list']>) {
|
|
3431
|
-
return ctx.paginate(
|
|
3432
|
-
(parsed) => this.list${r}UseCase.execute(parsed),
|
|
3433
|
-
${e.toUpperCase()}_QUERY_CONFIG,
|
|
3434
|
-
)
|
|
3435
|
-
}
|
|
3436
|
-
|
|
3437
|
-
@Get('/:id')
|
|
3438
|
-
@ApiTags('${e}')
|
|
3439
|
-
async getById(ctx: Ctx<KickRoutes.${e}Controller['getById']>) {
|
|
3440
|
-
const result = await this.get${e}UseCase.execute(ctx.params.id)
|
|
3441
|
-
if (!result) return ctx.notFound('${e} not found')
|
|
3442
|
-
ctx.json(result)
|
|
3443
|
-
}
|
|
3444
|
-
|
|
3445
|
-
@Post('/', { body: create${e}Schema, name: 'Create${e}' })
|
|
3446
|
-
@ApiTags('${e}')
|
|
3447
|
-
async create(ctx: Ctx<KickRoutes.${e}Controller['create']>) {
|
|
3448
|
-
const result = await this.create${e}UseCase.execute(ctx.body)
|
|
3449
|
-
ctx.created(result)
|
|
3450
|
-
}
|
|
3451
|
-
|
|
3452
|
-
@Put('/:id', { body: update${e}Schema, name: 'Update${e}' })
|
|
3453
|
-
@ApiTags('${e}')
|
|
3454
|
-
async update(ctx: Ctx<KickRoutes.${e}Controller['update']>) {
|
|
3455
|
-
const result = await this.update${e}UseCase.execute(ctx.params.id, ctx.body)
|
|
3456
|
-
ctx.json(result)
|
|
3457
|
-
}
|
|
3458
|
-
|
|
3459
|
-
@Delete('/:id')
|
|
3460
|
-
@ApiTags('${e}')
|
|
3461
|
-
async remove(ctx: Ctx<KickRoutes.${e}Controller['remove']>) {
|
|
3462
|
-
await this.delete${e}UseCase.execute(ctx.params.id)
|
|
3463
|
-
ctx.noContent()
|
|
3464
|
-
}
|
|
3465
|
-
}
|
|
3466
|
-
`}function $r(e,t,n){return`import { createToken } from '@forinda/kickjs'
|
|
3467
|
-
import type { ${e}ResponseDTO } from '../../application/dtos/${t}-response.dto'
|
|
3468
|
-
import type { Create${e}DTO } from '../../application/dtos/create-${t}.dto'
|
|
3469
|
-
import type { Update${e}DTO } from '../../application/dtos/update-${t}.dto'
|
|
3470
|
-
import type { ParsedQuery } from '@forinda/kickjs'
|
|
3471
|
-
|
|
3472
|
-
export interface I${e}Repository {
|
|
3473
|
-
findById(id: string): Promise<${e}ResponseDTO | null>
|
|
3474
|
-
findAll(): Promise<${e}ResponseDTO[]>
|
|
3475
|
-
findPaginated(parsed: ParsedQuery): Promise<{ data: ${e}ResponseDTO[]; total: number }>
|
|
3476
|
-
create(dto: Create${e}DTO): Promise<${e}ResponseDTO>
|
|
3477
|
-
update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO>
|
|
3478
|
-
delete(id: string): Promise<void>
|
|
3479
|
-
}
|
|
3480
|
-
|
|
3481
|
-
/**
|
|
3482
|
-
* Collision-safe DI token bound to \`I${e}Repository\`.
|
|
3483
|
-
* \`container.resolve(${e.toUpperCase()}_REPOSITORY)\` and
|
|
3484
|
-
* \`@Inject(${e.toUpperCase()}_REPOSITORY)\` both return the typed
|
|
3485
|
-
* interface — no manual generic, no \`any\` cast.
|
|
3486
|
-
*
|
|
3487
|
-
* The \`'${n}/'\` prefix matches the project scope so
|
|
3488
|
-
* \`kick-lint\`'s \`token-reserved-prefix\` rule never fires —
|
|
3489
|
-
* adopters must NOT use the reserved \`'kick/'\` namespace.
|
|
3490
|
-
*/
|
|
3491
|
-
export const ${e.toUpperCase()}_REPOSITORY = createToken<I${e}Repository>('${n}/${e}/repository')
|
|
3492
|
-
`}function ei(e,t){return`import { Service, Inject, HttpException } from '@forinda/kickjs'
|
|
3493
|
-
import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../repositories/${t}.repository'
|
|
3494
|
-
|
|
3495
|
-
@Service()
|
|
3496
|
-
export class ${e}DomainService {
|
|
3497
|
-
constructor(
|
|
3498
|
-
@Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
|
|
3499
|
-
) {}
|
|
3500
|
-
|
|
3501
|
-
async ensureExists(id: string): Promise<void> {
|
|
3502
|
-
const entity = await this.repo.findById(id)
|
|
3503
|
-
if (!entity) throw HttpException.notFound('${e} not found')
|
|
3504
|
-
}
|
|
3505
|
-
}
|
|
3506
|
-
`}function ti(e,t,n,r){return[{file:`create-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
3507
|
-
import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
|
|
3508
|
-
import type { Create${e}DTO } from '../dtos/create-${t}.dto'
|
|
3509
|
-
|
|
3510
|
-
@Service()
|
|
3511
|
-
export class Create${e}UseCase {
|
|
3512
|
-
constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
|
|
3513
|
-
async execute(dto: Create${e}DTO) { return this.repo.create(dto) }
|
|
3514
|
-
}
|
|
3515
|
-
`},{file:`get-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
3516
|
-
import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
|
|
3517
|
-
|
|
3518
|
-
@Service()
|
|
3519
|
-
export class Get${e}UseCase {
|
|
3520
|
-
constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
|
|
3521
|
-
async execute(id: string) { return this.repo.findById(id) }
|
|
3522
|
-
}
|
|
3523
|
-
`},{file:`list-${n}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
3524
|
-
import type { ParsedQuery } from '@forinda/kickjs'
|
|
3525
|
-
import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
|
|
3526
|
-
|
|
3527
|
-
@Service()
|
|
3528
|
-
export class List${r}UseCase {
|
|
3529
|
-
constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
|
|
3530
|
-
async execute(parsed: ParsedQuery) { return this.repo.findPaginated(parsed) }
|
|
3531
|
-
}
|
|
3532
|
-
`},{file:`update-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
3533
|
-
import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
|
|
3534
|
-
import type { Update${e}DTO } from '../dtos/update-${t}.dto'
|
|
3535
|
-
|
|
3536
|
-
@Service()
|
|
3537
|
-
export class Update${e}UseCase {
|
|
3538
|
-
constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
|
|
3539
|
-
async execute(id: string, dto: Update${e}DTO) { return this.repo.update(id, dto) }
|
|
3540
|
-
}
|
|
3541
|
-
`},{file:`delete-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
3542
|
-
import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
|
|
3543
|
-
|
|
3544
|
-
@Service()
|
|
3545
|
-
export class Delete${e}UseCase {
|
|
3546
|
-
constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
|
|
3547
|
-
async execute(id: string) { return this.repo.delete(id) }
|
|
3548
|
-
}
|
|
3549
|
-
`}]}async function ni(e){let{name:t,moduleName:n,modulesDir:r}=e,i=e.pluralize??!0,a=R(t),o=I(t),s=[],c;if(e.outDir)c=v(e.outDir);else if(n){let e=R(n),t=i?z(e):e;c=v(h(r??`src/modules`,t,`__tests__`))}else c=v(`src/__tests__`);let l=h(c,`${a}.test.ts`);return await M(l,`import { describe, it, expect, beforeEach } from 'vitest'
|
|
2325
|
+
`}async function vr(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=v(e.outDir);else if(n){let e=B(n),t=i?V(e):e;c=v(h(r??`src/modules`,t,`__tests__`))}else c=v(`src/__tests__`);let l=h(c,`${a}.test.ts`);return await P(l,`import { describe, it, expect, beforeEach } from 'vitest'
|
|
3550
2326
|
import { Container } from '@forinda/kickjs'
|
|
3551
2327
|
|
|
3552
2328
|
describe('${o}', () => {
|
|
@@ -3569,9 +2345,9 @@ describe('${o}', () => {
|
|
|
3569
2345
|
expect(true).toBe(true)
|
|
3570
2346
|
})
|
|
3571
2347
|
})
|
|
3572
|
-
`),s.push(l),s}const ri=[`Service`,`Controller`,`Repository`,`Injectable`,`Component`,`Module`],ii=[`.ts`,`.tsx`,`.mts`,`.cts`],ai=[`node_modules`,`.kickjs`,`dist`,`build`,`.test.`,`.spec.`,`.d.ts`],oi=new RegExp(String.raw`@(${ri.join(`|`)})\s*\([^)]*\)`+String.raw`(?:\s*@[A-Z]\w*(?:\s*\([^)]*\))?)*`+String.raw`\s*export\s+(default\s+)?(?:abstract\s+)?class\s+(\w+)`,`g`),si=new RegExp(String.raw`export\s+(default\s+)?(?:abstract\s+)?class\s+(\w+)`+String.raw`(?:\s+extends\s+\w+(?:<[^>]*>)?)?`+String.raw`\s+implements\s+[^{]*\bAppModule\b`,`g`),ci=/(?:export\s+)?const\s+(\w+)\s*(?::\s*[^=]+)?=\s*createToken\s*(?:<[^>]*>)?\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,li=/createToken\s*(?:<[^>]*>)?\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,ui=/@Inject\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,di=/\b(defineAdapter|definePlugin)\s*(?:<[^>]*>)?\s*\(/g,fi=/\b(?:defineContextDecorator|defineHttpContextDecorator)\s*(?:\.withParams\s*<(?:[^<>]|<[^<>]*>)*>\s*\(\s*\))?\s*(?:<(?:[^<>]|<[^<>]*>)*>)?\s*\(/g,pi=new RegExp(String.raw`export\s+(?:default\s+)?(?:abstract\s+)?class\s+(\w+)`+String.raw`(?:\s+extends\s+\w+(?:<[^>]*>)?)?`+String.raw`\s+implements\s+[^{]*\bAppAdapter\b`,`g`),mi=/\bname\s*(?::\s*[^=]+)?=\s*['"`]([^'"`]+)['"`]/,hi=/\bdefineAugmentation\s*\(\s*['"`]([^'"`]+)['"`]\s*(,\s*\{)?/g,gi=new RegExp(String.raw`@(${[`Get`,`Post`,`Put`,`Delete`,`Patch`].join(`|`)})\s*\(`,`g`);function _i(e,t){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 vi(e,t){let n=t;for(;n<e.length;){for(;n<e.length&&/\s/.test(e[n]);)n++;if(e[n]!==`@`)break;let t=e.slice(n).match(/^@([A-Z]\w*)/);if(!t)break;for(n+=t[0].length;n<e.length&&/\s/.test(e[n]);)n++;if(e[n]===`(`){let t=_i(e,n);if(t<0)return null;n=t+1}}for(;n<e.length&&/\s/.test(e[n]);)n++;for(let t of[`public`,`private`,`protected`])if(e.slice(n,n+t.length)===t&&/\s/.test(e.charAt(n+t.length))){for(n+=t.length;n<e.length&&/\s/.test(e[n]);)n++;break}if(e.slice(n,n+5)===`async`&&/\s/.test(e.charAt(n+5)))for(n+=5;n<e.length&&/\s/.test(e[n]);)n++;let r=e.slice(n).match(/^([a-zA-Z_]\w*)\s*\(/);return r?{methodName:r[1],endPos:n+r[0].length}:null}function yi(e){return(e.match(/:([a-zA-Z_]\w*)/g)??[]).map(e=>e.slice(1))}function bi(e,t){let n=e.endsWith(`/`)?e.slice(0,-1):e;return!t||t===`/`?n||`/`:n+(t.startsWith(`/`)?t:`/`+t)||`/`}const xi=/\b(?:public\s+|private\s+|protected\s+)?routes\s*\([^)]*\)\s*(?::\s*[A-Za-z_][\w<>[\]\s,|]*\s*)?\{/g,Si=/\bpath\s*:\s*['"`]([^'"`]*)['"`]/g,Ci=/\bcontroller\s*:\s*([A-Z]\w*)\b/g,wi=/\bimport\.meta\.glob\s*\(/g;function Ti(e){let t=[];for(wi.lastIndex=0;wi.exec(e)!==null;){let n=wi.lastIndex-1,r=_i(e,n);if(r<0)continue;let i=e.slice(n+1,r),a=/['"`]([^'"`]+)['"`]/g,o;for(;(o=a.exec(i))!==null;)t.push(o[1])}return t}function Ei(e){let t=e.replace(/[.+^$()|[\]\\]/g,`\\$&`).replace(/\?/g,`.`).replace(/\*\*\//g,`___DOUBLESTAR_SLASH___`).replace(/\*\*/g,`___DOUBLESTAR___`).replace(/\*/g,`[^/]*`).replace(/___DOUBLESTAR_SLASH___/g,`(?:.+/)?`).replace(/___DOUBLESTAR___/g,`.*`);return RegExp(`^`+t+`$`)}function Di(e,t){let n=e.startsWith(`./`)?e:`./`+e,r=!1;for(let e of t){let t=e.startsWith(`!`);Ei(t?e.slice(1):e).test(n)&&(r=!t)}return r}function Oi(e){let t=[];xi.lastIndex=0;let n;for(;(n=xi.exec(e))!==null;){let r=e.indexOf(`{`,n.index+n[0].length-1);if(r<0)continue;let i=Bi(e,r);if(i<0)continue;let a=e.slice(r+1,i),o=[];Si.lastIndex=0;let s;for(;(s=Si.exec(a))!==null;)o.push(s[1]??``);let c=[];Ci.lastIndex=0;let l;for(;(l=Ci.exec(a))!==null;)c.push(l[1]);let u=Math.min(o.length,c.length);for(let e=0;e<u;e++)t.push({controller:c[e],mountPath:o[e]})}return t}function ki(e,t){let n=new RegExp(String.raw`\b${t}\s*:\s*([A-Za-z_$][\w$]*)`,`g`).exec(e);return n?n[1]:null}function Ai(e,t){let n=new RegExp(String.raw`import\s*(?:type\s+)?\{[^}]*\b${t}\b[^}]*\}\s*from\s*['"\`]([^'"\`]+)['"\`]`).exec(e);if(n)return n[1];let r=new RegExp(String.raw`import\s+(?:type\s+)?${t}\s+from\s*['"\`]([^'"\`]+)['"\`]`).exec(e);if(r)return r[1];let i=new RegExp(String.raw`import\s*\*\s*as\s+${t}\s+from\s*['"\`]([^'"\`]+)['"\`]`).exec(e);return i?i[1]:new RegExp(String.raw`(?:^|\n)\s*(?:export\s+)?const\s+${t}\b`).test(e)?``:null}function ji(e,t){let n=/@ApiQueryParams\s*\(\s*([\s\S]*?)\s*\)\s*$/.exec(e);if(!n){let n=/@ApiQueryParams\s*\(([\s\S]*?)\)/.exec(e);return n?Mi(n[1].trim(),t):null}return Mi(n[1].trim(),t)}function Mi(e,t){if(e.startsWith(`{`))return Pi(e);let n=/^([A-Za-z_]\w*)/.exec(e);if(n){let e=n[1],r=new RegExp(String.raw`const\s+${e}\s*(?::\s*[^=]+)?=\s*(\{[\s\S]*?\n\})`,`m`).exec(t);if(r)return Pi(r[1])}return{filterable:[],sortable:[],searchable:[]}}function Ni(e,t){let n=new RegExp(String.raw`${t}\s*:\s*\[([\s\S]*?)\]`).exec(e);return n?Array.from(n[1].matchAll(/['"`]([^'"`]+)['"`]/g)).map(e=>e[1]):[]}function Pi(e){return{filterable:Ni(e,`filterable`),sortable:Ni(e,`sortable`),searchable:Ni(e,`searchable`)}}async function Fi(e,t){let n=t.extensions??ii,r=t.exclude??ai,i=[],a;try{a=await se(e,{withFileTypes:!0,encoding:`utf-8`})}catch{return i}for(let o of a){let a=h(e,o.name),s=_(t.cwd,a);r.some(e=>s.includes(e))||(o.isDirectory()?i.push(...await Fi(a,t)):o.isFile()&&n.some(e=>o.name.endsWith(e))&&i.push(a))}return i}function G(e,t){return _(t,e).split(y).join(`/`)}function Ii(e,t,n){let r=[],i=G(t,n);oi.lastIndex=0;let a;for(;(a=oi.exec(e))!==null;){let[,e,n,o]=a;r.push({className:o,decorator:e,filePath:t,relativePath:i,isDefault:!!n})}si.lastIndex=0;let o;for(;(o=si.exec(e))!==null;){let[,e,n]=o;r.some(e=>e.className===n&&e.filePath===t)||r.push({className:n,decorator:`Module`,filePath:t,relativePath:i,isDefault:!!e})}return r}function Li(e,t,n){let r=[],i=G(t,n),a=new Set;ci.lastIndex=0;let o;for(;(o=ci.exec(e))!==null;){let[e,n,s]=o;a.add(e),r.push({name:s,variable:n,filePath:t,relativePath:i})}for(li.lastIndex=0;(o=li.exec(e))!==null;)a.has(o[0])||r.push({name:o[1],variable:null,filePath:t,relativePath:i});return r}function Ri(e,t,n,r,i=new Map){let a=[];if(r.length===0)return a;let o=G(t,n),s=[];for(let t of r){let n=new RegExp(String.raw`class\s+${t.className}\b`).exec(e);n?.index!==void 0&&s.push({cls:t,start:n.index})}s.sort((e,t)=>e.start-t.start);for(let n=0;n<s.length;n++){let{cls:r,start:c}=s[n],l=n+1<s.length?s[n+1].start:e.length,u=e.slice(c,l);gi.lastIndex=0;let d;for(;(d=gi.exec(u))!==null;){let n=d[1],s=d.index,c=gi.lastIndex-1,l=_i(u,c);if(l<0)continue;let f=u.slice(c+1,l),p=f.match(/^\s*['"`]([^'"`]*)['"`]/),m=p&&p[1].length>0?p[1]:`/`,h=vi(u,l+1);if(!h)continue;let{methodName:g,endPos:_}=h;gi.lastIndex=_;let v=ji(u.slice(s,_),e),y=ki(f,`body`),b=ki(f,`query`),x=ki(f,`params`),ee=i.get(r.className)??``,S=ee?bi(ee,m):m;a.push({controller:r.className,method:g,httpMethod:n.toUpperCase(),path:m,pathParams:yi(S),queryFilterable:v?.filterable??null,querySortable:v?.sortable??null,querySearchable:v?.searchable??null,bodySchema:y?{identifier:y,source:Ai(e,y)}:null,querySchema:b?{identifier:b,source:Ai(e,b)}:null,paramsSchema:x?{identifier:x,source:Ai(e,x)}:null,filePath:t,relativePath:o})}}return a}function zi(e,t,n){let r=[],i=G(t,n);ui.lastIndex=0;let a;for(;(a=ui.exec(e))!==null;)r.push({name:a[1],filePath:t,relativePath:i});return r}function Bi(e,t){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 Vi(e,t,n){let r=[],i=G(t,n),a=new Set;di.lastIndex=0;let o;for(;(o=di.exec(e))!==null;){let n=o[1],s=di.lastIndex-1,c=_i(e,s);if(c<0)continue;let l=e.slice(s+1,c),u=/\bname\s*:\s*['"`]([^'"`]+)['"`]/.exec(l);if(!u)continue;let d=u[1],f=`${n}::${d}::${t}`;a.has(f)||(a.add(f),r.push({kind:n===`definePlugin`?`plugin`:`adapter`,name:d,filePath:t,relativePath:i}))}pi.lastIndex=0;let s;for(;(s=pi.exec(e))!==null;){let n=s.index,o=e.indexOf(`{`,n);if(o<0)continue;let c=Bi(e,o);if(c<0)continue;let l=e.slice(o+1,c),u=mi.exec(l);if(!u)continue;let d=u[1],f=`class::${d}::${t}`;a.has(f)||(a.add(f),r.push({kind:`adapter`,name:d,filePath:t,relativePath:i}))}return r}function Hi(e,t,n){let r=[],i=G(t,n),a=new Set;for(fi.lastIndex=0;fi.exec(e)!==null;){let n=fi.lastIndex-1,o=_i(e,n);if(o<0)continue;let s=e.slice(n+1,o),c=/\bkey\s*:\s*['"`]([^'"`]+)['"`]/.exec(s);if(!c)continue;let l=c[1];a.has(l)||(a.add(l),r.push({key:l,filePath:t,relativePath:i}))}return r}function Ui(e,t,n){let r=[],i=G(t,n);hi.lastIndex=0;let a;for(;(a=hi.exec(e))!==null;){let n=a[1],o=null,s=null;if(a[2]){let t=e.indexOf(`{`,a.index+a[0].length-1);if(t>=0){let n=Bi(e,t);if(n>=0){let r=e.slice(t+1,n);o=Wi(r,`description`),s=Wi(r,`example`)}}}r.push({name:n,description:o,example:s,filePath:t,relativePath:i})}return r}function Wi(e,t){let n=RegExp(`\\b${t}\\s*:\\s*(['"\`])`,`g`).exec(e);if(!n)return null;let r=n[1],i=n.index+n[0].length,a=i,o=null;for(;a<e.length;){let t=e[a];if(t===`\\`){a+=2;continue}if(t===r){o=e.slice(i,a);break}a++}return o===null?null:o.replace(/\\(.)/g,(e,t)=>t===`n`?`
|
|
3573
|
-
`:t===`t`?` `:t===`r`?`\r`:t)}const
|
|
3574
|
-
`)}function
|
|
2348
|
+
`),s.push(l),s}const yr=[`classes`,`tokens`,`injects`,`pluginsAndAdapters`,`augmentations`,`contextKeys`,`routes`,`moduleMounts`,`globPatterns`];function br(e){if(!e||typeof e!=`object`)return!1;let t=e;return yr.every(e=>Array.isArray(t[e]))}var xr=class e{path;prev;next=new Map;nextSig=new Map;constructor(e,t){this.path=e,this.prev=t}static async load(t){let n=h(t,`scan.json`),r=new Map;try{let e=await T(n,`utf-8`),t=JSON.parse(e);if(t.version===1&&t.files)for(let[e,n]of Object.entries(t.files))n&&typeof n.sig==`string`&&br(n.extract)&&r.set(e,n)}catch{}return new e(n,r)}static async signature(e){try{let t=await se(e);return`${t.mtimeMs}:${t.size}`}catch{return null}}get(e,t){let n=this.prev.get(e);return n&&n.sig===t?n.extract:null}set(e,t,n){this.next.set(e,n),this.nextSig.set(e,t)}cachedFiles(){return[...this.prev.keys()]}peek(e){return this.prev.get(e)?.extract??null}carry(e){let t=this.prev.get(e);return t?(this.next.set(e,t.extract),this.nextSig.set(e,t.sig),!0):!1}async save(){let e={};for(let[t,n]of this.next){let r=this.nextSig.get(t);r&&(e[t]={sig:r,extract:n})}let t={version:1,files:e};try{await w(f(this.path),{recursive:!0}),await E(this.path,JSON.stringify(t),`utf-8`)}catch{}}};const Sr=[`Service`,`Controller`,`Repository`,`Injectable`,`Component`,`Module`],Cr=[`.ts`,`.tsx`,`.mts`,`.cts`],wr=[`node_modules`,`.kickjs`,`dist`,`build`,`.test.`,`.spec.`,`.d.ts`],Tr=new RegExp(String.raw`@(${Sr.join(`|`)})\s*\([^)]*\)`+String.raw`(?:\s*@[A-Z]\w*(?:\s*\([^)]*\))?)*`+String.raw`\s*export\s+(default\s+)?(?:abstract\s+)?class\s+(\w+)`,`g`),Er=new RegExp(String.raw`export\s+(default\s+)?(?:abstract\s+)?class\s+(\w+)`+String.raw`(?:\s+extends\s+\w+(?:<[^>]*>)?)?`+String.raw`\s+implements\s+[^{]*\bAppModule\b`,`g`),Dr=/export\s+const\s+(\w+)\s*(?::\s*[^=]+)?=\s*defineModule\s*(?:<[^>]*>)?\s*\(/g,Or=/(?:export\s+)?const\s+(\w+)\s*(?::\s*[^=]+)?=\s*createToken\s*(?:<[^>]*>)?\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,kr=/createToken\s*(?:<[^>]*>)?\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,Ar=/@Inject\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g,jr=/\b(defineAdapter|definePlugin)\s*(?:<[^>]*>)?\s*\(/g,Mr=/\b(?:defineContextDecorator|defineHttpContextDecorator)\s*(?:\.withParams\s*<(?:[^<>]|<[^<>]*>)*>\s*\(\s*\))?\s*(?:<(?:[^<>]|<[^<>]*>)*>)?\s*\(/g,Nr=new RegExp(String.raw`export\s+(?:default\s+)?(?:abstract\s+)?class\s+(\w+)`+String.raw`(?:\s+extends\s+\w+(?:<[^>]*>)?)?`+String.raw`\s+implements\s+[^{]*\bAppAdapter\b`,`g`),Pr=/\bname\s*(?::\s*[^=]+)?=\s*['"`]([^'"`]+)['"`]/,Fr=/\bdefineAugmentation\s*\(\s*['"`]([^'"`]+)['"`]\s*(,\s*\{)?/g,Ir=new RegExp(String.raw`@(${[`Get`,`Post`,`Put`,`Delete`,`Patch`].join(`|`)})\s*\(`,`g`);function Lr(e,t){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 Rr(e,t){let n=t;for(;n<e.length;){for(;n<e.length&&/\s/.test(e[n]);)n++;if(e[n]!==`@`)break;let t=e.slice(n).match(/^@([A-Z]\w*)/);if(!t)break;for(n+=t[0].length;n<e.length&&/\s/.test(e[n]);)n++;if(e[n]===`(`){let t=Lr(e,n);if(t<0)return null;n=t+1}}for(;n<e.length&&/\s/.test(e[n]);)n++;for(let t of[`public`,`private`,`protected`])if(e.slice(n,n+t.length)===t&&/\s/.test(e.charAt(n+t.length))){for(n+=t.length;n<e.length&&/\s/.test(e[n]);)n++;break}if(e.slice(n,n+5)===`async`&&/\s/.test(e.charAt(n+5)))for(n+=5;n<e.length&&/\s/.test(e[n]);)n++;let r=e.slice(n).match(/^([a-zA-Z_]\w*)\s*\(/);return r?{methodName:r[1],endPos:n+r[0].length}:null}function zr(e){return(e.match(/:([a-zA-Z_]\w*)/g)??[]).map(e=>e.slice(1))}function Br(e,t){let n=e.endsWith(`/`)?e.slice(0,-1):e;return!t||t===`/`?n||`/`:n+(t.startsWith(`/`)?t:`/`+t)||`/`}const Vr=/\b(?:public\s+|private\s+|protected\s+)?routes\s*\([^)]*\)\s*(?::\s*[A-Za-z_][\w<>[\]\s,|]*\s*)?\{/g,Hr=/\bpath\s*:\s*['"`]([^'"`]*)['"`]/g,Ur=/\bcontroller\s*:\s*([A-Z]\w*)\b/g,Wr=/\bimport\.meta\.glob\s*\(/g;function Gr(e){let t=[];for(Wr.lastIndex=0;Wr.exec(e)!==null;){let n=Wr.lastIndex-1,r=Lr(e,n);if(r<0)continue;let i=e.slice(n+1,r),a=/['"`]([^'"`]+)['"`]/g,o;for(;(o=a.exec(i))!==null;)t.push(o[1])}return t}function Kr(e){let t=e.replace(/[.+^$()|[\]\\]/g,`\\$&`).replace(/\?/g,`.`).replace(/\*\*\//g,`___DOUBLESTAR_SLASH___`).replace(/\*\*/g,`___DOUBLESTAR___`).replace(/\*/g,`[^/]*`).replace(/___DOUBLESTAR_SLASH___/g,`(?:.+/)?`).replace(/___DOUBLESTAR___/g,`.*`);return RegExp(`^`+t+`$`)}function qr(e,t){let n=e.startsWith(`./`)?e:`./`+e,r=!1;for(let e of t){let t=e.startsWith(`!`);Kr(t?e.slice(1):e).test(n)&&(r=!t)}return r}function Jr(e){let t=[];Vr.lastIndex=0;let n;for(;(n=Vr.exec(e))!==null;){let r=e.indexOf(`{`,n.index+n[0].length-1);if(r<0)continue;let i=oi(e,r);if(i<0)continue;let a=e.slice(r+1,i),o=[];Hr.lastIndex=0;let s;for(;(s=Hr.exec(a))!==null;)o.push(s[1]??``);let c=[];Ur.lastIndex=0;let l;for(;(l=Ur.exec(a))!==null;)c.push(l[1]);let u=Math.min(o.length,c.length);for(let e=0;e<u;e++)t.push({controller:c[e],mountPath:o[e]})}return t}function Yr(e,t){let n=new RegExp(String.raw`\b${t}\s*:\s*([A-Za-z_$][\w$]*)`,`g`).exec(e);return n?n[1]:null}function Xr(e,t){let n=new RegExp(String.raw`import\s*(?:type\s+)?\{[^}]*\b${t}\b[^}]*\}\s*from\s*['"\`]([^'"\`]+)['"\`]`).exec(e);if(n)return n[1];let r=new RegExp(String.raw`import\s+(?:type\s+)?${t}\s+from\s*['"\`]([^'"\`]+)['"\`]`).exec(e);if(r)return r[1];let i=new RegExp(String.raw`import\s*\*\s*as\s+${t}\s+from\s*['"\`]([^'"\`]+)['"\`]`).exec(e);return i?i[1]:new RegExp(String.raw`(?:^|\n)\s*(?:export\s+)?const\s+${t}\b`).test(e)?``:null}function Zr(e,t){let n=/@ApiQueryParams\s*\(\s*([\s\S]*?)\s*\)\s*$/.exec(e);if(!n){let n=/@ApiQueryParams\s*\(([\s\S]*?)\)/.exec(e);return n?Qr(n[1].trim(),t):null}return Qr(n[1].trim(),t)}function Qr(e,t){if(e.startsWith(`{`))return ei(e);let n=/^([A-Za-z_]\w*)/.exec(e);if(n){let e=n[1],r=new RegExp(String.raw`const\s+${e}\s*(?::\s*[^=]+)?=\s*(\{[\s\S]*?\n\})`,`m`).exec(t);if(r)return ei(r[1])}return{filterable:[],sortable:[],searchable:[]}}function $r(e,t){let n=new RegExp(String.raw`${t}\s*:\s*\[([\s\S]*?)\]`).exec(e);return n?Array.from(n[1].matchAll(/['"`]([^'"`]+)['"`]/g)).map(e=>e[1]):[]}function ei(e){return{filterable:$r(e,`filterable`),sortable:$r(e,`sortable`),searchable:$r(e,`searchable`)}}async function ti(e,t){let n=t.extensions??Cr,r=t.exclude??wr,i=[],a;try{a=await ae(e,{withFileTypes:!0,encoding:`utf-8`})}catch{return i}for(let o of a){let a=h(e,o.name),s=_(t.cwd,a);r.some(e=>s.includes(e))||(o.isDirectory()?i.push(...await ti(a,t)):o.isFile()&&n.some(e=>o.name.endsWith(e))&&i.push(a))}return i}function K(e,t){return _(t,e).split(y).join(`/`)}function ni(e,t,n){let r=[],i=K(t,n);Tr.lastIndex=0;let a;for(;(a=Tr.exec(e))!==null;){let[,e,n,o]=a;r.push({className:o,decorator:e,filePath:t,relativePath:i,isDefault:!!n})}Er.lastIndex=0;let o;for(;(o=Er.exec(e))!==null;){let[,e,n]=o;r.some(e=>e.className===n&&e.filePath===t)||r.push({className:n,decorator:`Module`,filePath:t,relativePath:i,isDefault:!!e})}Dr.lastIndex=0;let s;for(;(s=Dr.exec(e))!==null;){let[,e]=s;r.some(n=>n.className===e&&n.filePath===t)||r.push({className:e,decorator:`Module`,filePath:t,relativePath:i,isDefault:!1})}return r}function ri(e,t,n){let r=[],i=K(t,n),a=new Set;Or.lastIndex=0;let o;for(;(o=Or.exec(e))!==null;){let[e,n,s]=o;a.add(e),r.push({name:s,variable:n,filePath:t,relativePath:i})}for(kr.lastIndex=0;(o=kr.exec(e))!==null;)a.has(o[0])||r.push({name:o[1],variable:null,filePath:t,relativePath:i});return r}function ii(e,t,n,r,i=new Map){let a=[];if(r.length===0)return a;let o=K(t,n),s=[];for(let t of r){let n=new RegExp(String.raw`class\s+${t.className}\b`).exec(e);n?.index!==void 0&&s.push({cls:t,start:n.index})}s.sort((e,t)=>e.start-t.start);for(let n=0;n<s.length;n++){let{cls:r,start:c}=s[n],l=n+1<s.length?s[n+1].start:e.length,u=e.slice(c,l);Ir.lastIndex=0;let d;for(;(d=Ir.exec(u))!==null;){let n=d[1],s=d.index,c=Ir.lastIndex-1,l=Lr(u,c);if(l<0)continue;let f=u.slice(c+1,l),p=f.match(/^\s*['"`]([^'"`]*)['"`]/),m=p&&p[1].length>0?p[1]:`/`,h=Rr(u,l+1);if(!h)continue;let{methodName:g,endPos:_}=h;Ir.lastIndex=_;let v=Zr(u.slice(s,_),e),y=Yr(f,`body`),b=Yr(f,`query`),x=Yr(f,`params`),S=i.get(r.className)??``,C=S?Br(S,m):m;a.push({controller:r.className,method:g,httpMethod:n.toUpperCase(),path:m,pathParams:zr(C),queryFilterable:v?.filterable??null,querySortable:v?.sortable??null,querySearchable:v?.searchable??null,bodySchema:y?{identifier:y,source:Xr(e,y)}:null,querySchema:b?{identifier:b,source:Xr(e,b)}:null,paramsSchema:x?{identifier:x,source:Xr(e,x)}:null,filePath:t,relativePath:o})}}return a}function ai(e,t,n){let r=[],i=K(t,n);Ar.lastIndex=0;let a;for(;(a=Ar.exec(e))!==null;)r.push({name:a[1],filePath:t,relativePath:i});return r}function oi(e,t){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 si(e,t,n){let r=[],i=K(t,n),a=new Set;jr.lastIndex=0;let o;for(;(o=jr.exec(e))!==null;){let n=o[1],s=jr.lastIndex-1,c=Lr(e,s);if(c<0)continue;let l=e.slice(s+1,c),u=/\bname\s*:\s*['"`]([^'"`]+)['"`]/.exec(l);if(!u)continue;let d=u[1],f=`${n}::${d}::${t}`;a.has(f)||(a.add(f),r.push({kind:n===`definePlugin`?`plugin`:`adapter`,name:d,filePath:t,relativePath:i}))}Nr.lastIndex=0;let s;for(;(s=Nr.exec(e))!==null;){let n=s.index,o=e.indexOf(`{`,n);if(o<0)continue;let c=oi(e,o);if(c<0)continue;let l=e.slice(o+1,c),u=Pr.exec(l);if(!u)continue;let d=u[1],f=`class::${d}::${t}`;a.has(f)||(a.add(f),r.push({kind:`adapter`,name:d,filePath:t,relativePath:i}))}return r}function ci(e,t,n){let r=[],i=K(t,n),a=new Set;for(Mr.lastIndex=0;Mr.exec(e)!==null;){let n=Mr.lastIndex-1,o=Lr(e,n);if(o<0)continue;let s=e.slice(n+1,o),c=/\bkey\s*:\s*['"`]([^'"`]+)['"`]/.exec(s);if(!c)continue;let l=c[1];a.has(l)||(a.add(l),r.push({key:l,filePath:t,relativePath:i}))}return r}function li(e,t,n){let r=[],i=K(t,n);Fr.lastIndex=0;let a;for(;(a=Fr.exec(e))!==null;){let n=a[1],o=null,s=null;if(a[2]){let t=e.indexOf(`{`,a.index+a[0].length-1);if(t>=0){let n=oi(e,t);if(n>=0){let r=e.slice(t+1,n);o=ui(r,`description`),s=ui(r,`example`)}}}r.push({name:n,description:o,example:s,filePath:t,relativePath:i})}return r}function ui(e,t){let n=RegExp(`\\b${t}\\s*:\\s*(['"\`])`,`g`).exec(e);if(!n)return null;let r=n[1],i=n.index+n[0].length,a=i,o=null;for(;a<e.length;){let t=e[a];if(t===`\\`){a+=2;continue}if(t===r){o=e.slice(i,a);break}a++}return o===null?null:o.replace(/\\(.)/g,(e,t)=>t===`n`?`
|
|
2349
|
+
`:t===`t`?` `:t===`r`?`\r`:t)}const di=[`src/config/index.ts`,`src/config/env.ts`,`src/config.ts`,`src/env.ts`];async function fi(e,t){let n=t===`src/env.ts`?di:[t];for(let t of n){let n=v(e,t),r;try{r=await T(n,`utf-8`)}catch{continue}if(!(!/\bdefineEnv\s*\(/.test(r)&&!/\bfrom(Zod|Valibot|Yup)\s*\(/.test(r))&&/export\s+default\b/.test(r)&&!/export\s+default\s+loadEnvFromSchema\s*\(/.test(r))return{filePath:n,relativePath:K(n,e)}}return null}function pi(e){let t=new Map;for(let n of e){let e=t.get(n.className)??[];e.push(n),t.set(n.className,e)}let n=[];for(let[e,r]of t)new Set(r.map(e=>e.filePath)).size>1&&n.push({className:e,classes:r});return n.sort((e,t)=>e.className.localeCompare(t.className)),n}function mi(e,t,n){let r=ni(e,t,n);return{classes:r,tokens:ri(e,t,n),injects:ai(e,t,n),pluginsAndAdapters:si(e,t,n),augmentations:li(e,t,n),contextKeys:ci(e,t,n),routes:ii(e,t,n,r,new Map),moduleMounts:Jr(e),globPatterns:/\.module\.[mc]?[tj]sx?$/.test(t)?Gr(e):[]}}async function hi(e,t,n){let r=n?await xr.signature(e):null;if(n&&r){let t=n.get(e,r);if(t)return n.set(e,r,t),t}let i;try{i=await T(e,`utf-8`)}catch{return null}let a=mi(i,e,t);return n&&r&&n.set(e,r,a),a}async function gi(e,t,n){let r=[],i=0,a=Array.from({length:Math.min(t,e.length)},async()=>{for(;;){let t=i++;if(t>=e.length)return;r[t]=await n(e[t],t)}});return await Promise.all(a),r}async function _i(e){let t=(await ti(v(e.root),e)).toSorted(),n=e.cacheDir?await xr.load(e.cacheDir):null,r=bi(t,await gi(t,16,t=>hi(t,e.cwd,n))),i=await fi(e.cwd,e.envFile??`src/env.ts`);return n&&await n.save(),{...r,env:i}}function vi(e,t,n){let r=n.extensions??Cr,i=n.exclude??wr;if(!e.startsWith(t+y)&&e!==t||!r.some(t=>e.endsWith(t)))return!1;let a=_(n.cwd,e);return!i.some(e=>a.includes(e))}async function yi(e,t){if(!e.cacheDir)return _i(e);let n=v(e.root),r=await xr.load(e.cacheDir),i=r.cachedFiles();if(i.length===0)return _i(e);let a=new Set(t.removed.map(t=>v(e.cwd,t))),o=t.changed.map(t=>v(e.cwd,t)).filter(t=>!a.has(t)&&vi(t,n,e)),s=new Set(o),c=new Set(i);for(let e of s)c.add(e);for(let e of a)c.delete(e);let l=new Map;await gi(o,16,async t=>{if(!c.has(t))return;let n=await xr.signature(t),i;try{i=await T(t,`utf-8`)}catch{c.delete(t);return}let a=mi(i,t,e.cwd);l.set(t,a),n&&r.set(t,n,a)});let u=[...c].toSorted(),d=bi(u,u.map(e=>l.get(e)||(r.carry(e),r.peek(e)))),f=await fi(e.cwd,e.envFile??`src/env.ts`);return await r.save(),{...d,env:f}}function bi(e,t){let n=[],r=[],i=[],a=[],o=[],s=[],c=[],l=new Map;for(let e of t)if(e)for(let{controller:t,mountPath:n}of e.moduleMounts)l.has(t)||l.set(t,n);let u=new Map;for(let d=0;d<e.length;d++){let f=t[d];if(f){n.push(...f.classes),i.push(...f.tokens),a.push(...f.injects),o.push(...f.pluginsAndAdapters),s.push(...f.augmentations),c.push(...f.contextKeys),f.globPatterns.length>0&&u.set(e[d],f.globPatterns);for(let e of f.routes){let t=l.get(e.controller);if(t){let n=Br(t,e.path);r.push({...e,pathParams:zr(n)})}else r.push(e)}}}let d=[];for(let[e,t]of u){if(!/\.module\.[mc]?[tj]sx?$/.test(e)||t.length===0)continue;let r=e.replaceAll(y,`/`),i=r.slice(0,r.lastIndexOf(`/`));for(let a of n){if(a.decorator===`Module`)continue;let n=a.filePath.replaceAll(y,`/`);n.startsWith(i+`/`)&&n!==r&&(qr(n.slice(i.length+1),t)||d.push({className:a.className,filePath:a.filePath,relativePath:a.relativePath,moduleFilePath:e,decorator:a.decorator}))}}n.sort((e,t)=>e.className===t.className?e.relativePath.localeCompare(t.relativePath):e.className.localeCompare(t.className)),i.sort((e,t)=>e.name.localeCompare(t.name)||e.relativePath.localeCompare(t.relativePath)),a.sort((e,t)=>e.name.localeCompare(t.name)||e.relativePath.localeCompare(t.relativePath)),r.sort((e,t)=>e.controller.localeCompare(t.controller)||e.method.localeCompare(t.method)),o.sort((e,t)=>e.name.localeCompare(t.name)||e.relativePath.localeCompare(t.relativePath)),s.sort((e,t)=>e.name.localeCompare(t.name)||e.relativePath.localeCompare(t.relativePath)),c.sort((e,t)=>e.key.localeCompare(t.key)||e.relativePath.localeCompare(t.relativePath));let f=pi(n);return d.sort((e,t)=>e.relativePath.localeCompare(t.relativePath)||e.className.localeCompare(t.className)),{classes:n,routes:r,tokens:i,injects:a,collisions:f,pluginsAndAdapters:o,augmentations:s,contextKeys:c,orphanedClasses:d}}const q="/* 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",xi=new Set([`Service`,`Repository`,`Injectable`,`Component`]);var Si=class extends Error{collisions;constructor(e){super(Ci(e)),this.name=`TokenCollisionError`,this.collisions=e}};function Ci(e){let t=[`kick typegen: token collision detected`];for(let n of e){t.push(``),t.push(` ${n.classes.length} classes named '${n.className}':`);for(let e of n.classes)t.push(` - ${e.relativePath}`)}return t.push(``),t.push(`Resolutions:`),t.push(` (a) Rename one of the classes`),t.push(` (b) Use createToken<T>('namespaced/Name') and import the token explicitly — see @forinda/kickjs`),t.push(` (c) Pass --allow-duplicates to namespace the registry keys automatically`),t.push(` (e.g. 'modules/users/UserService' instead of 'UserService')`),t.join(`
|
|
2350
|
+
`)}function wi(e,t){let n=_(f(t),e).split(y).join(`/`);return n=n.replace(/\.(ts|tsx|mts|cts)$/i,``),n.startsWith(`.`)||(n=`./`+n),n}function Ti(e){let t=e.relativePath.replace(/^src\//,``).replace(/\.(ts|tsx|mts|cts)$/i,``).split(`/`);t.pop();let n=t.join(`/`);return n?`${n}/${e.className}`:e.className}function Ei(e,t,n){let r=new Set,i=[];for(let a of e){if(!xi.has(a.decorator))continue;let e=n.has(a.className)?Ti(a):a.className;if(r.has(e))continue;r.add(e);let o=wi(a.filePath,t),s=a.isDefault?`import('${o}').default`:`import('${o}').${a.className}`;i.push(` '${e}': ${s}`)}return`${q}
|
|
3575
2351
|
declare module '@forinda/kickjs' {
|
|
3576
2352
|
interface KickJsRegistry {
|
|
3577
2353
|
${i.length?i.join(`
|
|
@@ -3580,7 +2356,7 @@ ${i.length?i.join(`
|
|
|
3580
2356
|
}
|
|
3581
2357
|
|
|
3582
2358
|
export {}
|
|
3583
|
-
`}function
|
|
2359
|
+
`}function Di(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)}function Oi(e){return`${q}
|
|
3584
2360
|
declare module '@forinda/kickjs' {
|
|
3585
2361
|
/**
|
|
3586
2362
|
* Key-only registry of every context key produced by a
|
|
@@ -3589,20 +2365,20 @@ declare module '@forinda/kickjs' {
|
|
|
3589
2365
|
* \`ContextMeta\`; this only records that the key exists.
|
|
3590
2366
|
*/
|
|
3591
2367
|
interface ContextKeys {
|
|
3592
|
-
${[...new Set(e.map(e=>e.key))].toSorted().map(e=>` ${
|
|
2368
|
+
${[...new Set(e.map(e=>e.key))].toSorted().map(e=>` ${Di(e)?e:JSON.stringify(e)}: true`).join(`
|
|
3593
2369
|
`)}
|
|
3594
2370
|
}
|
|
3595
2371
|
}
|
|
3596
2372
|
|
|
3597
2373
|
export {}
|
|
3598
|
-
`}function
|
|
2374
|
+
`}function ki(e,t,n){return t.length===0?`${q}
|
|
3599
2375
|
// ${n}
|
|
3600
2376
|
export type ${e} = never
|
|
3601
|
-
`:`${
|
|
2377
|
+
`:`${q}
|
|
3602
2378
|
export type ${e} =
|
|
3603
2379
|
${[...new Set(t)].toSorted().map(e=>` | '${e}'`).join(`
|
|
3604
2380
|
`)}
|
|
3605
|
-
`}function
|
|
2381
|
+
`}function Ai(e,t,n,r){return[...e.filter(e=>xi.has(e.decorator)).map(e=>r.has(e.className)?Ti(e):e.className),...t.map(e=>e.name),...n.map(e=>e.name)]}function ji(e){return e.filter(e=>e.decorator===`Module`).map(e=>e.className)}function Mi(e){let t=new Map;for(let n of e)t.has(n.name)||t.set(n.name,n);return`${q}
|
|
3606
2382
|
declare module '@forinda/kickjs' {
|
|
3607
2383
|
/**
|
|
3608
2384
|
* Map of every plugin/adapter \`name\` discovered in the project. The
|
|
@@ -3617,7 +2393,7 @@ ${[...t.values()].toSorted((e,t)=>e.name.localeCompare(t.name)).map(e=>` '${e
|
|
|
3617
2393
|
}
|
|
3618
2394
|
|
|
3619
2395
|
export {}
|
|
3620
|
-
`}function
|
|
2396
|
+
`}function Ni(e){if(e.length===0)return`${q}
|
|
3621
2397
|
// No augmentations discovered.
|
|
3622
2398
|
//
|
|
3623
2399
|
// Plugins advertise augmentable interfaces via:
|
|
@@ -3633,7 +2409,7 @@ export {}
|
|
|
3633
2409
|
`;let t=new Map;for(let n of e)t.has(n.name)||t.set(n.name,n);let n=[];for(let e of[...t.values()].toSorted((e,t)=>e.name.localeCompare(t.name))){let t=[];if(e.description)for(let n of e.description.split(`
|
|
3634
2410
|
`))t.push(` * ${n}`);if(e.example){t.push(` * @example`," * ```ts");for(let n of e.example.split(`
|
|
3635
2411
|
`))t.push(` * ${n}`);t.push(" * ```")}t.push(` * @see ${e.relativePath}`),n.push([`/**`,...t,` */`,`export interface ${e.name}Augmentation {}`].join(`
|
|
3636
|
-
`))}return`${
|
|
2412
|
+
`))}return`${q}
|
|
3637
2413
|
// Catalogue of augmentable interfaces in this project. The interfaces
|
|
3638
2414
|
// below are documentation only — augment the source-of-truth interfaces
|
|
3639
2415
|
// in your own \`d.ts\` files (the framework declares the actual types).
|
|
@@ -3641,7 +2417,7 @@ export {}
|
|
|
3641
2417
|
${n.join(`
|
|
3642
2418
|
|
|
3643
2419
|
`)}
|
|
3644
|
-
`}const
|
|
2420
|
+
`}const Pi=/^(kick\/)?([a-z][\w-]*\/[A-Z]\w*)(\/.+)?(:[a-z][\w-]+(:[a-z][\w-]+)*)?$/;function Fi(e){let t=[];for(let n of e){let e=n.name;e.startsWith(`kickjs.`)||Pi.test(e)||t.push({token:e,variable:n.variable,filePath:n.relativePath,reason:"does not match `<scope>/<PascalKey>[/<suffix>][:<instance>]`",suggestion:Ii(e)})}return t}function Ii(e){if(/^[A-Z]\w*$/.test(e))return`'<scope>/${e}' (e.g. 'mycorp/${e}')`;if(e.includes(`.`))return`consider '<scope>/PascalKey' instead of dotted form`;let t=/^([a-z][\w-]*)\/([a-z]\w*)$/.exec(e);if(t){let[,e,n]=t;return`'${e}/${n.charAt(0).toUpperCase()}${n.slice(1)}'`}}function Li(e,t){if(!e)return{entries:[],count:0};let n=new Map;for(let[r,i]of Object.entries(e)){if(!i||typeof i.src!=`string`)continue;let e=v(t,i.src);if(!Hi(e))continue;let a=fe(i.glob??`**/*`,{cwd:e,nodir:!0,dot:!1,posix:!0});a.sort();let{pairs:o}=pe(r,a,{strategy:i.keys??`auto`});for(let{key:e}of o){let t=e.slice(r.length+1);n.set(e,{namespace:r,key:t})}}return{entries:[...n.values()],count:n.size}}function Ri(e){let t="/* 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";if(e.entries.length===0)return`${t}
|
|
3645
2421
|
declare module '@forinda/kickjs' {
|
|
3646
2422
|
/**
|
|
3647
2423
|
* Map of every typed asset discovered in the project's assetMap.
|
|
@@ -3652,7 +2428,7 @@ declare module '@forinda/kickjs' {
|
|
|
3652
2428
|
}
|
|
3653
2429
|
|
|
3654
2430
|
export {}
|
|
3655
|
-
`;let n={};for(let t of e.entries){let e=`${t.namespace}/${t.key}`.split(`/`),r=n;for(let t=0;t<e.length-1;t++){let n=e[t],i=r[n];if(i===
|
|
2431
|
+
`;let n={};for(let t of e.entries){let e=`${t.namespace}/${t.key}`.split(`/`),r=n;for(let t=0;t<e.length-1;t++){let n=e[t],i=r[n];if(i===zi){let e={};r[n]=e,r=e}else i||(r[n]={}),r=r[n]}let i=e[e.length-1];typeof r[i]!=`object`&&(r[i]=zi)}return`${t}
|
|
3656
2432
|
declare module '@forinda/kickjs' {
|
|
3657
2433
|
/**
|
|
3658
2434
|
* Map of every typed asset discovered in the project's assetMap.
|
|
@@ -3661,71 +2437,71 @@ declare module '@forinda/kickjs' {
|
|
|
3661
2437
|
* prod → dist).
|
|
3662
2438
|
*/
|
|
3663
2439
|
interface KickAssets {
|
|
3664
|
-
${
|
|
2440
|
+
${Bi(n,` `)}
|
|
3665
2441
|
}
|
|
3666
2442
|
}
|
|
3667
2443
|
|
|
3668
2444
|
export {}
|
|
3669
|
-
`}const
|
|
3670
|
-
`)}function
|
|
2445
|
+
`}const zi=Symbol(`asset-leaf`);function Bi(e,t){let n=Object.keys(e).toSorted(),r=[];for(let i of n){let n=e[i],a=Vi(i)?i:JSON.stringify(i);n===zi?r.push(`${t}${a}: () => string`):(r.push(`${t}${a}: {`),r.push(Bi(n,`${t} `)),r.push(`${t}}`))}return r.join(`
|
|
2446
|
+
`)}function Vi(e){return/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(e)}function Hi(e){try{return c(e).isDirectory()}catch{return!1}}var Ui=A({runTypegen:()=>J,sweepStaleTypegen:()=>Ji,watchTypegen:()=>qi,writeTypegenArtifacts:()=>Ki});function Wi(e){let t=e.cwd??process.cwd();return{cwd:t,srcDir:v(t,e.srcDir??`src`),outDir:v(t,e.outDir??`.kickjs/types`),silent:e.silent??!1,allowDuplicates:e.allowDuplicates??!1,schemaValidator:e.schemaValidator??!1,envFile:e.envFile??`src/env.ts`}}async function J(e={}){let{cwd:t,srcDir:n,outDir:r,silent:i,allowDuplicates:a,envFile:o}=Wi(e),s=Date.now(),c={root:n,cwd:t,cacheDir:v(t,`.kickjs`,`cache`),envFile:o===!1?void 0:o},l=e.changedFiles?await yi(c,e.changedFiles):await _i(c);if(l.collisions.length>0&&!a)throw new Si(l.collisions);let u=Li(e.assetMap,t),d=[],f=[];if(e.runPlugins!==!1){try{let{runAllPluginTypegens:n}=await Promise.resolve().then(()=>sa),{loadKickConfig:r}=await Promise.resolve().then(()=>Se);d=await n({cwd:t,config:await r(t),silent:!0,changedFiles:e.changedFiles})}catch(e){if(!i){let t=e instanceof Error?e.message:String(e);console.warn(` kick typegen: plugin pipeline failed (${t}) — continuing`)}}f.push(...await Ki(r,d,i))}let p=Fi(l.tokens),m=Gi(l,u.count,f),h=Date.now()-s;if(!i){let e=r.replace(t+`/`,``),n=m.resolvedCollisions>0?`, ${m.resolvedCollisions} collisions namespaced`:``,i=m.envWritten?`, env typed`:``,a=m.pluginEntries>0?`, ${m.pluginEntries} plugins/adapters`:``,o=m.augmentationEntries>0?`, ${m.augmentationEntries} augmentations`:``,s=m.assetEntries>0?`, ${m.assetEntries} assets`:``;if(console.log(` kick typegen → ${m.serviceTokens} services, ${m.routeEntries} routes, ${m.moduleTokens} modules${a}${o}${s}${i}${n} → ${e} (${h}ms)`),p.length>0){console.warn(` kick typegen: ${p.length} token(s) don't match the §22.2 convention:`);for(let e of p){let t=e.variable?` [${e.variable}]`:``;console.warn(` '${e.token}' (${e.filePath})${t} — ${e.reason}`),e.suggestion&&console.warn(` → suggestion: ${e.suggestion}`)}}if(l.orphanedClasses.length>0){console.warn(` kick typegen: ${l.orphanedClasses.length} decorated class(es) not matched by any module's import.meta.glob():`);for(let e of l.orphanedClasses)console.warn(` @${e.decorator} ${e.className} (${e.relativePath})`),console.warn(` → not picked up by any glob in ${e.moduleFilePath}`)}}return{scan:l,result:m,tokenWarnings:p}}function Gi(e,t,n){let r=new Set(e.collisions.map(e=>e.className)),i=e.classes.filter(e=>xi.has(e.decorator)),a=Ai(e.classes,e.tokens,e.injects,r);return{registryEntries:i.length,serviceTokens:new Set(a).size,moduleTokens:ji(e.classes).length,routeEntries:e.routes.length,pluginEntries:new Set(e.pluginsAndAdapters.map(e=>e.name)).size,augmentationEntries:new Set(e.augmentations.map(e=>e.name)).size,assetEntries:t,envWritten:e.env!==null,written:n,resolvedCollisions:e.collisions.length}}async function Ki(e,t,n){await w(e,{recursive:!0}),await E(h(f(e),`.gitignore`),`# Auto-generated by kick typegen
|
|
3671
2447
|
*
|
|
3672
|
-
`,`utf-8`);let r=t.filter(e=>e.outFile).map(e=>e.outFile);return await
|
|
2448
|
+
`,`utf-8`);let r=t.filter(e=>e.outFile).map(e=>e.outFile);return await Ji(e,r,t,n),r}async function qi(e={}){let t=Wi(e),{srcDir:n,silent:r,cwd:i}=t,a={...t,allowDuplicates:!0,runPlugins:!1},o=process.env.KICKJS_WATCH_POLLING===`1`||process.env.KICKJS_WATCH_POLLING===`true`,[{runAllPluginTypegens:s},{loadKickConfig:c}]=await Promise.all([Promise.resolve().then(()=>sa),Promise.resolve().then(()=>Se)]),l=await c(i),u=async()=>{try{await J({...a})}catch(e){if(r)return;if(e instanceof Si)console.error(`
|
|
3673
2449
|
`+e.message+`
|
|
3674
|
-
`);else{let t=e instanceof Error?e.message:String(e);console.error(` kick typegen failed: ${t}`)}}},d=async()=>{try{let e=await s({cwd:i,config:l,silent:!0});await
|
|
3675
|
-
(dry run — no files were written)`),console.log()}async function
|
|
2450
|
+
`);else{let t=e instanceof Error?e.message:String(e);console.error(` kick typegen failed: ${t}`)}}},d=async()=>{try{let e=await s({cwd:i,config:l,silent:!0});await Ki(t.outDir,e,!0)}catch{}};await u(),await d();let{watch:f}=await import(`node:fs`),p=null,m=e=>{e&&/\.(ts|tsx|mts|cts)$/.test(e)&&(e.includes(`.kickjs`)||e.endsWith(`.d.ts`)||(p&&clearTimeout(p),p=setTimeout(()=>{u().then(d)},100)))};if(o){r||console.log(` kick typegen: polling mode (KICKJS_WATCH_POLLING)`);let e=setInterval(()=>{u().then(d)},2e3);return()=>clearInterval(e)}let h;try{h=f(n,{recursive:!0},(e,t)=>{m(t)})}catch(e){r||console.warn(` kick typegen: watch mode unavailable (${e?.message??e}). Falling back to polling.`);let t=setInterval(()=>{u().then(d)},2e3);return()=>clearInterval(t)}return()=>{p&&clearTimeout(p),h.close()}}async function Ji(e,t,n,r){let i=new Set;for(let e of t)i.add(d(e));for(let e of n)e.outFile&&i.add(d(e.outFile));let a;try{a=await ae(e)}catch{return[]}let o=[];for(let t of a){if(!Yi.has(t)||i.has(t))continue;let n=v(e,t);try{if(!(await se(n)).isFile())continue;await ce(n),o.push(t)}catch{}}return o.length>0&&!r&&console.log(` kick typegen: swept ${o.length} stale file(s): ${o.join(`, `)}`),o}const Yi=new Set([`assets.d.ts`,`env.ts`,`routes.ts`,`registry.d.ts`,`services.d.ts`,`modules.d.ts`,`plugins.d.ts`,`augmentations.d.ts`,`index.d.ts`]),Xi=[`agents`,`claude`,`skills`,`gemini`,`copilot`,`both`,`all`];function Y(e){return e.parent?.opts()?.dryRun??!1}function X(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(`
|
|
2451
|
+
(dry run — no files were written)`),console.log()}async function Zi(e){if(!e)try{let e=await M(process.cwd());await J({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 Qi=[{name:`module <name>`,description:`REST module (controller, service, DTOs, 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:`contributor <name>`,description:`Context contributor [--type http|bare] [--params a:string] [-m]`},{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`}],$i=new Set(Qi.map(e=>e.name.split(` `)[0]));async function ea(){console.log(`
|
|
3676
2452
|
Built-in generators:
|
|
3677
|
-
`);let e=Math.max(...
|
|
2453
|
+
`);let e=Math.max(...Qi.map(e=>e.name.length));for(let t of Qi)console.log(` kick g ${t.name.padEnd(e+2)} ${t.description}`);let t=await M(process.cwd()),n=Pe(t?.plugins??[],t?.commands??[]),r=await nn(process.cwd(),n.generators);if(r.generators.length>0){console.log(`
|
|
3678
2454
|
Plugin generators:
|
|
3679
2455
|
`);let e=Math.max(...r.generators.map(e=>`${e.spec.name} <name>`.length));for(let{source:t,spec:n}of r.generators){let r=`${n.name} <name>`;console.log(` kick g ${r.padEnd(e+2)} ${n.description} [${t}]`)}}if(r.failed.length>0){console.log(`
|
|
3680
2456
|
Failed to load:
|
|
3681
|
-
`);for(let{source:e,reason:t}of r.failed)console.log(` ${e} — ${t}`)}console.log()}async function
|
|
3682
|
-
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,n)=>{let r=
|
|
3683
|
-
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,n)=>{let r=
|
|
2457
|
+
`);for(let{source:e,reason:t}of r.failed)console.log(` ${e} — ${t}`)}console.log()}async function ta(e,t,n){let r=await M(process.cwd()),i=j(r),a=t.modulesDir??i.dir??`src/modules`,o=t.repo??Tn(i.repo);t.repo&&Ee(t.repo);let s=t.pattern??r?.pattern??`rest`,c=t.pluralize===!1?!1:i.pluralize??!0,l=Oe(r,process.cwd()),u=i.style??`define`;if(!n&&u===`define`){let e=await ur(v(a),`define`);if(e.length>0){console.error(`\n ${k.red(`Error:`)} ${e.length} module file(s) still use the legacy \`class … implements AppModule\` shape.\n ${k.dim(`Project setting:`)} modules.style: 'define' (default)\n\n ${k.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 ${k.bold(`Pick one:`)}\n 1. Migrate everything to defineModule:\n ${k.dim(`$`)} kick codemod modules --experimental --apply\n 2. Keep the class form — pin it in kick.config.ts:\n ${k.dim(`// kick.config.ts`)}\n ${k.dim(`export default defineConfig({ modules: { style: 'class' } })`)}\n`),process.exit(1)}}let d=[];for(let r of e){let e=await En({name:r,modulesDir:v(a),noEntity:t.entity===!1,noTests:t.tests===!1,repo:o,minimal:t.minimal,force:t.force,pattern:s,dryRun:n,pluralize:c,prismaClientPath:i.prismaClientPath,tokenScope:l,style:i.style});d.push(...e)}X(d,n),await Zi(n)}function na(e,t){let n=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 name: inmemory (default) or any DB name (e.g. postgres)`).option(`--pattern <pattern>`,`Override project pattern: rest | 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,r,i)=>{if(r.list){await ea();return}if(!e||e.length===0){n.help();return}let a=Y(i);N(a);let[o,s,...c]=e;if(o){let e=await M(process.cwd()),n=Pe(e?.plugins??[],e?.commands??[]),i=await tn({generatorName:o,itemName:s??``,args:c,flags:r,cwd:process.cwd(),projectRoot:t?.projectRoot},n.generators);if(i){X(i.files,a);return}if(o!==`module`&&$i.has(o)){console.error(`\n '${o}' is a generator, not a module name.`),console.error(` Did you mean: kick g ${o} ${s??`<name>`}`),console.error(` If that errors, your @forinda/kickjs-cli is older than the '${o}' generator — upgrade it.\n`),process.exitCode=1;return}}await ta(e,r,a)});n.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 name: inmemory (default) or any DB name (e.g. postgres)`).option(`--pattern <pattern>`,`Override project pattern: rest | 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=Y(n);N(r),await ta(e,{...n.optsWithGlobals(),...t},r)}),n.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=Y(n);N(r),X(await Pn({name:e,outDir:v(t.out)}),r)}),n.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=Y(n);N(r),X(await Fn({name:e,outDir:v(t.out)}),r)}),n.command(`middleware <name>`).description(`Generate an Express middleware function
|
|
2458
|
+
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,n)=>{let r=Y(n);N(r);let i=await M(process.cwd()),a=j(i),o=a.dir??`src/modules`;X(await Ln({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r)}),n.command(`guard <name>`).description(`Generate a route guard (auth, roles, etc.)
|
|
2459
|
+
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,n)=>{let r=Y(n);N(r);let i=await M(process.cwd()),a=j(i),o=a.dir??`src/modules`;X(await Rn({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r)}),n.command(`contributor <name>`).description(`Generate a Context Contributor (typed alternative to @Middleware for ctx.set)
|
|
3684
2460
|
--type http (default, RequestContext) | bare (ExecutionContext)
|
|
3685
2461
|
--params "source:string,region:number" → emits the withParams<T>() form
|
|
3686
|
-
Use -m to scope it to a module: kick g contributor tenant -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).option(`-t, --type <type>`,`Contributor flavour: http | bare`,`http`).option(`-k, --key <key>`,`Context key it writes (defaults to camelCase of name)`).option(`--params <fields>`,`Per-call params, e.g. "source:string,region:number"`).action(async(e,t,n)=>{let r=
|
|
3687
|
-
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,n)=>{let r=
|
|
3688
|
-
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,n)=>{let r=
|
|
3689
|
-
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,n)=>{let r=
|
|
3690
|
-
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,n)=>{let r=
|
|
2462
|
+
Use -m to scope it to a module: kick g contributor tenant -m users`).option(`-o, --out <dir>`,`Output directory (overrides --module)`).option(`-m, --module <module>`,`Place inside a module folder`).option(`-t, --type <type>`,`Contributor flavour: http | bare`,`http`).option(`-k, --key <key>`,`Context key it writes (defaults to camelCase of name)`).option(`--params <fields>`,`Per-call params, e.g. "source:string,region:number"`).action(async(e,t,n)=>{let r=Y(n);N(r);let i=(t.type??`http`).toLowerCase();i!==`http`&&i!==`bare`&&(console.warn(` kick g contributor: unknown --type '${t.type}', using 'http'.`),i=`http`);let a=await M(process.cwd()),o=j(a),s=o.dir??`src/modules`;X(await Vn({name:e,type:i,key:t.key,params:t.params,outDir:t.out,moduleName:t.module,modulesDir:s,pattern:a?.pattern,pluralize:o.pluralize??!0}),r)}),n.command(`service <name>`).description(`Generate a @Service() class
|
|
2463
|
+
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,n)=>{let r=Y(n);N(r);let i=await M(process.cwd()),a=j(i),o=a.dir??`src/modules`;X(await Hn({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r)}),n.command(`controller <name>`).description(`Generate a @Controller() class with basic routes
|
|
2464
|
+
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,n)=>{let r=Y(n);N(r);let i=await M(process.cwd()),a=j(i),o=a.dir??`src/modules`;X(await Un({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r),await Zi(r)}),n.command(`dto <name>`).description(`Generate a Zod DTO schema
|
|
2465
|
+
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,n)=>{let r=Y(n);N(r);let i=await M(process.cwd()),a=j(i),o=a.dir??`src/modules`;X(await Wn({name:e,outDir:t.out,moduleName:t.module,modulesDir:o,pattern:i?.pattern,pluralize:a.pluralize??!0}),r)}),n.command(`test <name>`).description(`Generate a Vitest test scaffold
|
|
2466
|
+
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,n)=>{let r=Y(n);N(r);let i=j(await M(process.cwd())),a=i.dir??`src/modules`;X(await vr({name:e,outDir:t.out,moduleName:t.module,modulesDir:a,pluralize:i.pluralize??!0}),r)}),n.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=Y(n);N(r),X(await dr({name:e,outDir:v(t.out),queue:t.queue}),r)}),n.command(`scaffold <name> [fields...]`).description(`Generate a full CRUD module from field definitions
|
|
3691
2467
|
Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
|
|
3692
2468
|
Types: string, text, number, int, float, boolean, date, email, url, uuid, json, enum:a,b,c
|
|
3693
2469
|
Optional: append :optional (shell-safe): description:text:optional
|
|
3694
|
-
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,t,n,r)=>{let i=
|
|
2470
|
+
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,t,n,r)=>{let i=Y(r);N(i),t.length===0&&(console.error(`
|
|
3695
2471
|
Error: At least one field is required.
|
|
3696
2472
|
Usage: kick g scaffold <name> <field:type> [field:type...]
|
|
3697
2473
|
Example: kick g scaffold Post title:string body:text:optional published:boolean:optional
|
|
3698
2474
|
Optional: append :optional (shell-safe, no quoting needed)
|
|
3699
|
-
`),process.exit(1));let a=await
|
|
3700
|
-
`,d=``;if(r(
|
|
3701
|
-
`,`utf-8`),s(` ✓ wrote manifest → ${_(n,p)} (${Object.keys(d).length} entries)`),{manifestPath:p,entries:u,manifest:f}}async function
|
|
2475
|
+
`),process.exit(1));let a=await M(process.cwd()),o=j(a),s=n.modulesDir??o.dir??`src/modules`,c=pr(t),l=Oe(a,process.cwd()),u=await mr({name:e,fields:c,modulesDir:v(s),noEntity:n.entity===!1,noTests:n.tests===!1,pluralize:n.pluralize===!1?!1:o.pluralize??!0,tokenScope:l,style:o.style});console.log(`\n Scaffolded ${e} with ${c.length} field(s):`);for(let e of c)console.log(` ${e.name}: ${e.type}${e.optional?` (optional)`:``}`);X(u,i),await Zi(i)}),n.command(`config`).description(`Generate a kick.config.ts at the project root`).option(`--modules-dir <dir>`,`Modules directory path`,`src/modules`).option(`--repo <type>`,`Repository name: inmemory (default) or any DB name`,`inmemory`).option(`-f, --force`,`Overwrite existing kick.config.ts without prompting`).action(async(e,t)=>{let n=Y(t);N(n),X(await Gn({outDir:v(`.`),modulesDir:e.modulesDir,defaultRepo:e.repo,force:e.force}),n)}),n.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 | minimal`).option(`-f, --force`,`Overwrite existing files without prompting`).action(async(e,t)=>{let n=Y(t);N(n);let r=e.only??`all`;if(!Xi.includes(r)){console.error(` Invalid --only value: ${r}. Expected: ${Xi.join(` | `)}`),process.exitCode=1;return}X(await Qn({outDir:v(`.`),only:r,name:e.name,pm:e.pm,template:e.template,force:e.force}),n)});for(let e of t?.generators??[])ra(n,e,t?.projectRoot)}function ra(e,t,n){let{source:r,spec:i}=t,a=i.args?.[0],o=a?.name??`itemName`,s=a?.required?`<${o}>`:`[${o}]`,c=`${i.name} ${s} [extraArgs...]`,l=e.command(c).description(`${i.description} [${r}]`);for(let e of i.flags??[]){let t=e.takesValue?`--${e.name} <value>`:`--${e.name}`,n=e.alias?`-${e.alias}, ${t}`:t;l.option(n,e.description??``)}l.action(async(e,r,a,o)=>{let s=Y(o);N(s);let c=await tn({generatorName:i.name,itemName:e??``,args:r??[],flags:a,cwd:process.cwd(),projectRoot:n},[t]);c&&X(c.files,s)})}async function ia(e){let t=u.resolve(e.cwd,`.kickjs/types`);await w(t,{recursive:!0});let n=new Map,i=e.scan??_i,a=u.resolve(e.cwd,`.kickjs`,`cache`),o=e.scan?void 0:e.changedFiles,s={cwd:e.cwd,config:e.config,async importTs(e){return await import(x(e).href)},async writeFile(t,n){let r=u.resolve(e.cwd,t);await w(u.dirname(r),{recursive:!0}),await E(r,n,`utf8`)},getScanResult:e=>{let t=aa(e),r=n.get(t);if(!r){let s={cacheDir:a,...e};r=o?yi(s,o):i(s),n.set(t,r)}return r},log:console},c=[];for(let n of e.plugins){let i=n.outExtension??`.d.ts`,a=u.join(t,`${n.id.replace(/\//g,`__`)}${i}`),o;try{o=await n.generate(s)}catch(e){let t=e instanceof Error?e.message:String(e);s.log.error(` ${n.id}: typegen failed (${t}) — keeping previous output`),c.push({id:n.id,status:`error`,outFile:a});continue}if(o===null){c.push({id:n.id,status:`skipped`});continue}let l=`/* AUTO-GENERATED by kick typegen — do not edit. Plugin: ${n.id} */\n\n`+o+`
|
|
2476
|
+
`,d=``;if(r(a)&&(d=await T(a,`utf8`)),d===l){c.push({id:n.id,status:`unchanged`,outFile:a});continue}if(e.check)throw Error(`kick typegen --check: drift detected for ${n.id} (${a})`);await E(a,l,`utf8`),c.push({id:n.id,status:`written`,outFile:a})}return c}function aa(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 oa(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 sa=A({applyDisableFilter:()=>oa,runAllPluginTypegens:()=>la});function ca(){let e=(process.env.LOG_LEVEL??process.env.KICKJS_LOG_LEVEL??``).toLowerCase();return e===`debug`||e===`trace`}async function la(e){let{enabled:t,skipped:n,unknown:r}=oa(Pe([..._s,...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 ia({cwd:e.cwd,config:e.config??{},plugins:t,check:e.check,changedFiles:e.changedFiles});if(!e.silent&&ca())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 ua(e,t){let{cwd:n,silent:r=!1}=t,a=t.distDir??e?.build?.outDir??`dist`,o=e?.assetMap;if(!o||Object.keys(o).length===0)return null;let s=r?()=>{}:console.log,c=v(n,a);i(c,{recursive:!0});let u=[],d={};for(let[e,t]of Object.entries(o)){let r=await da(e,t,n,c);u.push(r.entrySummary),Object.assign(d,r.manifestSlice),s(` ✓ ${e}: ${r.entrySummary.filesCopied} file(s) → ${r.entrySummary.dest}`)}let f={version:1,entries:d},p=h(c,`.kickjs-assets.json`);return l(p,JSON.stringify(f,null,2)+`
|
|
2477
|
+
`,`utf-8`),s(` ✓ wrote manifest → ${_(n,p)} (${Object.keys(d).length} entries)`),{manifestPath:p,entries:u,manifest:f}}async function da(e,t,a,o){let s=v(a,t.src),c=t.dest?v(a,t.dest):h(o,e);if(ma(c,a))return console.warn(` ⚠ assetMap.${e}.dest ('${t.dest}') resolves outside the project root — skipping copy`),{entrySummary:{namespace:e,src:t.src,dest:_(a,c),filesCopied:0},manifestSlice:{}};if(!r(s)||!ha(s))return{entrySummary:{namespace:e,src:t.src,dest:_(a,c),filesCopied:0},manifestSlice:{}};let l=await de(t.glob??`**/*`,{cwd:s,nodir:!0,dot:!1,posix:!0});i(c,{recursive:!0});let u={},{pairs:d,collisionGroupsResolved:p}=pe(e,[...l].toSorted(),{strategy:t.keys??`auto`}),m=0;for(let{rel:e,key:t}of d){let r=h(s,e),a=h(c,e);u[t]=pa(o,a),!fa(r,a)&&(i(f(a),{recursive:!0}),n(r,a),m++)}return p>0&&console.log(` ℹ assetMap.${e}: auto-resolved ${p} 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:_(a,c),filesCopied:m},manifestSlice:u}}function fa(e,t){if(!r(t))return!1;try{let n=c(e),r=c(t);return r.size===n.size&&r.mtimeMs>=n.mtimeMs}catch{return!1}}function pa(e,t){return _(e,t).split(/[\\/]/).filter(Boolean).join(`/`)}function ma(e,t){let n=_(t,e);return n===``?!1:n.startsWith(`..`)||m(n)}function ha(e){try{return c(e).isDirectory()}catch{return!1}}function ga(e){if(typeof e==`boolean`)return e;let t=process.env.KICKJS_WATCH_POLLING;return t===`1`||t===`true`}async function _a(e,t,n={}){t&&(process.env.PORT=t);let r=ga(n.polling),i=process.cwd(),a=await M(i),o=a?.typegen?.schemaValidator??`zod`,s=a?.typegen?.envFile;try{await J({cwd:i,allowDuplicates:!0,schemaValidator:o,envFile:s,srcDir:a?.typegen?.srcDir,outDir:a?.typegen?.outDir,assetMap:a?.assetMap,runPlugins:!1})}catch(e){console.warn(` kick typegen: skipped (${e?.message??e})`)}let c=v(i,a?.typegen?.outDir??`.kickjs/types`);try{await Ki(c,await la({cwd:i,config:a}),!1)}catch(e){console.warn(` kick typegen: plugin pass skipped (${e?.message??e})`)}let{createRequire:l}=await import(`node:module`),{createServer:u}=await import(x(l(v(`package.json`)).resolve(`vite`)).href),d=await u({configFile:v(`vite.config.ts`),server:{port:t?parseInt(t,10):void 0,...r?{watch:{usePolling:!0,interval:100}}:{}}}),f=a?.assetMap?Object.values(a.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>v(i,e)):[],p=e=>f.some(t=>e===t||e.startsWith(`${t}/`)),m=null,h=new Set,g=new Set,_=!1,y=!1,b=!!a?.assetMap&&Object.keys(a.assetMap).length>0,S=(e,t)=>{if(!t.includes(`.kickjs`)){if(e===`unlinkDir`)_=!0,b&&(y=!0);else{if(t.endsWith(`.d.ts`))return;let n=/\.(ts|tsx|mts|cts)$/.test(t),r=p(t);if(!n&&!r)return;r&&b&&(y=!0),n&&(e===`unlink`?(g.add(t),h.delete(t)):(h.add(t),g.delete(t)))}m&&clearTimeout(m),m=setTimeout(()=>{let e=_?void 0:{changed:[...h],removed:[...g]},t=y;h.clear(),g.clear(),_=!1,y=!1,J({cwd:i,silent:!0,allowDuplicates:!0,schemaValidator:o,envFile:s,srcDir:a?.typegen?.srcDir,outDir:a?.typegen?.outDir,assetMap:a?.assetMap,changedFiles:e,runPlugins:!1}).catch(()=>{}),la({cwd:i,config:a,silent:!0,changedFiles:e}).then(e=>Ki(c,e,!0)).catch(()=>{}),t&&ua(a,{cwd:i,silent:!0}).catch(()=>{})},100)}};d.watcher.on(`add`,e=>S(`add`,e)),d.watcher.on(`unlink`,e=>S(`unlink`,e)),d.watcher.on(`change`,e=>S(`change`,e)),d.watcher.on(`unlinkDir`,e=>S(`unlinkDir`,e)),f.length>0&&d.watcher.add(f),await d.listen(),d.printUrls(),console.log(`
|
|
3702
2478
|
KickJS dev server running (Vite + @forinda/kickjs-vite)
|
|
3703
|
-
`);let
|
|
2479
|
+
`);let C=!1,ee=async()=>{if(!C){C=!0,m&&clearTimeout(m);try{await globalThis.__kickjs_app_shutdown?.()}catch(e){console.error(` app shutdown hook failed: ${e?.message??e}`)}await d.close(),process.exit(0)}};process.on(`SIGINT`,ee),process.on(`SIGTERM`,ee),process.on(`SIGBREAK`,ee)}function va(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 _a(e.entry,e.port,{polling:e.polling})}catch(e){e.code===`ERR_MODULE_NOT_FOUND`&&e.message?.includes(`vite`)?console.error(`
|
|
3704
2480
|
Error: vite is not installed.
|
|
3705
2481
|
Run: pnpm add -D vite unplugin-swc
|
|
3706
2482
|
`):console.error(`
|
|
3707
2483
|
Dev server failed:`,e.message??e),process.exit(1)}}),e.command(`build`).description(`Build for production via Vite`).action(async()=>{console.log(`
|
|
3708
2484
|
Building for production...
|
|
3709
|
-
`);let{createRequire:e}=await import(`node:module`),{build:t}=await import(x(e(v(`package.json`)).resolve(`vite`)).href);await t({configFile:v(`vite.config.ts`)});let a=await
|
|
2485
|
+
`);let{createRequire:e}=await import(`node:module`),{build:t}=await import(x(e(v(`package.json`)).resolve(`vite`)).href);await t({configFile:v(`vite.config.ts`)});let a=await M(process.cwd()),o=a?.copyDirs??[];if(o.length>0){console.log(`
|
|
3710
2486
|
Copying directories to dist...`);for(let e of o){let t=typeof e==`string`?e:e.src,a=typeof e==`string`?h(`dist`,e):e.dest??h(`dist`,t),o=v(t),s=v(a);if(!r(o)){console.log(` ⚠ Skipped ${t} (not found)`);continue}i(s,{recursive:!0}),n(o,s,{recursive:!0}),console.log(` ✓ ${t} → ${a}`)}}if(a?.assetMap&&Object.keys(a.assetMap).length>0){console.log(`
|
|
3711
|
-
Building asset map...`);try{await
|
|
2487
|
+
Building asset map...`);try{await ua(a,{cwd:process.cwd()})}catch(e){console.error(` ✗ asset build failed: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}console.log(`
|
|
3712
2488
|
Build complete.
|
|
3713
|
-
`)}),e.command(`build:assets`).description(`Rebuild the .kickjs-assets.json manifest under the configured outDir (no JS rebuild)`).action(async()=>{let e=await
|
|
3714
|
-
Building asset map...`);try{await
|
|
2489
|
+
`)}),e.command(`build:assets`).description(`Rebuild the .kickjs-assets.json manifest under the configured outDir (no JS rebuild)`).action(async()=>{let e=await M(process.cwd());if(!e?.assetMap||Object.keys(e.assetMap).length===0){console.log(` No assetMap entries — nothing to build.`);return}console.log(`
|
|
2490
|
+
Building asset map...`);try{await ua(e,{cwd:process.cwd()}),console.log(`
|
|
3715
2491
|
Asset build complete.
|
|
3716
|
-
`)}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)),
|
|
3717
|
-
Dev server (debug) failed:`,e.message??e),process.exit(1)}})}function
|
|
2492
|
+
`)}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)),ye(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 _a(e.entry,e.port)}catch(e){console.error(`
|
|
2493
|
+
Dev server (debug) failed:`,e.message??e),process.exit(1)}})}function ya(e){e.command(`info`).description(`Print system and framework info`).action(()=>{console.log(`
|
|
3718
2494
|
KickJS CLI
|
|
3719
2495
|
|
|
3720
2496
|
System:
|
|
3721
|
-
OS: ${
|
|
2497
|
+
OS: ${he()} ${ge()} (${me()})
|
|
3722
2498
|
Node: ${process.version}
|
|
3723
2499
|
|
|
3724
2500
|
Packages:
|
|
3725
2501
|
@forinda/kickjs workspace
|
|
3726
2502
|
@forinda/kickjs-vite workspace
|
|
3727
2503
|
@forinda/kickjs-cli workspace
|
|
3728
|
-
`)})}const{bold:
|
|
2504
|
+
`)})}const{bold:Z,dim:Q,green:ba,red:xa,yellow:Sa,blue:Ca}=k;function wa(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 Ta(e){let t=await fetch(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)throw Error(`${t.status} ${t.statusText}`);return t.json()}async function Ea(e,t){try{return await Ta(`${e}${t}`)}catch{return null}}async function Da(e){let[t,n,r,i,a]=await Promise.all([Ea(e,`/health`),Ea(e,`/metrics`),Ea(e,`/routes`),Ea(e,`/container`),Ea(e,`/ws`)]);return{health:t,metrics:n,routes:r,container:i,ws:a}}function Oa(e,t){let{health:n,metrics:r,routes:i,container:a,ws:o}=t,s=Q(`─`.repeat(60));if(console.log(),console.log(Z(` KickJS Inspector`)+Q(` → ${e}`)),console.log(s),n){let e=n.status===`healthy`?ba(`● healthy`):xa(`● `+n.status);console.log(` ${Z(`Health:`)} ${e}`)}else console.log(` ${Z(`Health:`)} ${xa(`● unreachable`)}`);if(r){let e=((r.errorRate??0)*100).toFixed(1),t=r.errorRate>.1?xa:r.errorRate>0?Sa:ba;console.log(` ${Z(`Uptime:`)} ${wa(r.uptimeSeconds)}`),console.log(` ${Z(`Requests:`)} ${r.requests}`),console.log(` ${Z(`Errors:`)} ${r.serverErrors} server, ${r.clientErrors??0} client ${Q(`(`)}${t(e+`%`)}${Q(`)`)}`)}if(a&&console.log(` ${Z(`DI:`)} ${a.count} bindings`),o&&o.enabled&&console.log(` ${Z(`WS:`)} ${o.connections??0} connections, ${o.namespaces??0} namespaces`),i?.routes?.length){console.log(),console.log(Z(` Routes`)),console.log(s),console.log(` ${Q(`METHOD`)} ${Q(`PATH`.padEnd(36))} ${Q(`CONTROLLER`)}`);for(let e of i.routes){let t=e.path.length>36?e.path.slice(0,33)+`...`:e.path.padEnd(36);console.log(` ${Ct(e.method)} ${t} ${Ca(e.controller)}.${Q(e.handler)}`)}}console.log(s),console.log()}function ka(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 Da(r);t.json?console.log(JSON.stringify(e,null,2)):Oa(n,e)}catch(e){t.json?console.log(JSON.stringify({error:String(e)})):(console.error(xa(` ✖ Could not connect to ${n}`)),console.error(Q(` ${e instanceof Error?e.message:String(e)}`))),t.watch||(process.exitCode=1)}};if(t.watch){let e=async()=>{process.stdout.write(`\x1B[2J\x1B[H`),await i()};await e(),setInterval(e,5e3)}else await i()})}function Aa(e,t){let n=e.toLowerCase();return t.every(e=>n.includes(e.toLowerCase()))}function $(e,t){let n=e.toLowerCase();return t.some(e=>n.includes(e.toLowerCase()))}const ja=[{match(e,t){let n=Aa(e,[`config`,`get`])&&$(e,[`undefined`,`null`]),r=e.includes(`@Value`)&&$(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
|
|
3729
2505
|
registers the env schema with kickjs at module-load time. Without it,
|
|
3730
2506
|
ConfigService falls back to the base schema (PORT/NODE_ENV/LOG_LEVEL only)
|
|
3731
2507
|
and every user-defined key reads as undefined. @Value() may *appear* to
|
|
@@ -3737,7 +2513,7 @@ import { modules } from './modules'
|
|
|
3737
2513
|
import './config' // ← add this — registers env schema
|
|
3738
2514
|
import { bootstrap } from '@forinda/kickjs'
|
|
3739
2515
|
import { modules } from './modules'
|
|
3740
|
-
`,docs:`https://forinda.github.io/kick-js/guide/configuration.html#wiring-the-schema-at-startup`}}}},{match(e,t){let n
|
|
2516
|
+
`,docs:`https://forinda.github.io/kick-js/guide/configuration.html#wiring-the-schema-at-startup`}}}},{match(e,t){let n=$(e,[`vitest`,`test`,`spec`,`__tests__`,`.test.`]);return $(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.
|
|
3741
2517
|
When vitest re-imports your modules across tests, the same class can be
|
|
3742
2518
|
registered twice and the container throws. The fix is to wipe the
|
|
3743
2519
|
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'
|
|
@@ -3747,7 +2523,7 @@ describe('UserController', () => {
|
|
|
3747
2523
|
beforeEach(() => Container.reset())
|
|
3748
2524
|
|
|
3749
2525
|
it('does the thing', async () => { /* ... */ })
|
|
3750
|
-
})`,docs:`https://forinda.github.io/kick-js/guide/testing.html`}}:null}},{match(e,t){return e.includes(`@Module`)||
|
|
2526
|
+
})`,docs:`https://forinda.github.io/kick-js/guide/testing.html`}}:null}},{match(e,t){return e.includes(`@Module`)||Aa(e,[`Module`,`is not a function`])||Aa(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
|
|
3751
2527
|
pattern instead: a class implements AppModule and exposes routes() that
|
|
3752
2528
|
returns the controller wiring. This was a deliberate choice — modules
|
|
3753
2529
|
become explicit values rather than metadata, which makes them easier to
|
|
@@ -3774,7 +2550,7 @@ KickRoutes["POST /users"]. The new form is per-controller, per-method,
|
|
|
3774
2550
|
and matches the actual class names so refactors propagate via
|
|
3775
2551
|
rename-symbol instead of grep.`,fix:`Update the Ctx<...> type parameter to use the namespace form:`,codeBefore:`@Post('/', { body: createUserSchema })
|
|
3776
2552
|
create(ctx: Ctx<KickRoutes['POST /users']>) { /* ... */ }`,codeAfter:`@Post('/', { body: createUserSchema, name: 'CreateUser' })
|
|
3777
|
-
create(ctx: Ctx<KickRoutes.UserController['create']>) { /* ... */ }`,docs:`https://forinda.github.io/kick-js/guide/typegen.html`}}:null}},{match(e,t){let n
|
|
2553
|
+
create(ctx: Ctx<KickRoutes.UserController['create']>) { /* ... */ }`,docs:`https://forinda.github.io/kick-js/guide/typegen.html`}}:null}},{match(e,t){let n=$(e,[`cluster`,`workers`,`two ports`,`duplicate server`]),r=$(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
|
|
3778
2554
|
cluster: { workers: N }, the framework forks N workers, each of which
|
|
3779
2555
|
spins up its own Vite instance on a separate port. The fix landed in
|
|
3780
2556
|
v2.2.5: McpAdapter (and bootstrap()) now detects Vite dev mode and
|
|
@@ -3782,7 +2558,7 @@ silently skips cluster, with a warning. If you see this on an older
|
|
|
3782
2558
|
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({
|
|
3783
2559
|
modules,
|
|
3784
2560
|
cluster: process.env.NODE_ENV === 'production' ? { workers: 4 } : false,
|
|
3785
|
-
})`,docs:`https://forinda.github.io/kick-js/guide/cluster.html`}}}},{match(e,t){return
|
|
2561
|
+
})`,docs:`https://forinda.github.io/kick-js/guide/cluster.html`}}}},{match(e,t){return $(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
|
|
3786
2562
|
reflect-metadata polyfill. The polyfill must be imported once,
|
|
3787
2563
|
before any decorator runs. Most projects do this at the top of
|
|
3788
2564
|
src/index.ts; missing the import causes obscure "design:paramtypes"
|
|
@@ -3791,7 +2567,7 @@ import './config'
|
|
|
3791
2567
|
import { bootstrap } from '@forinda/kickjs'
|
|
3792
2568
|
import { modules } from './modules'
|
|
3793
2569
|
|
|
3794
|
-
export const app = await bootstrap({ modules })`,docs:`https://forinda.github.io/kick-js/guide/dependency-injection.html`}}:null}},{match(e,t){return
|
|
2570
|
+
export const app = await bootstrap({ modules })`,docs:`https://forinda.github.io/kick-js/guide/dependency-injection.html`}}:null}},{match(e,t){return $(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
|
|
3795
2571
|
generated a module via \`kick g module foo\` but the routes don't appear,
|
|
3796
2572
|
the most likely cause is that the module is missing from the exported
|
|
3797
2573
|
array. The CLI usually wires this automatically, but a hand-edit can
|
|
@@ -3799,24 +2575,24 @@ drop the entry.`,fix:`Open src/modules/index.ts and verify the module is in the
|
|
|
3799
2575
|
import { UserModule } from './users/user.module'
|
|
3800
2576
|
import { TaskModule } from './tasks/task.module' // ← was this missing?
|
|
3801
2577
|
|
|
3802
|
-
export const modules: AppModuleEntry[] = [UserModule(), TaskModule()]`,docs:`https://forinda.github.io/kick-js/guide/project-structure.html`}}:null}}];function
|
|
2578
|
+
export const modules: AppModuleEntry[] = [UserModule(), TaskModule()]`,docs:`https://forinda.github.io/kick-js/guide/project-structure.html`}}:null}}];function Ma(e,t){let n=null;for(let r of ja){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 Na(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.
|
|
3803
2579
|
export OPENAI_API_KEY="sk-..."
|
|
3804
2580
|
|
|
3805
2581
|
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:
|
|
3806
2582
|
kick add ai
|
|
3807
2583
|
|
|
3808
2584
|
Or manually:
|
|
3809
|
-
pnpm add @forinda/kickjs-ai`}}let{OpenAIProvider:i}=r,a=new i({apiKey:n,defaultChatModel:e.model??`gpt-4o-mini`}),o=
|
|
3810
|
-
`)}function
|
|
2585
|
+
pnpm add @forinda/kickjs-ai`}}let{OpenAIProvider:i}=r,a=new i({apiKey:n,defaultChatModel:e.model??`gpt-4o-mini`}),o=Pa(e.cwd),s=`Error or stack trace:\n\n${e.input.trim()}`;try{let e=Fa((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 Pa(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(`
|
|
2586
|
+
`)}function Fa(e){let t=[e,Ia(e),La(e)].filter(e=>e!==null);for(let e of t)try{let t=JSON.parse(e);if(Ra(t))return t}catch{continue}return null}function Ia(e){let t=e.match(/```(?:json)?\s*\n([\s\S]*?)```/);return t?t[1]?.trim()??null:null}function La(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 Ra(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 za(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 Ha(e,t.message);(!n||n.trim().length===0)&&(process.stderr.write(`Error: no input provided.
|
|
3811
2587
|
|
|
3812
2588
|
Pass a message as a positional arg, --message flag, or pipe via stdin:
|
|
3813
2589
|
kick explain "config.get returned undefined"
|
|
3814
2590
|
pnpm test 2>&1 | kick explain
|
|
3815
|
-
`),process.exit(1));let r=
|
|
3816
|
-
`);return}if(i){
|
|
3817
|
-
`),process.exit(2)),
|
|
3818
|
-
`),process.exit(a.kind===`ok`?0:2)),
|
|
3819
|
-
`)}function
|
|
2591
|
+
`),process.exit(1));let r=Wa(),i=Ma(n,r);if(t.json&&i){process.stdout.write(JSON.stringify({matched:!0,...i},null,2)+`
|
|
2592
|
+
`);return}if(i){Ga(n,i.diagnosis,i.confidence);return}t.ai||(t.json&&(process.stdout.write(JSON.stringify({matched:!1},null,2)+`
|
|
2593
|
+
`),process.exit(2)),Ka(n,!1),process.exit(2));let a=await Na({input:n,model:t.model,cwd:r.cwd});t.json&&(process.stdout.write(JSON.stringify(Ba(a),null,2)+`
|
|
2594
|
+
`),process.exit(a.kind===`ok`?0:2)),Va(n,a),process.exit(a.kind===`ok`?0:2)})}function Ba(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 Va(e,t){if(t.kind===`ok`){Ga(e,t.diagnosis,-1,!0);return}if(t.kind===`unavailable`){process.stdout.write(`\n Explaining: ${Ja(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback unavailable: ${t.reason}\n\n`),process.stdout.write(`${qa(t.suggestion,` `)}\n\n`);return}process.stdout.write(`\n Explaining: ${Ja(e.trim(),200)}\n\n`),process.stdout.write(` AI fallback error: ${t.message}\n\n`)}async function Ha(e,t){return e&&e.trim().length>0?e:t&&t.trim().length>0?t:process.stdin.isTTY?``:Ua()}function Ua(){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 Wa(){let e=process.cwd();return{cwd:e,hasFile:t=>r(v(e,t))}}function Ga(e,t,n,r=!1){let i=Ja(e.trim(),200),a=r?`AI-generated — verify before applying`:Ya(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${qa(t.explanation,` `)}\n`),process.stdout.write(`\n Fix:\n${qa(t.fix,` `)}\n`),t.codeBefore&&process.stdout.write(`\n Before:\n${qa(t.codeBefore,` `)}\n`),t.codeAfter&&process.stdout.write(`\n After:\n${qa(t.codeAfter,` `)}\n`),t.docs&&process.stdout.write(`\n Docs: ${t.docs}\n`),process.stdout.write(`
|
|
2595
|
+
`)}function Ka(e,t){let n=Ja(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.
|
|
3820
2596
|
When @forinda/kickjs-ai ships its provider implementations,
|
|
3821
2597
|
this command will call the configured LLM with the error +
|
|
3822
2598
|
project context and return a structured fix.
|
|
@@ -3833,12 +2609,12 @@ Pass a message as a positional arg, --message flag, or pipe via stdin:
|
|
|
3833
2609
|
3. File an issue with the error text:
|
|
3834
2610
|
https://github.com/forinda/kick-js/issues/new
|
|
3835
2611
|
|
|
3836
|
-
`)}function
|
|
2612
|
+
`)}function qa(e,t){return e.split(`
|
|
3837
2613
|
`).map(e=>`${t}${e}`).join(`
|
|
3838
|
-
`)}function
|
|
3839
|
-
`,`utf8`),process.stdout.write(`\n ✓ Wrote MCP server entry "${i}" to ${o}\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
|
|
2614
|
+
`)}function Ja(e,t){return e.length<=t?e:e.slice(0,t-1)+`…`}function Ya(e){return e>=90?`high confidence`:e>=70?`good match`:e>=50?`medium confidence`:`low confidence — verify manually`}function Xa(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(Za),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(Qa)}function Za(e){let t=v(e.entry);r(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],i=te(process.execPath,n,{stdio:`inherit`,env:{...process.env,KICK_MCP_STDIO:`1`,NODE_ENV:process.env.NODE_ENV??`production`}});i.on(`error`,e=>{process.stderr.write(`Failed to start MCP server: ${e.message}\n`),process.exit(1)}),i.on(`exit`,(e,t)=>{if(t){process.kill(process.pid,t);return}process.exit(e??0)});let a=e=>{i.killed||i.kill(e)};process.on(`SIGINT`,()=>a(`SIGINT`)),process.on(`SIGTERM`,()=>a(`SIGTERM`))}function Qa(e){let t=process.cwd(),n=$a(t)??d(t),i=e.name??n,o=e.global?v(process.env.HOME??`.`,`.mcp.json`):v(t,e.out),s={command:`kick`,args:[`mcp`],cwd:t},c={mcpServers:{}};if(r(o))try{let e=a(o,`utf8`),t=JSON.parse(e);t&&typeof t==`object`&&t.mcpServers&&(c={mcpServers:{...t.mcpServers}})}catch(e){let t=e instanceof Error?e.message:String(e);process.stderr.write(`Error: existing ${o} is not valid JSON (${t}).\nFix the file or pass --force to overwrite the entry.\n`),process.exit(1)}c.mcpServers[i]&&!e.force&&(process.stderr.write(`Error: an entry for "${i}" already exists in ${o}.\nPass --force to overwrite it, or use --name to pick a different key.\n`),process.exit(1)),c.mcpServers[i]=s,l(o,JSON.stringify(c,null,2)+`
|
|
2615
|
+
`,`utf8`),process.stdout.write(`\n ✓ Wrote MCP server entry "${i}" to ${o}\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 $a(e){let t=v(e,`package.json`);if(!r(t))return null;try{let e=a(t,`utf8`),n=JSON.parse(e);return typeof n.name==`string`?n.name:null}catch{return null}}function eo(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=v(t,e.entry);r(n)||(console.error(`\n Error: ${e.entry} not found.\n`),process.exit(1));let i=no(t,`tsx`);i||(console.error(`
|
|
3840
2616
|
Error: tsx not found. Install it: pnpm add -D tsx
|
|
3841
|
-
`),process.exit(1));let a=
|
|
2617
|
+
`),process.exit(1));let a=to(n,e.entry),o=h(t,`.kick-tinker.mjs`),{writeFileSync:s,unlinkSync:c}=await import(`node:fs`);s(o,a,`utf-8`);try{let e=ee(o,[],{cwd:t,execPath:i,stdio:`inherit`});await new Promise(t=>{e.on(`exit`,()=>t())})}finally{try{c(o)}catch{}}})}function to(e,t){return`
|
|
3842
2618
|
import 'reflect-metadata'
|
|
3843
2619
|
|
|
3844
2620
|
// Prevent bootstrap() from starting the HTTP server
|
|
@@ -3892,40 +2668,39 @@ server.on('exit', () => {
|
|
|
3892
2668
|
console.log('\\n Goodbye!\\n')
|
|
3893
2669
|
process.exit(0)
|
|
3894
2670
|
})
|
|
3895
|
-
`}function
|
|
2671
|
+
`}function no(e,t){let n=e;for(;;){let e=h(n,`node_modules`,`.bin`,t);if(r(e))return e;let i=v(n,`..`);if(i===n)break;n=i}return null}function ro(e,t){let n=RegExp(`^\\s*${H(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]!==`
|
|
3896
2672
|
`;)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]===`
|
|
3897
|
-
`);)t--;a=a.slice(0,t)+a.slice(s+1),r=!0,i=t;continue}i=s+1}return{content:a,changed:r}}function
|
|
2673
|
+
`);)t--;a=a.slice(0,t)+a.slice(s+1),r=!0,i=t;continue}i=s+1}return{content:a,changed:r}}function io(e,t){let n=kn(e);if(!n)return e;let r=n.rhsStart,i=n.rhsEnd+1,a=e.slice(r,i);return a=ro(a,t).content,a=a.replace(RegExp(`\\s*,?\\s*${H(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 ao(e){let{name:t,modulesDir:n,force:r}=e,i=e.pluralize!==!1,a=B(t),o=R(t),s=i?V(a):a,c=h(n,s);if(!await Ue(c)){console.log(`\n Module not found: ${c}\n`);return}if(!r&&!await I({message:k.red(`Delete module '${s}' at ${c}? This cannot be undone.`),initialValue:!1})){console.log(`
|
|
3898
2674
|
Cancelled.
|
|
3899
|
-
`);return}await
|
|
2675
|
+
`);return}await oe(c,{recursive:!0,force:!0}),console.log(` Deleted: ${c}`);let l=h(n,`index.ts`);if(await Ue(l)){let e=await T(l,`utf-8`),t=e,n=RegExp(`^import\\s*\\{\\s*${H(o)}Module\\s*\\}\\s*from\\s*['"][^'"]*${H(s)}(?:/[^'"]*)?['"].*\\n?`,`gm`);e=e.replace(n,``),e=io(e,o),e=e.replace(/\n{3,}/g,`
|
|
3900
2676
|
|
|
3901
|
-
`),e!==t&&(await
|
|
2677
|
+
`),e!==t&&(await E(l,e,`utf-8`),console.log(` Unregistered: ${o}Module from ${l}`))}console.log(`\n Module '${s}' removed.\n`)}function oo(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 n=j(await M(process.cwd())),r=t.modulesDir??n.dir??`src/modules`,i=t.pluralize===!1?!1:n.pluralize??!0;for(let n of e)await ao({name:n,modulesDir:v(r),force:t.force,pluralize:i})})}function so(e){if(e!==void 0){if(e===`false`||e===`off`||e===`none`)return!1;if(e===`zod`)return`zod`;if(e===`kickjs-schema`||e===`schema`)return`kickjs-schema`;console.warn(` kick typegen: unknown --schema-validator '${e}' (supported: 'zod', 'kickjs-schema', 'false'). Falling back to project config.`)}}function co(e){if(e!==void 0)return e===`false`||e===`off`||e===`none`?!1:e}function lo(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=Gt(process.cwd()),n=await M(t);if(e.list){let{mergeCliPlugins:e}=await Promise.resolve().then(()=>Fe),{builtinCliPlugins:t}=await Promise.resolve().then(()=>gs),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(`
|
|
3902
2678
|
Registered typegen plugins:
|
|
3903
|
-
`);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 r=
|
|
2679
|
+
`);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 r=so(e.schemaValidator)??n?.typegen?.schemaValidator??`zod`,i=co(e.envFile)??n?.typegen?.envFile,a={cwd:t,srcDir:e.src??n?.typegen?.srcDir,outDir:e.out??n?.typegen?.outDir,silent:e.silent,allowDuplicates:e.allowDuplicates,schemaValidator:r,envFile:i,assetMap:n?.assetMap,runPlugins:!1};try{if(e.watch){let t=await qi(a);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 J(a);let r=await la({cwd:t,config:n??null,silent:e.silent,check:e.check});e.check&&r.some(e=>e.status===`written`)&&process.exit(1),e.check||await Ki(v(t,e.out??n?.typegen?.outDir??`.kickjs/types`),r,e.silent??!1)}}catch(e){e instanceof Si?console.error(`
|
|
3904
2680
|
`+e.message+`
|
|
3905
|
-
`):e instanceof Error?console.error(`\n kick typegen failed: ${e.message}`):console.error(`\n kick typegen failed: ${JSON.stringify(e)}`),process.exit(1)}})}function
|
|
2681
|
+
`):e instanceof Error?console.error(`\n kick typegen failed: ${e.message}`):console.error(`\n kick typegen failed: ${JSON.stringify(e)}`),process.exit(1)}})}function uo(e){let t=[];if(!r(e))return t;let n=o(e,{withFileTypes:!0});for(let r of n){let n=h(e,r.name);if(r.isDirectory()){if([`node_modules`,`dist`,`.kickjs`,`.git`].includes(r.name))continue;t.push(...uo(n))}else r.isFile()&&/\.tsx?$/.test(r.name)&&!r.name.endsWith(`.d.ts`)&&t.push(n)}return t}function fo(e){try{return a(e,`utf-8`)}catch{return``}}const po=new Set([`secret`,`changeme`,`password`,`test`,`default`,``]);function mo(e,t){let n=fo(h(e,`.env`));if(n){let e=n.match(/^JWT_SECRET\s*=\s*['"]?([^'"\n]*)['"]?/m);if(e){let t=e[1].trim();if(po.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 ho(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 go(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 _o(){return process.env.NODE_ENV===`production`?null:{severity:`WARNING`,message:`NODE_ENV is '${process.env.NODE_ENV??`undefined`}', not 'production'`}}function vo(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 yo(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 bo(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 xo(e){let t=uo(h(e,`src`)).map(e=>fo(e)),n=[],r=mo(e,t);r&&n.push(r);let i=ho(t);i&&n.push(i);let a=go(t);a&&n.push(a);let o=_o();o&&n.push(o);let s=vo(t);return s&&n.push(s),n.push(yo(t)),n.push(bo(t)),n}function So(e){e.command(`check`).description(`Audit project for common issues`).option(`--deploy`,`Run production readiness checks`).action(e=>{if(!e.deploy){console.log(`
|
|
3906
2682
|
Usage: kick check --deploy
|
|
3907
2683
|
|
|
3908
2684
|
Available checks:
|
|
3909
2685
|
--deploy Audit for production readiness (security, config, best practices)
|
|
3910
|
-
`);return}let t=process.cwd();
|
|
3911
|
-
Install a supported version via nvm / fnm / volta.`}:{name:`Node version`,status:`pass`,message:e}}function
|
|
2686
|
+
`);return}let t=process.cwd();Tt(`KickJS Deploy Check`);let n=At();n.start(`Scanning project...`);let r=xo(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)L.message(`${wt(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?k.red(`${a} critical`):`${a} critical`,o>0?k.yellow(`${o} ${c}`):`${o} ${c}`,`${s} info`].join(`, `);a>0?(F(k.red(`${l} — fix critical issues before deploying`)),process.exit(1)):F(k.green(`${l} — looking good!`))})}function Co(e){try{return JSON.parse(a(e,`utf-8`))}catch{return null}}function wo(e){try{return a(e,`utf-8`)}catch{return null}}function To(e){let t=wo(h(e,`tsconfig.json`));if(!t)return null;let n=t.replace(/\/\*[\s\S]*?\*\//g,``).replace(/\/\/.*$/gm,``),r;try{r=JSON.parse(n)}catch{return null}if(typeof r?.extends==`string`){let t=Eo(e,r.extends);if(t){let e=Co(t)??{};r.compilerOptions={...e.compilerOptions,...r.compilerOptions}}}return r}function Eo(e,t){if(t.startsWith(`.`)){let n=v(e,t);return r(n)?n:null}let n=h(e,`node_modules`,t);return r(n)?n:null}function Do(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function Oo(){let e=process.version,t=Number.parseInt(e.replace(/^v/,``).split(`.`)[0],10);return Number.isNaN(t)||t<20?{name:`Node version`,status:`fail`,message:e,fix:`KickJS requires Node 20 or newer.
|
|
2687
|
+
Install a supported version via nvm / fnm / volta.`}:{name:`Node version`,status:`pass`,message:e}}function ko(e){if(!e.pkg)return{name:`@forinda/kickjs installed`,status:`warn`,message:`no package.json`};let t={...e.pkg.dependencies,...e.pkg.peerDependencies};return t[`@forinda/kickjs`]?{name:`@forinda/kickjs installed`,status:`pass`,message:t[`@forinda/kickjs`]}:{name:`@forinda/kickjs installed`,status:`fail`,fix:"This directory does not look like a KickJS project — `@forinda/kickjs` is not in your package.json. Run `kick doctor` from the project root, or scaffold a fresh project with `kick new <name>`."}}function Ao(e){if(!e.pkg)return null;let t={...e.pkg.dependencies,...e.pkg.peerDependencies};return t[`@forinda/kickjs`]&&!t.express?{name:`express installed`,status:`fail`,fix:"`@forinda/kickjs` declares `express` as a required peer dependency, but your package.json does not include it. Install: pnpm add express"}:t.express?{name:`express installed`,status:`pass`,message:t.express}:null}function jo(e){if(!e.pkg)return{name:`reflect-metadata installed`,status:`warn`,message:`no package.json`};let t={...e.pkg.dependencies,...e.pkg.peerDependencies,...e.pkg.devDependencies};return t[`reflect-metadata`]?{name:`reflect-metadata installed`,status:`pass`,message:t[`reflect-metadata`]}:{name:`reflect-metadata installed`,status:`fail`,fix:`KickJS decorators require the reflect-metadata polyfill.
|
|
3912
2688
|
Install it: pnpm add reflect-metadata
|
|
3913
2689
|
Then import it at the top of src/index.ts:
|
|
3914
2690
|
|
|
3915
2691
|
import 'reflect-metadata'
|
|
3916
|
-
// ... rest of bootstrap`}}function
|
|
3917
|
-
`).map(e=>` ${
|
|
3918
|
-
`)}function
|
|
2692
|
+
// ... rest of bootstrap`}}function Mo(e){if(!e.tsconfig)return[{name:`tsconfig.json present`,status:`fail`,fix:"Create a tsconfig.json with `experimentalDecorators: true` and `emitDecoratorMetadata: true`. `kick new` scaffolds one automatically."}];let t=e.tsconfig.compilerOptions??{},n=[];return n.push(t.experimentalDecorators===!0?{name:`tsconfig: experimentalDecorators`,status:`pass`}:{name:`tsconfig: experimentalDecorators`,status:`fail`,fix:'Add `"experimentalDecorators": true` to compilerOptions in tsconfig.json. Without it, @Service / @Controller / @Get etc. don\'t register any metadata at compile time.'}),n.push(t.emitDecoratorMetadata===!0?{name:`tsconfig: emitDecoratorMetadata`,status:`pass`}:{name:`tsconfig: emitDecoratorMetadata`,status:`fail`,fix:'Add `"emitDecoratorMetadata": true` to compilerOptions in tsconfig.json. The DI container uses this metadata for constructor-parameter injection.'}),n}function No(e){let t=[`src/env.ts`,`src/env/index.ts`,`src/config/env.ts`,`src/config/index.ts`].map(t=>h(e.cwd,t)).filter(e=>r(e)).filter(e=>/\bloadEnv\s*\(/.test(wo(e)??``));if(t.length===0)return null;let n=[`src/index.ts`,`src/main.ts`].map(t=>h(e.cwd,t)).find(e=>r(e));if(!n)return{name:`env wiring`,status:`warn`,message:`env-init file exists but no src/index.ts or src/main.ts found`};let i=wo(n)??``,a=f(n),o=[];for(let e of t){let t=_(a,e).replace(/\\/g,`/`).replace(/\.ts$/,``),n=t.startsWith(`.`)?t:`./`+t,r=n.replace(/\/index$/,``);o.push(n,r);let i=e.replace(/\\/g,`/`).match(/\/src\/(.+?)(?:\.ts)?$/);if(i){let e=`@/`+i[1],t=e.replace(/\/index$/,``);o.push(e,t)}}let s=-1;for(let e of new Set(o)){let t=RegExp(`^import\\s+(?:.*?from\\s+)?['"]${Do(e)}['"]`,`m`),n=i.match(t);n&&n.index!==void 0&&(s===-1||n.index<s)&&(s=n.index)}let c=i.search(/\bbootstrap\s*\(/),l=t.map(t=>_(e.cwd,t).replace(/\\/g,`/`)).join(`, `);return s===-1?{name:`env wiring`,status:`fail`,message:l,fix:`An env-init file (${l}) calls \`loadEnv(...)\` but \`${_(e.cwd,n).replace(/\\/g,`/`)}\` doesn't import it.\nWithout this, ConfigService.get('X') returns undefined while @Value('X') works via process.env fallback — a half-broken config you won't notice until something is missing.\n\nFix: add a side-effect import at the top of ${_(e.cwd,n).replace(/\\/g,`/`)} (above bootstrap()), pointing at one of the detected files. For example:\n\n import './env'\n // or\n import './config'\n // or, with the @/ alias:\n import '@/config/env'`}:c!==-1&&s>c?{name:`env wiring`,status:`warn`,message:`env-init imported AFTER bootstrap() — should be before`,fix:`Move the env import above the bootstrap() call so the schema runs before any service reads from ConfigService.`}:{name:`env wiring`,status:`pass`}}function Po(e,t=Fo){let n=0,r=0,i=[e];for(;i.length>0&&r<t;){let e=i.pop(),a;try{a=o(e,{withFileTypes:!0})}catch{continue}for(let o of a){if(r>=t)break;r++;let a=h(e,o.name);if(o.isDirectory()){i.push(a);continue}try{let e=c(a).mtimeMs;e>n&&(n=e)}catch{}}}return n}const Fo=2e3;function Io(e){let t=h(e.cwd,`.kickjs`,`types`);if(!r(t))return null;let n=Po(t);if(n===0)return null;let i=Date.now()-n,a=Math.floor(i/6e4);return a>60?{name:`typegen freshness`,status:`warn`,message:`last updated ${a} minutes ago`,fix:"Re-run `kick typegen` (or `kick dev`, which runs it on every reload) so generated types match the current code."}:{name:`typegen freshness`,status:`pass`,message:a===0?`just now`:`${a}m ago`}}const Lo=[()=>Oo(),ko,Ao,jo,Mo,No,Io];async function Ro(e,t={}){let n={cwd:e,pkg:Co(h(e,`package.json`)),tsconfig:To(e)},r=[...Lo,...t.extraChecks??[]],i=[];for(let e of r){let t;try{t=await e(n)}catch(t){i.push({name:e.name||`doctor check`,status:`fail`,message:t instanceof Error?t.message:String(t)});continue}t!=null&&(Array.isArray(t)?i.push(...t):i.push(t))}return i}function zo(e){switch(e){case`pass`:return k.green(`✔`);case`warn`:return k.yellow(`⚠`);case`fail`:return k.red(`✖`)}}function Bo(e){let t=zo(e.status),n=e.message?` ${k.dim(`(${e.message})`)}`:``;return`${t} ${e.name}${n}`}function Vo(e){return e.split(`
|
|
2693
|
+
`).map(e=>` ${k.dim(`→`)} ${e}`).join(`
|
|
2694
|
+
`)}function Ho(e){return e?.doctor?.checks??[]}function Uo(e){e.command(`doctor`).description(`Pre-flight checks for your KickJS project (dev environment health)`).action(async()=>{let e=process.cwd(),t=Ho(await M(e));Tt(`KickJS Doctor`);let n=await Ro(e,{extraChecks:t});for(let e of n)L.message(Bo(e)),e.fix&&e.status!==`pass`&&L.message(Vo(e.fix));let r=n.filter(e=>e.status===`pass`).length,i=n.filter(e=>e.status===`warn`).length,a=n.filter(e=>e.status===`fail`).length,o=[k.green(`${r} passed`),i>0?k.yellow(`${i} warning${i===1?``:`s`}`):`${i} warnings`,a>0?k.red(`${a} error${a===1?``:`s`}`):`${a} errors`].join(`, `);a>0?(F(`${o} — fix the errors above before running the app`),process.exit(1)):F(i>0?`${o} — review the warnings`:k.green(`${o} — your environment looks good`))})}function Wo(e){return e.optsWithGlobals().dryRun??!1}function Go(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.
|
|
3919
2695
|
Direction defaults to \`modules.style\` from kick.config (or "define").
|
|
3920
2696
|
--target define|class Override the migration direction.
|
|
3921
2697
|
--apply Apply the changes (default: dry-run preview).
|
|
3922
|
-
--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 n=
|
|
3923
|
-
`+
|
|
2698
|
+
--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 n=Wo(t)||!e.apply;N(n),e.experimental||(console.error(`
|
|
2699
|
+
`+k.red(`Error:`)+` kick codemod modules is experimental — pass --experimental to acknowledge.
|
|
3924
2700
|
The regex-based rewrite handles the shapes our templates produce.
|
|
3925
2701
|
Hand-rolled modules with non-standard structures may be skipped.
|
|
3926
2702
|
Always commit before running with --apply.
|
|
3927
|
-
`),process.exit(1));let r=
|
|
3928
|
-
`)}});function Cs(e){for(let t of xs){let n=u.resolve(e,t);if(t.endsWith(`.ts`)){if(r(n))return n}else{let e=u.join(n,`index.ts`);if(r(e))return e}}return null}function ws(e){return e.replace(/\\/g,`/`)}const Ts=()=>({id:`kick/assets`,inputs:[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`],async generate(e){if(!r(u.resolve(e.cwd,`kick.config.ts`)))return null;let t=await k(e.cwd);if(!t?.assetMap)return null;let n=da(t.assetMap,e.cwd);return n.count===0?null:fa(n)}}),Es="/* 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 Ds(e,t,n){if(e.length===0)return`${Es}
|
|
2703
|
+
`),process.exit(1));let r=j(await M(process.cwd())),i=v(e.modulesDir??r.dir??`src/modules`),a;e.target===`define`||e.target===`class`?a=e.target:e.target===void 0?a=r.style??`define`:(console.error(`\n ${k.red(`Error:`)} --target must be 'define' or 'class' (got '${e.target}').\n`),process.exit(1));let o=k.dim(`→ ${a}`),s=n?k.dim(`(dry-run)`):k.bold(`(applying)`);console.log(`\n ${k.bold(`kick codemod modules`)} ${o} ${s}`),console.log(` modulesDir: ${k.dim(i)}\n`);let c=e.backup!==!1&&!n,l=await lr(i,{dryRun:n,target:a,backup:c});if(l.backupDir){let e=l.backupDir;console.log(` ${k.green(`✓`)} backup: ${k.dim(e)}\n ${k.dim(`(restore: rm -rf <modulesDir> && mv "<backup>" <modulesDir>)`)}\n`)}else !n&&e.backup===!1&&console.log(` ${k.dim(`(--no-backup — skipping snapshot)`)}\n`);let u=0,d=0;for(let e of l.files)if(e.status===`migrated`)u++,console.log(` ${k.green(`✓`)} ${e.path}`);else{d++;let t=k.dim(`(${e.reason??`skipped`})`);console.log(` ${k.dim(`-`)} ${e.path} ${t}`)}if(console.log(),l.indexStatus===`migrated`)console.log(` ${k.green(`✓`)} ${l.indexPath}`);else if(l.indexStatus===`skipped`){let e=k.dim(`(${l.indexReason??`skipped`})`);console.log(` ${k.dim(`-`)} ${l.indexPath} ${e}`)}else console.log(` ${k.dim(`-`)} ${l.indexPath} ${k.dim(`(not found)`)}`);let f=n?k.dim(` (dry-run — pass --apply to write)`):``;console.log(`\n ${k.bold(String(u))} migrated, ${k.bold(String(d))} skipped${f}\n`)})}const Ko=()=>({id:`kick/assets`,inputs:[`kick.config.ts`,`kick.config.js`,`kick.config.mjs`],async generate(e){if(!r(u.resolve(e.cwd,`kick.config.ts`)))return null;let t=await M(e.cwd);if(!t?.assetMap)return null;let n=Li(t.assetMap,e.cwd);return n.count===0?null:Ri(n)}}),qo="/* 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 Jo(e,t,n){if(e.length===0)return`${qo}
|
|
3929
2704
|
// (no routes discovered yet — annotate a controller method with
|
|
3930
2705
|
// @Get/@Post/@Put/@Delete/@Patch and re-run \`kick typegen\`)
|
|
3931
2706
|
declare global {
|
|
@@ -3934,8 +2709,8 @@ declare global {
|
|
|
3934
2709
|
}
|
|
3935
2710
|
|
|
3936
2711
|
export {}
|
|
3937
|
-
`;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=
|
|
3938
|
-
`))}return`${
|
|
2712
|
+
`;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=Zo(e,r,t,n,i);return a?n===`kickjs-schema`?`import('@forinda/kickjs-schema').InferSchemaOutput<typeof ${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??Yo(e),l=Xo(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(`
|
|
2713
|
+
`))}return`${qo}${Qo(i)}
|
|
3939
2714
|
declare global {
|
|
3940
2715
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
3941
2716
|
namespace KickRoutes {
|
|
@@ -3945,9 +2720,9 @@ ${o.join(`
|
|
|
3945
2720
|
}
|
|
3946
2721
|
|
|
3947
2722
|
export {}
|
|
3948
|
-
`}function
|
|
2723
|
+
`}function Yo(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 Xo(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 Zo(e,t,n,r,i){if(!e||r===!1||e.source===null)return null;let a=$o(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 Qo(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(`
|
|
3949
2724
|
`)+`
|
|
3950
|
-
`}function
|
|
2725
|
+
`}function $o(e,t,n){if(e===null)return`unknown`;let r=f(n);if(e===``){let e=_(r,t).split(y).join(`/`);return e=e.replace(/\.(ts|tsx|mts|cts)$/i,``),e.startsWith(`.`)||(e=`./`+e),e}if(!e.startsWith(`.`)&&!e.startsWith(`/`))return e;let i=_(r,v(f(t),e)).split(y).join(`/`);return i=i.replace(/\.(ts|tsx|mts|cts)$/i,``),i.startsWith(`.`)||(i=`./`+i),i}const es=()=>({id:`kick/routes`,outExtension:`.ts`,inputs:[`src/**/*.controller.ts`,`src/**/*.module.ts`],async generate(e){let t=await e.getScanResult({root:ts(e),cwd:e.cwd,envFile:ns(e)}),n=e.config?.typegen?.schemaValidator??`zod`,r=u.resolve(e.cwd,`.kickjs/types/kick__routes.ts`);return Jo(t.routes,r,n)}});function ts(e){return u.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function ns(e){let t=e.config?.typegen?.envFile;if(t!==!1)return t}function rs(e,t,n=`zod`){if(!e)return null;let r=_(f(t),e.filePath).split(y).join(`/`);return r=r.replace(/\.(ts|tsx|mts|cts)$/i,``),r.startsWith(`.`)||(r=`./`+r),`/* eslint-disable */
|
|
3951
2726
|
// AUTO-GENERATED by \`kick typegen\`. DO NOT EDIT.
|
|
3952
2727
|
// Re-run with \`kick typegen\` or rely on \`kick dev\` to refresh.
|
|
3953
2728
|
|
|
@@ -3985,4 +2760,4 @@ declare global {
|
|
|
3985
2760
|
}
|
|
3986
2761
|
|
|
3987
2762
|
export {}
|
|
3988
|
-
`}const
|
|
2763
|
+
`}const is=()=>({id:`kick/env`,outExtension:`.ts`,inputs:[`src/env.ts`,`src/**/env.ts`,`src/**/*.env.ts`],async generate(e){let t=os(e);if(t===!1)return null;let n=await e.getScanResult({root:as(e),cwd:e.cwd,envFile:t});if(!n.env)return null;let r=e.config?.typegen?.schemaValidator??`zod`,i=u.resolve(e.cwd,`.kickjs/types/kick__env.ts`);return rs(n.env,i,r)}});function as(e){return u.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function os(e){return e.config?.typegen?.envFile}function ss(e){return u.resolve(e.cwd,e.config?.typegen?.srcDir??`src`)}function cs(e){let t=e.config?.typegen?.envFile;if(t!==!1)return t}function ls(e){return{root:ss(e),cwd:e.cwd,envFile:cs(e)}}const us=()=>({id:`kick/registry`,inputs:[`src/**/*.ts`],async generate(e){let t=await e.getScanResult(ls(e)),n=u.resolve(e.cwd,`.kickjs/types/kick__registry.d.ts`),r=new Set(t.collisions.map(e=>e.className));return Ei(t.classes,n,r)}}),ds=()=>({id:`kick/services`,inputs:[`src/**/*.ts`],async generate(e){let t=await e.getScanResult(ls(e)),n=new Set(t.collisions.map(e=>e.className));return ki(`ServiceToken`,Ai(t.classes,t.tokens,t.injects,n),"(no tokens discovered — declare with createToken<T>() or `kick g service <name>`)")}}),fs=()=>({id:`kick/modules`,inputs:[`src/**/*.ts`],async generate(e){return ki(`ModuleToken`,ji((await e.getScanResult(ls(e))).classes),"(no @Module classes discovered — `kick g module <name>` to add one)")}}),ps=()=>({id:`kick/plugins`,inputs:[`src/**/*.ts`],async generate(e){return Mi((await e.getScanResult(ls(e))).pluginsAndAdapters)}}),ms=()=>({id:`kick/augmentations`,inputs:[`src/**/*.ts`],async generate(e){return Ni((await e.getScanResult(ls(e))).augmentations)}}),hs=()=>({id:`kick/context`,inputs:[`src/**/*.ts`],async generate(e){let t=await e.getScanResult(ls(e));return t.contextKeys.length===0?null:Oi(t.contextKeys)}});var gs=A({builtinCliPlugins:()=>_s});const _s=[D({name:`kick/init`,register:Vt}),D({name:`kick/generate`,register:na}),D({name:`kick/run`,register:va}),D({name:`kick/info`,register:ya}),D({name:`kick/inspect`,register:ka}),D({name:`kick/add`,register:zt}),D({name:`kick/list`,register:Rt}),D({name:`kick/explain`,register:za}),D({name:`kick/mcp`,register:Xa}),D({name:`kick/tinker`,register:eo}),D({name:`kick/remove`,register:oo}),D({name:`kick/typegen`,register:lo}),D({name:`kick/check`,register:So}),D({name:`kick/doctor`,register:Uo}),D({name:`kick/codemod`,register:Go}),D({name:`kick/registry`,typegens:[us()]}),D({name:`kick/services`,typegens:[ds()]}),D({name:`kick/modules`,typegens:[fs()]}),D({name:`kick/plugins`,typegens:[ps()]}),D({name:`kick/augmentations`,typegens:[ms()]}),D({name:`kick/context`,typegens:[hs()]}),D({name:`kick/assets`,typegens:[Ko()]}),D({name:`kick/routes`,typegens:[es()]}),D({name:`kick/env`,typegens:[is()]})],vs=f(b(import.meta.url)),ys=JSON.parse(a(h(vs,`..`,`package.json`),`utf-8`));async function bs(){let e=new t;e.name(`kick`).description(`KickJS — A production-grade, decorator-driven Node.js framework`).version(ys.version);let n=Gt(process.cwd()),r=n,i=await M(r)??{},a=Pe([..._s,...i.plugins??[]],i.commands??[]);await a.register(e,{cwd:r,projectRoot:n,config:i,log:e=>console.log(e)}),be(e,{...i,commands:a.commands}),e.showHelpAfterError(),await e.parseAsync(process.argv)}bs().catch(e=>{console.error(e instanceof Error?e.message:e),process.exitCode=1});export{};
|