@forinda/kickjs-cli 5.3.2 → 5.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{builtins-CnQ6lxcV.mjs → builtins-BCRm8cUp.mjs} +163 -100
- package/dist/builtins-BCRm8cUp.mjs.map +1 -0
- package/dist/{builtins-BxGfcEP6.mjs → builtins-BbJqgLmS.mjs} +432 -220
- package/dist/cli.mjs +2 -2
- package/dist/config-cyBKEKWL.mjs +12 -0
- package/dist/config-cyBKEKWL.mjs.map +1 -0
- package/dist/config-q_jBP6Ln.mjs +11 -0
- package/dist/{generator-extension-BNgcYlom.mjs → generator-extension-CsT2e6Fj.mjs} +274 -125
- package/dist/generator-extension-CsT2e6Fj.mjs.map +1 -0
- package/dist/index.d.mts +45 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{plugin-Ccvf-gn6.mjs → plugin-8R67gfJG.mjs} +3 -3
- package/dist/{plugin-Ccvf-gn6.mjs.map → plugin-8R67gfJG.mjs.map} +1 -1
- package/dist/{plugin-B56zClEX.mjs → plugin-D6nfJWY0.mjs} +2 -2
- package/dist/{rolldown-runtime-CP9PNXAB.mjs → rolldown-runtime-B9Lxtctb.mjs} +1 -1
- package/dist/{run-plugins-sjeIm8hS.mjs → run-plugins-CYRt77yP.mjs} +2 -2
- package/dist/{typegen-CFW1vIgv.mjs → typegen-CgWQM1FH.mjs} +3 -3
- package/dist/{typegen-CYA1y8NJ.mjs → typegen-SyGEEyKv.mjs} +4 -4
- package/dist/{typegen-CYA1y8NJ.mjs.map → typegen-SyGEEyKv.mjs.map} +1 -1
- package/dist/{types-2ICiQzlQ.mjs → types-dPuCyHXz.mjs} +2 -2
- package/dist/{types-2ICiQzlQ.mjs.map → types-dPuCyHXz.mjs.map} +1 -1
- package/package.json +2 -2
- package/dist/builtins-CnQ6lxcV.mjs.map +0 -1
- package/dist/config-B5g_GsV2.mjs +0 -12
- package/dist/config-B5g_GsV2.mjs.map +0 -1
- package/dist/config-DE9Vo6LN.mjs +0 -11
- package/dist/generator-extension-BNgcYlom.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v5.
|
|
2
|
+
* @forinda/kickjs-cli v5.4.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import{createRequire as e}from"node:module";import{dirname as t,extname as n,join as r,resolve as i}from"node:path";import{existsSync as a,readFileSync as o}from"node:fs";import{access as s,mkdir as c,readFile as l,writeFile as u}from"node:fs/promises";import*as d from"@clack/prompts";import f from"picocolors";import p from"pluralize";import{execSync as
|
|
11
|
+
import{createRequire as e}from"node:module";import{dirname as t,extname as n,join as r,resolve as i}from"node:path";import{existsSync as a,readFileSync as o}from"node:fs";import{access as s,mkdir as c,readFile as l,writeFile as u}from"node:fs/promises";import*as d from"@clack/prompts";import f from"picocolors";import p from"pluralize";import{execFileSync as m,execSync as h}from"node:child_process";import{fileURLToPath as g,pathToFileURL as _}from"node:url";let v=!1;function y(e){v=e}const ee=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function b(e,r){v||(await c(t(e),{recursive:!0}),await u(e,r,`utf-8`),ee.has(n(e))&&await ne(e,r).catch(()=>{}))}let x;async function te(t){if(x!==void 0)return x;try{x=await import(e(r(t,`package.json`)).resolve(`oxfmt`))}catch{x=null}return x}async function ne(e,t){let n=await te(process.cwd());if(!n)return;let r=await re(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await u(e,i.code,`utf-8`)}const S=new Map;async function re(e){let n=t(e),i=n;if(S.has(i))return S.get(i);for(;;){let e=r(n,`.oxfmtrc.json`);if(a(e))try{let t=await l(e,`utf-8`),n=JSON.parse(t);return delete n.$schema,delete n.ignorePatterns,S.set(i,n),n}catch{return S.set(i,null),null}let o=t(n);if(o===n)return S.set(i,null),null;n=o}}async function C(e){try{return await s(e),!0}catch{return!1}}const ie={GET:f.green,POST:f.cyan,PUT:f.yellow,PATCH:f.magenta,DELETE:f.red};function ae(e){return(ie[e]??f.dim)(e.padEnd(7))}function oe(e){let t=`[${e}]`.padEnd(10);switch(e){case`CRITICAL`:return f.red(t);case`WARNING`:return f.yellow(t);case`INFO`:return f.blue(f.dim(t));default:return t}}f.green(`✓`),f.red(`✖`),f.yellow(`⚠`),f.blue(`ℹ`);function se(e){d.intro(f.bgCyan(f.black(` ${e} `)))}function ce(e){d.outro(e)}function w(e){d.isCancel(e)&&(d.cancel(`Operation cancelled.`),process.exit(0))}async function le(e){let t=await d.text(e);return w(t),t}async function ue(e){let t=await d.select(e);return w(t),t}async function de(e){let t=await d.multiselect(e);return w(t),t}async function T(e){let t=await d.confirm(e);return w(t),t}function fe(){return d.spinner()}const E=d.log;function D(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function O(e){let t=D(e);return t.charAt(0).toLowerCase()+t.slice(1)}function k(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function A(e){return p.plural(e)}function j(e){return p.plural(e)}function M(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const pe={inmemory:`in-memory`,drizzle:`Drizzle`,prisma:`Prisma`};function me(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function he(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function N(e){return pe[e]??me(e)}function P(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]??`${me(n)}${e}Repository`,repoFile:i[n]??`${he(n)}-${t}`}}function F(e){return e??`define`}function ge(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=P(t,n,i),c=F(a),l=`/**
|
|
12
12
|
* ${t} Module
|
|
13
13
|
*
|
|
14
14
|
* Self-contained feature module following Domain-Driven Design (DDD).
|
|
@@ -19,18 +19,34 @@ import{createRequire as e}from"node:module";import{dirname as t,extname as n,joi
|
|
|
19
19
|
* application/ — Use cases (orchestration) and DTOs (validation)
|
|
20
20
|
* domain/ — Entities, value objects, repository interfaces, domain services
|
|
21
21
|
* infrastructure/ — Repository implementations (currently ${N(i)})
|
|
22
|
-
|
|
23
|
-
import {
|
|
24
|
-
import { buildRoutes } from '@forinda/kickjs'
|
|
25
|
-
import { ${t.toUpperCase()}_REPOSITORY } from './domain/repositories/${n}.repository'
|
|
26
|
-
import { ${a} } from './infrastructure/repositories/${o}.repository'
|
|
22
|
+
*/`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './domain/repositories/${n}.repository'
|
|
23
|
+
import { ${o} } from './infrastructure/repositories/${s}.repository'
|
|
27
24
|
import { ${t}Controller } from './presentation/${n}.controller'
|
|
28
25
|
|
|
29
26
|
// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
|
|
30
27
|
import.meta.glob(
|
|
31
28
|
['./domain/services/**/*.ts', './application/use-cases/**/*.ts', '!./**/*.test.ts'],
|
|
32
29
|
{ eager: true },
|
|
33
|
-
)
|
|
30
|
+
)`,d=` /**
|
|
31
|
+
* Declare HTTP routes for this module. Return value shape:
|
|
32
|
+
*
|
|
33
|
+
* - \`path\` — URL prefix for this route set, mounted under
|
|
34
|
+
* \`/{apiPrefix}/v{version}{path}\`.
|
|
35
|
+
* - \`controller\` — Controller class. Used both for the route
|
|
36
|
+
* handler bindings and OpenAPI spec generation.
|
|
37
|
+
* - \`version\` — Optional. Overrides the app-wide API version
|
|
38
|
+
* for this route set only.
|
|
39
|
+
*
|
|
40
|
+
* Return an **array** to mount multiple route sets under the
|
|
41
|
+
* same module (e.g. side-by-side v1 + v2 controllers):
|
|
42
|
+
*
|
|
43
|
+
* return [
|
|
44
|
+
* { path: '/${r}', version: 1, controller: ${t}V1Controller },
|
|
45
|
+
* { path: '/${r}', version: 2, controller: ${t}V2Controller },
|
|
46
|
+
* ]
|
|
47
|
+
*/`;return c===`class`?`${l}
|
|
48
|
+
import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
|
|
49
|
+
${u}
|
|
34
50
|
|
|
35
51
|
export class ${t}Module implements AppModule {
|
|
36
52
|
/**
|
|
@@ -40,24 +56,46 @@ export class ${t}Module implements AppModule {
|
|
|
40
56
|
*/
|
|
41
57
|
register(container: Container): void {
|
|
42
58
|
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
43
|
-
container.resolve(${
|
|
59
|
+
container.resolve(${o}),
|
|
44
60
|
)
|
|
45
61
|
}
|
|
46
62
|
|
|
47
|
-
|
|
48
|
-
* Declare HTTP routes for this module.
|
|
49
|
-
* The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${r}).
|
|
50
|
-
* Passing 'controller' enables automatic OpenAPI spec generation via SwaggerAdapter.
|
|
51
|
-
*/
|
|
63
|
+
${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
|
|
52
64
|
routes(): ModuleRoutes {
|
|
53
65
|
return {
|
|
54
66
|
path: '/${r}',
|
|
55
|
-
router: buildRoutes(${t}Controller),
|
|
56
67
|
controller: ${t}Controller,
|
|
57
68
|
}
|
|
58
69
|
}
|
|
59
70
|
}
|
|
60
|
-
|
|
71
|
+
`:`${l}
|
|
72
|
+
import { defineModule } from '@forinda/kickjs'
|
|
73
|
+
${u}
|
|
74
|
+
|
|
75
|
+
export const ${t}Module = defineModule({
|
|
76
|
+
name: '${t}Module',
|
|
77
|
+
build: () => ({
|
|
78
|
+
/**
|
|
79
|
+
* Register module dependencies in the DI container.
|
|
80
|
+
* Bind repository interface tokens to their implementations here.
|
|
81
|
+
* Currently wired to ${N(i)}. To swap implementations, change the factory target.
|
|
82
|
+
*/
|
|
83
|
+
register(container) {
|
|
84
|
+
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
85
|
+
container.resolve(${o}),
|
|
86
|
+
)
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
${d}
|
|
90
|
+
routes() {
|
|
91
|
+
return {
|
|
92
|
+
path: '/${r}',
|
|
93
|
+
controller: ${t}Controller,
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
})
|
|
98
|
+
`}function _e(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=P(t,n,i),c=F(a),l=`/**
|
|
61
99
|
* ${t} Module
|
|
62
100
|
*
|
|
63
101
|
* REST module with a flat folder structure.
|
|
@@ -67,47 +105,109 @@ export class ${t}Module implements AppModule {
|
|
|
67
105
|
* ${n}.controller.ts — HTTP routes (CRUD)
|
|
68
106
|
* ${n}.service.ts — Business logic
|
|
69
107
|
* ${n}.repository.ts — Repository interface
|
|
70
|
-
* ${
|
|
108
|
+
* ${s}.repository.ts — Repository implementation
|
|
71
109
|
* dtos/ — Request/response schemas
|
|
72
|
-
|
|
73
|
-
import {
|
|
74
|
-
import { buildRoutes } from '@forinda/kickjs'
|
|
75
|
-
import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
|
|
76
|
-
import { ${a} } from './${o}.repository'
|
|
110
|
+
*/`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
|
|
111
|
+
import { ${o} } from './${s}.repository'
|
|
77
112
|
import { ${t}Controller } from './${n}.controller'
|
|
78
113
|
|
|
79
114
|
// Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
|
|
80
|
-
import.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })
|
|
115
|
+
import.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })`,d=` /**
|
|
116
|
+
* Declare HTTP routes for this module. Return value shape:
|
|
117
|
+
*
|
|
118
|
+
* - \`path\` — URL prefix for this route set.
|
|
119
|
+
* - \`controller\` — Controller class (also drives OpenAPI).
|
|
120
|
+
* - \`version\` — Optional. Overrides the app-wide API version.
|
|
121
|
+
*
|
|
122
|
+
* Return an **array** to mount multiple route sets — admin
|
|
123
|
+
* surfaces, side-by-side v1 + v2 controllers, etc:
|
|
124
|
+
*
|
|
125
|
+
* return [
|
|
126
|
+
* { path: '/${r}', version: 1, controller: ${t}V1Controller },
|
|
127
|
+
* { path: '/${r}', version: 2, controller: ${t}V2Controller },
|
|
128
|
+
* ]
|
|
129
|
+
*/`;return c===`class`?`${l}
|
|
130
|
+
import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
|
|
131
|
+
${u}
|
|
81
132
|
|
|
82
133
|
export class ${t}Module implements AppModule {
|
|
83
134
|
register(container: Container): void {
|
|
84
135
|
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
85
|
-
container.resolve(${
|
|
136
|
+
container.resolve(${o}),
|
|
86
137
|
)
|
|
87
138
|
}
|
|
88
139
|
|
|
140
|
+
${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
|
|
89
141
|
routes(): ModuleRoutes {
|
|
90
142
|
return {
|
|
91
143
|
path: '/${r}',
|
|
92
|
-
router: buildRoutes(${t}Controller),
|
|
93
144
|
controller: ${t}Controller,
|
|
94
145
|
}
|
|
95
146
|
}
|
|
96
147
|
}
|
|
97
|
-
|
|
98
|
-
import {
|
|
148
|
+
`:`${l}
|
|
149
|
+
import { defineModule } from '@forinda/kickjs'
|
|
150
|
+
${u}
|
|
151
|
+
|
|
152
|
+
export const ${t}Module = defineModule({
|
|
153
|
+
name: '${t}Module',
|
|
154
|
+
build: () => ({
|
|
155
|
+
register(container) {
|
|
156
|
+
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
157
|
+
container.resolve(${o}),
|
|
158
|
+
)
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
${d}
|
|
162
|
+
routes() {
|
|
163
|
+
return {
|
|
164
|
+
path: '/${r}',
|
|
165
|
+
controller: ${t}Controller,
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
}),
|
|
169
|
+
})
|
|
170
|
+
`}function ve(e){let{pascal:t,kebab:n,plural:r=``,style:i}=e,a=F(i),o=` /**
|
|
171
|
+
* Declare HTTP routes. Return value shape:
|
|
172
|
+
*
|
|
173
|
+
* - \`path\` — URL prefix for this route set.
|
|
174
|
+
* - \`controller\` — Controller class (also drives OpenAPI).
|
|
175
|
+
* - \`version\` — Optional. Overrides the app-wide API version.
|
|
176
|
+
*
|
|
177
|
+
* Return an array to mount multiple route sets:
|
|
178
|
+
*
|
|
179
|
+
* return [
|
|
180
|
+
* { path: '/${r}', version: 1, controller: ${t}V1Controller },
|
|
181
|
+
* { path: '/${r}', version: 2, controller: ${t}V2Controller },
|
|
182
|
+
* ]
|
|
183
|
+
*/`;return a===`class`?`import { type AppModule, type ModuleRoutes } from '@forinda/kickjs'
|
|
99
184
|
import { ${t}Controller } from './${n}.controller'
|
|
100
185
|
|
|
101
186
|
export class ${t}Module implements AppModule {
|
|
187
|
+
${o.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
|
|
102
188
|
routes(): ModuleRoutes {
|
|
103
189
|
return {
|
|
104
190
|
path: '/${r}',
|
|
105
|
-
router: buildRoutes(${t}Controller),
|
|
106
191
|
controller: ${t}Controller,
|
|
107
192
|
}
|
|
108
193
|
}
|
|
109
194
|
}
|
|
110
|
-
|
|
195
|
+
`:`import { defineModule } from '@forinda/kickjs'
|
|
196
|
+
import { ${t}Controller } from './${n}.controller'
|
|
197
|
+
|
|
198
|
+
export const ${t}Module = defineModule({
|
|
199
|
+
name: '${t}Module',
|
|
200
|
+
build: () => ({
|
|
201
|
+
${o}
|
|
202
|
+
routes() {
|
|
203
|
+
return {
|
|
204
|
+
path: '/${r}',
|
|
205
|
+
controller: ${t}Controller,
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
})
|
|
210
|
+
`}function ye(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'
|
|
111
211
|
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
112
212
|
import { Create${t}UseCase } from '../application/use-cases/create-${n}.use-case'
|
|
113
213
|
import { Get${t}UseCase } from '../application/use-cases/get-${n}.use-case'
|
|
@@ -170,7 +270,7 @@ export class ${t}Controller {
|
|
|
170
270
|
ctx.noContent()
|
|
171
271
|
}
|
|
172
272
|
}
|
|
173
|
-
`}function
|
|
273
|
+
`}function be(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'
|
|
174
274
|
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
175
275
|
import { ${t}Service } from './${n}.service'
|
|
176
276
|
import { create${t}Schema } from './dtos/create-${n}.dto'
|
|
@@ -225,14 +325,14 @@ export class ${t}Controller {
|
|
|
225
325
|
ctx.noContent()
|
|
226
326
|
}
|
|
227
327
|
}
|
|
228
|
-
`}function
|
|
328
|
+
`}function xe(e){let{pascal:t}=e;return`import type { QueryParamsConfig } from '@forinda/kickjs'
|
|
229
329
|
|
|
230
330
|
export const ${t.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {
|
|
231
331
|
filterable: ['name'],
|
|
232
332
|
sortable: ['name', 'createdAt'],
|
|
233
333
|
searchable: ['name'],
|
|
234
334
|
}
|
|
235
|
-
`}function
|
|
335
|
+
`}function I(e){let{pascal:t}=e;return`import { z } from 'zod'
|
|
236
336
|
|
|
237
337
|
/**
|
|
238
338
|
* Create ${t} DTO — Zod schema for validating POST request bodies.
|
|
@@ -248,20 +348,20 @@ export const create${t}Schema = z.object({
|
|
|
248
348
|
})
|
|
249
349
|
|
|
250
350
|
export type Create${t}DTO = z.infer<typeof create${t}Schema>
|
|
251
|
-
`}function
|
|
351
|
+
`}function L(e){let{pascal:t}=e;return`import { z } from 'zod'
|
|
252
352
|
|
|
253
353
|
export const update${t}Schema = z.object({
|
|
254
354
|
name: z.string().min(1).max(200).optional(),
|
|
255
355
|
})
|
|
256
356
|
|
|
257
357
|
export type Update${t}DTO = z.infer<typeof update${t}Schema>
|
|
258
|
-
`}function
|
|
358
|
+
`}function R(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
|
|
259
359
|
id: string
|
|
260
360
|
name: string
|
|
261
361
|
createdAt: string
|
|
262
362
|
updatedAt: string
|
|
263
363
|
}
|
|
264
|
-
`}function
|
|
364
|
+
`}function Se(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`create-${n}.use-case.ts`,content:`/**
|
|
265
365
|
* Create ${t} Use Case
|
|
266
366
|
*
|
|
267
367
|
* Application layer — orchestrates a single business operation.
|
|
@@ -339,7 +439,7 @@ export class Delete${t}UseCase {
|
|
|
339
439
|
await this.repo.delete(id)
|
|
340
440
|
}
|
|
341
441
|
}
|
|
342
|
-
`}]}function
|
|
442
|
+
`}]}function z(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
|
|
343
443
|
* ${t} Repository Interface
|
|
344
444
|
*
|
|
345
445
|
* Defines the contract for data access.
|
|
@@ -374,7 +474,7 @@ export interface I${t}Repository {
|
|
|
374
474
|
* adopters must NOT use the reserved \`'kick/'\` namespace.
|
|
375
475
|
*/
|
|
376
476
|
export const ${t.toUpperCase()}_REPOSITORY = createToken<I${t}Repository>('${i}/${t}/repository')
|
|
377
|
-
`}function
|
|
477
|
+
`}function B(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
|
|
378
478
|
* In-Memory ${t} Repository
|
|
379
479
|
*
|
|
380
480
|
* Implements the repository interface using a Map.
|
|
@@ -434,7 +534,7 @@ export class InMemory${t}Repository implements I${t}Repository {
|
|
|
434
534
|
this.store.delete(id)
|
|
435
535
|
}
|
|
436
536
|
}
|
|
437
|
-
`}function
|
|
537
|
+
`}function V(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`/**
|
|
438
538
|
* ${o} ${t} Repository
|
|
439
539
|
*
|
|
440
540
|
* Stub implementation for a custom '${r}' repository.
|
|
@@ -503,7 +603,7 @@ export class ${o}${t}Repository implements I${t}Repository {
|
|
|
503
603
|
this.store.delete(id)
|
|
504
604
|
}
|
|
505
605
|
}
|
|
506
|
-
`}function
|
|
606
|
+
`}function Ce(e){let{pascal:t,kebab:n}=e;return`/**
|
|
507
607
|
* ${t} Domain Service
|
|
508
608
|
*
|
|
509
609
|
* Domain layer — contains business rules that don't belong to a single entity.
|
|
@@ -526,7 +626,7 @@ export class ${t}DomainService {
|
|
|
526
626
|
}
|
|
527
627
|
}
|
|
528
628
|
}
|
|
529
|
-
`}function
|
|
629
|
+
`}function we(e){let{pascal:t,kebab:n}=e;return`/**
|
|
530
630
|
* ${t} Entity
|
|
531
631
|
*
|
|
532
632
|
* Domain layer — the core business object.
|
|
@@ -595,7 +695,7 @@ export class ${t} {
|
|
|
595
695
|
}
|
|
596
696
|
}
|
|
597
697
|
}
|
|
598
|
-
`}function
|
|
698
|
+
`}function Te(e){let{pascal:t}=e;return`/**
|
|
599
699
|
* ${t} ID Value Object
|
|
600
700
|
*
|
|
601
701
|
* Domain layer — wraps a primitive ID with type safety and validation.
|
|
@@ -629,7 +729,7 @@ export class ${t}Id {
|
|
|
629
729
|
return this.value === other.value
|
|
630
730
|
}
|
|
631
731
|
}
|
|
632
|
-
`}function
|
|
732
|
+
`}function H(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
|
|
633
733
|
import { Container } from '@forinda/kickjs'
|
|
634
734
|
|
|
635
735
|
describe('${t}Controller', () => {
|
|
@@ -681,7 +781,7 @@ describe('${t}Controller', () => {
|
|
|
681
781
|
})
|
|
682
782
|
})
|
|
683
783
|
})
|
|
684
|
-
`}function
|
|
784
|
+
`}function U(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'
|
|
685
785
|
import { InMemory${t}Repository } from '${i}'
|
|
686
786
|
|
|
687
787
|
describe('InMemory${t}Repository', () => {
|
|
@@ -743,7 +843,7 @@ describe('InMemory${t}Repository', () => {
|
|
|
743
843
|
expect(found).toBeNull()
|
|
744
844
|
})
|
|
745
845
|
})
|
|
746
|
-
`}function
|
|
846
|
+
`}function Ee(e){let{pascal:t,kebab:n}=e;return`import { Service, Inject, HttpException } from '@forinda/kickjs'
|
|
747
847
|
import type { ParsedQuery } from '@forinda/kickjs'
|
|
748
848
|
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from './${n}.repository'
|
|
749
849
|
import type { ${t}ResponseDTO } from './dtos/${n}-response.dto'
|
|
@@ -780,14 +880,14 @@ export class ${t}Service {
|
|
|
780
880
|
await this.repo.delete(id)
|
|
781
881
|
}
|
|
782
882
|
}
|
|
783
|
-
`}function
|
|
883
|
+
`}function W(e){let{pascal:t}=e;return`import type { QueryFieldConfig } from '@forinda/kickjs'
|
|
784
884
|
|
|
785
885
|
export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
|
|
786
886
|
filterable: ['name'],
|
|
787
887
|
sortable: ['name', 'createdAt'],
|
|
788
888
|
searchable: ['name'],
|
|
789
889
|
}
|
|
790
|
-
`}function
|
|
890
|
+
`}function De(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,o={inmemory:`InMemory${t}Repository`,drizzle:`Drizzle${t}Repository`,prisma:`Prisma${t}Repository`},s={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},c=o[i]??o.inmemory,l=s[i]??s.inmemory,u=a??`define`,d=`/**
|
|
791
891
|
* ${t} Module — CQRS Pattern
|
|
792
892
|
*
|
|
793
893
|
* Separates read (queries) and write (commands) operations.
|
|
@@ -799,11 +899,8 @@ export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
|
|
|
799
899
|
* queries/ — Read operations (get, list)
|
|
800
900
|
* events/ — Domain events + handlers (WS broadcast, queue dispatch)
|
|
801
901
|
* dtos/ — Request/response schemas
|
|
802
|
-
|
|
803
|
-
import {
|
|
804
|
-
import { buildRoutes } from '@forinda/kickjs'
|
|
805
|
-
import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
|
|
806
|
-
import { ${s} } from './${c}.repository'
|
|
902
|
+
*/`,f=`import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
|
|
903
|
+
import { ${c} } from './${l}.repository'
|
|
807
904
|
import { ${t}Controller } from './${n}.controller'
|
|
808
905
|
|
|
809
906
|
// Eagerly load decorated classes
|
|
@@ -815,24 +912,61 @@ import.meta.glob(
|
|
|
815
912
|
'!./**/*.test.ts',
|
|
816
913
|
],
|
|
817
914
|
{ eager: true },
|
|
818
|
-
)
|
|
915
|
+
)`,p=` /**
|
|
916
|
+
* Declare HTTP routes for this CQRS module. Return value shape:
|
|
917
|
+
*
|
|
918
|
+
* - \`path\` — URL prefix for this route set.
|
|
919
|
+
* - \`controller\` — Controller class (also drives OpenAPI).
|
|
920
|
+
* - \`version\` — Optional. Overrides the app-wide API version.
|
|
921
|
+
*
|
|
922
|
+
* Return an array to mount multiple route sets:
|
|
923
|
+
*
|
|
924
|
+
* return [
|
|
925
|
+
* { path: '/${r}', version: 1, controller: ${t}V1Controller },
|
|
926
|
+
* { path: '/${r}', version: 2, controller: ${t}V2Controller },
|
|
927
|
+
* ]
|
|
928
|
+
*/`;return u===`class`?`${d}
|
|
929
|
+
import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
|
|
930
|
+
${f}
|
|
819
931
|
|
|
820
932
|
export class ${t}Module implements AppModule {
|
|
821
933
|
register(container: Container): void {
|
|
822
934
|
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
823
|
-
container.resolve(${
|
|
935
|
+
container.resolve(${c}),
|
|
824
936
|
)
|
|
825
937
|
}
|
|
826
938
|
|
|
939
|
+
${p.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
|
|
827
940
|
routes(): ModuleRoutes {
|
|
828
941
|
return {
|
|
829
942
|
path: '/${r}',
|
|
830
|
-
router: buildRoutes(${t}Controller),
|
|
831
943
|
controller: ${t}Controller,
|
|
832
944
|
}
|
|
833
945
|
}
|
|
834
946
|
}
|
|
835
|
-
|
|
947
|
+
`:`${d}
|
|
948
|
+
import { defineModule } from '@forinda/kickjs'
|
|
949
|
+
${f}
|
|
950
|
+
|
|
951
|
+
export const ${t}Module = defineModule({
|
|
952
|
+
name: '${t}Module',
|
|
953
|
+
build: () => ({
|
|
954
|
+
register(container) {
|
|
955
|
+
container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
|
|
956
|
+
container.resolve(${c}),
|
|
957
|
+
)
|
|
958
|
+
},
|
|
959
|
+
|
|
960
|
+
${p}
|
|
961
|
+
routes() {
|
|
962
|
+
return {
|
|
963
|
+
path: '/${r}',
|
|
964
|
+
controller: ${t}Controller,
|
|
965
|
+
}
|
|
966
|
+
},
|
|
967
|
+
}),
|
|
968
|
+
})
|
|
969
|
+
`}function Oe(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'
|
|
836
970
|
import { ApiTags } from '@forinda/kickjs-swagger'
|
|
837
971
|
import { Create${t}Command } from './commands/create-${n}.command'
|
|
838
972
|
import { Update${t}Command } from './commands/update-${n}.command'
|
|
@@ -895,7 +1029,7 @@ export class ${t}Controller {
|
|
|
895
1029
|
ctx.noContent()
|
|
896
1030
|
}
|
|
897
1031
|
}
|
|
898
|
-
`}function
|
|
1032
|
+
`}function ke(e){let{pascal:t,kebab:n}=e;return[{file:`create-${n}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
899
1033
|
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
900
1034
|
import type { Create${t}DTO } from '../dtos/create-${n}.dto'
|
|
901
1035
|
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
@@ -949,7 +1083,7 @@ export class Delete${t}Command {
|
|
|
949
1083
|
this.events.emit('${n}.deleted', { id })
|
|
950
1084
|
}
|
|
951
1085
|
}
|
|
952
|
-
`}]}function
|
|
1086
|
+
`}]}function Ae(e){let{pascal:t,kebab:n,plural:r=``,pluralPascal:i=``}=e;return[{file:`get-${n}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs'
|
|
953
1087
|
import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from '../${n}.repository'
|
|
954
1088
|
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
955
1089
|
|
|
@@ -977,7 +1111,7 @@ export class List${i}Query {
|
|
|
977
1111
|
return this.repo.findPaginated(parsed)
|
|
978
1112
|
}
|
|
979
1113
|
}
|
|
980
|
-
`}]}function
|
|
1114
|
+
`}]}function je(e){let{pascal:t,kebab:n}=e;return[{file:`${n}.events.ts`,content:`import { Service } from '@forinda/kickjs'
|
|
981
1115
|
import { EventEmitter } from 'node:events'
|
|
982
1116
|
import type { ${t}ResponseDTO } from '../dtos/${n}-response.dto'
|
|
983
1117
|
|
|
@@ -1063,7 +1197,7 @@ export class On${t}ChangeHandler {
|
|
|
1063
1197
|
})
|
|
1064
1198
|
}
|
|
1065
1199
|
}
|
|
1066
|
-
`}]}function
|
|
1200
|
+
`}]}function G(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
|
|
1067
1201
|
* Drizzle ${t} Repository
|
|
1068
1202
|
*
|
|
1069
1203
|
* Implements the repository interface using Drizzle ORM.
|
|
@@ -1145,7 +1279,7 @@ export class Drizzle${t}Repository implements I${t}Repository {
|
|
|
1145
1279
|
throw new Error('Drizzle ${t} repository not yet implemented')
|
|
1146
1280
|
}
|
|
1147
1281
|
}
|
|
1148
|
-
`}function
|
|
1282
|
+
`}function Me(e){let{pascal:t,kebab:n}=e;return`import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
|
|
1149
1283
|
// TODO: Import your schema table and reference actual columns for type safety
|
|
1150
1284
|
// import { ${n}s } from '@/db/schema'
|
|
1151
1285
|
|
|
@@ -1163,7 +1297,7 @@ export const ${t.toUpperCase()}_QUERY_CONFIG: DrizzleQueryParamsConfig = {
|
|
|
1163
1297
|
// ${n}s.name,
|
|
1164
1298
|
],
|
|
1165
1299
|
}
|
|
1166
|
-
`}function
|
|
1300
|
+
`}function K(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`/**
|
|
1167
1301
|
* Prisma ${t} Repository
|
|
1168
1302
|
*
|
|
1169
1303
|
* Implements the repository interface using Prisma Client.
|
|
@@ -1221,7 +1355,7 @@ export class Prisma${t}Repository implements I${t}Repository {
|
|
|
1221
1355
|
await this.prisma.${a}.deleteMany({ where: { id } })
|
|
1222
1356
|
}
|
|
1223
1357
|
}
|
|
1224
|
-
`}function
|
|
1358
|
+
`}function Ne(e,t,n,r=[]){switch(t){case`cqrs`:{let t=[],i=[];return r.includes(`devtools`)&&(t.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`),i.push(` DevToolsAdapter(),`)),r.includes(`swagger`)&&(t.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`),i.push(` SwaggerAdapter({\n info: { title: '${e}', version: '${n}' },\n }),`)),`import 'reflect-metadata'
|
|
1225
1359
|
// Side-effect import — registers the extended env schema with kickjs
|
|
1226
1360
|
// **before** any controller / service / @Value gets resolved. Without
|
|
1227
1361
|
// this line ConfigService.get('YOUR_KEY') returns undefined because the
|
|
@@ -1291,12 +1425,14 @@ export const app = await bootstrap({
|
|
|
1291
1425
|
express.json(),
|
|
1292
1426
|
],
|
|
1293
1427
|
})
|
|
1294
|
-
`}}}function
|
|
1428
|
+
`}}}function Pe(){return`import { defineModules } from '@forinda/kickjs'
|
|
1295
1429
|
import { HelloModule } from './hello/hello.module'
|
|
1296
1430
|
|
|
1297
1431
|
// Remove HelloModule and run: kick g module <name>
|
|
1298
|
-
|
|
1299
|
-
|
|
1432
|
+
// \`defineModules()\` returns a chainable list — \`kick g module\` appends
|
|
1433
|
+
// \`.mount(NewModule())\` to the chain on every generation.
|
|
1434
|
+
export const modules = defineModules().mount(HelloModule())
|
|
1435
|
+
`}function Fe(){return`import { defineEnv, loadEnv } from '@forinda/kickjs/config'
|
|
1300
1436
|
import { z } from 'zod'
|
|
1301
1437
|
|
|
1302
1438
|
/**
|
|
@@ -1331,7 +1467,7 @@ const envSchema = defineEnv((base) =>
|
|
|
1331
1467
|
export const env = loadEnv(envSchema)
|
|
1332
1468
|
|
|
1333
1469
|
export default envSchema
|
|
1334
|
-
`}function
|
|
1470
|
+
`}function Ie(){return`import { Service } from '@forinda/kickjs'
|
|
1335
1471
|
|
|
1336
1472
|
@Service()
|
|
1337
1473
|
export class HelloService {
|
|
@@ -1343,7 +1479,7 @@ export class HelloService {
|
|
|
1343
1479
|
return { status: 'ok', uptime: process.uptime() }
|
|
1344
1480
|
}
|
|
1345
1481
|
}
|
|
1346
|
-
`}function
|
|
1482
|
+
`}function Le(){return`import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'
|
|
1347
1483
|
import { HelloService } from './hello.service'
|
|
1348
1484
|
|
|
1349
1485
|
// \`Ctx<KickRoutes.HelloController['<method>']>\` is generated by
|
|
@@ -1365,26 +1501,28 @@ export class HelloController {
|
|
|
1365
1501
|
ctx.json(this.helloService.healthCheck())
|
|
1366
1502
|
}
|
|
1367
1503
|
}
|
|
1368
|
-
`}function
|
|
1504
|
+
`}function Re(){return`import { defineModule } from '@forinda/kickjs'
|
|
1369
1505
|
import { HelloController } from './hello.controller'
|
|
1370
1506
|
|
|
1371
|
-
export
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1507
|
+
export const HelloModule = defineModule({
|
|
1508
|
+
name: 'HelloModule',
|
|
1509
|
+
build: () => ({
|
|
1510
|
+
// \`register(container)\` is optional — only implement it when you need
|
|
1511
|
+
// to bind a token to a concrete implementation, e.g.
|
|
1512
|
+
// register(container) {
|
|
1513
|
+
// container.registerFactory(USER_REPOSITORY, () => container.resolve(InMemoryUserRepository))
|
|
1514
|
+
// }
|
|
1515
|
+
// The HelloService uses @Service() so the decorator handles registration.
|
|
1516
|
+
|
|
1517
|
+
routes() {
|
|
1518
|
+
return {
|
|
1519
|
+
path: '/hello',
|
|
1520
|
+
controller: HelloController,
|
|
1521
|
+
}
|
|
1522
|
+
},
|
|
1523
|
+
}),
|
|
1524
|
+
})
|
|
1525
|
+
`}function ze(e,t=`inmemory`,n=`pnpm`){return`import { defineConfig } from '@forinda/kickjs-cli'
|
|
1388
1526
|
|
|
1389
1527
|
export default defineConfig({
|
|
1390
1528
|
pattern: '${e}',
|
|
@@ -1428,7 +1566,7 @@ export default defineConfig({
|
|
|
1428
1566
|
},
|
|
1429
1567
|
],
|
|
1430
1568
|
})
|
|
1431
|
-
`}async function
|
|
1569
|
+
`}async function Be(e){let{pascal:t,kebab:n,plural:r,style:i,write:a}=e;await a(`${n}.module.ts`,ve({pascal:t,kebab:n,plural:r,style:i})),await a(`${n}.controller.ts`,`import { Controller, Get, type Ctx } from '@forinda/kickjs'
|
|
1432
1570
|
|
|
1433
1571
|
// \`Ctx<KickRoutes.${t}Controller['<method>']>\` is generated by
|
|
1434
1572
|
// \`kick typegen\` (auto-run on \`kick dev\`).
|
|
@@ -1440,14 +1578,19 @@ export class ${t}Controller {
|
|
|
1440
1578
|
ctx.json({ message: '${t} list' })
|
|
1441
1579
|
}
|
|
1442
1580
|
}
|
|
1443
|
-
`)}async function
|
|
1444
|
-
import { ${t}Module } from '${
|
|
1445
|
-
|
|
1446
|
-
export const modules:
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1581
|
+
`)}async function Ve(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,style:l,write:u}=e;await u(`${n}.module.ts`,_e({pascal:t,kebab:n,plural:r,repo:a,style:l})),await u(`${n}.constants.ts`,W({pascal:t,kebab:n})),await u(`${n}.controller.ts`,be({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`${n}.service.ts`,Ee({pascal:t,kebab:n})),await u(`dtos/create-${n}.dto.ts`,I({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,L({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,R({pascal:t,kebab:n})),await u(`${n}.repository.ts`,z({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let d={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},f={inmemory:()=>B({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>G({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>K({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},p=d[a]??`${k(a)}-${n}`,m=f[a]??(()=>V({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await u(`${p}.repository.ts`,m()),o||(a!==`inmemory`&&await u(`in-memory-${n}.repository.ts`,B({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await u(`__tests__/${n}.controller.test.ts`,H({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,U({pascal:t,kebab:n,plural:r,repoPrefix:`../${d.inmemory??`in-memory-${n}`}.repository`})))}async function He(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,prismaClientPath:s,tokenScope:c,style:l,write:u}=e;await u(`${n}.module.ts`,De({pascal:t,kebab:n,plural:r,repo:a,style:l})),await u(`${n}.constants.ts`,W({pascal:t,kebab:n})),await u(`${n}.controller.ts`,Oe({pascal:t,kebab:n,plural:r,pluralPascal:i})),await u(`dtos/create-${n}.dto.ts`,I({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,L({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,R({pascal:t,kebab:n}));let d=ke({pascal:t,kebab:n});for(let e of d)await u(`commands/${e.file}`,e.content);let f=Ae({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of f)await u(`queries/${e.file}`,e.content);let p=je({pascal:t,kebab:n});for(let e of p)await u(`events/${e.file}`,e.content);await u(`${n}.repository.ts`,z({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:c}));let m={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},h={inmemory:()=>B({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),drizzle:()=>G({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}),prisma:()=>K({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`,prismaClientPath:s})},g=m[a]??`${k(a)}-${n}`,_=h[a]??(()=>V({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`}));await u(`${g}.repository.ts`,_()),o||(a!==`inmemory`&&await u(`in-memory-${n}.repository.ts`,B({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await u(`__tests__/${n}.controller.test.ts`,H({pascal:t,kebab:n,plural:r})),await u(`__tests__/${n}.repository.test.ts`,U({pascal:t,kebab:n,plural:r,repoPrefix:`../${m.inmemory??`in-memory-${n}`}.repository`})))}async function Ue(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noEntity:o,noTests:s,prismaClientPath:c,tokenScope:l,style:u,write:d}=e;await d(`${n}.module.ts`,ge({pascal:t,kebab:n,plural:r,repo:a,style:u})),await d(`constants.ts`,a===`drizzle`?Me({pascal:t,kebab:n}):xe({pascal:t,kebab:n})),await d(`presentation/${n}.controller.ts`,ye({pascal:t,kebab:n,plural:r,pluralPascal:i})),await d(`application/dtos/create-${n}.dto.ts`,I({pascal:t,kebab:n})),await d(`application/dtos/update-${n}.dto.ts`,L({pascal:t,kebab:n})),await d(`application/dtos/${n}-response.dto.ts`,R({pascal:t,kebab:n}));let f=Se({pascal:t,kebab:n,plural:r,pluralPascal:i});for(let e of f)await d(`application/use-cases/${e.file}`,e.content);await d(`domain/repositories/${n}.repository.ts`,z({pascal:t,kebab:n,tokenScope:l})),await d(`domain/services/${n}-domain.service.ts`,Ce({pascal:t,kebab:n}));let p={inmemory:`in-memory-${n}`,drizzle:`drizzle-${n}`,prisma:`prisma-${n}`},m={inmemory:()=>B({pascal:t,kebab:n}),drizzle:()=>G({pascal:t,kebab:n}),prisma:()=>K({pascal:t,kebab:n,prismaClientPath:c})},h=p[a]??`${k(a)}-${n}`,g=m[a]??(()=>V({pascal:t,kebab:n,repoType:a}));await d(`infrastructure/repositories/${h}.repository.ts`,g()),o||(await d(`domain/entities/${n}.entity.ts`,we({pascal:t,kebab:n})),await d(`domain/value-objects/${n}-id.vo.ts`,Te({pascal:t,kebab:n}))),s||(a!==`inmemory`&&await d(`infrastructure/repositories/in-memory-${n}.repository.ts`,B({pascal:t,kebab:n})),await d(`__tests__/${n}.controller.test.ts`,H({pascal:t,kebab:n,plural:r})),await d(`__tests__/${n}.repository.test.ts`,U({pascal:t,kebab:n,plural:r})))}function We(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function Ge(e){let{name:t,modulesDir:n,noEntity:i,noTests:a,repo:o=`inmemory`,force:s,dryRun:c}=e,l=e.pluralize!==!1,u=e.pattern??`ddd`;e.minimal&&(u=`minimal`);let d=k(t),p=D(t),m=l?A(d):d,h=l?j(p):p,g=r(n,m),_=[],v=s??!1,y={kebab:d,pascal:p,plural:m,pluralPascal:h,moduleDir:g,repo:o,noEntity:i??!1,noTests:a??!1,prismaClientPath:e.prismaClientPath??`@prisma/client`,tokenScope:e.tokenScope??`app`,style:e.style??`define`,write:async(e,t)=>{let n=r(g,e);if(c){_.push(n);return}if(!v&&await C(n)&&!await T({message:`File exists: ${f.dim(e)}. Overwrite?`,initialValue:!1})){E.warn(`Skipped: ${e}`);return}await b(n,t),_.push(n)},files:_};switch(u){case`minimal`:await Be(y);break;case`rest`:await Ve(y);break;case`cqrs`:await He(y);break;default:await Ue(y);break}return c||await q(n,p,m,d,y.style),_}async function q(e,t,n,i,a=`define`){let o=r(e,`index.ts`),s=await C(o),c=`./${n}/${i}.module`,d=a===`class`?`${t}Module`:`${t}Module()`;if(!s){await b(o,a===`class`?`import type { AppModuleEntry } from '@forinda/kickjs'
|
|
1582
|
+
import { ${t}Module } from '${c}'
|
|
1583
|
+
|
|
1584
|
+
export const modules: AppModuleEntry[] = [${d}]
|
|
1585
|
+
`:`import { defineModules } from '@forinda/kickjs'
|
|
1586
|
+
import { ${t}Module } from '${c}'
|
|
1587
|
+
|
|
1588
|
+
export const modules = defineModules().mount(${d})
|
|
1589
|
+
`);return}let f=await l(o,`utf-8`),p=`import { ${t}Module } from '${c}'`,m=M(c);if(!RegExp(`^import\\s*\\{[^}]*\\b${M(t)}Module\\b[^}]*\\}\\s*from\\s*['"]${m}['"]`,`m`).test(f)){let e=f.lastIndexOf(`import `);if(e!==-1){let t=f.indexOf(`
|
|
1590
|
+
`,e);f=f.slice(0,t+1)+p+`
|
|
1591
|
+
`+f.slice(t+1)}else f=p+`
|
|
1592
|
+
`+f}let h=Y(f);if(h){let e=f.slice(h.rhsStart,h.rhsEnd+1);RegExp(`\\b${M(t)}Module\\b`).test(e)||(f=J(f,d))}else f=J(f,d);await u(o,f,`utf-8`)}function J(e,t){let n=Y(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 Y(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=qe(e,n);return t===-1?null:{shape:`array`,rhsStart:n,rhsEnd:t}}if(e.slice(n,n+13)===`defineModules`){let t=Ke(e,n);return t===-1?null:{shape:`chain`,rhsStart:n,rhsEnd:t-1,chainEnd:t}}return null}function Ke(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=Z(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=Z(e,t);if(n===-1)break;i=n+1}return i}function X(e,t){let n=e.slice(t,t+2);if(n===`//`){for(t+=2;t<e.length&&e[t]!==`
|
|
1593
|
+
`;)t++;return t}if(n===`/*`){for(t+=2;t+1<e.length&&!(e[t]===`*`&&e[t+1]===`/`);)t++;return t+2}return t}function qe(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=X(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 Z(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=X(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 Je(e){let{name:t,outDir:n}=e,i=k(t),a=D(t),o=[],s=r(n,`${i}.adapter.ts`);return await b(s,`import {
|
|
1451
1594
|
defineAdapter,
|
|
1452
1595
|
type AdapterContext,
|
|
1453
1596
|
type AdapterMiddleware,
|
|
@@ -1616,7 +1759,7 @@ export const ${a}Adapter = defineAdapter<${a}AdapterConfig>({
|
|
|
1616
1759
|
}
|
|
1617
1760
|
},
|
|
1618
1761
|
})
|
|
1619
|
-
`),o.push(s),o}const
|
|
1762
|
+
`),o.push(s),o}const Ye={controller:`presentation`,service:`domain/services`,dto:`application/dtos`,guard:`presentation/guards`,middleware:`middleware`},Xe={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`},Ze={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,command:`commands`,query:`queries`,event:`events`};function Q(e){let{type:t,outDir:n,moduleName:a,modulesDir:o=`src/modules`,defaultDir:s,pattern:c=`ddd`,shouldPluralize:l=!0}=e;if(n)return i(n);if(a){let e=c===`ddd`?Ye:c===`cqrs`?Ze:Xe,n=k(a),s=l?A(n):n,u=e[t]??``,d=r(o,s);return i(u?r(d,u):d)}return i(s)}async function Qe(e){let{name:t,moduleName:n,modulesDir:i,pattern:a}=e,o=Q({type:`middleware`,outDir:e.outDir,moduleName:n,modulesDir:i,defaultDir:`src/middleware`,pattern:a,shouldPluralize:e.pluralize??!0}),s=k(t),c=O(t),l=[],u=r(o,`${s}.middleware.ts`);return await b(u,`import type { Request, Response, NextFunction } from 'express'
|
|
1620
1763
|
|
|
1621
1764
|
export interface ${D(t)}Options {
|
|
1622
1765
|
// Add configuration options here
|
|
@@ -1640,7 +1783,7 @@ export function ${c}(options: ${D(t)}Options = {}) {
|
|
|
1640
1783
|
next()
|
|
1641
1784
|
}
|
|
1642
1785
|
}
|
|
1643
|
-
`),l.push(u),l}async function
|
|
1786
|
+
`),l.push(u),l}async function $e(e){let{name:t,moduleName:n,modulesDir:i,pattern:a}=e,o=Q({type:`guard`,outDir:e.outDir,moduleName:n,modulesDir:i,defaultDir:`src/guards`,pattern:a,shouldPluralize:e.pluralize??!0}),s=k(t),c=O(t),l=D(t),u=[],d=r(o,`${s}.guard.ts`);return await b(d,`import { Container, HttpException } from '@forinda/kickjs'
|
|
1644
1787
|
import type { RequestContext } from '@forinda/kickjs'
|
|
1645
1788
|
|
|
1646
1789
|
/**
|
|
@@ -1676,7 +1819,7 @@ export async function ${c}Guard(ctx: RequestContext, next: () => void): Promise<
|
|
|
1676
1819
|
ctx.res.status(401).json({ message: 'Invalid or expired token' })
|
|
1677
1820
|
}
|
|
1678
1821
|
}
|
|
1679
|
-
`),u.push(d),u}async function
|
|
1822
|
+
`),u.push(d),u}async function et(e){let{name:t,moduleName:n,modulesDir:i,pattern:a}=e,o=Q({type:`service`,outDir:e.outDir,moduleName:n,modulesDir:i,defaultDir:`src/services`,pattern:a,shouldPluralize:e.pluralize??!0}),s=k(t),c=D(t),l=[],u=r(o,`${s}.service.ts`);return await b(u,`import { Service } from '@forinda/kickjs'
|
|
1680
1823
|
|
|
1681
1824
|
@Service()
|
|
1682
1825
|
export class ${c}Service {
|
|
@@ -1685,7 +1828,7 @@ export class ${c}Service {
|
|
|
1685
1828
|
// @Inject(MY_REPO) private readonly repo: IMyRepository,
|
|
1686
1829
|
// ) {}
|
|
1687
1830
|
}
|
|
1688
|
-
`),l.push(u),l}async function
|
|
1831
|
+
`),l.push(u),l}async function tt(e){let{name:t,moduleName:n,modulesDir:i,pattern:a}=e,o=Q({type:`controller`,outDir:e.outDir,moduleName:n,modulesDir:i,defaultDir:`src/controllers`,pattern:a,shouldPluralize:e.pluralize??!0}),s=k(t),c=D(t),l=[],u=r(o,`${s}.controller.ts`);return await b(u,`import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
|
|
1689
1832
|
|
|
1690
1833
|
// \`Ctx<KickRoutes.${c}Controller['<method>']>\` is generated by
|
|
1691
1834
|
// \`kick typegen\` (auto-run on \`kick dev\`). After the first run, your IDE
|
|
@@ -1706,7 +1849,7 @@ export class ${c}Controller {
|
|
|
1706
1849
|
ctx.created({ message: '${c} created', data: ctx.body })
|
|
1707
1850
|
}
|
|
1708
1851
|
}
|
|
1709
|
-
`),l.push(u),l}async function
|
|
1852
|
+
`),l.push(u),l}async function nt(e){let{name:t,moduleName:n,modulesDir:i,pattern:a}=e,o=Q({type:`dto`,outDir:e.outDir,moduleName:n,modulesDir:i,defaultDir:`src/dtos`,pattern:a,shouldPluralize:e.pluralize??!0}),s=k(t),c=D(t),l=O(t),u=[],d=r(o,`${s}.dto.ts`);return await b(d,`import { z } from 'zod'
|
|
1710
1853
|
|
|
1711
1854
|
export const ${l}Schema = z.object({
|
|
1712
1855
|
// Define your schema fields here
|
|
@@ -1714,7 +1857,7 @@ export const ${l}Schema = z.object({
|
|
|
1714
1857
|
})
|
|
1715
1858
|
|
|
1716
1859
|
export type ${c}DTO = z.infer<typeof ${l}Schema>
|
|
1717
|
-
`),u.push(d),u}const
|
|
1860
|
+
`),u.push(d),u}const rt={auth:`@forinda/kickjs-auth`,swagger:`@forinda/kickjs-swagger`,ws:`@forinda/kickjs-ws`,queue:`@forinda/kickjs-queue`,devtools:`@forinda/kickjs-devtools`};function $(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 it(e,t,n,r=[]){let i={"@forinda/kickjs":$(n,`@forinda/kickjs`),dotenv:`^17.3.1`,express:`^5.1.0`,"reflect-metadata":`^0.2.2`,zod:`^4.3.6`,pino:`^10.3.1`,"pino-pretty":`^13.1.3`};for(let e of r){let t=rt[e];t&&!i[t]&&(i[t]=$(n,t))}return JSON.stringify({name:e,version:`0.0.0`,type:`module`,scripts:{dev:`vite`,"dev:debug":`kick dev:debug`,build:`kick build`,start:`kick start`,test:`vitest run`,"test:watch":`vitest`,typecheck:`tsc --noEmit`,typegen:`kick typegen`,lint:`eslint src/`,format:`prettier --write src/`},dependencies:i,devDependencies:{"@forinda/kickjs-cli":$(n,`@forinda/kickjs-cli`),"@forinda/kickjs-vite":$(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 at(){return`import { defineConfig } from 'vite'
|
|
1718
1861
|
import { resolve } from 'node:path'
|
|
1719
1862
|
import swc from 'unplugin-swc'
|
|
1720
1863
|
import { kickjsVitePlugin, envWatchPlugin } from '@forinda/kickjs-vite'
|
|
@@ -1749,7 +1892,7 @@ export default defineConfig({
|
|
|
1749
1892
|
},
|
|
1750
1893
|
},
|
|
1751
1894
|
})
|
|
1752
|
-
`}function
|
|
1895
|
+
`}function ot(){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 st(){return JSON.stringify({semi:!1,singleQuote:!0,trailingComma:`all`,printWidth:100,tabWidth:2},null,2)}function ct(){return`# https://editorconfig.org
|
|
1753
1896
|
root = true
|
|
1754
1897
|
|
|
1755
1898
|
[*]
|
|
@@ -1762,14 +1905,14 @@ insert_final_newline = true
|
|
|
1762
1905
|
|
|
1763
1906
|
[*.md]
|
|
1764
1907
|
trim_trailing_whitespace = false
|
|
1765
|
-
`}function
|
|
1908
|
+
`}function lt(){return`node_modules/
|
|
1766
1909
|
dist/
|
|
1767
1910
|
.env
|
|
1768
1911
|
coverage/
|
|
1769
1912
|
.DS_Store
|
|
1770
1913
|
*.tsbuildinfo
|
|
1771
1914
|
.kickjs/
|
|
1772
|
-
`}function
|
|
1915
|
+
`}function ut(){return`# Auto-detect text files and normalise line endings to LF
|
|
1773
1916
|
* text=auto eol=lf
|
|
1774
1917
|
|
|
1775
1918
|
# Explicitly mark generated / binary files
|
|
@@ -1787,11 +1930,11 @@ coverage/
|
|
|
1787
1930
|
pnpm-lock.yaml -diff linguist-generated
|
|
1788
1931
|
yarn.lock -diff linguist-generated
|
|
1789
1932
|
package-lock.json -diff linguist-generated
|
|
1790
|
-
`}function
|
|
1933
|
+
`}function dt(){return`PORT=3000
|
|
1791
1934
|
NODE_ENV=development
|
|
1792
|
-
`}function
|
|
1935
|
+
`}function ft(){return`PORT=3000
|
|
1793
1936
|
NODE_ENV=development
|
|
1794
|
-
`}function
|
|
1937
|
+
`}function pt(){return`import { defineConfig } from 'vitest/config'
|
|
1795
1938
|
import swc from 'unplugin-swc'
|
|
1796
1939
|
|
|
1797
1940
|
export default defineConfig({
|
|
@@ -1802,7 +1945,7 @@ export default defineConfig({
|
|
|
1802
1945
|
include: ['src/**/*.test.ts'],
|
|
1803
1946
|
},
|
|
1804
1947
|
})
|
|
1805
|
-
`}function
|
|
1948
|
+
`}function mt(e,t,n){let r={rest:`REST API`,ddd:`Domain-Driven Design`,cqrs:`CQRS + Event-Driven`,minimal:`Minimal`},i=[`@forinda/kickjs`,`@forinda/kickjs-vite`];return t!==`minimal`&&i.push(`@forinda/kickjs-swagger`,`@forinda/kickjs-devtools`),t===`cqrs`&&i.push(`@forinda/kickjs-queue`,`@forinda/kickjs-ws`),`# ${e}
|
|
1806
1949
|
|
|
1807
1950
|
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.
|
|
1808
1951
|
|
|
@@ -1865,7 +2008,7 @@ Copy \`.env.example\` to \`.env\` and configure:
|
|
|
1865
2008
|
|
|
1866
2009
|
- [KickJS Documentation](https://forinda.github.io/kick-js/)
|
|
1867
2010
|
- [CLI Reference](https://forinda.github.io/kick-js/api/cli.html)
|
|
1868
|
-
`}function
|
|
2011
|
+
`}function ht(e,t,n){return`# CLAUDE.md — ${e}
|
|
1869
2012
|
|
|
1870
2013
|
**Read \`./AGENTS.md\` first.** It is the canonical, multi-agent
|
|
1871
2014
|
reference for this project (Claude, Copilot, Codex, Gemini, etc.) —
|
|
@@ -1931,7 +2074,7 @@ When generating or modifying code in this project, stay aligned with the v4 conv
|
|
|
1931
2074
|
- **Refresh these files**: \`kick g agents -f\` regenerates \`AGENTS.md\` + \`CLAUDE.md\` from the latest CLI templates. Hand-edited content is overwritten — keep customisation in \`AGENTS.local.md\`.
|
|
1932
2075
|
|
|
1933
2076
|
For everything else (controllers, services, modules, RequestContext API, generators, CLI commands, package additions, env wiring, troubleshooting) → \`AGENTS.md\`.
|
|
1934
|
-
`}function
|
|
2077
|
+
`}function gt(e,t,n){return`# AGENTS.md — AI Agent Guide for ${e}
|
|
1935
2078
|
|
|
1936
2079
|
This guide is the **canonical, multi-agent reference** for this KickJS
|
|
1937
2080
|
application — Claude, Copilot, Codex, Gemini, etc. all read it first.
|
|
@@ -2035,8 +2178,10 @@ mistakes:
|
|
|
2035
2178
|
inline registration in the entry file:
|
|
2036
2179
|
|
|
2037
2180
|
\`\`\`ts
|
|
2038
|
-
// src/modules/index.ts
|
|
2039
|
-
export const modules
|
|
2181
|
+
// src/modules/index.ts — fluent chain (default for \`modules.style: 'define'\`)
|
|
2182
|
+
export const modules = defineModules().mount(HelloModule()).mount(UsersModule())
|
|
2183
|
+
// OR with \`modules.style: 'class'\`:
|
|
2184
|
+
// export const modules: AppModuleEntry[] = [HelloModule, UsersModule]
|
|
2040
2185
|
|
|
2041
2186
|
// src/middleware/index.ts
|
|
2042
2187
|
export const middleware = [helmet(), cors(), requestId(), ...]
|
|
@@ -2100,7 +2245,7 @@ ${t===`ddd`?`\`\`\`
|
|
|
2100
2245
|
├── <name>.repository.ts # Data access (@Repository)
|
|
2101
2246
|
├── <name>.dto.ts # Request/response schemas (Zod)
|
|
2102
2247
|
├── <name>.entity.ts # Domain entity (optional)
|
|
2103
|
-
└── <name>.module.ts # Module definition (
|
|
2248
|
+
└── <name>.module.ts # Module definition (defineModule factory)
|
|
2104
2249
|
\`\`\`
|
|
2105
2250
|
`:t===`cqrs`?`\`\`\`
|
|
2106
2251
|
<name>/
|
|
@@ -2114,14 +2259,14 @@ ${t===`ddd`?`\`\`\`
|
|
|
2114
2259
|
│ └── <name>-created.event.ts
|
|
2115
2260
|
├── <name>.controller.ts # HTTP routes
|
|
2116
2261
|
├── <name>.repository.ts # Data access
|
|
2117
|
-
└── <name>.module.ts # Module definition (
|
|
2262
|
+
└── <name>.module.ts # Module definition (defineModule factory)
|
|
2118
2263
|
\`\`\`
|
|
2119
2264
|
`:t===`rest`?`\`\`\`
|
|
2120
2265
|
<name>/
|
|
2121
2266
|
├── <name>.controller.ts # HTTP routes (@Controller)
|
|
2122
2267
|
├── <name>.service.ts # Business logic (@Service)
|
|
2123
2268
|
├── <name>.dto.ts # Request/response schemas (Zod)
|
|
2124
|
-
└── <name>.module.ts # Module definition (
|
|
2269
|
+
└── <name>.module.ts # Module definition (defineModule factory)
|
|
2125
2270
|
\`\`\`
|
|
2126
2271
|
`:"```\nsrc/\n├── index.ts # Add routes here\n└── ... # Custom structure\n```\n"}
|
|
2127
2272
|
|
|
@@ -2152,8 +2297,8 @@ If not using generators:
|
|
|
2152
2297
|
- [ ] Create \`src/modules/<name>/<name>.controller.ts\`
|
|
2153
2298
|
- [ ] Add \`@Controller()\` decorator
|
|
2154
2299
|
- [ ] Add route handlers with \`@Get()\`, \`@Post()\`, etc.
|
|
2155
|
-
- [ ] Create module file
|
|
2156
|
-
- [ ] Register module in \`src/modules/index.ts\` (\`
|
|
2300
|
+
- [ ] Create module file with \`defineModule({ name, build: () => ({ routes() { return { path, controller } } }) })\` — the framework derives the Express router from the controller. Class-form (\`class XModule implements AppModule\`) is the legacy alternative; toggle via \`kick.config.ts > modules.style\`.
|
|
2301
|
+
- [ ] Register module in \`src/modules/index.ts\`. Default form is the fluent chain: \`defineModules().mount(MyModule()).mount(...)\`. \`kick g module <name>\` appends \`.mount(NewModule())\` automatically.
|
|
2157
2302
|
- [ ] Test with \`kick dev\`
|
|
2158
2303
|
|
|
2159
2304
|
### Manual Service
|
|
@@ -2355,7 +2500,9 @@ These work anywhere — scripts, plain files, outside \`@Service\`/\`@Controller
|
|
|
2355
2500
|
### Dependency Injection
|
|
2356
2501
|
| Decorator | Purpose |
|
|
2357
2502
|
|-----------|---------|
|
|
2358
|
-
| \`
|
|
2503
|
+
| \`defineModule({...})\` | Define feature module (factory; preferred — paired with \`defineModules()\` registry) |
|
|
2504
|
+
| \`defineModules()\` | Build the modules registry as a chainable list (\`.mount(X())\`) |
|
|
2505
|
+
| \`AppModule\` interface | Legacy module shape — \`class X implements AppModule\` (toggle via \`modules.style: 'class'\`) |
|
|
2359
2506
|
| \`@Service()\` | Register singleton service |
|
|
2360
2507
|
| \`@Repository()\` | Register repository |
|
|
2361
2508
|
| \`@Autowired()\` | Property injection |
|
|
@@ -2372,7 +2519,7 @@ is a value other code reads off \`ctx\`.
|
|
|
2372
2519
|
|---------|----------------|
|
|
2373
2520
|
| \`defineContextDecorator({ key, deps, dependsOn, optional, onError, resolve })\` | \`@forinda/kickjs\` |
|
|
2374
2521
|
| Method/class decorator | \`@LoadX\` on a controller method/class |
|
|
2375
|
-
| Module hook | \`
|
|
2522
|
+
| Module hook | \`build: () => ({ contributors() { return [...] } })\` (\`defineModule\`) — or \`AppModule.contributors?()\` for class form |
|
|
2376
2523
|
| Adapter hook | \`AppAdapter.contributors?(): ContributorRegistration[]\` |
|
|
2377
2524
|
| Global registration | \`bootstrap({ contributors: [LoadX.registration] })\` |
|
|
2378
2525
|
| Type augmentation | \`declare module '@forinda/kickjs' { interface ContextMeta { ... } }\` |
|
|
@@ -2434,7 +2581,7 @@ ${t===`cqrs`?`### Background Jobs
|
|
|
2434
2581
|
- [Decorators Guide](https://forinda.github.io/kick-js/guide/decorators.html)
|
|
2435
2582
|
- [DI System](https://forinda.github.io/kick-js/guide/dependency-injection.html)
|
|
2436
2583
|
- [Testing](https://forinda.github.io/kick-js/api/testing.html)
|
|
2437
|
-
`}function
|
|
2584
|
+
`}function _t(e,t,n){return`# kickjs-skills.md — Task Skills for AI Agents (${e})
|
|
2438
2585
|
|
|
2439
2586
|
This file is the agent-facing **skills index** for KickJS work in this
|
|
2440
2587
|
repo. Each block below is a short, rigid workflow keyed to a specific
|
|
@@ -2569,8 +2716,10 @@ description: Use when src/index.ts is accumulating module/middleware/plugin/adap
|
|
|
2569
2716
|
**Refactor target**:
|
|
2570
2717
|
|
|
2571
2718
|
\`\`\`ts
|
|
2572
|
-
// src/modules/index.ts
|
|
2573
|
-
export const modules
|
|
2719
|
+
// src/modules/index.ts — fluent chain (default for \`modules.style: 'define'\`)
|
|
2720
|
+
export const modules = defineModules().mount(HelloModule()).mount(UsersModule())
|
|
2721
|
+
// OR for class-form projects (\`modules.style: 'class'\`):
|
|
2722
|
+
// export const modules: AppModuleEntry[] = [HelloModule, UsersModule]
|
|
2574
2723
|
|
|
2575
2724
|
// src/middleware/index.ts
|
|
2576
2725
|
export const middleware = [helmet(), cors(), requestId(), ...]
|
|
@@ -2681,7 +2830,7 @@ description: Patterns to refuse outright when the user asks for them — they br
|
|
|
2681
2830
|
- [Decorators](https://forinda.github.io/kick-js/guide/decorators.html)
|
|
2682
2831
|
- [Context Decorators](https://forinda.github.io/kick-js/guide/context-decorators.html)
|
|
2683
2832
|
- [Testing](https://forinda.github.io/kick-js/api/testing.html)
|
|
2684
|
-
`}const
|
|
2685
|
-
Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${i} install failed. Run it manually.`)}}try{let{runTypegen:e}=await import(`./typegen-
|
|
2686
|
-
Project scaffolded successfully!`),console.log();let
|
|
2687
|
-
//# sourceMappingURL=generator-extension-
|
|
2833
|
+
`}const vt=t(g(import.meta.url)),yt=JSON.parse(o(r(vt,`..`,`package.json`),`utf-8`)),bt=`^${yt.version}`,xt=[`@forinda/kickjs`,`@forinda/kickjs-cli`,`@forinda/kickjs-vite`,`@forinda/kickjs-auth`,`@forinda/kickjs-swagger`,`@forinda/kickjs-ws`,`@forinda/kickjs-queue`,`@forinda/kickjs-devtools`,`@forinda/kickjs-testing`];async function St(){let e=await Promise.all(xt.map(async e=>{try{let t=m(`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,bt]}));return Object.fromEntries(e)}async function Ct(e){let{name:t,directory:n,packageManager:i=`pnpm`,template:a=`rest`,defaultRepo:o=`inmemory`,packages:s=[]}=e,c=n,l=e=>console.log(` ${e}`);console.log(`\n Creating KickJS project: ${t}\n`),l(`Resolving package versions...`);let u=await St();if(await b(r(c,`package.json`),it(t,a,u,s)),await b(r(c,`vite.config.ts`),at()),await b(r(c,`tsconfig.json`),ot()),await b(r(c,`.prettierrc`),st()),await b(r(c,`.editorconfig`),ct()),await b(r(c,`.gitignore`),lt()),await b(r(c,`.gitattributes`),ut()),await b(r(c,`.env`),dt()),await b(r(c,`.env.example`),ft()),await b(r(c,`src/config/index.ts`),Fe()),await b(r(c,`src/index.ts`),Ne(t,a,yt.version,s)),await b(r(c,`src/modules/index.ts`),Pe()),await b(r(c,`src/modules/hello/hello.service.ts`),Ie()),await b(r(c,`src/modules/hello/hello.controller.ts`),Le()),await b(r(c,`src/modules/hello/hello.module.ts`),Re()),await b(r(c,`kick.config.ts`),ze(a,o,i)),await b(r(c,`vitest.config.ts`),pt()),await b(r(c,`README.md`),mt(t,a,i)),await b(r(c,`CLAUDE.md`),ht(t,a,i)),await b(r(c,`AGENTS.md`),gt(t,a,i)),await b(r(c,`kickjs-skills.md`),_t(t,a,i)),e.installDeps){console.log(`\n Installing dependencies with ${i}...\n`);try{h(`${i} install`,{cwd:c,stdio:`inherit`}),console.log(`
|
|
2834
|
+
Dependencies installed successfully!`)}catch{console.log(`\n Warning: ${i} install failed. Run it manually.`)}}try{let{runTypegen:e}=await import(`./typegen-SyGEEyKv.mjs`).then(e=>e.n);await e({cwd:c,allowDuplicates:!0,silent:!0})}catch{}if(e.initGit)try{h(`git init`,{cwd:c,stdio:`pipe`}),h(`git branch -M main`,{cwd:c,stdio:`pipe`}),h(`git add -A`,{cwd:c,stdio:`pipe`}),h(`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(`
|
|
2835
|
+
Project scaffolded successfully!`),console.log();let d=c!==process.cwd();l(`Next steps:`),d&&l(` cd ${t}`),e.installDeps||l(` ${i} install`);let f={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(` ${f[a]??f.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`),a===`cqrs`&&l(` kick g job <name> Queue job processor`),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(``)}function wt(e){return e}function Tt(e){return k(e).replace(/-/g,`_`)}function Et(e){let t=e.cwd??process.cwd(),n=e.pluralize??!0,r=D(e.name),i=O(e.name),a=k(e.name),o=Tt(e.name),s={name:e.name,pascal:r,camel:i,kebab:a,snake:o,modulesDir:e.modulesDir??`src/modules`,cwd:t,args:e.args??[],flags:e.flags??{}};if(n){let e=A(a);s.pluralKebab=e,s.pluralPascal=D(e),s.pluralCamel=O(e)}return s}function Dt(e,t){return i(e.cwd,t)}async function Ot(e){return import(_(e).href)}const kt=new Map;async function At(e){let t=kt.get(e);if(t)return t;let n=jt(e);return kt.set(e,n),n}async function jt(n){let r=i(n,`package.json`);if(!a(r))return{generators:[],loaded:[],failed:[]};let o=Mt(JSON.parse(await l(r,`utf-8`))),s=e(i(n,`package.json`)),c=[],u=[],d=[];for(let e of o){let n;try{n=s.resolve(`${e}/package.json`)}catch{continue}let r;try{r=JSON.parse(await l(n,`utf-8`))}catch(t){d.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!r.kickjs?.generators)continue;let o=r.kickjs.generators,f=i(t(n),o);if(!a(f)){d.push({source:e,reason:`kickjs.generators points to missing file: ${o}`});continue}let p;try{p=await Ot(f)}catch(t){d.push({source:e,reason:`failed to import manifest: ${t}`});continue}let m=p.default;if(!Array.isArray(m)){d.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of m){if(!Nt(t)){d.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}c.push({source:e,spec:t})}u.push(e)}return{generators:c,loaded:u,failed:d}}function Mt(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 Nt(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function Pt(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return Lt(r.spec,r.source,e,n);let i=It(await At(n),e.generatorName);return i?Lt(i.spec,i.source,e,n):null}async function Ft(e,t=[]){let n=await At(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 It(e,t){return e.generators.find(e=>e.spec.name===t)}async function Lt(e,t,n,r){let i=Et({name:n.itemName,args:n.args,flags:n.flags,modulesDir:n.modulesDir,pluralize:n.pluralize,cwd:r}),a=await e.files(i),o=[];for(let e of a){let t=Dt(i,e.path);await b(t,e.content),o.push(t)}return{files:o,source:t}}export{ue as A,k as C,E as D,se as E,oe as F,C as I,y as L,le as M,ae as N,de as O,f as P,b as R,O as S,T,Ge as _,Ct as a,A as b,_t as c,et as d,$e as f,Y as g,q as h,wt as i,fe as j,ce as k,nt as l,Je as m,Pt as n,gt as o,Qe as p,Et as r,ht as s,Ft as t,tt as u,We as v,D as w,j as x,M as y};
|
|
2836
|
+
//# sourceMappingURL=generator-extension-CsT2e6Fj.mjs.map
|