@forinda/kickjs-cli 1.2.11 → 1.2.12

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/cli.js CHANGED
@@ -1,116 +1,7 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
4
-
5
- // src/cli.ts
6
- import { Command } from "commander";
7
- import { readFileSync as readFileSync2 } from "fs";
8
- import { dirname as dirname3, join as join18 } from "path";
9
- import { fileURLToPath as fileURLToPath2 } from "url";
10
-
11
- // src/commands/init.ts
12
- import { resolve, basename } from "path";
13
- import { createInterface } from "readline";
14
- import { existsSync, readdirSync, rmSync } from "fs";
15
-
16
- // src/generators/project.ts
17
- import { join, dirname as dirname2 } from "path";
18
- import { execSync } from "child_process";
19
- import { readFileSync } from "fs";
20
- import { fileURLToPath } from "url";
21
-
22
- // src/utils/fs.ts
23
- import { writeFile, mkdir, access, readFile } from "fs/promises";
24
- import { dirname } from "path";
25
- var _dryRun = false;
26
- function setDryRun(enabled) {
27
- _dryRun = enabled;
28
- }
29
- __name(setDryRun, "setDryRun");
30
- async function writeFileSafe(filePath, content) {
31
- if (_dryRun) return;
32
- await mkdir(dirname(filePath), {
33
- recursive: true
34
- });
35
- await writeFile(filePath, content, "utf-8");
36
- }
37
- __name(writeFileSafe, "writeFileSafe");
38
- async function fileExists(filePath) {
39
- try {
40
- await access(filePath);
41
- return true;
42
- } catch {
43
- return false;
44
- }
45
- }
46
- __name(fileExists, "fileExists");
47
-
48
- // src/generators/project.ts
49
- var __dirname = dirname2(fileURLToPath(import.meta.url));
50
- var cliPkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
51
- var KICKJS_VERSION = `^${cliPkg.version}`;
52
- async function initProject(options) {
53
- const { name, directory, packageManager = "pnpm", template = "rest" } = options;
54
- const dir = directory;
55
- console.log(`
56
- Creating KickJS project: ${name}
57
- `);
58
- const baseDeps = {
59
- "@forinda/kickjs-core": KICKJS_VERSION,
60
- "@forinda/kickjs-http": KICKJS_VERSION,
61
- "@forinda/kickjs-config": KICKJS_VERSION,
62
- express: "^5.1.0",
63
- "reflect-metadata": "^0.2.2",
64
- zod: "^4.3.6",
65
- pino: "^10.3.1",
66
- "pino-pretty": "^13.1.3"
67
- };
68
- if (template !== "minimal") {
69
- baseDeps["@forinda/kickjs-swagger"] = KICKJS_VERSION;
70
- baseDeps["@forinda/kickjs-devtools"] = KICKJS_VERSION;
71
- }
72
- if (template === "graphql") {
73
- baseDeps["@forinda/kickjs-graphql"] = KICKJS_VERSION;
74
- baseDeps["graphql"] = "^16.11.0";
75
- }
76
- if (template === "cqrs") {
77
- baseDeps["@forinda/kickjs-queue"] = KICKJS_VERSION;
78
- baseDeps["@forinda/kickjs-ws"] = KICKJS_VERSION;
79
- baseDeps["@forinda/kickjs-otel"] = KICKJS_VERSION;
80
- }
81
- if (template === "ddd") {
82
- baseDeps["@forinda/kickjs-swagger"] = KICKJS_VERSION;
83
- }
84
- await writeFileSafe(join(dir, "package.json"), JSON.stringify({
85
- name,
86
- version: cliPkg.version,
87
- type: "module",
88
- scripts: {
89
- dev: "kick dev",
90
- "dev:debug": "kick dev:debug",
91
- build: "kick build",
92
- start: "kick start",
93
- test: "vitest run",
94
- "test:watch": "vitest",
95
- typecheck: "tsc --noEmit",
96
- lint: "eslint src/",
97
- format: "prettier --write src/"
98
- },
99
- dependencies: baseDeps,
100
- devDependencies: {
101
- "@forinda/kickjs-cli": KICKJS_VERSION,
102
- "@swc/core": "^1.7.28",
103
- "@types/express": "^5.0.6",
104
- "@types/node": "^24.5.2",
105
- "unplugin-swc": "^1.5.9",
106
- vite: "^7.3.1",
107
- "vite-node": "^5.3.0",
108
- vitest: "^3.2.4",
109
- typescript: "^5.9.2",
110
- prettier: "^3.8.1"
111
- }
112
- }, null, 2));
113
- await writeFileSafe(join(dir, "vite.config.ts"), `import { defineConfig } from 'vite'
2
+ var $t=Object.defineProperty;var s=(e,t)=>$t(e,"name",{value:t,configurable:!0});import{Command as No}from"commander";import{readFileSync as Yo}from"fs";import{dirname as Bo,join as Ho}from"path";import{fileURLToPath as Ko}from"url";import{resolve as te,basename as Ot}from"path";import{createInterface as It}from"readline";import{existsSync as St,readdirSync as Tt,rmSync as jt}from"fs";import{join as h,dirname as vt}from"path";import{execSync as V}from"child_process";import{readFileSync as Ct}from"fs";import{fileURLToPath as xt}from"url";import{writeFile as gt,mkdir as yt,access as ht,readFile as er}from"fs/promises";import{dirname as wt}from"path";var Se=!1;function v(e){Se=e}s(v,"setDryRun");async function l(e,t){Se||(await yt(wt(e),{recursive:!0}),await gt(e,t,"utf-8"))}s(l,"writeFileSafe");async function _(e){try{return await ht(e),!0}catch{return!1}}s(_,"fileExists");var kt=vt(xt(import.meta.url)),Z=JSON.parse(Ct(h(kt,"..","package.json"),"utf-8")),D=`^${Z.version}`;async function Te(e){let{name:t,directory:o,packageManager:r="pnpm",template:n="rest"}=e,i=o;console.log(`
3
+ Creating KickJS project: ${t}
4
+ `);let d={"@forinda/kickjs-core":D,"@forinda/kickjs-http":D,"@forinda/kickjs-config":D,express:"^5.1.0","reflect-metadata":"^0.2.2",zod:"^4.3.6",pino:"^10.3.1","pino-pretty":"^13.1.3"};if(n!=="minimal"&&(d["@forinda/kickjs-swagger"]=D,d["@forinda/kickjs-devtools"]=D),n==="graphql"&&(d["@forinda/kickjs-graphql"]=D,d.graphql="^16.11.0"),n==="cqrs"&&(d["@forinda/kickjs-queue"]=D,d["@forinda/kickjs-ws"]=D,d["@forinda/kickjs-otel"]=D),n==="ddd"&&(d["@forinda/kickjs-swagger"]=D),await l(h(i,"package.json"),JSON.stringify({name:t,version:Z.version,type:"module",scripts:{dev:"kick dev","dev:debug":"kick dev:debug",build:"kick build",start:"kick start",test:"vitest run","test:watch":"vitest",typecheck:"tsc --noEmit",lint:"eslint src/",format:"prettier --write src/"},dependencies:d,devDependencies:{"@forinda/kickjs-cli":D,"@swc/core":"^1.7.28","@types/express":"^5.0.6","@types/node":"^24.5.2","unplugin-swc":"^1.5.9",vite:"^7.3.1","vite-node":"^5.3.0",vitest:"^3.2.4",typescript:"^5.9.2",prettier:"^3.8.1"}},null,2)),await l(h(i,"vite.config.ts"),`import { defineConfig } from 'vite'
114
5
  import { resolve } from 'path'
115
6
  import swc from 'unplugin-swc'
116
7
 
@@ -136,46 +27,7 @@ export default defineConfig({
136
27
  },
137
28
  },
138
29
  })
139
- `);
140
- await writeFileSafe(join(dir, "tsconfig.json"), JSON.stringify({
141
- compilerOptions: {
142
- target: "ES2022",
143
- module: "ESNext",
144
- moduleResolution: "bundler",
145
- lib: [
146
- "ES2022"
147
- ],
148
- types: [
149
- "node",
150
- "vite/client"
151
- ],
152
- strict: true,
153
- esModuleInterop: true,
154
- skipLibCheck: true,
155
- sourceMap: true,
156
- declaration: true,
157
- experimentalDecorators: true,
158
- emitDecoratorMetadata: true,
159
- outDir: "dist",
160
- rootDir: "src",
161
- paths: {
162
- "@/*": [
163
- "./src/*"
164
- ]
165
- }
166
- },
167
- include: [
168
- "src"
169
- ]
170
- }, null, 2));
171
- await writeFileSafe(join(dir, ".prettierrc"), JSON.stringify({
172
- semi: false,
173
- singleQuote: true,
174
- trailingComma: "all",
175
- printWidth: 100,
176
- tabWidth: 2
177
- }, null, 2));
178
- await writeFileSafe(join(dir, ".editorconfig"), `# https://editorconfig.org
30
+ `),await l(h(i,"tsconfig.json"),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",rootDir:"src",paths:{"@/*":["./src/*"]}},include:["src"]},null,2)),await l(h(i,".prettierrc"),JSON.stringify({semi:!1,singleQuote:!0,trailingComma:"all",printWidth:100,tabWidth:2},null,2)),await l(h(i,".editorconfig"),`# https://editorconfig.org
179
31
  root = true
180
32
 
181
33
  [*]
@@ -188,15 +40,13 @@ insert_final_newline = true
188
40
 
189
41
  [*.md]
190
42
  trim_trailing_whitespace = false
191
- `);
192
- await writeFileSafe(join(dir, ".gitignore"), `node_modules/
43
+ `),await l(h(i,".gitignore"),`node_modules/
193
44
  dist/
194
45
  .env
195
46
  coverage/
196
47
  .DS_Store
197
48
  *.tsbuildinfo
198
- `);
199
- await writeFileSafe(join(dir, ".gitattributes"), `# Auto-detect text files and normalise line endings to LF
49
+ `),await l(h(i,".gitattributes"),`# Auto-detect text files and normalise line endings to LF
200
50
  * text=auto eol=lf
201
51
 
202
52
  # Explicitly mark generated / binary files
@@ -214,25 +64,17 @@ coverage/
214
64
  pnpm-lock.yaml -diff linguist-generated
215
65
  yarn.lock -diff linguist-generated
216
66
  package-lock.json -diff linguist-generated
217
- `);
218
- await writeFileSafe(join(dir, ".env"), `PORT=3000
67
+ `),await l(h(i,".env"),`PORT=3000
219
68
  NODE_ENV=development
220
- `);
221
- await writeFileSafe(join(dir, ".env.example"), `PORT=3000
69
+ `),await l(h(i,".env.example"),`PORT=3000
222
70
  NODE_ENV=development
223
- `);
224
- await writeFileSafe(join(dir, "src/index.ts"), getEntryFile(name, template));
225
- await writeFileSafe(join(dir, "src/modules/index.ts"), `import type { AppModuleClass } from '@forinda/kickjs-core'
71
+ `),await l(h(i,"src/index.ts"),Rt(t,n)),await l(h(i,"src/modules/index.ts"),`import type { AppModuleClass } from '@forinda/kickjs-core'
226
72
 
227
73
  export const modules: AppModuleClass[] = []
228
- `);
229
- if (template === "graphql") {
230
- await writeFileSafe(join(dir, "src/resolvers/.gitkeep"), "");
231
- }
232
- await writeFileSafe(join(dir, "kick.config.ts"), `import { defineConfig } from '@forinda/kickjs-cli'
74
+ `),n==="graphql"&&await l(h(i,"src/resolvers/.gitkeep"),""),await l(h(i,"kick.config.ts"),`import { defineConfig } from '@forinda/kickjs-cli'
233
75
 
234
76
  export default defineConfig({
235
- pattern: '${template}',
77
+ pattern: '${n}',
236
78
  modulesDir: 'src/modules',
237
79
  defaultRepo: 'inmemory',
238
80
 
@@ -260,8 +102,7 @@ export default defineConfig({
260
102
  },
261
103
  ],
262
104
  })
263
- `);
264
- await writeFileSafe(join(dir, "vitest.config.ts"), `import { defineConfig } from 'vitest/config'
105
+ `),await l(h(i,"vitest.config.ts"),`import { defineConfig } from 'vitest/config'
265
106
  import swc from 'unplugin-swc'
266
107
 
267
108
  export default defineConfig({
@@ -272,89 +113,12 @@ export default defineConfig({
272
113
  include: ['src/**/*.test.ts'],
273
114
  },
274
115
  })
275
- `);
276
- await writeFileSafe(join(dir, "README.md"), generateReadme(name, template, packageManager));
277
- if (options.initGit) {
278
- try {
279
- execSync("git init", {
280
- cwd: dir,
281
- stdio: "pipe"
282
- });
283
- execSync("git add -A", {
284
- cwd: dir,
285
- stdio: "pipe"
286
- });
287
- execSync('git commit -m "chore: initial commit from kick new"', {
288
- cwd: dir,
289
- stdio: "pipe"
290
- });
291
- console.log(" Git repository initialized");
292
- } catch {
293
- console.log(" Warning: git init failed (git may not be installed)");
294
- }
295
- }
296
- if (options.installDeps) {
297
- console.log(`
298
- Installing dependencies with ${packageManager}...
299
- `);
300
- try {
301
- execSync(`${packageManager} install`, {
302
- cwd: dir,
303
- stdio: "inherit"
304
- });
305
- console.log("\n Dependencies installed successfully!");
306
- } catch {
307
- console.log(`
308
- Warning: ${packageManager} install failed. Run it manually.`);
309
- }
310
- }
311
- console.log("\n Project scaffolded successfully!");
312
- console.log();
313
- const needsCd = dir !== process.cwd();
314
- console.log(" Next steps:");
315
- if (needsCd) console.log(` cd ${name}`);
316
- if (!options.installDeps) console.log(` ${packageManager} install`);
317
- const genHint = {
318
- rest: "kick g module user",
319
- graphql: "kick g resolver user",
320
- ddd: "kick g module user --repo drizzle",
321
- cqrs: "kick g module user --pattern cqrs",
322
- minimal: "# add your routes to src/index.ts"
323
- };
324
- console.log(` ${genHint[template] ?? genHint.rest}`);
325
- console.log(" kick dev");
326
- console.log();
327
- console.log(" Commands:");
328
- console.log(" kick dev Start dev server with Vite HMR");
329
- console.log(" kick build Production build via Vite");
330
- console.log(" kick start Run production build");
331
- console.log();
332
- console.log(" Generators:");
333
- console.log(" kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)");
334
- console.log(" kick g scaffold <n> <f..> CRUD module from field definitions");
335
- console.log(" kick g controller <name> Standalone controller");
336
- console.log(" kick g service <name> @Service() class");
337
- console.log(" kick g middleware <name> Express middleware");
338
- console.log(" kick g guard <name> Route guard (auth, roles, etc.)");
339
- console.log(" kick g adapter <name> AppAdapter with lifecycle hooks");
340
- console.log(" kick g dto <name> Zod DTO schema");
341
- if (template === "graphql") console.log(" kick g resolver <name> GraphQL resolver");
342
- if (template === "cqrs") console.log(" kick g job <name> Queue job processor");
343
- console.log(" kick g config Generate kick.config.ts");
344
- console.log();
345
- console.log(" Add packages:");
346
- console.log(" kick add <pkg> Install a KickJS package + peers");
347
- console.log(" kick add --list Show all available packages");
348
- console.log();
349
- console.log(" Available: auth, swagger, graphql, drizzle, prisma, ws,");
350
- console.log(" cron, queue, mailer, otel, multi-tenant, notifications, testing");
351
- console.log();
352
- }
353
- __name(initProject, "initProject");
354
- function getEntryFile(name, template) {
355
- switch (template) {
356
- case "graphql":
357
- return `import 'reflect-metadata'
116
+ `),await l(h(i,"README.md"),Dt(t,n,r)),e.initGit)try{V("git init",{cwd:i,stdio:"pipe"}),V("git add -A",{cwd:i,stdio:"pipe"}),V('git commit -m "chore: initial commit from kick new"',{cwd:i,stdio:"pipe"}),console.log(" Git repository initialized")}catch{console.log(" Warning: git init failed (git may not be installed)")}if(e.installDeps){console.log(`
117
+ Installing dependencies with ${r}...
118
+ `);try{V(`${r} install`,{cwd:i,stdio:"inherit"}),console.log(`
119
+ Dependencies installed successfully!`)}catch{console.log(`
120
+ Warning: ${r} install failed. Run it manually.`)}}console.log(`
121
+ Project scaffolded successfully!`),console.log();let c=i!==process.cwd();console.log(" Next steps:"),c&&console.log(` cd ${t}`),e.installDeps||console.log(` ${r} install`);let a={rest:"kick g module user",graphql:"kick g resolver user",ddd:"kick g module user --repo drizzle",cqrs:"kick g module user --pattern cqrs",minimal:"# add your routes to src/index.ts"};console.log(` ${a[n]??a.rest}`),console.log(" kick dev"),console.log(),console.log(" Commands:"),console.log(" kick dev Start dev server with Vite HMR"),console.log(" kick build Production build via Vite"),console.log(" kick start Run production build"),console.log(),console.log(" Generators:"),console.log(" kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)"),console.log(" kick g scaffold <n> <f..> CRUD module from field definitions"),console.log(" kick g controller <name> Standalone controller"),console.log(" kick g service <name> @Service() class"),console.log(" kick g middleware <name> Express middleware"),console.log(" kick g guard <name> Route guard (auth, roles, etc.)"),console.log(" kick g adapter <name> AppAdapter with lifecycle hooks"),console.log(" kick g dto <name> Zod DTO schema"),n==="graphql"&&console.log(" kick g resolver <name> GraphQL resolver"),n==="cqrs"&&console.log(" kick g job <name> Queue job processor"),console.log(" kick g config Generate kick.config.ts"),console.log(),console.log(" Add packages:"),console.log(" kick add <pkg> Install a KickJS package + peers"),console.log(" kick add --list Show all available packages"),console.log(),console.log(" Available: auth, swagger, graphql, drizzle, prisma, ws,"),console.log(" cron, queue, mailer, otel, multi-tenant, notifications, testing"),console.log()}s(Te,"initProject");function Rt(e,t){switch(t){case"graphql":return`import 'reflect-metadata'
358
122
  import { bootstrap } from '@forinda/kickjs-http'
359
123
  import { DevToolsAdapter } from '@forinda/kickjs-devtools'
360
124
  import { GraphQLAdapter } from '@forinda/kickjs-graphql'
@@ -374,9 +138,7 @@ bootstrap({
374
138
  }),
375
139
  ],
376
140
  })
377
- `;
378
- case "cqrs":
379
- return `import 'reflect-metadata'
141
+ `;case"cqrs":return`import 'reflect-metadata'
380
142
  import { bootstrap } from '@forinda/kickjs-http'
381
143
  import { DevToolsAdapter } from '@forinda/kickjs-devtools'
382
144
  import { SwaggerAdapter } from '@forinda/kickjs-swagger'
@@ -388,10 +150,10 @@ import { modules } from './modules'
388
150
  bootstrap({
389
151
  modules,
390
152
  adapters: [
391
- new OtelAdapter({ serviceName: '${name}' }),
153
+ new OtelAdapter({ serviceName: '${e}' }),
392
154
  new DevToolsAdapter(),
393
155
  new SwaggerAdapter({
394
- info: { title: '${name}', version: '${cliPkg.version}' },
156
+ info: { title: '${e}', version: '${Z.version}' },
395
157
  }),
396
158
  // Uncomment for WebSocket support:
397
159
  // new WsAdapter(),
@@ -401,18 +163,12 @@ bootstrap({
401
163
  // }),
402
164
  ],
403
165
  })
404
- `;
405
- case "minimal":
406
- return `import 'reflect-metadata'
166
+ `;case"minimal":return`import 'reflect-metadata'
407
167
  import { bootstrap } from '@forinda/kickjs-http'
408
168
  import { modules } from './modules'
409
169
 
410
170
  bootstrap({ modules })
411
- `;
412
- case "ddd":
413
- case "rest":
414
- default:
415
- return `import 'reflect-metadata'
171
+ `;default:return`import 'reflect-metadata'
416
172
  import { bootstrap } from '@forinda/kickjs-http'
417
173
  import { DevToolsAdapter } from '@forinda/kickjs-devtools'
418
174
  import { SwaggerAdapter } from '@forinda/kickjs-swagger'
@@ -423,42 +179,18 @@ bootstrap({
423
179
  adapters: [
424
180
  new DevToolsAdapter(),
425
181
  new SwaggerAdapter({
426
- info: { title: '${name}', version: '${cliPkg.version}' },
182
+ info: { title: '${e}', version: '${Z.version}' },
427
183
  }),
428
184
  ],
429
185
  })
430
- `;
431
- }
432
- }
433
- __name(getEntryFile, "getEntryFile");
434
- function generateReadme(name, template, pm) {
435
- const templateLabels = {
436
- rest: "REST API",
437
- graphql: "GraphQL API",
438
- ddd: "Domain-Driven Design",
439
- cqrs: "CQRS + Event-Driven",
440
- minimal: "Minimal"
441
- };
442
- const packages = [
443
- "@forinda/kickjs-core",
444
- "@forinda/kickjs-http",
445
- "@forinda/kickjs-config"
446
- ];
447
- if (template !== "minimal") {
448
- packages.push("@forinda/kickjs-swagger", "@forinda/kickjs-devtools");
449
- }
450
- if (template === "graphql") packages.push("@forinda/kickjs-graphql");
451
- if (template === "cqrs") {
452
- packages.push("@forinda/kickjs-queue", "@forinda/kickjs-ws", "@forinda/kickjs-otel");
453
- }
454
- return `# ${name}
186
+ `}}s(Rt,"getEntryFile");function Dt(e,t,o){let r={rest:"REST API",graphql:"GraphQL API",ddd:"Domain-Driven Design",cqrs:"CQRS + Event-Driven",minimal:"Minimal"},n=["@forinda/kickjs-core","@forinda/kickjs-http","@forinda/kickjs-config"];return t!=="minimal"&&n.push("@forinda/kickjs-swagger","@forinda/kickjs-devtools"),t==="graphql"&&n.push("@forinda/kickjs-graphql"),t==="cqrs"&&n.push("@forinda/kickjs-queue","@forinda/kickjs-ws","@forinda/kickjs-otel"),`# ${e}
455
187
 
456
- A **${templateLabels[template] ?? "REST API"}** built with [KickJS](https://forinda.github.io/kick-js/) \u2014 a decorator-driven Node.js framework on Express 5 and TypeScript.
188
+ A **${r[t]??"REST API"}** built with [KickJS](https://forinda.github.io/kick-js/) \u2014 a decorator-driven Node.js framework on Express 5 and TypeScript.
457
189
 
458
190
  ## Getting Started
459
191
 
460
192
  \`\`\`bash
461
- ${pm} install
193
+ ${o} install
462
194
  kick dev
463
195
  \`\`\`
464
196
 
@@ -469,7 +201,7 @@ kick dev
469
201
  | \`kick dev\` | Start dev server with Vite HMR |
470
202
  | \`kick build\` | Production build |
471
203
  | \`kick start\` | Run production build |
472
- | \`${pm} run test\` | Run tests with Vitest |
204
+ | \`${o} run test\` | Run tests with Vitest |
473
205
  | \`kick g module <name>\` | Generate a DDD module |
474
206
  | \`kick g scaffold <name> <fields...>\` | Generate CRUD from field definitions |
475
207
  | \`kick add <package>\` | Add a KickJS package |
@@ -486,7 +218,8 @@ src/
486
218
 
487
219
  ## Packages
488
220
 
489
- ${packages.map((p) => `- \`${p}\``).join("\n")}
221
+ ${n.map(i=>`- \`${i}\``).join(`
222
+ `)}
490
223
 
491
224
  ## Adding Features
492
225
 
@@ -513,199 +246,10 @@ Copy \`.env.example\` to \`.env\` and configure:
513
246
 
514
247
  - [KickJS Documentation](https://forinda.github.io/kick-js/)
515
248
  - [CLI Reference](https://forinda.github.io/kick-js/api/cli.html)
516
- `;
517
- }
518
- __name(generateReadme, "generateReadme");
519
-
520
- // src/commands/init.ts
521
- function ask(question, defaultValue) {
522
- const rl = createInterface({
523
- input: process.stdin,
524
- output: process.stdout
525
- });
526
- const suffix = defaultValue ? ` (${defaultValue})` : "";
527
- return new Promise((res) => {
528
- rl.question(` ${question}${suffix}: `, (answer) => {
529
- rl.close();
530
- res(answer.trim() || defaultValue || "");
531
- });
532
- });
533
- }
534
- __name(ask, "ask");
535
- async function choose(question, options, defaultIdx = 0) {
536
- console.log(` ${question}`);
537
- for (let i = 0; i < options.length; i++) {
538
- const marker = i === defaultIdx ? ">" : " ";
539
- console.log(` ${marker} ${i + 1}. ${options[i]}`);
540
- }
541
- const answer = await ask("Choose", String(defaultIdx + 1));
542
- const idx = parseInt(answer, 10) - 1;
543
- return options[idx] ?? options[defaultIdx];
544
- }
545
- __name(choose, "choose");
546
- async function confirm(question, defaultYes = true) {
547
- const hint = defaultYes ? "Y/n" : "y/N";
548
- const answer = await ask(`${question} (${hint})`);
549
- if (!answer) return defaultYes;
550
- return answer.toLowerCase().startsWith("y");
551
- }
552
- __name(confirm, "confirm");
553
- function registerInitCommand(program) {
554
- program.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").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 | graphql | ddd | cqrs | minimal").action(async (name, opts) => {
555
- console.log();
556
- if (!name) {
557
- name = await ask("Project name", "my-api");
558
- }
559
- let directory;
560
- if (name === ".") {
561
- directory = resolve(".");
562
- name = basename(directory);
563
- } else {
564
- directory = resolve(opts.directory || name);
565
- }
566
- if (existsSync(directory)) {
567
- const entries = readdirSync(directory);
568
- if (entries.length > 0) {
569
- if (opts.force) {
570
- console.log(` Clearing existing files in ${directory}...
571
- `);
572
- } else {
573
- console.log(` Directory "${name}" is not empty:`);
574
- const shown = entries.slice(0, 5);
575
- for (const entry of shown) {
576
- console.log(` - ${entry}`);
577
- }
578
- if (entries.length > 5) {
579
- console.log(` ... and ${entries.length - 5} more`);
580
- }
581
- console.log();
582
- const shouldClear = await confirm("Remove all existing files and proceed?", false);
583
- if (!shouldClear) {
584
- console.log(" Aborted.\n");
585
- return;
586
- }
587
- }
588
- for (const entry of entries) {
589
- rmSync(resolve(directory, entry), {
590
- recursive: true,
591
- force: true
592
- });
593
- }
594
- }
595
- }
596
- let template = opts.template;
597
- if (!template) {
598
- template = await choose("Project template:", [
599
- "REST API (Express + Swagger)",
600
- "GraphQL API (GraphQL + GraphiQL)",
601
- "DDD (Domain-Driven Design modules)",
602
- "CQRS (Commands, Queries, Events + WS/Queue)",
603
- "Minimal (bare Express)"
604
- ], 0);
605
- const templateMap = {
606
- "REST API (Express + Swagger)": "rest",
607
- "GraphQL API (GraphQL + GraphiQL)": "graphql",
608
- "DDD (Domain-Driven Design modules)": "ddd",
609
- "CQRS (Commands, Queries, Events + WS/Queue)": "cqrs",
610
- "Minimal (bare Express)": "minimal"
611
- };
612
- template = templateMap[template] ?? "rest";
613
- }
614
- let packageManager = opts.pm;
615
- if (!packageManager) {
616
- packageManager = await choose("Package manager:", [
617
- "pnpm",
618
- "npm",
619
- "yarn"
620
- ], 0);
621
- }
622
- let initGit;
623
- if (opts.git === void 0) {
624
- initGit = await confirm("Initialize git repository?", true);
625
- } else {
626
- initGit = opts.git;
627
- }
628
- let installDeps;
629
- if (opts.install === void 0) {
630
- installDeps = await confirm("Install dependencies?", true);
631
- } else {
632
- installDeps = opts.install;
633
- }
634
- await initProject({
635
- name,
636
- directory,
637
- packageManager,
638
- initGit,
639
- installDeps,
640
- template
641
- });
642
- });
643
- }
644
- __name(registerInitCommand, "registerInitCommand");
645
-
646
- // src/commands/generate.ts
647
- import { resolve as resolve4 } from "path";
648
-
649
- // src/generators/module.ts
650
- import { join as join2 } from "path";
651
- import { createInterface as createInterface2 } from "readline";
652
-
653
- // src/utils/naming.ts
654
- function toPascalCase(name) {
655
- return name.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (c) => c.toUpperCase());
656
- }
657
- __name(toPascalCase, "toPascalCase");
658
- function toCamelCase(name) {
659
- const pascal = toPascalCase(name);
660
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
661
- }
662
- __name(toCamelCase, "toCamelCase");
663
- function toKebabCase(name) {
664
- return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
665
- }
666
- __name(toKebabCase, "toKebabCase");
667
- function pluralize(name) {
668
- if (name.endsWith("s")) return name;
669
- if (name.endsWith("x") || name.endsWith("z")) return name + "es";
670
- if (name.endsWith("sh") || name.endsWith("ch")) return name + "es";
671
- if (name.endsWith("y") && !/[aeiou]y$/.test(name)) return name.slice(0, -1) + "ies";
672
- return name + "s";
673
- }
674
- __name(pluralize, "pluralize");
675
- function pluralizePascal(name) {
676
- if (name.endsWith("s")) return name;
677
- if (name.endsWith("x") || name.endsWith("z")) return name + "es";
678
- if (name.endsWith("sh") || name.endsWith("ch")) return name + "es";
679
- if (name.endsWith("y") && !/[aeiou]y$/i.test(name)) return name.slice(0, -1) + "ies";
680
- return name + "s";
681
- }
682
- __name(pluralizePascal, "pluralizePascal");
683
-
684
- // src/generators/module.ts
685
- import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
686
-
687
- // src/generators/templates/module-index.ts
688
- function repoMaps(pascal, kebab, repo) {
689
- const repoClassMap = {
690
- inmemory: `InMemory${pascal}Repository`,
691
- drizzle: `Drizzle${pascal}Repository`,
692
- prisma: `Prisma${pascal}Repository`
693
- };
694
- const repoFileMap = {
695
- inmemory: `in-memory-${kebab}`,
696
- drizzle: `drizzle-${kebab}`,
697
- prisma: `prisma-${kebab}`
698
- };
699
- return {
700
- repoClass: repoClassMap[repo] ?? repoClassMap.inmemory,
701
- repoFile: repoFileMap[repo] ?? repoFileMap.inmemory
702
- };
703
- }
704
- __name(repoMaps, "repoMaps");
705
- function generateModuleIndex(pascal, kebab, plural, repo) {
706
- const { repoClass, repoFile } = repoMaps(pascal, kebab, repo);
707
- return `/**
708
- * ${pascal} Module
249
+ `}s(Dt,"generateReadme");function re(e,t){let o=It({input:process.stdin,output:process.stdout}),r=t?` (${t})`:"";return new Promise(n=>{o.question(` ${e}${r}: `,i=>{o.close(),n(i.trim()||t||"")})})}s(re,"ask");async function je(e,t,o=0){console.log(` ${e}`);for(let i=0;i<t.length;i++)console.log(` ${i===o?">":" "} ${i+1}. ${t[i]}`);let r=await re("Choose",String(o+1)),n=parseInt(r,10)-1;return t[n]??t[o]}s(je,"choose");async function oe(e,t=!0){let r=await re(`${e} (${t?"Y/n":"y/N"})`);return r?r.toLowerCase().startsWith("y"):t}s(oe,"confirm");function Pe(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").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 | graphql | ddd | cqrs | minimal").action(async(t,o)=>{console.log(),t||(t=await re("Project name","my-api"));let r;if(t==="."?(r=te("."),t=Ot(r)):r=te(o.directory||t),St(r)){let a=Tt(r);if(a.length>0){if(o.force)console.log(` Clearing existing files in ${r}...
250
+ `);else{console.log(` Directory "${t}" is not empty:`);let p=a.slice(0,5);for(let u of p)console.log(` - ${u}`);if(a.length>5&&console.log(` ... and ${a.length-5} more`),console.log(),!await oe("Remove all existing files and proceed?",!1)){console.log(` Aborted.
251
+ `);return}}for(let p of a)jt(te(r,p),{recursive:!0,force:!0})}}let n=o.template;n||(n=await je("Project template:",["REST API (Express + Swagger)","GraphQL API (GraphQL + GraphiQL)","DDD (Domain-Driven Design modules)","CQRS (Commands, Queries, Events + WS/Queue)","Minimal (bare Express)"],0),n={"REST API (Express + Swagger)":"rest","GraphQL API (GraphQL + GraphiQL)":"graphql","DDD (Domain-Driven Design modules)":"ddd","CQRS (Commands, Queries, Events + WS/Queue)":"cqrs","Minimal (bare Express)":"minimal"}[n]??"rest");let i=o.pm;i||(i=await je("Package manager:",["pnpm","npm","yarn"],0));let d;o.git===void 0?d=await oe("Initialize git repository?",!0):d=o.git;let c;o.install===void 0?c=await oe("Install dependencies?",!0):c=o.install,await Te({name:t,directory:r,packageManager:i,initGit:d,installDeps:c,template:n})})}s(Pe,"registerInitCommand");import{resolve as z}from"path";import{join as Ce}from"path";import{createInterface as Pt}from"readline";function f(e){return e.replace(/[-_\s]+(.)?/g,(t,o)=>o?o.toUpperCase():"").replace(/^(.)/,t=>t.toUpperCase())}s(f,"toPascalCase");function x(e){let t=f(e);return t.charAt(0).toLowerCase()+t.slice(1)}s(x,"toCamelCase");function $(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}s($,"toKebabCase");function j(e){return e.endsWith("s")?e:e.endsWith("x")||e.endsWith("z")||e.endsWith("sh")||e.endsWith("ch")?e+"es":e.endsWith("y")&&!/[aeiou]y$/.test(e)?e.slice(0,-1)+"ies":e+"s"}s(j,"pluralize");function X(e){return e.endsWith("s")?e:e.endsWith("x")||e.endsWith("z")||e.endsWith("sh")||e.endsWith("ch")?e+"es":e.endsWith("y")&&!/[aeiou]y$/i.test(e)?e.slice(0,-1)+"ies":e+"s"}s(X,"pluralizePascal");import{readFile as Et,writeFile as At}from"fs/promises";function Ee(e,t,o){let r={inmemory:`InMemory${e}Repository`,drizzle:`Drizzle${e}Repository`,prisma:`Prisma${e}Repository`},n={inmemory:`in-memory-${t}`,drizzle:`drizzle-${t}`,prisma:`prisma-${t}`};return{repoClass:r[o]??r.inmemory,repoFile:n[o]??n.inmemory}}s(Ee,"repoMaps");function ie(e,t,o,r){let{repoClass:n,repoFile:i}=Ee(e,t,r);return`/**
252
+ * ${e} Module
709
253
  *
710
254
  * Self-contained feature module following Domain-Driven Design (DDD).
711
255
  * Registers dependencies in the DI container and declares HTTP routes.
@@ -718,9 +262,9 @@ function generateModuleIndex(pascal, kebab, plural, repo) {
718
262
  */
719
263
  import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'
720
264
  import { buildRoutes } from '@forinda/kickjs-http'
721
- import { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'
722
- import { ${repoClass} } from './infrastructure/repositories/${repoFile}.repository'
723
- import { ${pascal}Controller } from './presentation/${kebab}.controller'
265
+ import { ${e.toUpperCase()}_REPOSITORY } from './domain/repositories/${t}.repository'
266
+ import { ${n} } from './infrastructure/repositories/${i}.repository'
267
+ import { ${e}Controller } from './presentation/${t}.controller'
724
268
 
725
269
  // Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
726
270
  import.meta.glob(
@@ -728,402 +272,325 @@ import.meta.glob(
728
272
  { eager: true },
729
273
  )
730
274
 
731
- export class ${pascal}Module implements AppModule {
275
+ export class ${e}Module implements AppModule {
732
276
  /**
733
277
  * Register module dependencies in the DI container.
734
278
  * Bind repository interface tokens to their implementations here.
735
279
  * To swap implementations (e.g. in-memory -> Drizzle), change the factory target.
736
280
  */
737
281
  register(container: Container): void {
738
- container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>
739
- container.resolve(${repoClass}),
282
+ container.registerFactory(${e.toUpperCase()}_REPOSITORY, () =>
283
+ container.resolve(${n}),
740
284
  )
741
285
  }
742
286
 
743
287
  /**
744
288
  * Declare HTTP routes for this module.
745
- * The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${plural}).
289
+ * The path is prefixed with the global apiPrefix and version (e.g. /api/v1/${o}).
746
290
  * Passing 'controller' enables automatic OpenAPI spec generation via SwaggerAdapter.
747
291
  */
748
292
  routes(): ModuleRoutes {
749
293
  return {
750
- path: '/${plural}',
751
- router: buildRoutes(${pascal}Controller),
752
- controller: ${pascal}Controller,
294
+ path: '/${o}',
295
+ router: buildRoutes(${e}Controller),
296
+ controller: ${e}Controller,
753
297
  }
754
298
  }
755
299
  }
756
- `;
757
- }
758
- __name(generateModuleIndex, "generateModuleIndex");
759
- function generateRestModuleIndex(pascal, kebab, plural, repo) {
760
- const { repoClass, repoFile } = repoMaps(pascal, kebab, repo);
761
- return `/**
762
- * ${pascal} Module
300
+ `}s(ie,"generateModuleIndex");function ne(e,t,o,r){let{repoClass:n,repoFile:i}=Ee(e,t,r);return`/**
301
+ * ${e} Module
763
302
  *
764
303
  * REST module with a flat folder structure.
765
304
  * Controller delegates to service, service wraps the repository.
766
305
  *
767
306
  * Structure:
768
- * ${kebab}.controller.ts \u2014 HTTP routes (CRUD)
769
- * ${kebab}.service.ts \u2014 Business logic
770
- * ${kebab}.repository.ts \u2014 Repository interface
771
- * ${repoFile}.repository.ts \u2014 Repository implementation
307
+ * ${t}.controller.ts \u2014 HTTP routes (CRUD)
308
+ * ${t}.service.ts \u2014 Business logic
309
+ * ${t}.repository.ts \u2014 Repository interface
310
+ * ${i}.repository.ts \u2014 Repository implementation
772
311
  * dtos/ \u2014 Request/response schemas
773
312
  */
774
313
  import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'
775
314
  import { buildRoutes } from '@forinda/kickjs-http'
776
- import { ${pascal.toUpperCase()}_REPOSITORY } from './${kebab}.repository'
777
- import { ${repoClass} } from './${repoFile}.repository'
778
- import { ${pascal}Controller } from './${kebab}.controller'
315
+ import { ${e.toUpperCase()}_REPOSITORY } from './${t}.repository'
316
+ import { ${n} } from './${i}.repository'
317
+ import { ${e}Controller } from './${t}.controller'
779
318
 
780
319
  // Eagerly load decorated classes so @Service()/@Repository() decorators register in the DI container
781
320
  import.meta.glob(['./**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'], { eager: true })
782
321
 
783
- export class ${pascal}Module implements AppModule {
322
+ export class ${e}Module implements AppModule {
784
323
  register(container: Container): void {
785
- container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>
786
- container.resolve(${repoClass}),
324
+ container.registerFactory(${e.toUpperCase()}_REPOSITORY, () =>
325
+ container.resolve(${n}),
787
326
  )
788
327
  }
789
328
 
790
329
  routes(): ModuleRoutes {
791
330
  return {
792
- path: '/${plural}',
793
- router: buildRoutes(${pascal}Controller),
794
- controller: ${pascal}Controller,
331
+ path: '/${o}',
332
+ router: buildRoutes(${e}Controller),
333
+ controller: ${e}Controller,
795
334
  }
796
335
  }
797
336
  }
798
- `;
799
- }
800
- __name(generateRestModuleIndex, "generateRestModuleIndex");
801
- function generateMinimalModuleIndex(pascal, kebab, plural) {
802
- return `import { type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'
337
+ `}s(ne,"generateRestModuleIndex");function se(e,t,o){return`import { type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'
803
338
  import { buildRoutes } from '@forinda/kickjs-http'
804
- import { ${pascal}Controller } from './${kebab}.controller'
339
+ import { ${e}Controller } from './${t}.controller'
805
340
 
806
- export class ${pascal}Module implements AppModule {
341
+ export class ${e}Module implements AppModule {
807
342
  routes(): ModuleRoutes {
808
343
  return {
809
- path: '/${plural}',
810
- router: buildRoutes(${pascal}Controller),
811
- controller: ${pascal}Controller,
344
+ path: '/${o}',
345
+ router: buildRoutes(${e}Controller),
346
+ controller: ${e}Controller,
812
347
  }
813
348
  }
814
349
  }
815
- `;
816
- }
817
- __name(generateMinimalModuleIndex, "generateMinimalModuleIndex");
818
-
819
- // src/generators/templates/controller.ts
820
- function generateController(pascal, kebab, plural, pluralPascal) {
821
- return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
350
+ `}s(se,"generateMinimalModuleIndex");function ae(e,t,o,r){return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
822
351
  import type { RequestContext } from '@forinda/kickjs-http'
823
352
  import { ApiTags } from '@forinda/kickjs-swagger'
824
- import { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'
825
- import { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'
826
- import { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'
827
- import { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}.use-case'
828
- import { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'
829
- import { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'
830
- import { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'
831
- import { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../constants'
353
+ import { Create${e}UseCase } from '../application/use-cases/create-${t}.use-case'
354
+ import { Get${e}UseCase } from '../application/use-cases/get-${t}.use-case'
355
+ import { List${r}UseCase } from '../application/use-cases/list-${o}.use-case'
356
+ import { Update${e}UseCase } from '../application/use-cases/update-${t}.use-case'
357
+ import { Delete${e}UseCase } from '../application/use-cases/delete-${t}.use-case'
358
+ import { create${e}Schema } from '../application/dtos/create-${t}.dto'
359
+ import { update${e}Schema } from '../application/dtos/update-${t}.dto'
360
+ import { ${e.toUpperCase()}_QUERY_CONFIG } from '../constants'
832
361
 
833
362
  @Controller()
834
- export class ${pascal}Controller {
835
- @Autowired() private create${pascal}UseCase!: Create${pascal}UseCase
836
- @Autowired() private get${pascal}UseCase!: Get${pascal}UseCase
837
- @Autowired() private list${pluralPascal}UseCase!: List${pluralPascal}UseCase
838
- @Autowired() private update${pascal}UseCase!: Update${pascal}UseCase
839
- @Autowired() private delete${pascal}UseCase!: Delete${pascal}UseCase
363
+ export class ${e}Controller {
364
+ @Autowired() private create${e}UseCase!: Create${e}UseCase
365
+ @Autowired() private get${e}UseCase!: Get${e}UseCase
366
+ @Autowired() private list${r}UseCase!: List${r}UseCase
367
+ @Autowired() private update${e}UseCase!: Update${e}UseCase
368
+ @Autowired() private delete${e}UseCase!: Delete${e}UseCase
840
369
 
841
370
  @Get('/')
842
- @ApiTags('${pascal}')
843
- @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)
371
+ @ApiTags('${e}')
372
+ @ApiQueryParams(${e.toUpperCase()}_QUERY_CONFIG)
844
373
  async list(ctx: RequestContext) {
845
374
  return ctx.paginate(
846
- (parsed) => this.list${pluralPascal}UseCase.execute(parsed),
847
- ${pascal.toUpperCase()}_QUERY_CONFIG,
375
+ (parsed) => this.list${r}UseCase.execute(parsed),
376
+ ${e.toUpperCase()}_QUERY_CONFIG,
848
377
  )
849
378
  }
850
379
 
851
380
  @Get('/:id')
852
- @ApiTags('${pascal}')
381
+ @ApiTags('${e}')
853
382
  async getById(ctx: RequestContext) {
854
- const result = await this.get${pascal}UseCase.execute(ctx.params.id)
855
- if (!result) return ctx.notFound('${pascal} not found')
383
+ const result = await this.get${e}UseCase.execute(ctx.params.id)
384
+ if (!result) return ctx.notFound('${e} not found')
856
385
  ctx.json(result)
857
386
  }
858
387
 
859
- @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })
860
- @ApiTags('${pascal}')
388
+ @Post('/', { body: create${e}Schema, name: 'Create${e}' })
389
+ @ApiTags('${e}')
861
390
  async create(ctx: RequestContext) {
862
- const result = await this.create${pascal}UseCase.execute(ctx.body)
391
+ const result = await this.create${e}UseCase.execute(ctx.body)
863
392
  ctx.created(result)
864
393
  }
865
394
 
866
- @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })
867
- @ApiTags('${pascal}')
395
+ @Put('/:id', { body: update${e}Schema, name: 'Update${e}' })
396
+ @ApiTags('${e}')
868
397
  async update(ctx: RequestContext) {
869
- const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)
398
+ const result = await this.update${e}UseCase.execute(ctx.params.id, ctx.body)
870
399
  ctx.json(result)
871
400
  }
872
401
 
873
402
  @Delete('/:id')
874
- @ApiTags('${pascal}')
403
+ @ApiTags('${e}')
875
404
  async remove(ctx: RequestContext) {
876
- await this.delete${pascal}UseCase.execute(ctx.params.id)
405
+ await this.delete${e}UseCase.execute(ctx.params.id)
877
406
  ctx.noContent()
878
407
  }
879
408
  }
880
- `;
881
- }
882
- __name(generateController, "generateController");
883
- function generateRestController(pascal, kebab, plural, pluralPascal) {
884
- const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1);
885
- return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
409
+ `}s(ae,"generateController");function de(e,t,o,r){let n=e.charAt(0).toLowerCase()+e.slice(1);return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
886
410
  import type { RequestContext } from '@forinda/kickjs-http'
887
411
  import { ApiTags } from '@forinda/kickjs-swagger'
888
- import { ${pascal}Service } from './${kebab}.service'
889
- import { create${pascal}Schema } from './dtos/create-${kebab}.dto'
890
- import { update${pascal}Schema } from './dtos/update-${kebab}.dto'
891
- import { ${pascal.toUpperCase()}_QUERY_CONFIG } from './${kebab}.constants'
412
+ import { ${e}Service } from './${t}.service'
413
+ import { create${e}Schema } from './dtos/create-${t}.dto'
414
+ import { update${e}Schema } from './dtos/update-${t}.dto'
415
+ import { ${e.toUpperCase()}_QUERY_CONFIG } from './${t}.constants'
892
416
 
893
417
  @Controller()
894
- export class ${pascal}Controller {
895
- @Autowired() private ${camel}Service!: ${pascal}Service
418
+ export class ${e}Controller {
419
+ @Autowired() private ${n}Service!: ${e}Service
896
420
 
897
421
  @Get('/')
898
- @ApiTags('${pascal}')
899
- @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)
422
+ @ApiTags('${e}')
423
+ @ApiQueryParams(${e.toUpperCase()}_QUERY_CONFIG)
900
424
  async list(ctx: RequestContext) {
901
425
  return ctx.paginate(
902
- (parsed) => this.${camel}Service.findPaginated(parsed),
903
- ${pascal.toUpperCase()}_QUERY_CONFIG,
426
+ (parsed) => this.${n}Service.findPaginated(parsed),
427
+ ${e.toUpperCase()}_QUERY_CONFIG,
904
428
  )
905
429
  }
906
430
 
907
431
  @Get('/:id')
908
- @ApiTags('${pascal}')
432
+ @ApiTags('${e}')
909
433
  async getById(ctx: RequestContext) {
910
- const result = await this.${camel}Service.findById(ctx.params.id)
911
- if (!result) return ctx.notFound('${pascal} not found')
434
+ const result = await this.${n}Service.findById(ctx.params.id)
435
+ if (!result) return ctx.notFound('${e} not found')
912
436
  ctx.json(result)
913
437
  }
914
438
 
915
- @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })
916
- @ApiTags('${pascal}')
439
+ @Post('/', { body: create${e}Schema, name: 'Create${e}' })
440
+ @ApiTags('${e}')
917
441
  async create(ctx: RequestContext) {
918
- const result = await this.${camel}Service.create(ctx.body)
442
+ const result = await this.${n}Service.create(ctx.body)
919
443
  ctx.created(result)
920
444
  }
921
445
 
922
- @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })
923
- @ApiTags('${pascal}')
446
+ @Put('/:id', { body: update${e}Schema, name: 'Update${e}' })
447
+ @ApiTags('${e}')
924
448
  async update(ctx: RequestContext) {
925
- const result = await this.${camel}Service.update(ctx.params.id, ctx.body)
449
+ const result = await this.${n}Service.update(ctx.params.id, ctx.body)
926
450
  ctx.json(result)
927
451
  }
928
452
 
929
453
  @Delete('/:id')
930
- @ApiTags('${pascal}')
454
+ @ApiTags('${e}')
931
455
  async remove(ctx: RequestContext) {
932
- await this.${camel}Service.delete(ctx.params.id)
456
+ await this.${n}Service.delete(ctx.params.id)
933
457
  ctx.noContent()
934
458
  }
935
459
  }
936
- `;
937
- }
938
- __name(generateRestController, "generateRestController");
939
-
940
- // src/generators/templates/constants.ts
941
- function generateConstants(pascal) {
942
- return `import type { QueryParamsConfig } from '@forinda/kickjs-core'
460
+ `}s(de,"generateRestController");function ce(e){return`import type { QueryParamsConfig } from '@forinda/kickjs-core'
943
461
 
944
- export const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {
462
+ export const ${e.toUpperCase()}_QUERY_CONFIG: QueryParamsConfig = {
945
463
  filterable: ['name'],
946
464
  sortable: ['name', 'createdAt'],
947
465
  searchable: ['name'],
948
466
  }
949
- `;
950
- }
951
- __name(generateConstants, "generateConstants");
952
- function generateDrizzleConstants(pascal, kebab) {
953
- return `import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
467
+ `}s(ce,"generateConstants");function pe(e,t){return`import type { DrizzleQueryParamsConfig } from '@forinda/kickjs-drizzle'
954
468
  // TODO: Import your schema table and reference actual columns for type safety
955
- // import { ${kebab}s } from '@/db/schema'
469
+ // import { ${t}s } from '@/db/schema'
956
470
 
957
- export const ${pascal.toUpperCase()}_QUERY_CONFIG: DrizzleQueryParamsConfig = {
471
+ export const ${e.toUpperCase()}_QUERY_CONFIG: DrizzleQueryParamsConfig = {
958
472
  columns: {
959
473
  // Replace with actual Drizzle Column references for type-safe filtering:
960
- // name: ${kebab}s.name,
961
- // status: ${kebab}s.status,
474
+ // name: ${t}s.name,
475
+ // status: ${t}s.status,
962
476
  },
963
477
  sortable: {
964
- // name: ${kebab}s.name,
965
- // createdAt: ${kebab}s.createdAt,
478
+ // name: ${t}s.name,
479
+ // createdAt: ${t}s.createdAt,
966
480
  },
967
481
  searchColumns: [
968
- // ${kebab}s.name,
482
+ // ${t}s.name,
969
483
  ],
970
484
  }
971
- `;
972
- }
973
- __name(generateDrizzleConstants, "generateDrizzleConstants");
974
-
975
- // src/generators/templates/dtos.ts
976
- function generateCreateDTO(pascal, kebab) {
977
- return `import { z } from 'zod'
485
+ `}s(pe,"generateDrizzleConstants");function M(e,t){return`import { z } from 'zod'
978
486
 
979
487
  /**
980
- * Create ${pascal} DTO \u2014 Zod schema for validating POST request bodies.
981
- * This schema is passed to @Post('/', { body: create${pascal}Schema }) for automatic validation.
488
+ * Create ${e} DTO \u2014 Zod schema for validating POST request bodies.
489
+ * This schema is passed to @Post('/', { body: create${e}Schema }) for automatic validation.
982
490
  * It also generates OpenAPI request body docs when SwaggerAdapter is used.
983
491
  *
984
492
  * Add more fields as needed. Supported Zod types:
985
493
  * z.string(), z.number(), z.boolean(), z.enum([...]),
986
494
  * z.array(), z.object(), .optional(), .default(), .transform()
987
495
  */
988
- export const create${pascal}Schema = z.object({
496
+ export const create${e}Schema = z.object({
989
497
  name: z.string().min(1, 'Name is required').max(200),
990
498
  })
991
499
 
992
- export type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>
993
- `;
994
- }
995
- __name(generateCreateDTO, "generateCreateDTO");
996
- function generateUpdateDTO(pascal, kebab) {
997
- return `import { z } from 'zod'
500
+ export type Create${e}DTO = z.infer<typeof create${e}Schema>
501
+ `}s(M,"generateCreateDTO");function b(e,t){return`import { z } from 'zod'
998
502
 
999
- export const update${pascal}Schema = z.object({
503
+ export const update${e}Schema = z.object({
1000
504
  name: z.string().min(1).max(200).optional(),
1001
505
  })
1002
506
 
1003
- export type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>
1004
- `;
1005
- }
1006
- __name(generateUpdateDTO, "generateUpdateDTO");
1007
- function generateResponseDTO(pascal, kebab) {
1008
- return `export interface ${pascal}ResponseDTO {
507
+ export type Update${e}DTO = z.infer<typeof update${e}Schema>
508
+ `}s(b,"generateUpdateDTO");function Q(e,t){return`export interface ${e}ResponseDTO {
1009
509
  id: string
1010
510
  name: string
1011
511
  createdAt: string
1012
512
  updatedAt: string
1013
513
  }
1014
- `;
1015
- }
1016
- __name(generateResponseDTO, "generateResponseDTO");
1017
-
1018
- // src/generators/templates/use-cases.ts
1019
- function generateUseCases(pascal, kebab, plural, pluralPascal) {
1020
- return [
1021
- {
1022
- file: `create-${kebab}.use-case.ts`,
1023
- content: `/**
1024
- * Create ${pascal} Use Case
514
+ `}s(Q,"generateResponseDTO");function me(e,t,o,r){return[{file:`create-${t}.use-case.ts`,content:`/**
515
+ * Create ${e} Use Case
1025
516
  *
1026
517
  * Application layer \u2014 orchestrates a single business operation.
1027
518
  * Use cases are thin: validate input (via DTO), call domain/repo, return response.
1028
519
  * Keep business rules in the domain service, not here.
1029
520
  */
1030
521
  import { Service, Inject } from '@forinda/kickjs-core'
1031
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
1032
- import type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'
1033
- import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
522
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
523
+ import type { Create${e}DTO } from '../dtos/create-${t}.dto'
524
+ import type { ${e}ResponseDTO } from '../dtos/${t}-response.dto'
1034
525
 
1035
526
  @Service()
1036
- export class Create${pascal}UseCase {
527
+ export class Create${e}UseCase {
1037
528
  constructor(
1038
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
529
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1039
530
  ) {}
1040
531
 
1041
- async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {
532
+ async execute(dto: Create${e}DTO): Promise<${e}ResponseDTO> {
1042
533
  return this.repo.create(dto)
1043
534
  }
1044
535
  }
1045
- `
1046
- },
1047
- {
1048
- file: `get-${kebab}.use-case.ts`,
1049
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1050
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
1051
- import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
536
+ `},{file:`get-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
537
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
538
+ import type { ${e}ResponseDTO } from '../dtos/${t}-response.dto'
1052
539
 
1053
540
  @Service()
1054
- export class Get${pascal}UseCase {
541
+ export class Get${e}UseCase {
1055
542
  constructor(
1056
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
543
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1057
544
  ) {}
1058
545
 
1059
- async execute(id: string): Promise<${pascal}ResponseDTO | null> {
546
+ async execute(id: string): Promise<${e}ResponseDTO | null> {
1060
547
  return this.repo.findById(id)
1061
548
  }
1062
549
  }
1063
- `
1064
- },
1065
- {
1066
- file: `list-${plural}.use-case.ts`,
1067
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1068
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
550
+ `},{file:`list-${o}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
551
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
1069
552
  import type { ParsedQuery } from '@forinda/kickjs-http'
1070
553
 
1071
554
  @Service()
1072
- export class List${pluralPascal}UseCase {
555
+ export class List${r}UseCase {
1073
556
  constructor(
1074
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
557
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1075
558
  ) {}
1076
559
 
1077
560
  async execute(parsed: ParsedQuery) {
1078
561
  return this.repo.findPaginated(parsed)
1079
562
  }
1080
563
  }
1081
- `
1082
- },
1083
- {
1084
- file: `update-${kebab}.use-case.ts`,
1085
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1086
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
1087
- import type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'
1088
- import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
564
+ `},{file:`update-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
565
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
566
+ import type { Update${e}DTO } from '../dtos/update-${t}.dto'
567
+ import type { ${e}ResponseDTO } from '../dtos/${t}-response.dto'
1089
568
 
1090
569
  @Service()
1091
- export class Update${pascal}UseCase {
570
+ export class Update${e}UseCase {
1092
571
  constructor(
1093
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
572
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1094
573
  ) {}
1095
574
 
1096
- async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {
575
+ async execute(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO> {
1097
576
  return this.repo.update(id, dto)
1098
577
  }
1099
578
  }
1100
- `
1101
- },
1102
- {
1103
- file: `delete-${kebab}.use-case.ts`,
1104
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1105
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
579
+ `},{file:`delete-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
580
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
1106
581
 
1107
582
  @Service()
1108
- export class Delete${pascal}UseCase {
583
+ export class Delete${e}UseCase {
1109
584
  constructor(
1110
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
585
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1111
586
  ) {}
1112
587
 
1113
588
  async execute(id: string): Promise<void> {
1114
589
  await this.repo.delete(id)
1115
590
  }
1116
591
  }
1117
- `
1118
- }
1119
- ];
1120
- }
1121
- __name(generateUseCases, "generateUseCases");
1122
-
1123
- // src/generators/templates/repository.ts
1124
- function generateRepositoryInterface(pascal, kebab, dtoPrefix = "../../application/dtos") {
1125
- return `/**
1126
- * ${pascal} Repository Interface
592
+ `}]}s(me,"generateUseCases");function G(e,t,o="../../application/dtos"){return`/**
593
+ * ${e} Repository Interface
1127
594
  *
1128
595
  * Defines the contract for data access.
1129
596
  * The interface declares what operations are available;
@@ -1131,27 +598,23 @@ function generateRepositoryInterface(pascal, kebab, dtoPrefix = "../../applicati
1131
598
  *
1132
599
  * To swap implementations, change the factory in the module's register() method.
1133
600
  */
1134
- import type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'
1135
- import type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'
1136
- import type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'
601
+ import type { ${e}ResponseDTO } from '${o}/${t}-response.dto'
602
+ import type { Create${e}DTO } from '${o}/create-${t}.dto'
603
+ import type { Update${e}DTO } from '${o}/update-${t}.dto'
1137
604
  import type { ParsedQuery } from '@forinda/kickjs-http'
1138
605
 
1139
- export interface I${pascal}Repository {
1140
- findById(id: string): Promise<${pascal}ResponseDTO | null>
1141
- findAll(): Promise<${pascal}ResponseDTO[]>
1142
- findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }>
1143
- create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>
1144
- update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>
606
+ export interface I${e}Repository {
607
+ findById(id: string): Promise<${e}ResponseDTO | null>
608
+ findAll(): Promise<${e}ResponseDTO[]>
609
+ findPaginated(parsed: ParsedQuery): Promise<{ data: ${e}ResponseDTO[]; total: number }>
610
+ create(dto: Create${e}DTO): Promise<${e}ResponseDTO>
611
+ update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO>
1145
612
  delete(id: string): Promise<void>
1146
613
  }
1147
614
 
1148
- export const ${pascal.toUpperCase()}_REPOSITORY = Symbol('I${pascal}Repository')
1149
- `;
1150
- }
1151
- __name(generateRepositoryInterface, "generateRepositoryInterface");
1152
- function generateInMemoryRepository(pascal, kebab, repoPrefix = "../../domain/repositories", dtoPrefix = "../../application/dtos") {
1153
- return `/**
1154
- * In-Memory ${pascal} Repository
615
+ export const ${e.toUpperCase()}_REPOSITORY = Symbol('I${e}Repository')
616
+ `}s(G,"generateRepositoryInterface");function F(e,t,o="../../domain/repositories",r="../../application/dtos"){return`/**
617
+ * In-Memory ${e} Repository
1155
618
  *
1156
619
  * Implements the repository interface using a Map.
1157
620
  * Useful for prototyping and testing. Replace with a database implementation
@@ -1162,32 +625,32 @@ function generateInMemoryRepository(pascal, kebab, repoPrefix = "../../domain/re
1162
625
  import { randomUUID } from 'node:crypto'
1163
626
  import { Repository, HttpException } from '@forinda/kickjs-core'
1164
627
  import type { ParsedQuery } from '@forinda/kickjs-http'
1165
- import type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'
1166
- import type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'
1167
- import type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'
1168
- import type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'
628
+ import type { I${e}Repository } from '${o}/${t}.repository'
629
+ import type { ${e}ResponseDTO } from '${r}/${t}-response.dto'
630
+ import type { Create${e}DTO } from '${r}/create-${t}.dto'
631
+ import type { Update${e}DTO } from '${r}/update-${t}.dto'
1169
632
 
1170
633
  @Repository()
1171
- export class InMemory${pascal}Repository implements I${pascal}Repository {
1172
- private store = new Map<string, ${pascal}ResponseDTO>()
634
+ export class InMemory${e}Repository implements I${e}Repository {
635
+ private store = new Map<string, ${e}ResponseDTO>()
1173
636
 
1174
- async findById(id: string): Promise<${pascal}ResponseDTO | null> {
637
+ async findById(id: string): Promise<${e}ResponseDTO | null> {
1175
638
  return this.store.get(id) ?? null
1176
639
  }
1177
640
 
1178
- async findAll(): Promise<${pascal}ResponseDTO[]> {
641
+ async findAll(): Promise<${e}ResponseDTO[]> {
1179
642
  return Array.from(this.store.values())
1180
643
  }
1181
644
 
1182
- async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {
645
+ async findPaginated(parsed: ParsedQuery): Promise<{ data: ${e}ResponseDTO[]; total: number }> {
1183
646
  const all = Array.from(this.store.values())
1184
647
  const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)
1185
648
  return { data, total: all.length }
1186
649
  }
1187
650
 
1188
- async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {
651
+ async create(dto: Create${e}DTO): Promise<${e}ResponseDTO> {
1189
652
  const now = new Date().toISOString()
1190
- const entity: ${pascal}ResponseDTO = {
653
+ const entity: ${e}ResponseDTO = {
1191
654
  id: randomUUID(),
1192
655
  name: dto.name,
1193
656
  createdAt: now,
@@ -1197,25 +660,21 @@ export class InMemory${pascal}Repository implements I${pascal}Repository {
1197
660
  return entity
1198
661
  }
1199
662
 
1200
- async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {
663
+ async update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO> {
1201
664
  const existing = this.store.get(id)
1202
- if (!existing) throw HttpException.notFound('${pascal} not found')
665
+ if (!existing) throw HttpException.notFound('${e} not found')
1203
666
  const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }
1204
667
  this.store.set(id, updated)
1205
668
  return updated
1206
669
  }
1207
670
 
1208
671
  async delete(id: string): Promise<void> {
1209
- if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')
672
+ if (!this.store.has(id)) throw HttpException.notFound('${e} not found')
1210
673
  this.store.delete(id)
1211
674
  }
1212
675
  }
1213
- `;
1214
- }
1215
- __name(generateInMemoryRepository, "generateInMemoryRepository");
1216
- function generateDrizzleRepository(pascal, kebab, repoPrefix = "../../domain/repositories", dtoPrefix = "../../application/dtos") {
1217
- return `/**
1218
- * Drizzle ${pascal} Repository
676
+ `}s(F,"generateInMemoryRepository");function L(e,t,o="../../domain/repositories",r="../../application/dtos"){return`/**
677
+ * Drizzle ${e} Repository
1219
678
  *
1220
679
  * Implements the repository interface using Drizzle ORM.
1221
680
  * Uses buildFromColumns() with Column objects for type-safe query building.
@@ -1229,185 +688,170 @@ import { eq, ne, gt, gte, lt, lte, ilike, inArray, between, and, or, asc, desc,
1229
688
  import { Repository, HttpException, Inject } from '@forinda/kickjs-core'
1230
689
  import { DRIZZLE_DB, DrizzleQueryAdapter } from '@forinda/kickjs-drizzle'
1231
690
  import type { ParsedQuery } from '@forinda/kickjs-http'
1232
- import type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'
1233
- import type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'
1234
- import type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'
1235
- import type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'
1236
- import { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../../constants'
691
+ import type { I${e}Repository } from '${o}/${t}.repository'
692
+ import type { ${e}ResponseDTO } from '${r}/${t}-response.dto'
693
+ import type { Create${e}DTO } from '${r}/create-${t}.dto'
694
+ import type { Update${e}DTO } from '${r}/update-${t}.dto'
695
+ import { ${e.toUpperCase()}_QUERY_CONFIG } from '../../constants'
1237
696
 
1238
697
  // TODO: Import your Drizzle schema table \u2014 e.g.:
1239
- // import { ${kebab}s } from '@/db/schema'
698
+ // import { ${t}s } from '@/db/schema'
1240
699
 
1241
700
  const queryAdapter = new DrizzleQueryAdapter({
1242
701
  eq, ne, gt, gte, lt, lte, ilike, inArray, between, and, or, asc, desc,
1243
702
  })
1244
703
 
1245
704
  @Repository()
1246
- export class Drizzle${pascal}Repository implements I${pascal}Repository {
705
+ export class Drizzle${e}Repository implements I${e}Repository {
1247
706
  constructor(@Inject(DRIZZLE_DB) private db: any) {}
1248
707
 
1249
- async findById(id: string): Promise<${pascal}ResponseDTO | null> {
708
+ async findById(id: string): Promise<${e}ResponseDTO | null> {
1250
709
  // TODO: Implement with Drizzle
1251
- // const row = this.db.select().from(${kebab}s).where(eq(${kebab}s.id, id)).get()
710
+ // const row = this.db.select().from(${t}s).where(eq(${t}s.id, id)).get()
1252
711
  // return row ?? null
1253
- throw new Error('Drizzle ${pascal} repository not yet implemented \u2014 update schema imports and queries')
712
+ throw new Error('Drizzle ${e} repository not yet implemented \u2014 update schema imports and queries')
1254
713
  }
1255
714
 
1256
- async findAll(): Promise<${pascal}ResponseDTO[]> {
715
+ async findAll(): Promise<${e}ResponseDTO[]> {
1257
716
  // TODO: Implement with Drizzle
1258
- // return this.db.select().from(${kebab}s).all()
1259
- throw new Error('Drizzle ${pascal} repository not yet implemented')
717
+ // return this.db.select().from(${t}s).all()
718
+ throw new Error('Drizzle ${e} repository not yet implemented')
1260
719
  }
1261
720
 
1262
- async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {
721
+ async findPaginated(parsed: ParsedQuery): Promise<{ data: ${e}ResponseDTO[]; total: number }> {
1263
722
  // TODO: Use buildFromColumns() with your query config for type-safe filtering
1264
- // const query = queryAdapter.buildFromColumns(parsed, ${pascal.toUpperCase()}_QUERY_CONFIG)
723
+ // const query = queryAdapter.buildFromColumns(parsed, ${e.toUpperCase()}_QUERY_CONFIG)
1265
724
  //
1266
725
  // const data = this.db
1267
- // .select().from(${kebab}s).$dynamic()
726
+ // .select().from(${t}s).$dynamic()
1268
727
  // .where(query.where).orderBy(...query.orderBy)
1269
728
  // .limit(query.limit).offset(query.offset).all()
1270
729
  //
1271
730
  // const totalResult = this.db
1272
- // .select({ count: count() }).from(${kebab}s)
731
+ // .select({ count: count() }).from(${t}s)
1273
732
  // .$dynamic().where(query.where).get()
1274
733
  //
1275
734
  // return { data, total: totalResult?.count ?? 0 }
1276
- throw new Error('Drizzle ${pascal} repository not yet implemented')
735
+ throw new Error('Drizzle ${e} repository not yet implemented')
1277
736
  }
1278
737
 
1279
- async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {
738
+ async create(dto: Create${e}DTO): Promise<${e}ResponseDTO> {
1280
739
  // TODO: Implement with Drizzle
1281
- // return this.db.insert(${kebab}s).values(dto).returning().get()
1282
- throw new Error('Drizzle ${pascal} repository not yet implemented')
740
+ // return this.db.insert(${t}s).values(dto).returning().get()
741
+ throw new Error('Drizzle ${e} repository not yet implemented')
1283
742
  }
1284
743
 
1285
- async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {
744
+ async update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO> {
1286
745
  // TODO: Implement with Drizzle
1287
- // const row = this.db.update(${kebab}s).set(dto).where(eq(${kebab}s.id, id)).returning().get()
1288
- // if (!row) throw HttpException.notFound('${pascal} not found')
746
+ // const row = this.db.update(${t}s).set(dto).where(eq(${t}s.id, id)).returning().get()
747
+ // if (!row) throw HttpException.notFound('${e} not found')
1289
748
  // return row
1290
- throw new Error('Drizzle ${pascal} repository not yet implemented')
749
+ throw new Error('Drizzle ${e} repository not yet implemented')
1291
750
  }
1292
751
 
1293
752
  async delete(id: string): Promise<void> {
1294
753
  // TODO: Implement with Drizzle
1295
- // this.db.delete(${kebab}s).where(eq(${kebab}s.id, id)).run()
1296
- throw new Error('Drizzle ${pascal} repository not yet implemented')
754
+ // this.db.delete(${t}s).where(eq(${t}s.id, id)).run()
755
+ throw new Error('Drizzle ${e} repository not yet implemented')
1297
756
  }
1298
757
  }
1299
- `;
1300
- }
1301
- __name(generateDrizzleRepository, "generateDrizzleRepository");
1302
- function generatePrismaRepository(pascal, kebab, repoPrefix = "../../domain/repositories", dtoPrefix = "../../application/dtos") {
1303
- const camel = kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
1304
- return `/**
1305
- * Prisma ${pascal} Repository
758
+ `}s(L,"generateDrizzleRepository");function N(e,t,o="../../domain/repositories",r="../../application/dtos"){let n=t.replace(/-([a-z])/g,(i,d)=>d.toUpperCase());return`/**
759
+ * Prisma ${e} Repository
1306
760
  *
1307
761
  * Implements the repository interface using Prisma Client.
1308
762
  * Requires a PrismaClient instance injected via the DI container.
1309
763
  *
1310
- * TODO: Ensure your Prisma schema has a '${pascal}' model defined.
764
+ * TODO: Ensure your Prisma schema has a '${e}' model defined.
1311
765
  * TODO: Replace 'PRISMA_CLIENT' with your actual Prisma injection token.
1312
766
  *
1313
767
  * @Repository() registers this class in the DI container as a singleton.
1314
768
  */
1315
769
  import { Repository, HttpException, Autowired } from '@forinda/kickjs-core'
1316
770
  import type { ParsedQuery } from '@forinda/kickjs-http'
1317
- import type { I${pascal}Repository } from '${repoPrefix}/${kebab}.repository'
1318
- import type { ${pascal}ResponseDTO } from '${dtoPrefix}/${kebab}-response.dto'
1319
- import type { Create${pascal}DTO } from '${dtoPrefix}/create-${kebab}.dto'
1320
- import type { Update${pascal}DTO } from '${dtoPrefix}/update-${kebab}.dto'
771
+ import type { I${e}Repository } from '${o}/${t}.repository'
772
+ import type { ${e}ResponseDTO } from '${r}/${t}-response.dto'
773
+ import type { Create${e}DTO } from '${r}/create-${t}.dto'
774
+ import type { Update${e}DTO } from '${r}/update-${t}.dto'
1321
775
 
1322
776
  // TODO: Import your Prisma injection token \u2014 e.g.:
1323
777
  // import { PRISMA_CLIENT } from '@/db/prisma.provider'
1324
778
  // import type { PrismaClient } from '@prisma/client'
1325
779
 
1326
780
  @Repository()
1327
- export class Prisma${pascal}Repository implements I${pascal}Repository {
781
+ export class Prisma${e}Repository implements I${e}Repository {
1328
782
  // TODO: Uncomment and configure your Prisma injection:
1329
783
  // @Autowired(PRISMA_CLIENT) private prisma!: PrismaClient
1330
784
 
1331
- async findById(id: string): Promise<${pascal}ResponseDTO | null> {
785
+ async findById(id: string): Promise<${e}ResponseDTO | null> {
1332
786
  // TODO: Implement with Prisma
1333
- // return this.prisma.${camel}.findUnique({ where: { id } })
1334
- throw new Error('Prisma ${pascal} repository not yet implemented \u2014 update Prisma imports and queries')
787
+ // return this.prisma.${n}.findUnique({ where: { id } })
788
+ throw new Error('Prisma ${e} repository not yet implemented \u2014 update Prisma imports and queries')
1335
789
  }
1336
790
 
1337
- async findAll(): Promise<${pascal}ResponseDTO[]> {
791
+ async findAll(): Promise<${e}ResponseDTO[]> {
1338
792
  // TODO: Implement with Prisma
1339
- // return this.prisma.${camel}.findMany()
1340
- throw new Error('Prisma ${pascal} repository not yet implemented')
793
+ // return this.prisma.${n}.findMany()
794
+ throw new Error('Prisma ${e} repository not yet implemented')
1341
795
  }
1342
796
 
1343
- async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {
797
+ async findPaginated(parsed: ParsedQuery): Promise<{ data: ${e}ResponseDTO[]; total: number }> {
1344
798
  // TODO: Implement with Prisma
1345
799
  // const [data, total] = await Promise.all([
1346
- // this.prisma.${camel}.findMany({
800
+ // this.prisma.${n}.findMany({
1347
801
  // skip: parsed.pagination.offset,
1348
802
  // take: parsed.pagination.limit,
1349
803
  // }),
1350
- // this.prisma.${camel}.count(),
804
+ // this.prisma.${n}.count(),
1351
805
  // ])
1352
806
  // return { data, total }
1353
- throw new Error('Prisma ${pascal} repository not yet implemented')
807
+ throw new Error('Prisma ${e} repository not yet implemented')
1354
808
  }
1355
809
 
1356
- async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {
810
+ async create(dto: Create${e}DTO): Promise<${e}ResponseDTO> {
1357
811
  // TODO: Implement with Prisma
1358
- // return this.prisma.${camel}.create({ data: dto })
1359
- throw new Error('Prisma ${pascal} repository not yet implemented')
812
+ // return this.prisma.${n}.create({ data: dto })
813
+ throw new Error('Prisma ${e} repository not yet implemented')
1360
814
  }
1361
815
 
1362
- async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {
816
+ async update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO> {
1363
817
  // TODO: Implement with Prisma
1364
- // const row = await this.prisma.${camel}.update({ where: { id }, data: dto })
1365
- // if (!row) throw HttpException.notFound('${pascal} not found')
818
+ // const row = await this.prisma.${n}.update({ where: { id }, data: dto })
819
+ // if (!row) throw HttpException.notFound('${e} not found')
1366
820
  // return row
1367
- throw new Error('Prisma ${pascal} repository not yet implemented')
821
+ throw new Error('Prisma ${e} repository not yet implemented')
1368
822
  }
1369
823
 
1370
824
  async delete(id: string): Promise<void> {
1371
825
  // TODO: Implement with Prisma
1372
- // await this.prisma.${camel}.delete({ where: { id } })
1373
- throw new Error('Prisma ${pascal} repository not yet implemented')
826
+ // await this.prisma.${n}.delete({ where: { id } })
827
+ throw new Error('Prisma ${e} repository not yet implemented')
1374
828
  }
1375
829
  }
1376
- `;
1377
- }
1378
- __name(generatePrismaRepository, "generatePrismaRepository");
1379
-
1380
- // src/generators/templates/domain.ts
1381
- function generateDomainService(pascal, kebab) {
1382
- return `/**
1383
- * ${pascal} Domain Service
830
+ `}s(N,"generatePrismaRepository");function le(e,t){return`/**
831
+ * ${e} Domain Service
1384
832
  *
1385
833
  * Domain layer \u2014 contains business rules that don't belong to a single entity.
1386
834
  * Use this for cross-entity logic, validation rules, and domain invariants.
1387
835
  * Keep it free of HTTP/framework concerns.
1388
836
  */
1389
837
  import { Service, Inject, HttpException } from '@forinda/kickjs-core'
1390
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'
838
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../repositories/${t}.repository'
1391
839
 
1392
840
  @Service()
1393
- export class ${pascal}DomainService {
841
+ export class ${e}DomainService {
1394
842
  constructor(
1395
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
843
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1396
844
  ) {}
1397
845
 
1398
846
  async ensureExists(id: string): Promise<void> {
1399
847
  const entity = await this.repo.findById(id)
1400
848
  if (!entity) {
1401
- throw HttpException.notFound('${pascal} not found')
849
+ throw HttpException.notFound('${e} not found')
1402
850
  }
1403
851
  }
1404
852
  }
1405
- `;
1406
- }
1407
- __name(generateDomainService, "generateDomainService");
1408
- function generateEntity(pascal, kebab) {
1409
- return `/**
1410
- * ${pascal} Entity
853
+ `}s(le,"generateDomainService");function ue(e,t){return`/**
854
+ * ${e} Entity
1411
855
  *
1412
856
  * Domain layer \u2014 the core business object.
1413
857
  * Uses a private constructor with static factory methods (create, reconstitute)
@@ -1419,33 +863,33 @@ function generateEntity(pascal, kebab) {
1419
863
  * - reconstitute(): factory for rebuilding from persistence (no side effects)
1420
864
  * - changeName(): mutation method that enforces business rules
1421
865
  */
1422
- import { ${pascal}Id } from '../value-objects/${kebab}-id.vo'
866
+ import { ${e}Id } from '../value-objects/${t}-id.vo'
1423
867
 
1424
- interface ${pascal}Props {
1425
- id: ${pascal}Id
868
+ interface ${e}Props {
869
+ id: ${e}Id
1426
870
  name: string
1427
871
  createdAt: Date
1428
872
  updatedAt: Date
1429
873
  }
1430
874
 
1431
- export class ${pascal} {
1432
- private constructor(private props: ${pascal}Props) {}
875
+ export class ${e} {
876
+ private constructor(private props: ${e}Props) {}
1433
877
 
1434
- static create(params: { name: string }): ${pascal} {
878
+ static create(params: { name: string }): ${e} {
1435
879
  const now = new Date()
1436
- return new ${pascal}({
1437
- id: ${pascal}Id.create(),
880
+ return new ${e}({
881
+ id: ${e}Id.create(),
1438
882
  name: params.name,
1439
883
  createdAt: now,
1440
884
  updatedAt: now,
1441
885
  })
1442
886
  }
1443
887
 
1444
- static reconstitute(props: ${pascal}Props): ${pascal} {
1445
- return new ${pascal}(props)
888
+ static reconstitute(props: ${e}Props): ${e} {
889
+ return new ${e}(props)
1446
890
  }
1447
891
 
1448
- get id(): ${pascal}Id {
892
+ get id(): ${e}Id {
1449
893
  return this.props.id
1450
894
  }
1451
895
  get name(): string {
@@ -1475,54 +919,44 @@ export class ${pascal} {
1475
919
  }
1476
920
  }
1477
921
  }
1478
- `;
1479
- }
1480
- __name(generateEntity, "generateEntity");
1481
- function generateValueObject(pascal, kebab) {
1482
- return `/**
1483
- * ${pascal} ID Value Object
922
+ `}s(ue,"generateEntity");function fe(e,t){return`/**
923
+ * ${e} ID Value Object
1484
924
  *
1485
925
  * Domain layer \u2014 wraps a primitive ID with type safety and validation.
1486
926
  * Value objects are immutable and compared by value, not reference.
1487
927
  *
1488
- * ${pascal}Id.create() \u2014 generate a new UUID
1489
- * ${pascal}Id.from(id) \u2014 wrap an existing ID string (validates non-empty)
928
+ * ${e}Id.create() \u2014 generate a new UUID
929
+ * ${e}Id.from(id) \u2014 wrap an existing ID string (validates non-empty)
1490
930
  * id.equals(other) \u2014 compare two IDs by value
1491
931
  */
1492
932
  import { randomUUID } from 'node:crypto'
1493
933
 
1494
- export class ${pascal}Id {
934
+ export class ${e}Id {
1495
935
  private constructor(private readonly value: string) {}
1496
936
 
1497
- static create(): ${pascal}Id {
1498
- return new ${pascal}Id(randomUUID())
937
+ static create(): ${e}Id {
938
+ return new ${e}Id(randomUUID())
1499
939
  }
1500
940
 
1501
- static from(id: string): ${pascal}Id {
941
+ static from(id: string): ${e}Id {
1502
942
  if (!id || id.trim().length === 0) {
1503
- throw new Error('${pascal}Id cannot be empty')
943
+ throw new Error('${e}Id cannot be empty')
1504
944
  }
1505
- return new ${pascal}Id(id)
945
+ return new ${e}Id(id)
1506
946
  }
1507
947
 
1508
948
  toString(): string {
1509
949
  return this.value
1510
950
  }
1511
951
 
1512
- equals(other: ${pascal}Id): boolean {
952
+ equals(other: ${e}Id): boolean {
1513
953
  return this.value === other.value
1514
954
  }
1515
955
  }
1516
- `;
1517
- }
1518
- __name(generateValueObject, "generateValueObject");
1519
-
1520
- // src/generators/templates/tests.ts
1521
- function generateControllerTest(pascal, kebab, plural) {
1522
- return `import { describe, it, expect, beforeEach } from 'vitest'
956
+ `}s(fe,"generateValueObject");function Y(e,t,o){return`import { describe, it, expect, beforeEach } from 'vitest'
1523
957
  import { Container } from '@forinda/kickjs-core'
1524
958
 
1525
- describe('${pascal}Controller', () => {
959
+ describe('${e}Controller', () => {
1526
960
  beforeEach(() => {
1527
961
  Container.reset()
1528
962
  })
@@ -1531,64 +965,60 @@ describe('${pascal}Controller', () => {
1531
965
  expect(true).toBe(true)
1532
966
  })
1533
967
 
1534
- describe('POST /${plural}', () => {
1535
- it('should create a new ${kebab}', async () => {
968
+ describe('POST /${o}', () => {
969
+ it('should create a new ${t}', async () => {
1536
970
  // TODO: Set up test module, call create endpoint, assert 201
1537
971
  expect(true).toBe(true)
1538
972
  })
1539
973
  })
1540
974
 
1541
- describe('GET /${plural}', () => {
1542
- it('should return paginated ${plural}', async () => {
975
+ describe('GET /${o}', () => {
976
+ it('should return paginated ${o}', async () => {
1543
977
  // TODO: Set up test module, call list endpoint, assert { data, meta }
1544
978
  expect(true).toBe(true)
1545
979
  })
1546
980
  })
1547
981
 
1548
- describe('GET /${plural}/:id', () => {
1549
- it('should return a ${kebab} by id', async () => {
1550
- // TODO: Create a ${kebab}, then fetch by id, assert match
982
+ describe('GET /${o}/:id', () => {
983
+ it('should return a ${t} by id', async () => {
984
+ // TODO: Create a ${t}, then fetch by id, assert match
1551
985
  expect(true).toBe(true)
1552
986
  })
1553
987
 
1554
- it('should return 404 for non-existent ${kebab}', async () => {
988
+ it('should return 404 for non-existent ${t}', async () => {
1555
989
  // TODO: Fetch non-existent id, assert 404
1556
990
  expect(true).toBe(true)
1557
991
  })
1558
992
  })
1559
993
 
1560
- describe('PUT /${plural}/:id', () => {
1561
- it('should update an existing ${kebab}', async () => {
994
+ describe('PUT /${o}/:id', () => {
995
+ it('should update an existing ${t}', async () => {
1562
996
  // TODO: Create, update, assert changes
1563
997
  expect(true).toBe(true)
1564
998
  })
1565
999
  })
1566
1000
 
1567
- describe('DELETE /${plural}/:id', () => {
1568
- it('should delete a ${kebab}', async () => {
1001
+ describe('DELETE /${o}/:id', () => {
1002
+ it('should delete a ${t}', async () => {
1569
1003
  // TODO: Create, delete, assert gone
1570
1004
  expect(true).toBe(true)
1571
1005
  })
1572
1006
  })
1573
1007
  })
1574
- `;
1575
- }
1576
- __name(generateControllerTest, "generateControllerTest");
1577
- function generateRepositoryTest(pascal, kebab, plural, repoImport = `../infrastructure/repositories/in-memory-${kebab}.repository`) {
1578
- return `import { describe, it, expect, beforeEach } from 'vitest'
1579
- import { InMemory${pascal}Repository } from '${repoImport}'
1008
+ `}s(Y,"generateControllerTest");function B(e,t,o,r=`../infrastructure/repositories/in-memory-${t}.repository`){return`import { describe, it, expect, beforeEach } from 'vitest'
1009
+ import { InMemory${e}Repository } from '${r}'
1580
1010
 
1581
- describe('InMemory${pascal}Repository', () => {
1582
- let repo: InMemory${pascal}Repository
1011
+ describe('InMemory${e}Repository', () => {
1012
+ let repo: InMemory${e}Repository
1583
1013
 
1584
1014
  beforeEach(() => {
1585
- repo = new InMemory${pascal}Repository()
1015
+ repo = new InMemory${e}Repository()
1586
1016
  })
1587
1017
 
1588
- it('should create and retrieve a ${kebab}', async () => {
1589
- const created = await repo.create({ name: 'Test ${pascal}' })
1018
+ it('should create and retrieve a ${t}', async () => {
1019
+ const created = await repo.create({ name: 'Test ${e}' })
1590
1020
  expect(created).toBeDefined()
1591
- expect(created.name).toBe('Test ${pascal}')
1021
+ expect(created.name).toBe('Test ${e}')
1592
1022
  expect(created.id).toBeDefined()
1593
1023
 
1594
1024
  const found = await repo.findById(created.id)
@@ -1600,18 +1030,18 @@ describe('InMemory${pascal}Repository', () => {
1600
1030
  expect(found).toBeNull()
1601
1031
  })
1602
1032
 
1603
- it('should list all ${plural}', async () => {
1604
- await repo.create({ name: '${pascal} 1' })
1605
- await repo.create({ name: '${pascal} 2' })
1033
+ it('should list all ${o}', async () => {
1034
+ await repo.create({ name: '${e} 1' })
1035
+ await repo.create({ name: '${e} 2' })
1606
1036
 
1607
1037
  const all = await repo.findAll()
1608
1038
  expect(all).toHaveLength(2)
1609
1039
  })
1610
1040
 
1611
1041
  it('should return paginated results', async () => {
1612
- await repo.create({ name: '${pascal} 1' })
1613
- await repo.create({ name: '${pascal} 2' })
1614
- await repo.create({ name: '${pascal} 3' })
1042
+ await repo.create({ name: '${e} 1' })
1043
+ await repo.create({ name: '${e} 2' })
1044
+ await repo.create({ name: '${e} 3' })
1615
1045
 
1616
1046
  const result = await repo.findPaginated({
1617
1047
  filters: [],
@@ -1624,43 +1054,37 @@ describe('InMemory${pascal}Repository', () => {
1624
1054
  expect(result.total).toBe(3)
1625
1055
  })
1626
1056
 
1627
- it('should update a ${kebab}', async () => {
1057
+ it('should update a ${t}', async () => {
1628
1058
  const created = await repo.create({ name: 'Original' })
1629
1059
  const updated = await repo.update(created.id, { name: 'Updated' })
1630
1060
  expect(updated.name).toBe('Updated')
1631
1061
  })
1632
1062
 
1633
- it('should delete a ${kebab}', async () => {
1063
+ it('should delete a ${t}', async () => {
1634
1064
  const created = await repo.create({ name: 'To Delete' })
1635
1065
  await repo.delete(created.id)
1636
1066
  const found = await repo.findById(created.id)
1637
1067
  expect(found).toBeNull()
1638
1068
  })
1639
1069
  })
1640
- `;
1641
- }
1642
- __name(generateRepositoryTest, "generateRepositoryTest");
1643
-
1644
- // src/generators/templates/rest-service.ts
1645
- function generateRestService(pascal, kebab) {
1646
- return `import { Service, Inject, HttpException } from '@forinda/kickjs-core'
1070
+ `}s(B,"generateRepositoryTest");function $e(e,t){return`import { Service, Inject, HttpException } from '@forinda/kickjs-core'
1647
1071
  import type { ParsedQuery } from '@forinda/kickjs-http'
1648
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from './${kebab}.repository'
1649
- import type { ${pascal}ResponseDTO } from './dtos/${kebab}-response.dto'
1650
- import type { Create${pascal}DTO } from './dtos/create-${kebab}.dto'
1651
- import type { Update${pascal}DTO } from './dtos/update-${kebab}.dto'
1072
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from './${t}.repository'
1073
+ import type { ${e}ResponseDTO } from './dtos/${t}-response.dto'
1074
+ import type { Create${e}DTO } from './dtos/create-${t}.dto'
1075
+ import type { Update${e}DTO } from './dtos/update-${t}.dto'
1652
1076
 
1653
1077
  @Service()
1654
- export class ${pascal}Service {
1078
+ export class ${e}Service {
1655
1079
  constructor(
1656
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
1080
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1657
1081
  ) {}
1658
1082
 
1659
- async findById(id: string): Promise<${pascal}ResponseDTO | null> {
1083
+ async findById(id: string): Promise<${e}ResponseDTO | null> {
1660
1084
  return this.repo.findById(id)
1661
1085
  }
1662
1086
 
1663
- async findAll(): Promise<${pascal}ResponseDTO[]> {
1087
+ async findAll(): Promise<${e}ResponseDTO[]> {
1664
1088
  return this.repo.findAll()
1665
1089
  }
1666
1090
 
@@ -1668,11 +1092,11 @@ export class ${pascal}Service {
1668
1092
  return this.repo.findPaginated(parsed)
1669
1093
  }
1670
1094
 
1671
- async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {
1095
+ async create(dto: Create${e}DTO): Promise<${e}ResponseDTO> {
1672
1096
  return this.repo.create(dto)
1673
1097
  }
1674
1098
 
1675
- async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {
1099
+ async update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO> {
1676
1100
  return this.repo.update(id, dto)
1677
1101
  }
1678
1102
 
@@ -1680,37 +1104,15 @@ export class ${pascal}Service {
1680
1104
  await this.repo.delete(id)
1681
1105
  }
1682
1106
  }
1683
- `;
1684
- }
1685
- __name(generateRestService, "generateRestService");
1686
- function generateRestConstants(pascal) {
1687
- return `import type { QueryFieldConfig } from '@forinda/kickjs-http'
1107
+ `}s($e,"generateRestService");function ee(e){return`import type { QueryFieldConfig } from '@forinda/kickjs-http'
1688
1108
 
1689
- export const ${pascal.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
1109
+ export const ${e.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
1690
1110
  filterable: ['name'],
1691
1111
  sortable: ['name', 'createdAt'],
1692
1112
  searchable: ['name'],
1693
1113
  }
1694
- `;
1695
- }
1696
- __name(generateRestConstants, "generateRestConstants");
1697
-
1698
- // src/generators/templates/cqrs.ts
1699
- function generateCqrsModuleIndex(pascal, kebab, plural, repo) {
1700
- const repoClassMap = {
1701
- inmemory: `InMemory${pascal}Repository`,
1702
- drizzle: `Drizzle${pascal}Repository`,
1703
- prisma: `Prisma${pascal}Repository`
1704
- };
1705
- const repoFileMap = {
1706
- inmemory: `in-memory-${kebab}`,
1707
- drizzle: `drizzle-${kebab}`,
1708
- prisma: `prisma-${kebab}`
1709
- };
1710
- const repoClass = repoClassMap[repo] ?? repoClassMap.inmemory;
1711
- const repoFile = repoFileMap[repo] ?? repoFileMap.inmemory;
1712
- return `/**
1713
- * ${pascal} Module \u2014 CQRS Pattern
1114
+ `}s(ee,"generateRestConstants");function ge(e,t,o,r){let n={inmemory:`InMemory${e}Repository`,drizzle:`Drizzle${e}Repository`,prisma:`Prisma${e}Repository`},i={inmemory:`in-memory-${t}`,drizzle:`drizzle-${t}`,prisma:`prisma-${t}`},d=n[r]??n.inmemory,c=i[r]??i.inmemory;return`/**
1115
+ * ${e} Module \u2014 CQRS Pattern
1714
1116
  *
1715
1117
  * Separates read (queries) and write (commands) operations.
1716
1118
  * Events are emitted after state changes and can be handled via
@@ -1724,9 +1126,9 @@ function generateCqrsModuleIndex(pascal, kebab, plural, repo) {
1724
1126
  */
1725
1127
  import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs-core'
1726
1128
  import { buildRoutes } from '@forinda/kickjs-http'
1727
- import { ${pascal.toUpperCase()}_REPOSITORY } from './${kebab}.repository'
1728
- import { ${repoClass} } from './${repoFile}.repository'
1729
- import { ${pascal}Controller } from './${kebab}.controller'
1129
+ import { ${e.toUpperCase()}_REPOSITORY } from './${t}.repository'
1130
+ import { ${d} } from './${c}.repository'
1131
+ import { ${e}Controller } from './${t}.controller'
1730
1132
 
1731
1133
  // Eagerly load decorated classes
1732
1134
  import.meta.glob(
@@ -1739,210 +1141,168 @@ import.meta.glob(
1739
1141
  { eager: true },
1740
1142
  )
1741
1143
 
1742
- export class ${pascal}Module implements AppModule {
1144
+ export class ${e}Module implements AppModule {
1743
1145
  register(container: Container): void {
1744
- container.registerFactory(${pascal.toUpperCase()}_REPOSITORY, () =>
1745
- container.resolve(${repoClass}),
1146
+ container.registerFactory(${e.toUpperCase()}_REPOSITORY, () =>
1147
+ container.resolve(${d}),
1746
1148
  )
1747
1149
  }
1748
1150
 
1749
1151
  routes(): ModuleRoutes {
1750
1152
  return {
1751
- path: '/${plural}',
1752
- router: buildRoutes(${pascal}Controller),
1753
- controller: ${pascal}Controller,
1153
+ path: '/${o}',
1154
+ router: buildRoutes(${e}Controller),
1155
+ controller: ${e}Controller,
1754
1156
  }
1755
1157
  }
1756
1158
  }
1757
- `;
1758
- }
1759
- __name(generateCqrsModuleIndex, "generateCqrsModuleIndex");
1760
- function generateCqrsController(pascal, kebab, plural, pluralPascal) {
1761
- const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1);
1762
- return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
1159
+ `}s(ge,"generateCqrsModuleIndex");function ye(e,t,o,r){let n=e.charAt(0).toLowerCase()+e.slice(1);return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
1763
1160
  import type { RequestContext } from '@forinda/kickjs-http'
1764
1161
  import { ApiTags } from '@forinda/kickjs-swagger'
1765
- import { Create${pascal}Command } from './commands/create-${kebab}.command'
1766
- import { Update${pascal}Command } from './commands/update-${kebab}.command'
1767
- import { Delete${pascal}Command } from './commands/delete-${kebab}.command'
1768
- import { Get${pascal}Query } from './queries/get-${kebab}.query'
1769
- import { List${pluralPascal}Query } from './queries/list-${plural}.query'
1770
- import { create${pascal}Schema } from './dtos/create-${kebab}.dto'
1771
- import { update${pascal}Schema } from './dtos/update-${kebab}.dto'
1772
- import { ${pascal.toUpperCase()}_QUERY_CONFIG } from './${kebab}.constants'
1162
+ import { Create${e}Command } from './commands/create-${t}.command'
1163
+ import { Update${e}Command } from './commands/update-${t}.command'
1164
+ import { Delete${e}Command } from './commands/delete-${t}.command'
1165
+ import { Get${e}Query } from './queries/get-${t}.query'
1166
+ import { List${r}Query } from './queries/list-${o}.query'
1167
+ import { create${e}Schema } from './dtos/create-${t}.dto'
1168
+ import { update${e}Schema } from './dtos/update-${t}.dto'
1169
+ import { ${e.toUpperCase()}_QUERY_CONFIG } from './${t}.constants'
1773
1170
 
1774
1171
  @Controller()
1775
- export class ${pascal}Controller {
1776
- @Autowired() private create${pascal}Command!: Create${pascal}Command
1777
- @Autowired() private update${pascal}Command!: Update${pascal}Command
1778
- @Autowired() private delete${pascal}Command!: Delete${pascal}Command
1779
- @Autowired() private get${pascal}Query!: Get${pascal}Query
1780
- @Autowired() private list${pluralPascal}Query!: List${pluralPascal}Query
1172
+ export class ${e}Controller {
1173
+ @Autowired() private create${e}Command!: Create${e}Command
1174
+ @Autowired() private update${e}Command!: Update${e}Command
1175
+ @Autowired() private delete${e}Command!: Delete${e}Command
1176
+ @Autowired() private get${e}Query!: Get${e}Query
1177
+ @Autowired() private list${r}Query!: List${r}Query
1781
1178
 
1782
1179
  @Get('/')
1783
- @ApiTags('${pascal}')
1784
- @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)
1180
+ @ApiTags('${e}')
1181
+ @ApiQueryParams(${e.toUpperCase()}_QUERY_CONFIG)
1785
1182
  async list(ctx: RequestContext) {
1786
1183
  return ctx.paginate(
1787
- (parsed) => this.list${pluralPascal}Query.execute(parsed),
1788
- ${pascal.toUpperCase()}_QUERY_CONFIG,
1184
+ (parsed) => this.list${r}Query.execute(parsed),
1185
+ ${e.toUpperCase()}_QUERY_CONFIG,
1789
1186
  )
1790
1187
  }
1791
1188
 
1792
1189
  @Get('/:id')
1793
- @ApiTags('${pascal}')
1190
+ @ApiTags('${e}')
1794
1191
  async getById(ctx: RequestContext) {
1795
- const result = await this.get${pascal}Query.execute(ctx.params.id)
1796
- if (!result) return ctx.notFound('${pascal} not found')
1192
+ const result = await this.get${e}Query.execute(ctx.params.id)
1193
+ if (!result) return ctx.notFound('${e} not found')
1797
1194
  ctx.json(result)
1798
1195
  }
1799
1196
 
1800
- @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })
1801
- @ApiTags('${pascal}')
1197
+ @Post('/', { body: create${e}Schema, name: 'Create${e}' })
1198
+ @ApiTags('${e}')
1802
1199
  async create(ctx: RequestContext) {
1803
- const result = await this.create${pascal}Command.execute(ctx.body)
1200
+ const result = await this.create${e}Command.execute(ctx.body)
1804
1201
  ctx.created(result)
1805
1202
  }
1806
1203
 
1807
- @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })
1808
- @ApiTags('${pascal}')
1204
+ @Put('/:id', { body: update${e}Schema, name: 'Update${e}' })
1205
+ @ApiTags('${e}')
1809
1206
  async update(ctx: RequestContext) {
1810
- const result = await this.update${pascal}Command.execute(ctx.params.id, ctx.body)
1207
+ const result = await this.update${e}Command.execute(ctx.params.id, ctx.body)
1811
1208
  ctx.json(result)
1812
1209
  }
1813
1210
 
1814
1211
  @Delete('/:id')
1815
- @ApiTags('${pascal}')
1212
+ @ApiTags('${e}')
1816
1213
  async remove(ctx: RequestContext) {
1817
- await this.delete${pascal}Command.execute(ctx.params.id)
1214
+ await this.delete${e}Command.execute(ctx.params.id)
1818
1215
  ctx.noContent()
1819
1216
  }
1820
1217
  }
1821
- `;
1822
- }
1823
- __name(generateCqrsController, "generateCqrsController");
1824
- function generateCqrsCommands(pascal, kebab) {
1825
- return [
1826
- {
1827
- file: `create-${kebab}.command.ts`,
1828
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1829
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'
1830
- import type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'
1831
- import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
1832
- import { ${pascal}Events } from '../events/${kebab}.events'
1218
+ `}s(ye,"generateCqrsController");function he(e,t){return[{file:`create-${t}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1219
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../${t}.repository'
1220
+ import type { Create${e}DTO } from '../dtos/create-${t}.dto'
1221
+ import type { ${e}ResponseDTO } from '../dtos/${t}-response.dto'
1222
+ import { ${e}Events } from '../events/${t}.events'
1833
1223
 
1834
1224
  @Service()
1835
- export class Create${pascal}Command {
1225
+ export class Create${e}Command {
1836
1226
  constructor(
1837
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
1838
- @Inject(${pascal}Events) private readonly events: ${pascal}Events,
1227
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1228
+ @Inject(${e}Events) private readonly events: ${e}Events,
1839
1229
  ) {}
1840
1230
 
1841
- async execute(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {
1231
+ async execute(dto: Create${e}DTO): Promise<${e}ResponseDTO> {
1842
1232
  const result = await this.repo.create(dto)
1843
- this.events.emit('${kebab}.created', result)
1233
+ this.events.emit('${t}.created', result)
1844
1234
  return result
1845
1235
  }
1846
1236
  }
1847
- `
1848
- },
1849
- {
1850
- file: `update-${kebab}.command.ts`,
1851
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1852
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'
1853
- import type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'
1854
- import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
1855
- import { ${pascal}Events } from '../events/${kebab}.events'
1237
+ `},{file:`update-${t}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1238
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../${t}.repository'
1239
+ import type { Update${e}DTO } from '../dtos/update-${t}.dto'
1240
+ import type { ${e}ResponseDTO } from '../dtos/${t}-response.dto'
1241
+ import { ${e}Events } from '../events/${t}.events'
1856
1242
 
1857
1243
  @Service()
1858
- export class Update${pascal}Command {
1244
+ export class Update${e}Command {
1859
1245
  constructor(
1860
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
1861
- @Inject(${pascal}Events) private readonly events: ${pascal}Events,
1246
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1247
+ @Inject(${e}Events) private readonly events: ${e}Events,
1862
1248
  ) {}
1863
1249
 
1864
- async execute(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {
1250
+ async execute(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO> {
1865
1251
  const result = await this.repo.update(id, dto)
1866
- this.events.emit('${kebab}.updated', result)
1252
+ this.events.emit('${t}.updated', result)
1867
1253
  return result
1868
1254
  }
1869
1255
  }
1870
- `
1871
- },
1872
- {
1873
- file: `delete-${kebab}.command.ts`,
1874
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1875
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'
1876
- import { ${pascal}Events } from '../events/${kebab}.events'
1256
+ `},{file:`delete-${t}.command.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1257
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../${t}.repository'
1258
+ import { ${e}Events } from '../events/${t}.events'
1877
1259
 
1878
1260
  @Service()
1879
- export class Delete${pascal}Command {
1261
+ export class Delete${e}Command {
1880
1262
  constructor(
1881
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
1882
- @Inject(${pascal}Events) private readonly events: ${pascal}Events,
1263
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1264
+ @Inject(${e}Events) private readonly events: ${e}Events,
1883
1265
  ) {}
1884
1266
 
1885
1267
  async execute(id: string): Promise<void> {
1886
1268
  await this.repo.delete(id)
1887
- this.events.emit('${kebab}.deleted', { id })
1269
+ this.events.emit('${t}.deleted', { id })
1888
1270
  }
1889
1271
  }
1890
- `
1891
- }
1892
- ];
1893
- }
1894
- __name(generateCqrsCommands, "generateCqrsCommands");
1895
- function generateCqrsQueries(pascal, kebab, plural, pluralPascal) {
1896
- return [
1897
- {
1898
- file: `get-${kebab}.query.ts`,
1899
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1900
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'
1901
- import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
1272
+ `}]}s(he,"generateCqrsCommands");function we(e,t,o,r){return[{file:`get-${t}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1273
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../${t}.repository'
1274
+ import type { ${e}ResponseDTO } from '../dtos/${t}-response.dto'
1902
1275
 
1903
1276
  @Service()
1904
- export class Get${pascal}Query {
1277
+ export class Get${e}Query {
1905
1278
  constructor(
1906
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
1279
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1907
1280
  ) {}
1908
1281
 
1909
- async execute(id: string): Promise<${pascal}ResponseDTO | null> {
1282
+ async execute(id: string): Promise<${e}ResponseDTO | null> {
1910
1283
  return this.repo.findById(id)
1911
1284
  }
1912
1285
  }
1913
- `
1914
- },
1915
- {
1916
- file: `list-${plural}.query.ts`,
1917
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1918
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../${kebab}.repository'
1286
+ `},{file:`list-${o}.query.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1287
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../${t}.repository'
1919
1288
  import type { ParsedQuery } from '@forinda/kickjs-http'
1920
1289
 
1921
1290
  @Service()
1922
- export class List${pluralPascal}Query {
1291
+ export class List${r}Query {
1923
1292
  constructor(
1924
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
1293
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
1925
1294
  ) {}
1926
1295
 
1927
1296
  async execute(parsed: ParsedQuery) {
1928
1297
  return this.repo.findPaginated(parsed)
1929
1298
  }
1930
1299
  }
1931
- `
1932
- }
1933
- ];
1934
- }
1935
- __name(generateCqrsQueries, "generateCqrsQueries");
1936
- function generateCqrsEvents(pascal, kebab) {
1937
- return [
1938
- {
1939
- file: `${kebab}.events.ts`,
1940
- content: `import { Service } from '@forinda/kickjs-core'
1300
+ `}]}s(we,"generateCqrsQueries");function ve(e,t){return[{file:`${t}.events.ts`,content:`import { Service } from '@forinda/kickjs-core'
1941
1301
  import { EventEmitter } from 'node:events'
1942
- import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
1302
+ import type { ${e}ResponseDTO } from '../dtos/${t}-response.dto'
1943
1303
 
1944
1304
  /**
1945
- * ${pascal} domain event types.
1305
+ * ${e} domain event types.
1946
1306
  *
1947
1307
  * These events are emitted by commands after state changes.
1948
1308
  * Subscribe to them in event handlers for side effects:
@@ -1951,346 +1311,118 @@ import type { ${pascal}ResponseDTO } from '../dtos/${kebab}-response.dto'
1951
1311
  * - Audit logging
1952
1312
  * - Cache invalidation
1953
1313
  */
1954
- export interface ${pascal}EventMap {
1955
- '${kebab}.created': ${pascal}ResponseDTO
1956
- '${kebab}.updated': ${pascal}ResponseDTO
1957
- '${kebab}.deleted': { id: string }
1314
+ export interface ${e}EventMap {
1315
+ '${t}.created': ${e}ResponseDTO
1316
+ '${t}.updated': ${e}ResponseDTO
1317
+ '${t}.deleted': { id: string }
1958
1318
  }
1959
1319
 
1960
1320
  @Service()
1961
- export class ${pascal}Events {
1321
+ export class ${e}Events {
1962
1322
  private emitter = new EventEmitter()
1963
1323
 
1964
- emit<K extends keyof ${pascal}EventMap>(event: K, data: ${pascal}EventMap[K]): void {
1324
+ emit<K extends keyof ${e}EventMap>(event: K, data: ${e}EventMap[K]): void {
1965
1325
  this.emitter.emit(event, data)
1966
1326
  }
1967
1327
 
1968
- on<K extends keyof ${pascal}EventMap>(event: K, handler: (data: ${pascal}EventMap[K]) => void): void {
1328
+ on<K extends keyof ${e}EventMap>(event: K, handler: (data: ${e}EventMap[K]) => void): void {
1969
1329
  this.emitter.on(event, handler)
1970
1330
  }
1971
1331
 
1972
- off<K extends keyof ${pascal}EventMap>(event: K, handler: (data: ${pascal}EventMap[K]) => void): void {
1332
+ off<K extends keyof ${e}EventMap>(event: K, handler: (data: ${e}EventMap[K]) => void): void {
1973
1333
  this.emitter.off(event, handler)
1974
1334
  }
1975
1335
  }
1976
- `
1977
- },
1978
- {
1979
- file: `on-${kebab}-change.handler.ts`,
1980
- content: `import { Service, Autowired } from '@forinda/kickjs-core'
1981
- import { ${pascal}Events } from './${kebab}.events'
1336
+ `},{file:`on-${t}-change.handler.ts`,content:`import { Service, Autowired } from '@forinda/kickjs-core'
1337
+ import { ${e}Events } from './${t}.events'
1982
1338
 
1983
1339
  /**
1984
- * ${pascal} Change Event Handler
1340
+ * ${e} Change Event Handler
1985
1341
  *
1986
1342
  * Reacts to domain events emitted by commands.
1987
1343
  * Wire up side effects here:
1988
1344
  *
1989
1345
  * 1. WebSocket broadcast \u2014 notify connected clients in real-time
1990
1346
  * import { WsGateway } from '@forinda/kickjs-ws'
1991
- * this.ws.broadcast('${kebab}-channel', { event, data })
1347
+ * this.ws.broadcast('${t}-channel', { event, data })
1992
1348
  *
1993
1349
  * 2. Queue dispatch \u2014 offload heavy processing to background workers
1994
1350
  * import { QueueService } from '@forinda/kickjs-queue'
1995
- * this.queue.add('${kebab}-etl', { action: event, payload: data })
1351
+ * this.queue.add('${t}-etl', { action: event, payload: data })
1996
1352
  *
1997
1353
  * 3. ETL pipeline \u2014 transform and load data to external systems
1998
1354
  * await this.etlPipeline.process(data)
1999
1355
  */
2000
1356
  @Service()
2001
- export class On${pascal}ChangeHandler {
2002
- @Autowired() private events!: ${pascal}Events
1357
+ export class On${e}ChangeHandler {
1358
+ @Autowired() private events!: ${e}Events
2003
1359
 
2004
1360
  // Uncomment to inject WebSocket and Queue services:
2005
1361
  // @Autowired() private ws!: WsGateway
2006
1362
  // @Autowired() private queue!: QueueService
2007
1363
 
2008
1364
  onInit(): void {
2009
- this.events.on('${kebab}.created', (data) => {
2010
- console.log('[${pascal}] Created:', data.id)
1365
+ this.events.on('${t}.created', (data) => {
1366
+ console.log('[${e}] Created:', data.id)
2011
1367
  // TODO: Broadcast via WebSocket
2012
- // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.created', data })
1368
+ // this.ws.broadcast('${t}-channel', { event: '${t}.created', data })
2013
1369
  // TODO: Dispatch to queue for async processing / ETL
2014
- // this.queue.add('${kebab}-etl', { action: 'create', payload: data })
1370
+ // this.queue.add('${t}-etl', { action: 'create', payload: data })
2015
1371
  })
2016
1372
 
2017
- this.events.on('${kebab}.updated', (data) => {
2018
- console.log('[${pascal}] Updated:', data.id)
1373
+ this.events.on('${t}.updated', (data) => {
1374
+ console.log('[${e}] Updated:', data.id)
2019
1375
  // TODO: Broadcast via WebSocket
2020
- // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.updated', data })
1376
+ // this.ws.broadcast('${t}-channel', { event: '${t}.updated', data })
2021
1377
  })
2022
1378
 
2023
- this.events.on('${kebab}.deleted', (data) => {
2024
- console.log('[${pascal}] Deleted:', data.id)
1379
+ this.events.on('${t}.deleted', (data) => {
1380
+ console.log('[${e}] Deleted:', data.id)
2025
1381
  // TODO: Broadcast via WebSocket
2026
- // this.ws.broadcast('${kebab}-channel', { event: '${kebab}.deleted', data })
1382
+ // this.ws.broadcast('${t}-channel', { event: '${t}.deleted', data })
2027
1383
  })
2028
1384
  }
2029
1385
  }
2030
- `
2031
- }
2032
- ];
2033
- }
2034
- __name(generateCqrsEvents, "generateCqrsEvents");
2035
-
2036
- // src/generators/module.ts
2037
- function promptUser(question) {
2038
- const rl = createInterface2({
2039
- input: process.stdin,
2040
- output: process.stdout
2041
- });
2042
- return new Promise((resolve8) => {
2043
- rl.question(question, (answer) => {
2044
- rl.close();
2045
- resolve8(answer.trim().toLowerCase());
2046
- });
2047
- });
2048
- }
2049
- __name(promptUser, "promptUser");
2050
- async function generateModule(options) {
2051
- const { name, modulesDir, noEntity, noTests, repo = "inmemory", force, dryRun } = options;
2052
- let pattern = options.pattern ?? "ddd";
2053
- if (options.minimal) pattern = "minimal";
2054
- const kebab = toKebabCase(name);
2055
- const pascal = toPascalCase(name);
2056
- const plural = pluralize(kebab);
2057
- const pluralPascal = pluralizePascal(pascal);
2058
- const moduleDir = join2(modulesDir, plural);
2059
- const files = [];
2060
- let overwriteAll = force ?? false;
2061
- const write = /* @__PURE__ */ __name(async (relativePath, content) => {
2062
- const fullPath = join2(moduleDir, relativePath);
2063
- if (dryRun) {
2064
- files.push(fullPath);
2065
- return;
2066
- }
2067
- if (!overwriteAll && await fileExists(fullPath)) {
2068
- const answer = await promptUser(` File already exists: ${relativePath}
2069
- Overwrite? (y/n/a = yes/no/all) `);
2070
- if (answer === "a") {
2071
- overwriteAll = true;
2072
- } else if (answer !== "y") {
2073
- console.log(` Skipped: ${relativePath}`);
2074
- return;
2075
- }
2076
- }
2077
- await writeFileSafe(fullPath, content);
2078
- files.push(fullPath);
2079
- }, "write");
2080
- const ctx = {
2081
- kebab,
2082
- pascal,
2083
- plural,
2084
- pluralPascal,
2085
- moduleDir,
2086
- repo,
2087
- noEntity: noEntity ?? false,
2088
- noTests: noTests ?? false,
2089
- write,
2090
- files
2091
- };
2092
- switch (pattern) {
2093
- case "minimal":
2094
- await generateMinimalFiles(ctx);
2095
- break;
2096
- case "rest":
2097
- await generateRestFiles(ctx);
2098
- break;
2099
- case "cqrs":
2100
- await generateCqrsFiles(ctx);
2101
- break;
2102
- case "graphql":
2103
- case "ddd":
2104
- default:
2105
- await generateDddFiles(ctx);
2106
- break;
2107
- }
2108
- if (!dryRun) {
2109
- await autoRegisterModule(modulesDir, pascal, plural);
2110
- }
2111
- return files;
2112
- }
2113
- __name(generateModule, "generateModule");
2114
- async function generateMinimalFiles(ctx) {
2115
- const { pascal, kebab, plural, write } = ctx;
2116
- await write("index.ts", generateMinimalModuleIndex(pascal, kebab, plural));
2117
- await write(`${kebab}.controller.ts`, `import { Controller, Get } from '@forinda/kickjs-core'
1386
+ `}]}s(ve,"generateCqrsEvents");function Ut(e){let t=Pt({input:process.stdin,output:process.stdout});return new Promise(o=>{t.question(e,r=>{t.close(),o(r.trim().toLowerCase())})})}s(Ut,"promptUser");async function Ae(e){let{name:t,modulesDir:o,noEntity:r,noTests:n,repo:i="inmemory",force:d,dryRun:c}=e,a=e.pattern??"ddd";e.minimal&&(a="minimal");let p=$(t),m=f(t),u=j(p),w=X(m),g=Ce(o,u),y=[],W=d??!1,A={kebab:p,pascal:m,plural:u,pluralPascal:w,moduleDir:g,repo:i,noEntity:r??!1,noTests:n??!1,write:s(async(U,ft)=>{let J=Ce(g,U);if(c){y.push(J);return}if(!W&&await _(J)){let Ie=await Ut(` File already exists: ${U}
1387
+ Overwrite? (y/n/a = yes/no/all) `);if(Ie==="a")W=!0;else if(Ie!=="y"){console.log(` Skipped: ${U}`);return}}await l(J,ft),y.push(J)},"write"),files:y};switch(a){case"minimal":await zt(A);break;case"rest":await qt(A);break;case"cqrs":await _t(A);break;default:await Mt(A);break}return c||await bt(o,m,u),y}s(Ae,"generateModule");async function zt(e){let{pascal:t,kebab:o,plural:r,write:n}=e;await n("index.ts",se(t,o,r)),await n(`${o}.controller.ts`,`import { Controller, Get } from '@forinda/kickjs-core'
2118
1388
  import type { RequestContext } from '@forinda/kickjs-http'
2119
1389
 
2120
1390
  @Controller()
2121
- export class ${pascal}Controller {
1391
+ export class ${t}Controller {
2122
1392
  @Get('/')
2123
1393
  async list(ctx: RequestContext) {
2124
- ctx.json({ message: '${pascal} list' })
2125
- }
2126
- }
2127
- `);
2128
- }
2129
- __name(generateMinimalFiles, "generateMinimalFiles");
2130
- async function generateRestFiles(ctx) {
2131
- const { pascal, kebab, plural, pluralPascal, repo, noTests, write } = ctx;
2132
- await write("index.ts", generateRestModuleIndex(pascal, kebab, plural, repo));
2133
- await write(`${kebab}.constants.ts`, generateRestConstants(pascal));
2134
- await write(`${kebab}.controller.ts`, generateRestController(pascal, kebab, plural, pluralPascal));
2135
- await write(`${kebab}.service.ts`, generateRestService(pascal, kebab));
2136
- await write(`dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab));
2137
- await write(`dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab));
2138
- await write(`dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab));
2139
- await write(`${kebab}.repository.ts`, generateRepositoryInterface(pascal, kebab, "./dtos"));
2140
- const repoFileMap = {
2141
- inmemory: `in-memory-${kebab}`,
2142
- drizzle: `drizzle-${kebab}`,
2143
- prisma: `prisma-${kebab}`
2144
- };
2145
- const repoGeneratorMap = {
2146
- inmemory: /* @__PURE__ */ __name(() => generateInMemoryRepository(pascal, kebab, ".", "./dtos"), "inmemory"),
2147
- drizzle: /* @__PURE__ */ __name(() => generateDrizzleRepository(pascal, kebab, ".", "./dtos"), "drizzle"),
2148
- prisma: /* @__PURE__ */ __name(() => generatePrismaRepository(pascal, kebab, ".", "./dtos"), "prisma")
2149
- };
2150
- await write(`${repoFileMap[repo]}.repository.ts`, repoGeneratorMap[repo]());
2151
- if (!noTests) {
2152
- await write(`__tests__/${kebab}.controller.test.ts`, generateControllerTest(pascal, kebab, plural));
2153
- await write(`__tests__/${kebab}.repository.test.ts`, generateRepositoryTest(pascal, kebab, plural, `../${repoFileMap.inmemory}.repository`));
2154
- }
2155
- }
2156
- __name(generateRestFiles, "generateRestFiles");
2157
- async function generateCqrsFiles(ctx) {
2158
- const { pascal, kebab, plural, pluralPascal, repo, noTests, write } = ctx;
2159
- await write("index.ts", generateCqrsModuleIndex(pascal, kebab, plural, repo));
2160
- await write(`${kebab}.constants.ts`, generateRestConstants(pascal));
2161
- await write(`${kebab}.controller.ts`, generateCqrsController(pascal, kebab, plural, pluralPascal));
2162
- await write(`dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab));
2163
- await write(`dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab));
2164
- await write(`dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab));
2165
- const commands = generateCqrsCommands(pascal, kebab);
2166
- for (const cmd of commands) {
2167
- await write(`commands/${cmd.file}`, cmd.content);
2168
- }
2169
- const queries = generateCqrsQueries(pascal, kebab, plural, pluralPascal);
2170
- for (const q of queries) {
2171
- await write(`queries/${q.file}`, q.content);
2172
- }
2173
- const events = generateCqrsEvents(pascal, kebab);
2174
- for (const e of events) {
2175
- await write(`events/${e.file}`, e.content);
2176
- }
2177
- await write(`${kebab}.repository.ts`, generateRepositoryInterface(pascal, kebab, "./dtos"));
2178
- const repoFileMap = {
2179
- inmemory: `in-memory-${kebab}`,
2180
- drizzle: `drizzle-${kebab}`,
2181
- prisma: `prisma-${kebab}`
2182
- };
2183
- const repoGeneratorMap = {
2184
- inmemory: /* @__PURE__ */ __name(() => generateInMemoryRepository(pascal, kebab, ".", "./dtos"), "inmemory"),
2185
- drizzle: /* @__PURE__ */ __name(() => generateDrizzleRepository(pascal, kebab, ".", "./dtos"), "drizzle"),
2186
- prisma: /* @__PURE__ */ __name(() => generatePrismaRepository(pascal, kebab, ".", "./dtos"), "prisma")
2187
- };
2188
- await write(`${repoFileMap[repo]}.repository.ts`, repoGeneratorMap[repo]());
2189
- if (!noTests) {
2190
- await write(`__tests__/${kebab}.controller.test.ts`, generateControllerTest(pascal, kebab, plural));
2191
- await write(`__tests__/${kebab}.repository.test.ts`, generateRepositoryTest(pascal, kebab, plural, `../${repoFileMap.inmemory}.repository`));
1394
+ ctx.json({ message: '${t} list' })
2192
1395
  }
2193
1396
  }
2194
- __name(generateCqrsFiles, "generateCqrsFiles");
2195
- async function generateDddFiles(ctx) {
2196
- const { pascal, kebab, plural, pluralPascal, repo, noEntity, noTests, write } = ctx;
2197
- await write("index.ts", generateModuleIndex(pascal, kebab, plural, repo));
2198
- await write("constants.ts", repo === "drizzle" ? generateDrizzleConstants(pascal, kebab) : generateConstants(pascal));
2199
- await write(`presentation/${kebab}.controller.ts`, generateController(pascal, kebab, plural, pluralPascal));
2200
- await write(`application/dtos/create-${kebab}.dto.ts`, generateCreateDTO(pascal, kebab));
2201
- await write(`application/dtos/update-${kebab}.dto.ts`, generateUpdateDTO(pascal, kebab));
2202
- await write(`application/dtos/${kebab}-response.dto.ts`, generateResponseDTO(pascal, kebab));
2203
- const useCases = generateUseCases(pascal, kebab, plural, pluralPascal);
2204
- for (const uc of useCases) {
2205
- await write(`application/use-cases/${uc.file}`, uc.content);
2206
- }
2207
- await write(`domain/repositories/${kebab}.repository.ts`, generateRepositoryInterface(pascal, kebab));
2208
- await write(`domain/services/${kebab}-domain.service.ts`, generateDomainService(pascal, kebab));
2209
- const repoFileMap = {
2210
- inmemory: `in-memory-${kebab}`,
2211
- drizzle: `drizzle-${kebab}`,
2212
- prisma: `prisma-${kebab}`
2213
- };
2214
- const repoGeneratorMap = {
2215
- inmemory: /* @__PURE__ */ __name(() => generateInMemoryRepository(pascal, kebab), "inmemory"),
2216
- drizzle: /* @__PURE__ */ __name(() => generateDrizzleRepository(pascal, kebab), "drizzle"),
2217
- prisma: /* @__PURE__ */ __name(() => generatePrismaRepository(pascal, kebab), "prisma")
2218
- };
2219
- await write(`infrastructure/repositories/${repoFileMap[repo]}.repository.ts`, repoGeneratorMap[repo]());
2220
- if (!noEntity) {
2221
- await write(`domain/entities/${kebab}.entity.ts`, generateEntity(pascal, kebab));
2222
- await write(`domain/value-objects/${kebab}-id.vo.ts`, generateValueObject(pascal, kebab));
2223
- }
2224
- if (!noTests) {
2225
- await write(`__tests__/${kebab}.controller.test.ts`, generateControllerTest(pascal, kebab, plural));
2226
- await write(`__tests__/${kebab}.repository.test.ts`, generateRepositoryTest(pascal, kebab, plural));
2227
- }
2228
- }
2229
- __name(generateDddFiles, "generateDddFiles");
2230
- async function autoRegisterModule(modulesDir, pascal, plural) {
2231
- const indexPath = join2(modulesDir, "index.ts");
2232
- const exists = await fileExists(indexPath);
2233
- if (!exists) {
2234
- await writeFileSafe(indexPath, `import type { AppModuleClass } from '@forinda/kickjs-core'
2235
- import { ${pascal}Module } from './${plural}'
2236
-
2237
- export const modules: AppModuleClass[] = [${pascal}Module]
2238
- `);
2239
- return;
2240
- }
2241
- let content = await readFile2(indexPath, "utf-8");
2242
- const importLine = `import { ${pascal}Module } from './${plural}'`;
2243
- if (!content.includes(`${pascal}Module`)) {
2244
- const lastImportIdx = content.lastIndexOf("import ");
2245
- if (lastImportIdx !== -1) {
2246
- const lineEnd = content.indexOf("\n", lastImportIdx);
2247
- content = content.slice(0, lineEnd + 1) + importLine + "\n" + content.slice(lineEnd + 1);
2248
- } else {
2249
- content = importLine + "\n" + content;
2250
- }
2251
- content = content.replace(/(=\s*\[)([\s\S]*?)(])/, (_match, open, existing, close) => {
2252
- const trimmed = existing.trim();
2253
- if (!trimmed) {
2254
- return `${open}${pascal}Module${close}`;
2255
- }
2256
- const needsComma = trimmed.endsWith(",") ? "" : ",";
2257
- return `${open}${existing.trimEnd()}${needsComma} ${pascal}Module${close}`;
2258
- });
2259
- }
2260
- await writeFile2(indexPath, content, "utf-8");
2261
- }
2262
- __name(autoRegisterModule, "autoRegisterModule");
2263
-
2264
- // src/generators/adapter.ts
2265
- import { join as join3 } from "path";
2266
- async function generateAdapter(options) {
2267
- const { name, outDir } = options;
2268
- const kebab = toKebabCase(name);
2269
- const pascal = toPascalCase(name);
2270
- const files = [];
2271
- const filePath = join3(outDir, `${kebab}.adapter.ts`);
2272
- await writeFileSafe(filePath, `import type { Express } from 'express'
1397
+ `)}s(zt,"generateMinimalFiles");async function qt(e){let{pascal:t,kebab:o,plural:r,pluralPascal:n,repo:i,noTests:d,write:c}=e;await c("index.ts",ne(t,o,r,i)),await c(`${o}.constants.ts`,ee(t)),await c(`${o}.controller.ts`,de(t,o,r,n)),await c(`${o}.service.ts`,$e(t,o)),await c(`dtos/create-${o}.dto.ts`,M(t,o)),await c(`dtos/update-${o}.dto.ts`,b(t,o)),await c(`dtos/${o}-response.dto.ts`,Q(t,o)),await c(`${o}.repository.ts`,G(t,o,"./dtos"));let a={inmemory:`in-memory-${o}`,drizzle:`drizzle-${o}`,prisma:`prisma-${o}`},p={inmemory:s(()=>F(t,o,".","./dtos"),"inmemory"),drizzle:s(()=>L(t,o,".","./dtos"),"drizzle"),prisma:s(()=>N(t,o,".","./dtos"),"prisma")};await c(`${a[i]}.repository.ts`,p[i]()),d||(await c(`__tests__/${o}.controller.test.ts`,Y(t,o,r)),await c(`__tests__/${o}.repository.test.ts`,B(t,o,r,`../${a.inmemory}.repository`)))}s(qt,"generateRestFiles");async function _t(e){let{pascal:t,kebab:o,plural:r,pluralPascal:n,repo:i,noTests:d,write:c}=e;await c("index.ts",ge(t,o,r,i)),await c(`${o}.constants.ts`,ee(t)),await c(`${o}.controller.ts`,ye(t,o,r,n)),await c(`dtos/create-${o}.dto.ts`,M(t,o)),await c(`dtos/update-${o}.dto.ts`,b(t,o)),await c(`dtos/${o}-response.dto.ts`,Q(t,o));let a=he(t,o);for(let g of a)await c(`commands/${g.file}`,g.content);let p=we(t,o,r,n);for(let g of p)await c(`queries/${g.file}`,g.content);let m=ve(t,o);for(let g of m)await c(`events/${g.file}`,g.content);await c(`${o}.repository.ts`,G(t,o,"./dtos"));let u={inmemory:`in-memory-${o}`,drizzle:`drizzle-${o}`,prisma:`prisma-${o}`},w={inmemory:s(()=>F(t,o,".","./dtos"),"inmemory"),drizzle:s(()=>L(t,o,".","./dtos"),"drizzle"),prisma:s(()=>N(t,o,".","./dtos"),"prisma")};await c(`${u[i]}.repository.ts`,w[i]()),d||(await c(`__tests__/${o}.controller.test.ts`,Y(t,o,r)),await c(`__tests__/${o}.repository.test.ts`,B(t,o,r,`../${u.inmemory}.repository`)))}s(_t,"generateCqrsFiles");async function Mt(e){let{pascal:t,kebab:o,plural:r,pluralPascal:n,repo:i,noEntity:d,noTests:c,write:a}=e;await a("index.ts",ie(t,o,r,i)),await a("constants.ts",i==="drizzle"?pe(t,o):ce(t)),await a(`presentation/${o}.controller.ts`,ae(t,o,r,n)),await a(`application/dtos/create-${o}.dto.ts`,M(t,o)),await a(`application/dtos/update-${o}.dto.ts`,b(t,o)),await a(`application/dtos/${o}-response.dto.ts`,Q(t,o));let p=me(t,o,r,n);for(let w of p)await a(`application/use-cases/${w.file}`,w.content);await a(`domain/repositories/${o}.repository.ts`,G(t,o)),await a(`domain/services/${o}-domain.service.ts`,le(t,o));let m={inmemory:`in-memory-${o}`,drizzle:`drizzle-${o}`,prisma:`prisma-${o}`},u={inmemory:s(()=>F(t,o),"inmemory"),drizzle:s(()=>L(t,o),"drizzle"),prisma:s(()=>N(t,o),"prisma")};await a(`infrastructure/repositories/${m[i]}.repository.ts`,u[i]()),d||(await a(`domain/entities/${o}.entity.ts`,ue(t,o)),await a(`domain/value-objects/${o}-id.vo.ts`,fe(t,o))),c||(await a(`__tests__/${o}.controller.test.ts`,Y(t,o,r)),await a(`__tests__/${o}.repository.test.ts`,B(t,o,r)))}s(Mt,"generateDddFiles");async function bt(e,t,o){let r=Ce(e,"index.ts");if(!await _(r)){await l(r,`import type { AppModuleClass } from '@forinda/kickjs-core'
1398
+ import { ${t}Module } from './${o}'
1399
+
1400
+ export const modules: AppModuleClass[] = [${t}Module]
1401
+ `);return}let i=await Et(r,"utf-8"),d=`import { ${t}Module } from './${o}'`;if(!i.includes(`${t}Module`)){let c=i.lastIndexOf("import ");if(c!==-1){let a=i.indexOf(`
1402
+ `,c);i=i.slice(0,a+1)+d+`
1403
+ `+i.slice(a+1)}else i=d+`
1404
+ `+i;i=i.replace(/(=\s*\[)([\s\S]*?)(])/,(a,p,m,u)=>{let w=m.trim();if(!w)return`${p}${t}Module${u}`;let g=w.endsWith(",")?"":",";return`${p}${m.trimEnd()}${g} ${t}Module${u}`})}await At(r,i,"utf-8")}s(bt,"autoRegisterModule");import{join as Qt}from"path";async function Ue(e){let{name:t,outDir:o}=e,r=$(t),n=f(t),i=[],d=Qt(o,`${r}.adapter.ts`);return await l(d,`import type { Express } from 'express'
2273
1405
  import type { AppAdapter, AdapterMiddleware, Container } from '@forinda/kickjs-core'
2274
1406
 
2275
- export interface ${pascal}AdapterOptions {
1407
+ export interface ${n}AdapterOptions {
2276
1408
  // Add your adapter configuration here
2277
1409
  }
2278
1410
 
2279
1411
  /**
2280
- * ${pascal} adapter.
1412
+ * ${n} adapter.
2281
1413
  *
2282
1414
  * Hooks into the Application lifecycle to add middleware, routes,
2283
1415
  * or external service connections.
2284
1416
  *
2285
1417
  * Usage:
2286
1418
  * bootstrap({
2287
- * adapters: [new ${pascal}Adapter({ ... })],
1419
+ * adapters: [new ${n}Adapter({ ... })],
2288
1420
  * })
2289
1421
  */
2290
- export class ${pascal}Adapter implements AppAdapter {
2291
- name = '${pascal}Adapter'
1422
+ export class ${n}Adapter implements AppAdapter {
1423
+ name = '${n}Adapter'
2292
1424
 
2293
- constructor(private options: ${pascal}AdapterOptions = {}) {}
1425
+ constructor(private options: ${n}AdapterOptions = {}) {}
2294
1426
 
2295
1427
  /**
2296
1428
  * Return middleware entries that the Application will mount.
@@ -2303,7 +1435,7 @@ export class ${pascal}Adapter implements AppAdapter {
2303
1435
  // {
2304
1436
  // phase: 'beforeGlobal',
2305
1437
  // handler: (_req: any, res: any, next: any) => {
2306
- // res.setHeader('X-${pascal}', 'true')
1438
+ // res.setHeader('X-${n}', 'true')
2307
1439
  // next()
2308
1440
  // },
2309
1441
  // },
@@ -2323,7 +1455,7 @@ export class ${pascal}Adapter implements AppAdapter {
2323
1455
  */
2324
1456
  beforeMount(app: Express, container: Container): void {
2325
1457
  // Example: mount a status route
2326
- // app.get('/${kebab}/status', (_req, res) => {
1458
+ // app.get('/${r}/status', (_req, res) => {
2327
1459
  // res.json({ status: 'ok' })
2328
1460
  // })
2329
1461
  }
@@ -2355,133 +1487,45 @@ export class ${pascal}Adapter implements AppAdapter {
2355
1487
  // await this.pool.end()
2356
1488
  }
2357
1489
  }
2358
- `);
2359
- files.push(filePath);
2360
- return files;
2361
- }
2362
- __name(generateAdapter, "generateAdapter");
2363
-
2364
- // src/generators/middleware.ts
2365
- import { join as join5 } from "path";
2366
-
2367
- // src/utils/resolve-out-dir.ts
2368
- import { resolve as resolve2, join as join4 } from "path";
2369
- var DDD_FOLDER_MAP = {
2370
- controller: "presentation",
2371
- service: "domain/services",
2372
- dto: "application/dtos",
2373
- guard: "presentation/guards",
2374
- middleware: "middleware"
2375
- };
2376
- var FLAT_FOLDER_MAP = {
2377
- controller: "",
2378
- service: "",
2379
- dto: "dtos",
2380
- guard: "guards",
2381
- middleware: "middleware"
2382
- };
2383
- var CQRS_FOLDER_MAP = {
2384
- controller: "",
2385
- service: "",
2386
- dto: "dtos",
2387
- guard: "guards",
2388
- middleware: "middleware",
2389
- command: "commands",
2390
- query: "queries",
2391
- event: "events"
2392
- };
2393
- function resolveOutDir(options) {
2394
- const { type, outDir, moduleName, modulesDir = "src/modules", defaultDir, pattern = "ddd" } = options;
2395
- if (outDir) return resolve2(outDir);
2396
- if (moduleName) {
2397
- const folderMap = pattern === "ddd" ? DDD_FOLDER_MAP : pattern === "cqrs" ? CQRS_FOLDER_MAP : FLAT_FOLDER_MAP;
2398
- const kebab = toKebabCase(moduleName);
2399
- const plural = pluralize(kebab);
2400
- const subfolder = folderMap[type] ?? "";
2401
- const base = join4(modulesDir, plural);
2402
- return resolve2(subfolder ? join4(base, subfolder) : base);
2403
- }
2404
- return resolve2(defaultDir);
2405
- }
2406
- __name(resolveOutDir, "resolveOutDir");
2407
-
2408
- // src/generators/middleware.ts
2409
- async function generateMiddleware(options) {
2410
- const { name, moduleName, modulesDir, pattern } = options;
2411
- const outDir = resolveOutDir({
2412
- type: "middleware",
2413
- outDir: options.outDir,
2414
- moduleName,
2415
- modulesDir,
2416
- defaultDir: "src/middleware",
2417
- pattern
2418
- });
2419
- const kebab = toKebabCase(name);
2420
- const camel = toCamelCase(name);
2421
- const files = [];
2422
- const filePath = join5(outDir, `${kebab}.middleware.ts`);
2423
- await writeFileSafe(filePath, `import type { Request, Response, NextFunction } from 'express'
2424
-
2425
- export interface ${toPascalCase(name)}Options {
1490
+ `),i.push(d),i}s(Ue,"generateAdapter");import{join as Nt}from"path";import{resolve as xe,join as ze}from"path";var Gt={controller:"presentation",service:"domain/services",dto:"application/dtos",guard:"presentation/guards",middleware:"middleware"},Ft={controller:"",service:"",dto:"dtos",guard:"guards",middleware:"middleware"},Lt={controller:"",service:"",dto:"dtos",guard:"guards",middleware:"middleware",command:"commands",query:"queries",event:"events"};function O(e){let{type:t,outDir:o,moduleName:r,modulesDir:n="src/modules",defaultDir:i,pattern:d="ddd"}=e;if(o)return xe(o);if(r){let c=d==="ddd"?Gt:d==="cqrs"?Lt:Ft,a=$(r),p=j(a),m=c[t]??"",u=ze(n,p);return xe(m?ze(u,m):u)}return xe(i)}s(O,"resolveOutDir");async function qe(e){let{name:t,moduleName:o,modulesDir:r,pattern:n}=e,i=O({type:"middleware",outDir:e.outDir,moduleName:o,modulesDir:r,defaultDir:"src/middleware",pattern:n}),d=$(t),c=x(t),a=[],p=Nt(i,`${d}.middleware.ts`);return await l(p,`import type { Request, Response, NextFunction } from 'express'
1491
+
1492
+ export interface ${f(t)}Options {
2426
1493
  // Add configuration options here
2427
1494
  }
2428
1495
 
2429
1496
  /**
2430
- * ${toPascalCase(name)} middleware.
1497
+ * ${f(t)} middleware.
2431
1498
  *
2432
1499
  * Usage in bootstrap:
2433
- * middleware: [${camel}()]
1500
+ * middleware: [${c}()]
2434
1501
  *
2435
1502
  * Usage with adapter:
2436
- * middleware() { return [{ handler: ${camel}(), phase: 'afterGlobal' }] }
1503
+ * middleware() { return [{ handler: ${c}(), phase: 'afterGlobal' }] }
2437
1504
  *
2438
1505
  * Usage with @Middleware decorator:
2439
- * @Middleware(${camel}())
1506
+ * @Middleware(${c}())
2440
1507
  */
2441
- export function ${camel}(options: ${toPascalCase(name)}Options = {}) {
1508
+ export function ${c}(options: ${f(t)}Options = {}) {
2442
1509
  return (req: Request, res: Response, next: NextFunction) => {
2443
1510
  // Implement your middleware logic here
2444
1511
  next()
2445
1512
  }
2446
1513
  }
2447
- `);
2448
- files.push(filePath);
2449
- return files;
2450
- }
2451
- __name(generateMiddleware, "generateMiddleware");
2452
-
2453
- // src/generators/guard.ts
2454
- import { join as join6 } from "path";
2455
- async function generateGuard(options) {
2456
- const { name, moduleName, modulesDir, pattern } = options;
2457
- const outDir = resolveOutDir({
2458
- type: "guard",
2459
- outDir: options.outDir,
2460
- moduleName,
2461
- modulesDir,
2462
- defaultDir: "src/guards",
2463
- pattern
2464
- });
2465
- const kebab = toKebabCase(name);
2466
- const camel = toCamelCase(name);
2467
- const pascal = toPascalCase(name);
2468
- const files = [];
2469
- const filePath = join6(outDir, `${kebab}.guard.ts`);
2470
- await writeFileSafe(filePath, `import { Container, HttpException } from '@forinda/kickjs-core'
1514
+ `),a.push(p),a}s(qe,"generateMiddleware");import{join as Yt}from"path";async function _e(e){let{name:t,moduleName:o,modulesDir:r,pattern:n}=e,i=O({type:"guard",outDir:e.outDir,moduleName:o,modulesDir:r,defaultDir:"src/guards",pattern:n}),d=$(t),c=x(t),a=f(t),p=[],m=Yt(i,`${d}.guard.ts`);return await l(m,`import { Container, HttpException } from '@forinda/kickjs-core'
2471
1515
  import type { RequestContext } from '@forinda/kickjs-http'
2472
1516
 
2473
1517
  /**
2474
- * ${pascal} guard.
1518
+ * ${a} guard.
2475
1519
  *
2476
1520
  * Guards protect routes by checking conditions before the handler runs.
2477
1521
  * Return early with an error response to block access.
2478
1522
  *
2479
1523
  * Usage:
2480
- * @Middleware(${camel}Guard)
1524
+ * @Middleware(${c}Guard)
2481
1525
  * @Get('/protected')
2482
1526
  * async handler(ctx: RequestContext) { ... }
2483
1527
  */
2484
- export async function ${camel}Guard(ctx: RequestContext, next: () => void): Promise<void> {
1528
+ export async function ${c}Guard(ctx: RequestContext, next: () => void): Promise<void> {
2485
1529
  // Example: check for an authorization header
2486
1530
  const header = ctx.headers.authorization
2487
1531
  if (!header?.startsWith('Bearer ')) {
@@ -2503,146 +1547,46 @@ export async function ${camel}Guard(ctx: RequestContext, next: () => void): Prom
2503
1547
  ctx.res.status(401).json({ message: 'Invalid or expired token' })
2504
1548
  }
2505
1549
  }
2506
- `);
2507
- files.push(filePath);
2508
- return files;
2509
- }
2510
- __name(generateGuard, "generateGuard");
2511
-
2512
- // src/generators/service.ts
2513
- import { join as join7 } from "path";
2514
- async function generateService(options) {
2515
- const { name, moduleName, modulesDir, pattern } = options;
2516
- const outDir = resolveOutDir({
2517
- type: "service",
2518
- outDir: options.outDir,
2519
- moduleName,
2520
- modulesDir,
2521
- defaultDir: "src/services",
2522
- pattern
2523
- });
2524
- const kebab = toKebabCase(name);
2525
- const pascal = toPascalCase(name);
2526
- const files = [];
2527
- const filePath = join7(outDir, `${kebab}.service.ts`);
2528
- await writeFileSafe(filePath, `import { Service } from '@forinda/kickjs-core'
1550
+ `),p.push(m),p}s(_e,"generateGuard");import{join as Bt}from"path";async function Me(e){let{name:t,moduleName:o,modulesDir:r,pattern:n}=e,i=O({type:"service",outDir:e.outDir,moduleName:o,modulesDir:r,defaultDir:"src/services",pattern:n}),d=$(t),c=f(t),a=[],p=Bt(i,`${d}.service.ts`);return await l(p,`import { Service } from '@forinda/kickjs-core'
2529
1551
 
2530
1552
  @Service()
2531
- export class ${pascal}Service {
1553
+ export class ${c}Service {
2532
1554
  // Inject dependencies via constructor
2533
1555
  // constructor(
2534
1556
  // @Inject(MY_REPO) private readonly repo: IMyRepository,
2535
1557
  // ) {}
2536
1558
  }
2537
- `);
2538
- files.push(filePath);
2539
- return files;
2540
- }
2541
- __name(generateService, "generateService");
2542
-
2543
- // src/generators/controller.ts
2544
- import { join as join8 } from "path";
2545
- async function generateController2(options) {
2546
- const { name, moduleName, modulesDir, pattern } = options;
2547
- const outDir = resolveOutDir({
2548
- type: "controller",
2549
- outDir: options.outDir,
2550
- moduleName,
2551
- modulesDir,
2552
- defaultDir: "src/controllers",
2553
- pattern
2554
- });
2555
- const kebab = toKebabCase(name);
2556
- const pascal = toPascalCase(name);
2557
- const files = [];
2558
- const filePath = join8(outDir, `${kebab}.controller.ts`);
2559
- await writeFileSafe(filePath, `import { Controller, Get, Post, Autowired } from '@forinda/kickjs-core'
1559
+ `),a.push(p),a}s(Me,"generateService");import{join as Ht}from"path";async function be(e){let{name:t,moduleName:o,modulesDir:r,pattern:n}=e,i=O({type:"controller",outDir:e.outDir,moduleName:o,modulesDir:r,defaultDir:"src/controllers",pattern:n}),d=$(t),c=f(t),a=[],p=Ht(i,`${d}.controller.ts`);return await l(p,`import { Controller, Get, Post, Autowired } from '@forinda/kickjs-core'
2560
1560
  import type { RequestContext } from '@forinda/kickjs-http'
2561
1561
 
2562
1562
  @Controller()
2563
- export class ${pascal}Controller {
1563
+ export class ${c}Controller {
2564
1564
  // @Autowired() private myService!: MyService
2565
1565
 
2566
1566
  @Get('/')
2567
1567
  async list(ctx: RequestContext) {
2568
- ctx.json({ message: '${pascal} list' })
1568
+ ctx.json({ message: '${c} list' })
2569
1569
  }
2570
1570
 
2571
1571
  @Post('/')
2572
1572
  async create(ctx: RequestContext) {
2573
- ctx.created({ message: '${pascal} created', data: ctx.body })
1573
+ ctx.created({ message: '${c} created', data: ctx.body })
2574
1574
  }
2575
1575
  }
2576
- `);
2577
- files.push(filePath);
2578
- return files;
2579
- }
2580
- __name(generateController2, "generateController");
2581
-
2582
- // src/generators/dto.ts
2583
- import { join as join9 } from "path";
2584
- async function generateDto(options) {
2585
- const { name, moduleName, modulesDir, pattern } = options;
2586
- const outDir = resolveOutDir({
2587
- type: "dto",
2588
- outDir: options.outDir,
2589
- moduleName,
2590
- modulesDir,
2591
- defaultDir: "src/dtos",
2592
- pattern
2593
- });
2594
- const kebab = toKebabCase(name);
2595
- const pascal = toPascalCase(name);
2596
- const camel = toCamelCase(name);
2597
- const files = [];
2598
- const filePath = join9(outDir, `${kebab}.dto.ts`);
2599
- await writeFileSafe(filePath, `import { z } from 'zod'
2600
-
2601
- export const ${camel}Schema = z.object({
1576
+ `),a.push(p),a}s(be,"generateController");import{join as Kt}from"path";async function Qe(e){let{name:t,moduleName:o,modulesDir:r,pattern:n}=e,i=O({type:"dto",outDir:e.outDir,moduleName:o,modulesDir:r,defaultDir:"src/dtos",pattern:n}),d=$(t),c=f(t),a=x(t),p=[],m=Kt(i,`${d}.dto.ts`);return await l(m,`import { z } from 'zod'
1577
+
1578
+ export const ${a}Schema = z.object({
2602
1579
  // Define your schema fields here
2603
1580
  name: z.string().min(1).max(200),
2604
1581
  })
2605
1582
 
2606
- export type ${pascal}DTO = z.infer<typeof ${camel}Schema>
2607
- `);
2608
- files.push(filePath);
2609
- return files;
2610
- }
2611
- __name(generateDto, "generateDto");
2612
-
2613
- // src/generators/config.ts
2614
- import { join as join10 } from "path";
2615
- import { existsSync as existsSync2 } from "fs";
2616
- import { createInterface as createInterface3 } from "readline";
2617
- async function confirm2(message) {
2618
- const rl = createInterface3({
2619
- input: process.stdin,
2620
- output: process.stdout
2621
- });
2622
- return new Promise((resolve8) => {
2623
- rl.question(` ${message} (y/N) `, (answer) => {
2624
- rl.close();
2625
- resolve8(answer.trim().toLowerCase() === "y");
2626
- });
2627
- });
2628
- }
2629
- __name(confirm2, "confirm");
2630
- async function generateConfig(options) {
2631
- const filePath = join10(options.outDir, "kick.config.ts");
2632
- const modulesDir = options.modulesDir ?? "src/modules";
2633
- const defaultRepo = options.defaultRepo ?? "inmemory";
2634
- if (existsSync2(filePath) && !options.force) {
2635
- const overwrite = await confirm2("kick.config.ts already exists. Overwrite?");
2636
- if (!overwrite) {
2637
- console.log("\n Skipped \u2014 existing kick.config.ts preserved.");
2638
- return [];
2639
- }
2640
- }
2641
- await writeFileSafe(filePath, `import { defineConfig } from '@forinda/kickjs-cli'
1583
+ export type ${c}DTO = z.infer<typeof ${a}Schema>
1584
+ `),p.push(m),p}s(Qe,"generateDto");import{join as Wt}from"path";import{existsSync as Jt}from"fs";import{createInterface as Vt}from"readline";async function Zt(e){let t=Vt({input:process.stdin,output:process.stdout});return new Promise(o=>{t.question(` ${e} (y/N) `,r=>{t.close(),o(r.trim().toLowerCase()==="y")})})}s(Zt,"confirm");async function Ge(e){let t=Wt(e.outDir,"kick.config.ts"),o=e.modulesDir??"src/modules",r=e.defaultRepo??"inmemory";return Jt(t)&&!e.force&&!await Zt("kick.config.ts already exists. Overwrite?")?(console.log(`
1585
+ Skipped \u2014 existing kick.config.ts preserved.`),[]):(await l(t,`import { defineConfig } from '@forinda/kickjs-cli'
2642
1586
 
2643
1587
  export default defineConfig({
2644
- modulesDir: '${modulesDir}',
2645
- defaultRepo: '${defaultRepo}',
1588
+ modulesDir: '${o}',
1589
+ defaultRepo: '${r}',
2646
1590
 
2647
1591
  commands: [
2648
1592
  {
@@ -2668,31 +1612,11 @@ export default defineConfig({
2668
1612
  },
2669
1613
  ],
2670
1614
  })
2671
- `);
2672
- return [
2673
- filePath
2674
- ];
2675
- }
2676
- __name(generateConfig, "generateConfig");
2677
-
2678
- // src/generators/resolver.ts
2679
- import { join as join11 } from "path";
2680
- async function generateResolver(options) {
2681
- const { name, outDir } = options;
2682
- const pascal = toPascalCase(name);
2683
- const kebab = toKebabCase(name);
2684
- const camel = toCamelCase(name);
2685
- const files = [];
2686
- const write = /* @__PURE__ */ __name(async (relativePath, content) => {
2687
- const fullPath = join11(outDir, relativePath);
2688
- await writeFileSafe(fullPath, content);
2689
- files.push(fullPath);
2690
- }, "write");
2691
- await write(`${kebab}.resolver.ts`, `import { Service } from '@forinda/kickjs-core'
1615
+ `),[t])}s(Ge,"generateConfig");import{join as Xt}from"path";async function Fe(e){let{name:t,outDir:o}=e,r=f(t),n=$(t),i=x(t),d=[],c=s(async(a,p)=>{let m=Xt(o,a);await l(m,p),d.push(m)},"write");return await c(`${n}.resolver.ts`,`import { Service } from '@forinda/kickjs-core'
2692
1616
  import { Resolver, Query, Mutation, Arg } from '@forinda/kickjs-graphql'
2693
1617
 
2694
1618
  /**
2695
- * ${pascal} GraphQL Resolver
1619
+ * ${r} GraphQL Resolver
2696
1620
  *
2697
1621
  * Decorators:
2698
1622
  * @Resolver(typeName?) \u2014 marks this class as a GraphQL resolver
@@ -2701,35 +1625,35 @@ import { Resolver, Query, Mutation, Arg } from '@forinda/kickjs-graphql'
2701
1625
  * @Arg(name, type?) \u2014 marks a method parameter as a GraphQL argument
2702
1626
  */
2703
1627
  @Service()
2704
- @Resolver('${pascal}')
2705
- export class ${pascal}Resolver {
1628
+ @Resolver('${r}')
1629
+ export class ${r}Resolver {
2706
1630
  private items: Array<{ id: string; name: string }> = []
2707
1631
 
2708
- @Query('${camel}s', { returnType: '[${pascal}]', description: 'List all ${camel}s' })
1632
+ @Query('${i}s', { returnType: '[${r}]', description: 'List all ${i}s' })
2709
1633
  findAll() {
2710
1634
  return this.items
2711
1635
  }
2712
1636
 
2713
- @Query('${camel}', { returnType: '${pascal}', description: 'Get a ${camel} by ID' })
1637
+ @Query('${i}', { returnType: '${r}', description: 'Get a ${i} by ID' })
2714
1638
  findById(@Arg('id', 'ID!') id: string) {
2715
1639
  return this.items.find((item) => item.id === id) ?? null
2716
1640
  }
2717
1641
 
2718
- @Mutation('create${pascal}', { returnType: '${pascal}', description: 'Create a new ${camel}' })
1642
+ @Mutation('create${r}', { returnType: '${r}', description: 'Create a new ${i}' })
2719
1643
  create(@Arg('name', 'String!') name: string) {
2720
1644
  const item = { id: String(this.items.length + 1), name }
2721
1645
  this.items.push(item)
2722
1646
  return item
2723
1647
  }
2724
1648
 
2725
- @Mutation('update${pascal}', { returnType: '${pascal}', description: 'Update a ${camel}' })
1649
+ @Mutation('update${r}', { returnType: '${r}', description: 'Update a ${i}' })
2726
1650
  update(@Arg('id', 'ID!') id: string, @Arg('name', 'String!') name: string) {
2727
1651
  const item = this.items.find((i) => i.id === id)
2728
1652
  if (item) item.name = name
2729
1653
  return item
2730
1654
  }
2731
1655
 
2732
- @Mutation('delete${pascal}', { returnType: 'Boolean', description: 'Delete a ${camel}' })
1656
+ @Mutation('delete${r}', { returnType: 'Boolean', description: 'Delete a ${i}' })
2733
1657
  remove(@Arg('id', 'ID!') id: string) {
2734
1658
  const idx = this.items.findIndex((i) => i.id === id)
2735
1659
  if (idx === -1) return false
@@ -2737,41 +1661,21 @@ export class ${pascal}Resolver {
2737
1661
  return true
2738
1662
  }
2739
1663
  }
2740
- `);
2741
- await write(`${kebab}.typedefs.ts`, `/**
2742
- * ${pascal} GraphQL type definitions.
1664
+ `),await c(`${n}.typedefs.ts`,`/**
1665
+ * ${r} GraphQL type definitions.
2743
1666
  * Pass to GraphQLAdapter's typeDefs option to register custom types.
2744
1667
  */
2745
- export const ${camel}TypeDefs = \`
2746
- type ${pascal} {
1668
+ export const ${i}TypeDefs = \`
1669
+ type ${r} {
2747
1670
  id: ID!
2748
1671
  name: String!
2749
1672
  }
2750
1673
  \`
2751
- `);
2752
- return files;
2753
- }
2754
- __name(generateResolver, "generateResolver");
2755
-
2756
- // src/generators/job.ts
2757
- import { join as join12 } from "path";
2758
- async function generateJob(options) {
2759
- const { name, outDir } = options;
2760
- const pascal = toPascalCase(name);
2761
- const kebab = toKebabCase(name);
2762
- const camel = toCamelCase(name);
2763
- const queueName = options.queue ?? `${kebab}-queue`;
2764
- const files = [];
2765
- const write = /* @__PURE__ */ __name(async (relativePath, content) => {
2766
- const fullPath = join12(outDir, relativePath);
2767
- await writeFileSafe(fullPath, content);
2768
- files.push(fullPath);
2769
- }, "write");
2770
- await write(`${kebab}.job.ts`, `import { Inject } from '@forinda/kickjs-core'
1674
+ `),d}s(Fe,"generateResolver");import{join as eo}from"path";async function Le(e){let{name:t,outDir:o}=e,r=f(t),n=$(t),i=x(t),d=e.queue??`${n}-queue`,c=[];return await s(async(p,m)=>{let u=eo(o,p);await l(u,m),c.push(u)},"write")(`${n}.job.ts`,`import { Inject } from '@forinda/kickjs-core'
2771
1675
  import { Job, Process, QUEUE_MANAGER, type QueueService } from '@forinda/kickjs-queue'
2772
1676
 
2773
1677
  /**
2774
- * ${pascal} Job Processor
1678
+ * ${r} Job Processor
2775
1679
  *
2776
1680
  * Decorators:
2777
1681
  * @Job(queueName) \u2014 marks this class as a job processor for a queue
@@ -2781,10 +1685,10 @@ import { Job, Process, QUEUE_MANAGER, type QueueService } from '@forinda/kickjs-
2781
1685
  *
2782
1686
  * To add jobs to this queue from a service or controller:
2783
1687
  * @Inject(QUEUE_MANAGER) private queue: QueueService
2784
- * await this.queue.add('${queueName}', '${camel}', { ... })
1688
+ * await this.queue.add('${d}', '${i}', { ... })
2785
1689
  */
2786
- @Job('${queueName}')
2787
- export class ${pascal}Job {
1690
+ @Job('${d}')
1691
+ export class ${r}Job {
2788
1692
  @Process()
2789
1693
  async handle(job: { name: string; data: any; id?: string }) {
2790
1694
  console.log(\`Processing \${job.name} (id: \${job.id})\`, job.data)
@@ -2794,239 +1698,74 @@ export class ${pascal}Job {
2794
1698
  // await this.emailService.send(job.data.to, job.data.subject, job.data.body)
2795
1699
  }
2796
1700
 
2797
- @Process('${camel}.priority')
1701
+ @Process('${i}.priority')
2798
1702
  async handlePriority(job: { name: string; data: any; id?: string }) {
2799
1703
  console.log(\`Priority job: \${job.name}\`, job.data)
2800
1704
  // Handle high-priority variant of this job
2801
1705
  }
2802
1706
  }
2803
- `);
2804
- return files;
2805
- }
2806
- __name(generateJob, "generateJob");
2807
-
2808
- // src/generators/scaffold.ts
2809
- import { join as join13 } from "path";
2810
- import { readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
2811
- var TYPE_MAP = {
2812
- string: {
2813
- ts: "string",
2814
- zod: "z.string()"
2815
- },
2816
- text: {
2817
- ts: "string",
2818
- zod: "z.string()"
2819
- },
2820
- number: {
2821
- ts: "number",
2822
- zod: "z.number()"
2823
- },
2824
- int: {
2825
- ts: "number",
2826
- zod: "z.number().int()"
2827
- },
2828
- float: {
2829
- ts: "number",
2830
- zod: "z.number()"
2831
- },
2832
- boolean: {
2833
- ts: "boolean",
2834
- zod: "z.boolean()"
2835
- },
2836
- date: {
2837
- ts: "string",
2838
- zod: "z.string().datetime()"
2839
- },
2840
- email: {
2841
- ts: "string",
2842
- zod: "z.string().email()"
2843
- },
2844
- url: {
2845
- ts: "string",
2846
- zod: "z.string().url()"
2847
- },
2848
- uuid: {
2849
- ts: "string",
2850
- zod: "z.string().uuid()"
2851
- },
2852
- json: {
2853
- ts: "any",
2854
- zod: "z.any()"
2855
- }
2856
- };
2857
- function parseFields(raw) {
2858
- return raw.map((f) => {
2859
- const colonIdx = f.indexOf(":");
2860
- if (colonIdx === -1) {
2861
- throw new Error(`Invalid field: "${f}". Use format: name:type (e.g. title:string)`);
2862
- }
2863
- const namePart = f.slice(0, colonIdx);
2864
- const typePart = f.slice(colonIdx + 1);
2865
- if (!namePart || !typePart) {
2866
- throw new Error(`Invalid field: "${f}". Use format: name:type (e.g. title:string)`);
2867
- }
2868
- const optional = typePart.endsWith("?");
2869
- const cleanType = optional ? typePart.slice(0, -1) : typePart;
2870
- if (cleanType.startsWith("enum:")) {
2871
- const values = cleanType.slice(5).split(",");
2872
- return {
2873
- name: namePart,
2874
- type: "enum",
2875
- tsType: values.map((v) => `'${v}'`).join(" | "),
2876
- zodType: `z.enum([${values.map((v) => `'${v}'`).join(", ")}])`,
2877
- optional
2878
- };
2879
- }
2880
- const mapped = TYPE_MAP[cleanType];
2881
- if (!mapped) {
2882
- const validTypes = [
2883
- ...Object.keys(TYPE_MAP),
2884
- "enum:a,b,c"
2885
- ].join(", ");
2886
- throw new Error(`Unknown field type: "${cleanType}". Valid types: ${validTypes}`);
2887
- }
2888
- return {
2889
- name: namePart,
2890
- type: cleanType,
2891
- tsType: mapped.ts,
2892
- zodType: mapped.zod,
2893
- optional
2894
- };
2895
- });
2896
- }
2897
- __name(parseFields, "parseFields");
2898
- async function generateScaffold(options) {
2899
- const { name, fields, modulesDir, noEntity, noTests, repo = "inmemory" } = options;
2900
- const kebab = toKebabCase(name);
2901
- const pascal = toPascalCase(name);
2902
- const camel = toCamelCase(name);
2903
- const plural = pluralize(kebab);
2904
- const pluralPascal = pluralizePascal(pascal);
2905
- const moduleDir = join13(modulesDir, plural);
2906
- const files = [];
2907
- const write = /* @__PURE__ */ __name(async (relativePath, content) => {
2908
- const fullPath = join13(moduleDir, relativePath);
2909
- await writeFileSafe(fullPath, content);
2910
- files.push(fullPath);
2911
- }, "write");
2912
- await write("index.ts", genModuleIndex(pascal, kebab, plural, repo));
2913
- await write("constants.ts", genConstants(pascal, fields));
2914
- await write(`presentation/${kebab}.controller.ts`, genController(pascal, kebab, plural, pluralPascal));
2915
- await write(`application/dtos/create-${kebab}.dto.ts`, genCreateDTO(pascal, fields));
2916
- await write(`application/dtos/update-${kebab}.dto.ts`, genUpdateDTO(pascal, fields));
2917
- await write(`application/dtos/${kebab}-response.dto.ts`, genResponseDTO(pascal, fields));
2918
- const useCases = genUseCases(pascal, kebab, plural, pluralPascal);
2919
- for (const uc of useCases) {
2920
- await write(`application/use-cases/${uc.file}`, uc.content);
2921
- }
2922
- await write(`domain/repositories/${kebab}.repository.ts`, genRepositoryInterface(pascal, kebab));
2923
- await write(`domain/services/${kebab}-domain.service.ts`, genDomainService(pascal, kebab));
2924
- if (repo === "inmemory") {
2925
- await write(`infrastructure/repositories/in-memory-${kebab}.repository.ts`, genInMemoryRepository(pascal, kebab, fields));
2926
- }
2927
- if (!noEntity) {
2928
- await write(`domain/entities/${kebab}.entity.ts`, genEntity(pascal, kebab, fields));
2929
- await write(`domain/value-objects/${kebab}-id.vo.ts`, genValueObject(pascal));
2930
- }
2931
- await autoRegisterModule2(modulesDir, pascal, plural);
2932
- return files;
2933
- }
2934
- __name(generateScaffold, "generateScaffold");
2935
- function genCreateDTO(pascal, fields) {
2936
- const zodFields = fields.map((f) => {
2937
- const base = f.zodType;
2938
- return ` ${f.name}: ${base}${f.optional ? ".optional()" : ""},`;
2939
- }).join("\n");
2940
- return `import { z } from 'zod'
2941
-
2942
- export const create${pascal}Schema = z.object({
2943
- ${zodFields}
1707
+ `),c}s(Le,"generateJob");import{join as ke}from"path";import{readFile as to,writeFile as oo}from"fs/promises";var Ne={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 Ye(e){return e.map(t=>{let o=t.indexOf(":");if(o===-1)throw new Error(`Invalid field: "${t}". Use format: name:type (e.g. title:string)`);let r=t.slice(0,o),n=t.slice(o+1);if(!r||!n)throw new Error(`Invalid field: "${t}". Use format: name:type (e.g. title:string)`);let i=n.endsWith("?"),d=i?n.slice(0,-1):n;if(d.startsWith("enum:")){let a=d.slice(5).split(",");return{name:r,type:"enum",tsType:a.map(p=>`'${p}'`).join(" | "),zodType:`z.enum([${a.map(p=>`'${p}'`).join(", ")}])`,optional:i}}let c=Ne[d];if(!c){let a=[...Object.keys(Ne),"enum:a,b,c"].join(", ");throw new Error(`Unknown field type: "${d}". Valid types: ${a}`)}return{name:r,type:d,tsType:c.ts,zodType:c.zod,optional:i}})}s(Ye,"parseFields");async function Be(e){let{name:t,fields:o,modulesDir:r,noEntity:n,noTests:i,repo:d="inmemory"}=e,c=$(t),a=f(t),p=x(t),m=j(c),u=X(a),w=ke(r,m),g=[],y=s(async(q,A)=>{let U=ke(w,q);await l(U,A),g.push(U)},"write");await y("index.ts",mo(a,c,m,d)),await y("constants.ts",so(a,o)),await y(`presentation/${c}.controller.ts`,lo(a,c,m,u)),await y(`application/dtos/create-${c}.dto.ts`,ro(a,o)),await y(`application/dtos/update-${c}.dto.ts`,io(a,o)),await y(`application/dtos/${c}-response.dto.ts`,no(a,o));let W=$o(a,c,m,u);for(let q of W)await y(`application/use-cases/${q.file}`,q.content);return await y(`domain/repositories/${c}.repository.ts`,uo(a,c)),await y(`domain/services/${c}-domain.service.ts`,fo(a,c)),d==="inmemory"&&await y(`infrastructure/repositories/in-memory-${c}.repository.ts`,ao(a,c,o)),n||(await y(`domain/entities/${c}.entity.ts`,co(a,c,o)),await y(`domain/value-objects/${c}-id.vo.ts`,po(a))),await go(r,a,m),g}s(Be,"generateScaffold");function ro(e,t){let o=t.map(r=>{let n=r.zodType;return` ${r.name}: ${n}${r.optional?".optional()":""},`}).join(`
1708
+ `);return`import { z } from 'zod'
1709
+
1710
+ export const create${e}Schema = z.object({
1711
+ ${o}
2944
1712
  })
2945
1713
 
2946
- export type Create${pascal}DTO = z.infer<typeof create${pascal}Schema>
2947
- `;
2948
- }
2949
- __name(genCreateDTO, "genCreateDTO");
2950
- function genUpdateDTO(pascal, fields) {
2951
- const zodFields = fields.map((f) => ` ${f.name}: ${f.zodType}.optional(),`).join("\n");
2952
- return `import { z } from 'zod'
1714
+ export type Create${e}DTO = z.infer<typeof create${e}Schema>
1715
+ `}s(ro,"genCreateDTO");function io(e,t){let o=t.map(r=>` ${r.name}: ${r.zodType}.optional(),`).join(`
1716
+ `);return`import { z } from 'zod'
2953
1717
 
2954
- export const update${pascal}Schema = z.object({
2955
- ${zodFields}
1718
+ export const update${e}Schema = z.object({
1719
+ ${o}
2956
1720
  })
2957
1721
 
2958
- export type Update${pascal}DTO = z.infer<typeof update${pascal}Schema>
2959
- `;
2960
- }
2961
- __name(genUpdateDTO, "genUpdateDTO");
2962
- function genResponseDTO(pascal, fields) {
2963
- const tsFields = fields.map((f) => ` ${f.name}${f.optional ? "?" : ""}: ${f.tsType}`).join("\n");
2964
- return `export interface ${pascal}ResponseDTO {
1722
+ export type Update${e}DTO = z.infer<typeof update${e}Schema>
1723
+ `}s(io,"genUpdateDTO");function no(e,t){let o=t.map(r=>` ${r.name}${r.optional?"?":""}: ${r.tsType}`).join(`
1724
+ `);return`export interface ${e}ResponseDTO {
2965
1725
  id: string
2966
- ${tsFields}
1726
+ ${o}
2967
1727
  createdAt: string
2968
1728
  updatedAt: string
2969
1729
  }
2970
- `;
2971
- }
2972
- __name(genResponseDTO, "genResponseDTO");
2973
- function genConstants(pascal, fields) {
2974
- const stringFields = fields.filter((f) => f.tsType === "string").map((f) => `'${f.name}'`);
2975
- const numberFields = fields.filter((f) => f.tsType === "number").map((f) => `'${f.name}'`);
2976
- const allFieldNames = fields.map((f) => `'${f.name}'`);
2977
- const filterable = [
2978
- ...allFieldNames
2979
- ].join(", ");
2980
- const sortable = [
2981
- ...allFieldNames,
2982
- "'createdAt'",
2983
- "'updatedAt'"
2984
- ].join(", ");
2985
- const searchable = stringFields.length > 0 ? stringFields.join(", ") : "'name'";
2986
- return `import type { ApiQueryParamsConfig } from '@forinda/kickjs-core'
2987
-
2988
- export const ${pascal.toUpperCase()}_QUERY_CONFIG: ApiQueryParamsConfig = {
2989
- filterable: [${filterable}],
2990
- sortable: [${sortable}],
2991
- searchable: [${searchable}],
2992
- }
2993
- `;
1730
+ `}s(no,"genResponseDTO");function so(e,t){let o=t.filter(a=>a.tsType==="string").map(a=>`'${a.name}'`),r=t.filter(a=>a.tsType==="number").map(a=>`'${a.name}'`),n=t.map(a=>`'${a.name}'`),i=[...n].join(", "),d=[...n,"'createdAt'","'updatedAt'"].join(", "),c=o.length>0?o.join(", "):"'name'";return`import type { ApiQueryParamsConfig } from '@forinda/kickjs-core'
1731
+
1732
+ export const ${e.toUpperCase()}_QUERY_CONFIG: ApiQueryParamsConfig = {
1733
+ filterable: [${i}],
1734
+ sortable: [${d}],
1735
+ searchable: [${c}],
2994
1736
  }
2995
- __name(genConstants, "genConstants");
2996
- function genInMemoryRepository(pascal, kebab, fields) {
2997
- const fieldAssignments = fields.map((f) => ` ${f.name}: dto.${f.name},`).join("\n");
2998
- const fieldSpread = "...dto";
2999
- return `import { randomUUID } from 'node:crypto'
1737
+ `}s(so,"genConstants");function ao(e,t,o){let r=o.map(i=>` ${i.name}: dto.${i.name},`).join(`
1738
+ `);return`import { randomUUID } from 'node:crypto'
3000
1739
  import { Repository, HttpException } from '@forinda/kickjs-core'
3001
1740
  import type { ParsedQuery } from '@forinda/kickjs-http'
3002
- import type { I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
3003
- import type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'
3004
- import type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'
3005
- import type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'
1741
+ import type { I${e}Repository } from '../../domain/repositories/${t}.repository'
1742
+ import type { ${e}ResponseDTO } from '../../application/dtos/${t}-response.dto'
1743
+ import type { Create${e}DTO } from '../../application/dtos/create-${t}.dto'
1744
+ import type { Update${e}DTO } from '../../application/dtos/update-${t}.dto'
3006
1745
 
3007
1746
  @Repository()
3008
- export class InMemory${pascal}Repository implements I${pascal}Repository {
3009
- private store = new Map<string, ${pascal}ResponseDTO>()
1747
+ export class InMemory${e}Repository implements I${e}Repository {
1748
+ private store = new Map<string, ${e}ResponseDTO>()
3010
1749
 
3011
- async findById(id: string): Promise<${pascal}ResponseDTO | null> {
1750
+ async findById(id: string): Promise<${e}ResponseDTO | null> {
3012
1751
  return this.store.get(id) ?? null
3013
1752
  }
3014
1753
 
3015
- async findAll(): Promise<${pascal}ResponseDTO[]> {
1754
+ async findAll(): Promise<${e}ResponseDTO[]> {
3016
1755
  return Array.from(this.store.values())
3017
1756
  }
3018
1757
 
3019
- async findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }> {
1758
+ async findPaginated(parsed: ParsedQuery): Promise<{ data: ${e}ResponseDTO[]; total: number }> {
3020
1759
  const all = Array.from(this.store.values())
3021
1760
  const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)
3022
1761
  return { data, total: all.length }
3023
1762
  }
3024
1763
 
3025
- async create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO> {
1764
+ async create(dto: Create${e}DTO): Promise<${e}ResponseDTO> {
3026
1765
  const now = new Date().toISOString()
3027
- const entity: ${pascal}ResponseDTO = {
1766
+ const entity: ${e}ResponseDTO = {
3028
1767
  id: randomUUID(),
3029
- ${fieldAssignments}
1768
+ ${r}
3030
1769
  createdAt: now,
3031
1770
  updatedAt: now,
3032
1771
  }
@@ -3034,337 +1773,240 @@ ${fieldAssignments}
3034
1773
  return entity
3035
1774
  }
3036
1775
 
3037
- async update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO> {
1776
+ async update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO> {
3038
1777
  const existing = this.store.get(id)
3039
- if (!existing) throw HttpException.notFound('${pascal} not found')
3040
- const updated = { ...existing, ${fieldSpread}, updatedAt: new Date().toISOString() }
1778
+ if (!existing) throw HttpException.notFound('${e} not found')
1779
+ const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }
3041
1780
  this.store.set(id, updated)
3042
1781
  return updated
3043
1782
  }
3044
1783
 
3045
1784
  async delete(id: string): Promise<void> {
3046
- if (!this.store.has(id)) throw HttpException.notFound('${pascal} not found')
1785
+ if (!this.store.has(id)) throw HttpException.notFound('${e} not found')
3047
1786
  this.store.delete(id)
3048
1787
  }
3049
1788
  }
3050
- `;
3051
- }
3052
- __name(genInMemoryRepository, "genInMemoryRepository");
3053
- function genEntity(pascal, kebab, fields) {
3054
- const propsInterface = fields.map((f) => ` ${f.name}${f.optional ? "?" : ""}: ${f.tsType}`).join("\n");
3055
- const createParams = fields.filter((f) => !f.optional).map((f) => `${f.name}: ${f.tsType}`).join("; ");
3056
- const createAssignments = fields.filter((f) => !f.optional).map((f) => ` ${f.name}: params.${f.name},`).join("\n");
3057
- const getters = fields.map((f) => ` get ${f.name}(): ${f.tsType}${f.optional ? " | undefined" : ""} {
3058
- return this.props.${f.name}
3059
- }`).join("\n");
3060
- const toJsonFields = fields.map((f) => ` ${f.name}: this.props.${f.name},`).join("\n");
3061
- return `import { ${pascal}Id } from '../value-objects/${kebab}-id.vo'
3062
-
3063
- interface ${pascal}Props {
3064
- id: ${pascal}Id
3065
- ${propsInterface}
1789
+ `}s(ao,"genInMemoryRepository");function co(e,t,o){let r=o.map(a=>` ${a.name}${a.optional?"?":""}: ${a.tsType}`).join(`
1790
+ `),n=o.filter(a=>!a.optional).map(a=>`${a.name}: ${a.tsType}`).join("; "),i=o.filter(a=>!a.optional).map(a=>` ${a.name}: params.${a.name},`).join(`
1791
+ `),d=o.map(a=>` get ${a.name}(): ${a.tsType}${a.optional?" | undefined":""} {
1792
+ return this.props.${a.name}
1793
+ }`).join(`
1794
+ `),c=o.map(a=>` ${a.name}: this.props.${a.name},`).join(`
1795
+ `);return`import { ${e}Id } from '../value-objects/${t}-id.vo'
1796
+
1797
+ interface ${e}Props {
1798
+ id: ${e}Id
1799
+ ${r}
3066
1800
  createdAt: Date
3067
1801
  updatedAt: Date
3068
1802
  }
3069
1803
 
3070
- export class ${pascal} {
3071
- private constructor(private props: ${pascal}Props) {}
1804
+ export class ${e} {
1805
+ private constructor(private props: ${e}Props) {}
3072
1806
 
3073
- static create(params: { ${createParams} }): ${pascal} {
1807
+ static create(params: { ${n} }): ${e} {
3074
1808
  const now = new Date()
3075
- return new ${pascal}({
3076
- id: ${pascal}Id.create(),
3077
- ${createAssignments}
1809
+ return new ${e}({
1810
+ id: ${e}Id.create(),
1811
+ ${i}
3078
1812
  createdAt: now,
3079
1813
  updatedAt: now,
3080
1814
  })
3081
1815
  }
3082
1816
 
3083
- static reconstitute(props: ${pascal}Props): ${pascal} {
3084
- return new ${pascal}(props)
1817
+ static reconstitute(props: ${e}Props): ${e} {
1818
+ return new ${e}(props)
3085
1819
  }
3086
1820
 
3087
- get id(): ${pascal}Id { return this.props.id }
3088
- ${getters}
1821
+ get id(): ${e}Id { return this.props.id }
1822
+ ${d}
3089
1823
  get createdAt(): Date { return this.props.createdAt }
3090
1824
  get updatedAt(): Date { return this.props.updatedAt }
3091
1825
 
3092
1826
  toJSON() {
3093
1827
  return {
3094
1828
  id: this.props.id.toString(),
3095
- ${toJsonFields}
1829
+ ${c}
3096
1830
  createdAt: this.props.createdAt.toISOString(),
3097
1831
  updatedAt: this.props.updatedAt.toISOString(),
3098
1832
  }
3099
1833
  }
3100
1834
  }
3101
- `;
3102
- }
3103
- __name(genEntity, "genEntity");
3104
- function genValueObject(pascal) {
3105
- return `import { randomUUID } from 'node:crypto'
1835
+ `}s(co,"genEntity");function po(e){return`import { randomUUID } from 'node:crypto'
3106
1836
 
3107
- export class ${pascal}Id {
1837
+ export class ${e}Id {
3108
1838
  private constructor(private readonly value: string) {}
3109
1839
 
3110
- static create(): ${pascal}Id { return new ${pascal}Id(randomUUID()) }
1840
+ static create(): ${e}Id { return new ${e}Id(randomUUID()) }
3111
1841
 
3112
- static from(id: string): ${pascal}Id {
3113
- if (!id || id.trim().length === 0) throw new Error('${pascal}Id cannot be empty')
3114
- return new ${pascal}Id(id)
1842
+ static from(id: string): ${e}Id {
1843
+ if (!id || id.trim().length === 0) throw new Error('${e}Id cannot be empty')
1844
+ return new ${e}Id(id)
3115
1845
  }
3116
1846
 
3117
1847
  toString(): string { return this.value }
3118
- equals(other: ${pascal}Id): boolean { return this.value === other.value }
1848
+ equals(other: ${e}Id): boolean { return this.value === other.value }
3119
1849
  }
3120
- `;
3121
- }
3122
- __name(genValueObject, "genValueObject");
3123
- function genModuleIndex(pascal, kebab, plural, repo) {
3124
- return `import type { AppModule, AppModuleClass } from '@forinda/kickjs-core'
3125
- import { ${pascal}Controller } from './presentation/${kebab}.controller'
3126
- import { ${pascal}DomainService } from './domain/services/${kebab}-domain.service'
3127
- import { ${pascal.toUpperCase()}_REPOSITORY } from './domain/repositories/${kebab}.repository'
3128
- import { InMemory${pascal}Repository } from './infrastructure/repositories/in-memory-${kebab}.repository'
3129
-
3130
- export class ${pascal}Module implements AppModule {
1850
+ `}s(po,"genValueObject");function mo(e,t,o,r){return`import type { AppModule, AppModuleClass } from '@forinda/kickjs-core'
1851
+ import { ${e}Controller } from './presentation/${t}.controller'
1852
+ import { ${e}DomainService } from './domain/services/${t}-domain.service'
1853
+ import { ${e.toUpperCase()}_REPOSITORY } from './domain/repositories/${t}.repository'
1854
+ import { InMemory${e}Repository } from './infrastructure/repositories/in-memory-${t}.repository'
1855
+
1856
+ export class ${e}Module implements AppModule {
3131
1857
  register(container: any): void {
3132
1858
  container.registerFactory(
3133
- ${pascal.toUpperCase()}_REPOSITORY,
3134
- () => container.resolve(InMemory${pascal}Repository),
1859
+ ${e.toUpperCase()}_REPOSITORY,
1860
+ () => container.resolve(InMemory${e}Repository),
3135
1861
  )
3136
1862
  }
3137
1863
 
3138
1864
  routes() {
3139
- return { prefix: '/${plural}', controllers: [${pascal}Controller] }
1865
+ return { prefix: '/${o}', controllers: [${e}Controller] }
3140
1866
  }
3141
1867
  }
3142
- `;
3143
- }
3144
- __name(genModuleIndex, "genModuleIndex");
3145
- function genController(pascal, kebab, plural, pluralPascal) {
3146
- return `import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
1868
+ `}s(mo,"genModuleIndex");function lo(e,t,o,r){return`import { Controller, Get, Post, Put, Delete, Autowired, ApiQueryParams } from '@forinda/kickjs-core'
3147
1869
  import type { RequestContext } from '@forinda/kickjs-http'
3148
1870
  import { ApiTags } from '@forinda/kickjs-swagger'
3149
- import { Create${pascal}UseCase } from '../application/use-cases/create-${kebab}.use-case'
3150
- import { Get${pascal}UseCase } from '../application/use-cases/get-${kebab}.use-case'
3151
- import { List${pluralPascal}UseCase } from '../application/use-cases/list-${plural}.use-case'
3152
- import { Update${pascal}UseCase } from '../application/use-cases/update-${kebab}.use-case'
3153
- import { Delete${pascal}UseCase } from '../application/use-cases/delete-${kebab}.use-case'
3154
- import { create${pascal}Schema } from '../application/dtos/create-${kebab}.dto'
3155
- import { update${pascal}Schema } from '../application/dtos/update-${kebab}.dto'
3156
- import { ${pascal.toUpperCase()}_QUERY_CONFIG } from '../constants'
1871
+ import { Create${e}UseCase } from '../application/use-cases/create-${t}.use-case'
1872
+ import { Get${e}UseCase } from '../application/use-cases/get-${t}.use-case'
1873
+ import { List${r}UseCase } from '../application/use-cases/list-${o}.use-case'
1874
+ import { Update${e}UseCase } from '../application/use-cases/update-${t}.use-case'
1875
+ import { Delete${e}UseCase } from '../application/use-cases/delete-${t}.use-case'
1876
+ import { create${e}Schema } from '../application/dtos/create-${t}.dto'
1877
+ import { update${e}Schema } from '../application/dtos/update-${t}.dto'
1878
+ import { ${e.toUpperCase()}_QUERY_CONFIG } from '../constants'
3157
1879
 
3158
1880
  @Controller()
3159
- export class ${pascal}Controller {
3160
- @Autowired() private create${pascal}UseCase!: Create${pascal}UseCase
3161
- @Autowired() private get${pascal}UseCase!: Get${pascal}UseCase
3162
- @Autowired() private list${pluralPascal}UseCase!: List${pluralPascal}UseCase
3163
- @Autowired() private update${pascal}UseCase!: Update${pascal}UseCase
3164
- @Autowired() private delete${pascal}UseCase!: Delete${pascal}UseCase
1881
+ export class ${e}Controller {
1882
+ @Autowired() private create${e}UseCase!: Create${e}UseCase
1883
+ @Autowired() private get${e}UseCase!: Get${e}UseCase
1884
+ @Autowired() private list${r}UseCase!: List${r}UseCase
1885
+ @Autowired() private update${e}UseCase!: Update${e}UseCase
1886
+ @Autowired() private delete${e}UseCase!: Delete${e}UseCase
3165
1887
 
3166
1888
  @Get('/')
3167
- @ApiTags('${pascal}')
3168
- @ApiQueryParams(${pascal.toUpperCase()}_QUERY_CONFIG)
1889
+ @ApiTags('${e}')
1890
+ @ApiQueryParams(${e.toUpperCase()}_QUERY_CONFIG)
3169
1891
  async list(ctx: RequestContext) {
3170
1892
  return ctx.paginate(
3171
- (parsed) => this.list${pluralPascal}UseCase.execute(parsed),
3172
- ${pascal.toUpperCase()}_QUERY_CONFIG,
1893
+ (parsed) => this.list${r}UseCase.execute(parsed),
1894
+ ${e.toUpperCase()}_QUERY_CONFIG,
3173
1895
  )
3174
1896
  }
3175
1897
 
3176
1898
  @Get('/:id')
3177
- @ApiTags('${pascal}')
1899
+ @ApiTags('${e}')
3178
1900
  async getById(ctx: RequestContext) {
3179
- const result = await this.get${pascal}UseCase.execute(ctx.params.id)
3180
- if (!result) return ctx.notFound('${pascal} not found')
1901
+ const result = await this.get${e}UseCase.execute(ctx.params.id)
1902
+ if (!result) return ctx.notFound('${e} not found')
3181
1903
  ctx.json(result)
3182
1904
  }
3183
1905
 
3184
- @Post('/', { body: create${pascal}Schema, name: 'Create${pascal}' })
3185
- @ApiTags('${pascal}')
1906
+ @Post('/', { body: create${e}Schema, name: 'Create${e}' })
1907
+ @ApiTags('${e}')
3186
1908
  async create(ctx: RequestContext) {
3187
- const result = await this.create${pascal}UseCase.execute(ctx.body)
1909
+ const result = await this.create${e}UseCase.execute(ctx.body)
3188
1910
  ctx.created(result)
3189
1911
  }
3190
1912
 
3191
- @Put('/:id', { body: update${pascal}Schema, name: 'Update${pascal}' })
3192
- @ApiTags('${pascal}')
1913
+ @Put('/:id', { body: update${e}Schema, name: 'Update${e}' })
1914
+ @ApiTags('${e}')
3193
1915
  async update(ctx: RequestContext) {
3194
- const result = await this.update${pascal}UseCase.execute(ctx.params.id, ctx.body)
1916
+ const result = await this.update${e}UseCase.execute(ctx.params.id, ctx.body)
3195
1917
  ctx.json(result)
3196
1918
  }
3197
1919
 
3198
1920
  @Delete('/:id')
3199
- @ApiTags('${pascal}')
1921
+ @ApiTags('${e}')
3200
1922
  async remove(ctx: RequestContext) {
3201
- await this.delete${pascal}UseCase.execute(ctx.params.id)
1923
+ await this.delete${e}UseCase.execute(ctx.params.id)
3202
1924
  ctx.noContent()
3203
1925
  }
3204
1926
  }
3205
- `;
3206
- }
3207
- __name(genController, "genController");
3208
- function genRepositoryInterface(pascal, kebab) {
3209
- return `import type { ${pascal}ResponseDTO } from '../../application/dtos/${kebab}-response.dto'
3210
- import type { Create${pascal}DTO } from '../../application/dtos/create-${kebab}.dto'
3211
- import type { Update${pascal}DTO } from '../../application/dtos/update-${kebab}.dto'
1927
+ `}s(lo,"genController");function uo(e,t){return`import type { ${e}ResponseDTO } from '../../application/dtos/${t}-response.dto'
1928
+ import type { Create${e}DTO } from '../../application/dtos/create-${t}.dto'
1929
+ import type { Update${e}DTO } from '../../application/dtos/update-${t}.dto'
3212
1930
  import type { ParsedQuery } from '@forinda/kickjs-http'
3213
1931
 
3214
- export interface I${pascal}Repository {
3215
- findById(id: string): Promise<${pascal}ResponseDTO | null>
3216
- findAll(): Promise<${pascal}ResponseDTO[]>
3217
- findPaginated(parsed: ParsedQuery): Promise<{ data: ${pascal}ResponseDTO[]; total: number }>
3218
- create(dto: Create${pascal}DTO): Promise<${pascal}ResponseDTO>
3219
- update(id: string, dto: Update${pascal}DTO): Promise<${pascal}ResponseDTO>
1932
+ export interface I${e}Repository {
1933
+ findById(id: string): Promise<${e}ResponseDTO | null>
1934
+ findAll(): Promise<${e}ResponseDTO[]>
1935
+ findPaginated(parsed: ParsedQuery): Promise<{ data: ${e}ResponseDTO[]; total: number }>
1936
+ create(dto: Create${e}DTO): Promise<${e}ResponseDTO>
1937
+ update(id: string, dto: Update${e}DTO): Promise<${e}ResponseDTO>
3220
1938
  delete(id: string): Promise<void>
3221
1939
  }
3222
1940
 
3223
- export const ${pascal.toUpperCase()}_REPOSITORY = Symbol('I${pascal}Repository')
3224
- `;
3225
- }
3226
- __name(genRepositoryInterface, "genRepositoryInterface");
3227
- function genDomainService(pascal, kebab) {
3228
- return `import { Service, Inject, HttpException } from '@forinda/kickjs-core'
3229
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../repositories/${kebab}.repository'
1941
+ export const ${e.toUpperCase()}_REPOSITORY = Symbol('I${e}Repository')
1942
+ `}s(uo,"genRepositoryInterface");function fo(e,t){return`import { Service, Inject, HttpException } from '@forinda/kickjs-core'
1943
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../repositories/${t}.repository'
3230
1944
 
3231
1945
  @Service()
3232
- export class ${pascal}DomainService {
1946
+ export class ${e}DomainService {
3233
1947
  constructor(
3234
- @Inject(${pascal.toUpperCase()}_REPOSITORY) private readonly repo: I${pascal}Repository,
1948
+ @Inject(${e.toUpperCase()}_REPOSITORY) private readonly repo: I${e}Repository,
3235
1949
  ) {}
3236
1950
 
3237
1951
  async ensureExists(id: string): Promise<void> {
3238
1952
  const entity = await this.repo.findById(id)
3239
- if (!entity) throw HttpException.notFound('${pascal} not found')
1953
+ if (!entity) throw HttpException.notFound('${e} not found')
3240
1954
  }
3241
1955
  }
3242
- `;
3243
- }
3244
- __name(genDomainService, "genDomainService");
3245
- function genUseCases(pascal, kebab, plural, pluralPascal) {
3246
- return [
3247
- {
3248
- file: `create-${kebab}.use-case.ts`,
3249
- content: `import { Service, Inject } from '@forinda/kickjs-core'
3250
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
3251
- import type { Create${pascal}DTO } from '../dtos/create-${kebab}.dto'
1956
+ `}s(fo,"genDomainService");function $o(e,t,o,r){return[{file:`create-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1957
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
1958
+ import type { Create${e}DTO } from '../dtos/create-${t}.dto'
3252
1959
 
3253
1960
  @Service()
3254
- export class Create${pascal}UseCase {
3255
- constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}
3256
- async execute(dto: Create${pascal}DTO) { return this.repo.create(dto) }
1961
+ export class Create${e}UseCase {
1962
+ constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
1963
+ async execute(dto: Create${e}DTO) { return this.repo.create(dto) }
3257
1964
  }
3258
- `
3259
- },
3260
- {
3261
- file: `get-${kebab}.use-case.ts`,
3262
- content: `import { Service, Inject } from '@forinda/kickjs-core'
3263
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
1965
+ `},{file:`get-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1966
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
3264
1967
 
3265
1968
  @Service()
3266
- export class Get${pascal}UseCase {
3267
- constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}
1969
+ export class Get${e}UseCase {
1970
+ constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
3268
1971
  async execute(id: string) { return this.repo.findById(id) }
3269
1972
  }
3270
- `
3271
- },
3272
- {
3273
- file: `list-${plural}.use-case.ts`,
3274
- content: `import { Service, Inject } from '@forinda/kickjs-core'
1973
+ `},{file:`list-${o}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
3275
1974
  import type { ParsedQuery } from '@forinda/kickjs-http'
3276
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
1975
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
3277
1976
 
3278
1977
  @Service()
3279
- export class List${pluralPascal}UseCase {
3280
- constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}
1978
+ export class List${r}UseCase {
1979
+ constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
3281
1980
  async execute(parsed: ParsedQuery) { return this.repo.findPaginated(parsed) }
3282
1981
  }
3283
- `
3284
- },
3285
- {
3286
- file: `update-${kebab}.use-case.ts`,
3287
- content: `import { Service, Inject } from '@forinda/kickjs-core'
3288
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
3289
- import type { Update${pascal}DTO } from '../dtos/update-${kebab}.dto'
1982
+ `},{file:`update-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1983
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
1984
+ import type { Update${e}DTO } from '../dtos/update-${t}.dto'
3290
1985
 
3291
1986
  @Service()
3292
- export class Update${pascal}UseCase {
3293
- constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}
3294
- async execute(id: string, dto: Update${pascal}DTO) { return this.repo.update(id, dto) }
1987
+ export class Update${e}UseCase {
1988
+ constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
1989
+ async execute(id: string, dto: Update${e}DTO) { return this.repo.update(id, dto) }
3295
1990
  }
3296
- `
3297
- },
3298
- {
3299
- file: `delete-${kebab}.use-case.ts`,
3300
- content: `import { Service, Inject } from '@forinda/kickjs-core'
3301
- import { ${pascal.toUpperCase()}_REPOSITORY, type I${pascal}Repository } from '../../domain/repositories/${kebab}.repository'
1991
+ `},{file:`delete-${t}.use-case.ts`,content:`import { Service, Inject } from '@forinda/kickjs-core'
1992
+ import { ${e.toUpperCase()}_REPOSITORY, type I${e}Repository } from '../../domain/repositories/${t}.repository'
3302
1993
 
3303
1994
  @Service()
3304
- export class Delete${pascal}UseCase {
3305
- constructor(@Inject(${pascal.toUpperCase()}_REPOSITORY) private repo: I${pascal}Repository) {}
1995
+ export class Delete${e}UseCase {
1996
+ constructor(@Inject(${e.toUpperCase()}_REPOSITORY) private repo: I${e}Repository) {}
3306
1997
  async execute(id: string) { return this.repo.delete(id) }
3307
1998
  }
3308
- `
3309
- }
3310
- ];
3311
- }
3312
- __name(genUseCases, "genUseCases");
3313
- async function autoRegisterModule2(modulesDir, pascal, plural) {
3314
- const indexPath = join13(modulesDir, "index.ts");
3315
- const exists = await fileExists(indexPath);
3316
- if (!exists) {
3317
- await writeFileSafe(indexPath, `import type { AppModuleClass } from '@forinda/kickjs-core'
3318
- import { ${pascal}Module } from './${plural}'
3319
-
3320
- export const modules: AppModuleClass[] = [${pascal}Module]
3321
- `);
3322
- return;
3323
- }
3324
- let content = await readFile3(indexPath, "utf-8");
3325
- const importLine = `import { ${pascal}Module } from './${plural}'`;
3326
- if (!content.includes(`${pascal}Module`)) {
3327
- const lastImportIdx = content.lastIndexOf("import ");
3328
- if (lastImportIdx !== -1) {
3329
- const lineEnd = content.indexOf("\n", lastImportIdx);
3330
- content = content.slice(0, lineEnd + 1) + importLine + "\n" + content.slice(lineEnd + 1);
3331
- } else {
3332
- content = importLine + "\n" + content;
3333
- }
3334
- content = content.replace(/(=\s*\[)([\s\S]*?)(])/, (_match, open, existing, close) => {
3335
- const trimmed = existing.trim();
3336
- if (!trimmed) return `${open}${pascal}Module${close}`;
3337
- const needsComma = trimmed.endsWith(",") ? "" : ",";
3338
- return `${open}${existing.trimEnd()}${needsComma} ${pascal}Module${close}`;
3339
- });
3340
- }
3341
- await writeFile3(indexPath, content, "utf-8");
3342
- }
3343
- __name(autoRegisterModule2, "autoRegisterModule");
3344
-
3345
- // src/generators/test.ts
3346
- import { join as join14, resolve as resolve3 } from "path";
3347
- async function generateTest(options) {
3348
- const { name, moduleName, modulesDir } = options;
3349
- const kebab = toKebabCase(name);
3350
- const pascal = toPascalCase(name);
3351
- const files = [];
3352
- let outDir;
3353
- if (options.outDir) {
3354
- outDir = resolve3(options.outDir);
3355
- } else if (moduleName) {
3356
- const modKebab = toKebabCase(moduleName);
3357
- const modPlural = pluralize(modKebab);
3358
- const modDir = modulesDir ?? "src/modules";
3359
- outDir = resolve3(join14(modDir, modPlural, "__tests__"));
3360
- } else {
3361
- outDir = resolve3("src/__tests__");
3362
- }
3363
- const filePath = join14(outDir, `${kebab}.test.ts`);
3364
- await writeFileSafe(filePath, `import { describe, it, expect, beforeEach } from 'vitest'
1999
+ `}]}s($o,"genUseCases");async function go(e,t,o){let r=ke(e,"index.ts");if(!await _(r)){await l(r,`import type { AppModuleClass } from '@forinda/kickjs-core'
2000
+ import { ${t}Module } from './${o}'
2001
+
2002
+ export const modules: AppModuleClass[] = [${t}Module]
2003
+ `);return}let i=await to(r,"utf-8"),d=`import { ${t}Module } from './${o}'`;if(!i.includes(`${t}Module`)){let c=i.lastIndexOf("import ");if(c!==-1){let a=i.indexOf(`
2004
+ `,c);i=i.slice(0,a+1)+d+`
2005
+ `+i.slice(a+1)}else i=d+`
2006
+ `+i;i=i.replace(/(=\s*\[)([\s\S]*?)(])/,(a,p,m,u)=>{let w=m.trim();if(!w)return`${p}${t}Module${u}`;let g=w.endsWith(",")?"":",";return`${p}${m.trimEnd()}${g} ${t}Module${u}`})}await oo(r,i,"utf-8")}s(go,"autoRegisterModule");import{join as He,resolve as Re}from"path";async function Ke(e){let{name:t,moduleName:o,modulesDir:r}=e,n=$(t),i=f(t),d=[],c;if(e.outDir)c=Re(e.outDir);else if(o){let p=$(o),m=j(p);c=Re(He(r??"src/modules",m,"__tests__"))}else c=Re("src/__tests__");let a=He(c,`${n}.test.ts`);return await l(a,`import { describe, it, expect, beforeEach } from 'vitest'
3365
2007
  import { Container } from '@forinda/kickjs-core'
3366
2008
 
3367
- describe('${pascal}', () => {
2009
+ describe('${i}', () => {
3368
2010
  beforeEach(() => {
3369
2011
  Container.reset()
3370
2012
  })
@@ -3384,385 +2026,36 @@ describe('${pascal}', () => {
3384
2026
  expect(true).toBe(true)
3385
2027
  })
3386
2028
  })
3387
- `);
3388
- files.push(filePath);
3389
- return files;
3390
- }
3391
- __name(generateTest, "generateTest");
3392
-
3393
- // src/config.ts
3394
- import { readFile as readFile4, access as access2 } from "fs/promises";
3395
- import { join as join15 } from "path";
3396
- var CONFIG_FILES = [
3397
- "kick.config.ts",
3398
- "kick.config.js",
3399
- "kick.config.mjs",
3400
- "kick.config.json"
3401
- ];
3402
- async function loadKickConfig(cwd) {
3403
- for (const filename of CONFIG_FILES) {
3404
- const filepath = join15(cwd, filename);
3405
- try {
3406
- await access2(filepath);
3407
- } catch {
3408
- continue;
3409
- }
3410
- if (filename.endsWith(".json")) {
3411
- const content = await readFile4(filepath, "utf-8");
3412
- return JSON.parse(content);
3413
- }
3414
- try {
3415
- const { pathToFileURL: pathToFileURL2 } = await import("url");
3416
- const mod = await import(pathToFileURL2(filepath).href);
3417
- return mod.default ?? mod;
3418
- } catch (err) {
3419
- if (filename.endsWith(".ts")) {
3420
- console.warn(`Warning: Failed to load ${filename}. TypeScript config files require a runtime loader (e.g. tsx, ts-node) or use kick.config.js/.mjs instead.`);
3421
- }
3422
- continue;
3423
- }
3424
- }
3425
- return null;
3426
- }
3427
- __name(loadKickConfig, "loadKickConfig");
3428
-
3429
- // src/commands/generate.ts
3430
- function isDryRun(cmd) {
3431
- return cmd.parent?.opts()?.dryRun ?? false;
3432
- }
3433
- __name(isDryRun, "isDryRun");
3434
- function printGenerated(files, dryRun = false) {
3435
- const cwd = process.cwd();
3436
- const label = dryRun ? "Would generate" : "Generated";
3437
- console.log(`
3438
- ${label} ${files.length} file${files.length === 1 ? "" : "s"}:`);
3439
- for (const f of files) {
3440
- console.log(` ${f.replace(cwd + "/", "")}`);
3441
- }
3442
- if (dryRun) console.log("\n (dry run \u2014 no files were written)");
3443
- console.log();
3444
- }
3445
- __name(printGenerated, "printGenerated");
3446
- var GENERATORS = [
3447
- {
3448
- name: "module <name>",
3449
- description: "Full DDD module (controller, DTOs, use-cases, repo)"
3450
- },
3451
- {
3452
- name: "scaffold <name> <fields...>",
3453
- description: "CRUD module from field definitions"
3454
- },
3455
- {
3456
- name: "controller <name>",
3457
- description: "@Controller() class [-m module]"
3458
- },
3459
- {
3460
- name: "service <name>",
3461
- description: "@Service() singleton [-m module]"
3462
- },
3463
- {
3464
- name: "middleware <name>",
3465
- description: "Express middleware function [-m module]"
3466
- },
3467
- {
3468
- name: "guard <name>",
3469
- description: "Route guard (auth, roles, etc.) [-m module]"
3470
- },
3471
- {
3472
- name: "dto <name>",
3473
- description: "Zod DTO schema [-m module]"
3474
- },
3475
- {
3476
- name: "adapter <name>",
3477
- description: "AppAdapter with lifecycle hooks (app-level only)"
3478
- },
3479
- {
3480
- name: "test <name>",
3481
- description: "Vitest test scaffold [-m module]"
3482
- },
3483
- {
3484
- name: "resolver <name>",
3485
- description: "GraphQL @Resolver class"
3486
- },
3487
- {
3488
- name: "job <name>",
3489
- description: "Queue @Job processor"
3490
- },
3491
- {
3492
- name: "config",
3493
- description: "Generate kick.config.ts"
3494
- }
3495
- ];
3496
- function printGeneratorList() {
3497
- console.log("\n Available generators:\n");
3498
- const maxName = Math.max(...GENERATORS.map((g) => g.name.length));
3499
- for (const g of GENERATORS) {
3500
- console.log(` kick g ${g.name.padEnd(maxName + 2)} ${g.description}`);
3501
- }
3502
- console.log();
3503
- }
3504
- __name(printGeneratorList, "printGeneratorList");
3505
- function registerGenerateCommand(program) {
3506
- const gen = program.command("generate").alias("g").description("Generate code scaffolds").option("--list", "List all available generators").option("--dry-run", "Preview files that would be generated without writing them").action((opts) => {
3507
- if (opts.list) {
3508
- printGeneratorList();
3509
- } else {
3510
- gen.help();
3511
- }
3512
- });
3513
- gen.command("module <name>").description("Generate a module (structure depends on project pattern)").option("--no-entity", "Skip entity and value object generation").option("--no-tests", "Skip test file generation").option("--repo <type>", "Repository implementation: inmemory | drizzle | prisma").option("--pattern <pattern>", "Override project pattern: rest | ddd | cqrs | minimal").option("--minimal", "Shorthand for --pattern minimal").option("--modules-dir <dir>", "Modules directory").option("-f, --force", "Overwrite existing files without prompting").action(async (name, opts, cmd) => {
3514
- const dryRun = isDryRun(cmd);
3515
- setDryRun(dryRun);
3516
- const config = await loadKickConfig(process.cwd());
3517
- const modulesDir = opts.modulesDir ?? config?.modulesDir ?? "src/modules";
3518
- const repo = opts.repo ?? config?.defaultRepo ?? "inmemory";
3519
- const pattern = opts.pattern ?? config?.pattern ?? "ddd";
3520
- const files = await generateModule({
3521
- name,
3522
- modulesDir: resolve4(modulesDir),
3523
- noEntity: opts.entity === false,
3524
- noTests: opts.tests === false,
3525
- repo,
3526
- minimal: opts.minimal,
3527
- force: opts.force,
3528
- pattern,
3529
- dryRun
3530
- });
3531
- printGenerated(files, dryRun);
3532
- });
3533
- gen.command("adapter <name>").description("Generate an AppAdapter with lifecycle hooks and middleware support").option("-o, --out <dir>", "Output directory", "src/adapters").action(async (name, opts, cmd) => {
3534
- const dryRun = isDryRun(cmd);
3535
- setDryRun(dryRun);
3536
- const files = await generateAdapter({
3537
- name,
3538
- outDir: resolve4(opts.out)
3539
- });
3540
- printGenerated(files, dryRun);
3541
- });
3542
- gen.command("middleware <name>").description("Generate an Express middleware function\n 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 (name, opts, cmd) => {
3543
- const dryRun = isDryRun(cmd);
3544
- setDryRun(dryRun);
3545
- const config = await loadKickConfig(process.cwd());
3546
- const modulesDir = config?.modulesDir ?? "src/modules";
3547
- const files = await generateMiddleware({
3548
- name,
3549
- outDir: opts.out,
3550
- moduleName: opts.module,
3551
- modulesDir,
3552
- pattern: config?.pattern
3553
- });
3554
- printGenerated(files, dryRun);
3555
- });
3556
- gen.command("guard <name>").description("Generate a route guard (auth, roles, etc.)\n 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 (name, opts, cmd) => {
3557
- const dryRun = isDryRun(cmd);
3558
- setDryRun(dryRun);
3559
- const config = await loadKickConfig(process.cwd());
3560
- const modulesDir = config?.modulesDir ?? "src/modules";
3561
- const files = await generateGuard({
3562
- name,
3563
- outDir: opts.out,
3564
- moduleName: opts.module,
3565
- modulesDir,
3566
- pattern: config?.pattern
3567
- });
3568
- printGenerated(files, dryRun);
3569
- });
3570
- gen.command("service <name>").description("Generate a @Service() class\n 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 (name, opts, cmd) => {
3571
- const dryRun = isDryRun(cmd);
3572
- setDryRun(dryRun);
3573
- const config = await loadKickConfig(process.cwd());
3574
- const modulesDir = config?.modulesDir ?? "src/modules";
3575
- const files = await generateService({
3576
- name,
3577
- outDir: opts.out,
3578
- moduleName: opts.module,
3579
- modulesDir,
3580
- pattern: config?.pattern
3581
- });
3582
- printGenerated(files, dryRun);
3583
- });
3584
- gen.command("controller <name>").description("Generate a @Controller() class with basic routes\n 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 (name, opts, cmd) => {
3585
- const dryRun = isDryRun(cmd);
3586
- setDryRun(dryRun);
3587
- const config = await loadKickConfig(process.cwd());
3588
- const modulesDir = config?.modulesDir ?? "src/modules";
3589
- const files = await generateController2({
3590
- name,
3591
- outDir: opts.out,
3592
- moduleName: opts.module,
3593
- modulesDir,
3594
- pattern: config?.pattern
3595
- });
3596
- printGenerated(files, dryRun);
3597
- });
3598
- gen.command("dto <name>").description("Generate a Zod DTO schema\n 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 (name, opts, cmd) => {
3599
- const dryRun = isDryRun(cmd);
3600
- setDryRun(dryRun);
3601
- const config = await loadKickConfig(process.cwd());
3602
- const modulesDir = config?.modulesDir ?? "src/modules";
3603
- const files = await generateDto({
3604
- name,
3605
- outDir: opts.out,
3606
- moduleName: opts.module,
3607
- modulesDir,
3608
- pattern: config?.pattern
3609
- });
3610
- printGenerated(files, dryRun);
3611
- });
3612
- gen.command("test <name>").description("Generate a Vitest test scaffold\n 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 (name, opts, cmd) => {
3613
- const dryRun = isDryRun(cmd);
3614
- setDryRun(dryRun);
3615
- const config = await loadKickConfig(process.cwd());
3616
- const modulesDir = config?.modulesDir ?? "src/modules";
3617
- const files = await generateTest({
3618
- name,
3619
- outDir: opts.out,
3620
- moduleName: opts.module,
3621
- modulesDir
3622
- });
3623
- printGenerated(files, dryRun);
3624
- });
3625
- gen.command("resolver <name>").description("Generate a GraphQL @Resolver class with @Query and @Mutation methods").option("-o, --out <dir>", "Output directory", "src/resolvers").action(async (name, opts, cmd) => {
3626
- const dryRun = isDryRun(cmd);
3627
- setDryRun(dryRun);
3628
- const files = await generateResolver({
3629
- name,
3630
- outDir: resolve4(opts.out)
3631
- });
3632
- printGenerated(files, dryRun);
3633
- });
3634
- gen.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 (name, opts, cmd) => {
3635
- const dryRun = isDryRun(cmd);
3636
- setDryRun(dryRun);
3637
- const files = await generateJob({
3638
- name,
3639
- outDir: resolve4(opts.out),
3640
- queue: opts.queue
3641
- });
3642
- printGenerated(files, dryRun);
3643
- });
3644
- gen.command("scaffold <name> [fields...]").description("Generate a full CRUD module from field definitions\n Example: kick g scaffold Post title:string body:text published:boolean?\n Types: string, text, number, int, float, boolean, date, email, url, uuid, json, enum:a,b,c\n Append ? for optional fields: description:text?").option("--no-entity", "Skip entity and value object generation").option("--no-tests", "Skip test file generation").option("--modules-dir <dir>", "Modules directory").action(async (name, rawFields, opts, cmd) => {
3645
- const dryRun = isDryRun(cmd);
3646
- setDryRun(dryRun);
3647
- if (rawFields.length === 0) {
3648
- console.error("\n Error: At least one field is required.\n Usage: kick g scaffold <name> <field:type> [field:type...]\n Example: kick g scaffold Post title:string body:text published:boolean\n");
3649
- process.exit(1);
3650
- }
3651
- const config = await loadKickConfig(process.cwd());
3652
- const modulesDir = opts.modulesDir ?? config?.modulesDir ?? "src/modules";
3653
- const fields = parseFields(rawFields);
3654
- const files = await generateScaffold({
3655
- name,
3656
- fields,
3657
- modulesDir: resolve4(modulesDir),
3658
- noEntity: opts.entity === false,
3659
- noTests: opts.tests === false
3660
- });
3661
- console.log(`
3662
- Scaffolded ${name} with ${fields.length} field(s):`);
3663
- for (const f of fields) {
3664
- console.log(` ${f.name}: ${f.type}${f.optional ? " (optional)" : ""}`);
3665
- }
3666
- printGenerated(files, dryRun);
3667
- });
3668
- gen.command("config").description("Generate a kick.config.ts at the project root").option("--modules-dir <dir>", "Modules directory path", "src/modules").option("--repo <type>", "Default repository type: inmemory | drizzle | prisma", "inmemory").option("-f, --force", "Overwrite existing kick.config.ts without prompting").action(async (opts, cmd) => {
3669
- const dryRun = isDryRun(cmd);
3670
- setDryRun(dryRun);
3671
- const files = await generateConfig({
3672
- outDir: resolve4("."),
3673
- modulesDir: opts.modulesDir,
3674
- defaultRepo: opts.repo,
3675
- force: opts.force
3676
- });
3677
- printGenerated(files, dryRun);
3678
- });
3679
- }
3680
- __name(registerGenerateCommand, "registerGenerateCommand");
3681
-
3682
- // src/commands/run.ts
3683
- import { cpSync, existsSync as existsSync3, mkdirSync } from "fs";
3684
- import { resolve as resolve5, join as join16 } from "path";
3685
-
3686
- // src/utils/shell.ts
3687
- import { execSync as execSync2 } from "child_process";
3688
- function runShellCommand(command, cwd) {
3689
- execSync2(command, {
3690
- cwd,
3691
- stdio: "inherit"
3692
- });
3693
- }
3694
- __name(runShellCommand, "runShellCommand");
3695
-
3696
- // src/commands/run.ts
3697
- function registerRunCommands(program) {
3698
- program.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").action((opts) => {
3699
- const envVars = [];
3700
- if (opts.port) envVars.push(`PORT=${opts.port}`);
3701
- const cmd = `npx vite-node --watch ${opts.entry}`;
3702
- const fullCmd = envVars.length ? `${envVars.join(" ")} ${cmd}` : cmd;
3703
- console.log(`
3704
- KickJS dev server starting...`);
3705
- console.log(` Entry: ${opts.entry}`);
3706
- console.log(` HMR: enabled (vite-node)
3707
- `);
3708
- try {
3709
- runShellCommand(fullCmd);
3710
- } catch {
3711
- }
3712
- });
3713
- program.command("build").description("Build for production via Vite").action(async () => {
3714
- console.log("\n Building for production...\n");
3715
- runShellCommand("npx vite build");
3716
- const config = await loadKickConfig(process.cwd());
3717
- const copyDirs = config?.copyDirs ?? [];
3718
- if (copyDirs.length > 0) {
3719
- console.log("\n Copying directories to dist...");
3720
- for (const entry of copyDirs) {
3721
- const src = typeof entry === "string" ? entry : entry.src;
3722
- const dest = typeof entry === "string" ? join16("dist", entry) : entry.dest ?? join16("dist", src);
3723
- const srcPath = resolve5(src);
3724
- const destPath = resolve5(dest);
3725
- if (!existsSync3(srcPath)) {
3726
- console.log(` \u26A0 Skipped ${src} (not found)`);
3727
- continue;
3728
- }
3729
- mkdirSync(destPath, {
3730
- recursive: true
3731
- });
3732
- cpSync(srcPath, destPath, {
3733
- recursive: true
3734
- });
3735
- console.log(` \u2713 ${src} \u2192 ${dest}`);
3736
- }
3737
- }
3738
- console.log("\n Build complete.\n");
3739
- });
3740
- program.command("start").description("Start production server").option("-e, --entry <file>", "Entry file", "dist/index.js").option("-p, --port <port>", "Port number").action((opts) => {
3741
- const envVars = [
3742
- "NODE_ENV=production"
3743
- ];
3744
- if (opts.port) envVars.push(`PORT=${opts.port}`);
3745
- runShellCommand(`${envVars.join(" ")} node ${opts.entry}`);
3746
- });
3747
- program.command("dev:debug").description("Start dev server with Node.js inspector").option("-e, --entry <file>", "Entry file", "src/index.ts").option("-p, --port <port>", "Port number").action((opts) => {
3748
- const envVars = opts.port ? `PORT=${opts.port} ` : "";
3749
- try {
3750
- runShellCommand(`${envVars}npx vite-node --inspect --watch ${opts.entry}`);
3751
- } catch {
3752
- }
3753
- });
3754
- }
3755
- __name(registerRunCommands, "registerRunCommands");
3756
-
3757
- // src/commands/info.ts
3758
- import { platform, release, arch } from "os";
3759
- function registerInfoCommand(program) {
3760
- program.command("info").description("Print system and framework info").action(() => {
3761
- console.log(`
2029
+ `),d.push(a),d}s(Ke,"generateTest");import{readFile as yo,access as ho}from"fs/promises";import{join as wo}from"path";var vo=["kick.config.ts","kick.config.js","kick.config.mjs","kick.config.json"];async function C(e){for(let t of vo){let o=wo(e,t);try{await ho(o)}catch{continue}if(t.endsWith(".json")){let r=await yo(o,"utf-8");return JSON.parse(r)}try{let{pathToFileURL:r}=await import("url"),n=await import(r(o).href);return n.default??n}catch{t.endsWith(".ts")&&console.warn(`Warning: Failed to load ${t}. TypeScript config files require a runtime loader (e.g. tsx, ts-node) or use kick.config.js/.mjs instead.`);continue}}return null}s(C,"loadKickConfig");function k(e){return e.parent?.opts()?.dryRun??!1}s(k,"isDryRun");function R(e,t=!1){let o=process.cwd();console.log(`
2030
+ ${t?"Would generate":"Generated"} ${e.length} file${e.length===1?"":"s"}:`);for(let n of e)console.log(` ${n.replace(o+"/","")}`);t&&console.log(`
2031
+ (dry run \u2014 no files were written)`),console.log()}s(R,"printGenerated");var We=[{name:"module <name>",description:"Full DDD module (controller, DTOs, use-cases, repo)"},{name:"scaffold <name> <fields...>",description:"CRUD module from field definitions"},{name:"controller <name>",description:"@Controller() class [-m module]"},{name:"service <name>",description:"@Service() singleton [-m module]"},{name:"middleware <name>",description:"Express middleware function [-m module]"},{name:"guard <name>",description:"Route guard (auth, roles, etc.) [-m module]"},{name:"dto <name>",description:"Zod DTO schema [-m module]"},{name:"adapter <name>",description:"AppAdapter with lifecycle hooks (app-level only)"},{name:"test <name>",description:"Vitest test scaffold [-m module]"},{name:"resolver <name>",description:"GraphQL @Resolver class"},{name:"job <name>",description:"Queue @Job processor"},{name:"config",description:"Generate kick.config.ts"}];function Co(){console.log(`
2032
+ Available generators:
2033
+ `);let e=Math.max(...We.map(t=>t.name.length));for(let t of We)console.log(` kick g ${t.name.padEnd(e+2)} ${t.description}`);console.log()}s(Co,"printGeneratorList");function Je(e){let t=e.command("generate").alias("g").description("Generate code scaffolds").option("--list","List all available generators").option("--dry-run","Preview files that would be generated without writing them").action(o=>{o.list?Co():t.help()});t.command("module <name>").description("Generate a module (structure depends on project pattern)").option("--no-entity","Skip entity and value object generation").option("--no-tests","Skip test file generation").option("--repo <type>","Repository implementation: inmemory | drizzle | prisma").option("--pattern <pattern>","Override project pattern: rest | ddd | cqrs | minimal").option("--minimal","Shorthand for --pattern minimal").option("--modules-dir <dir>","Modules directory").option("-f, --force","Overwrite existing files without prompting").action(async(o,r,n)=>{let i=k(n);v(i);let d=await C(process.cwd()),c=r.modulesDir??d?.modulesDir??"src/modules",a=r.repo??d?.defaultRepo??"inmemory",p=r.pattern??d?.pattern??"ddd",m=await Ae({name:o,modulesDir:z(c),noEntity:r.entity===!1,noTests:r.tests===!1,repo:a,minimal:r.minimal,force:r.force,pattern:p,dryRun:i});R(m,i)}),t.command("adapter <name>").description("Generate an AppAdapter with lifecycle hooks and middleware support").option("-o, --out <dir>","Output directory","src/adapters").action(async(o,r,n)=>{let i=k(n);v(i);let d=await Ue({name:o,outDir:z(r.out)});R(d,i)}),t.command("middleware <name>").description(`Generate an Express middleware function
2034
+ 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(o,r,n)=>{let i=k(n);v(i);let d=await C(process.cwd()),c=d?.modulesDir??"src/modules",a=await qe({name:o,outDir:r.out,moduleName:r.module,modulesDir:c,pattern:d?.pattern});R(a,i)}),t.command("guard <name>").description(`Generate a route guard (auth, roles, etc.)
2035
+ 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(o,r,n)=>{let i=k(n);v(i);let d=await C(process.cwd()),c=d?.modulesDir??"src/modules",a=await _e({name:o,outDir:r.out,moduleName:r.module,modulesDir:c,pattern:d?.pattern});R(a,i)}),t.command("service <name>").description(`Generate a @Service() class
2036
+ 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(o,r,n)=>{let i=k(n);v(i);let d=await C(process.cwd()),c=d?.modulesDir??"src/modules",a=await Me({name:o,outDir:r.out,moduleName:r.module,modulesDir:c,pattern:d?.pattern});R(a,i)}),t.command("controller <name>").description(`Generate a @Controller() class with basic routes
2037
+ 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(o,r,n)=>{let i=k(n);v(i);let d=await C(process.cwd()),c=d?.modulesDir??"src/modules",a=await be({name:o,outDir:r.out,moduleName:r.module,modulesDir:c,pattern:d?.pattern});R(a,i)}),t.command("dto <name>").description(`Generate a Zod DTO schema
2038
+ 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(o,r,n)=>{let i=k(n);v(i);let d=await C(process.cwd()),c=d?.modulesDir??"src/modules",a=await Qe({name:o,outDir:r.out,moduleName:r.module,modulesDir:c,pattern:d?.pattern});R(a,i)}),t.command("test <name>").description(`Generate a Vitest test scaffold
2039
+ 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(o,r,n)=>{let i=k(n);v(i);let c=(await C(process.cwd()))?.modulesDir??"src/modules",a=await Ke({name:o,outDir:r.out,moduleName:r.module,modulesDir:c});R(a,i)}),t.command("resolver <name>").description("Generate a GraphQL @Resolver class with @Query and @Mutation methods").option("-o, --out <dir>","Output directory","src/resolvers").action(async(o,r,n)=>{let i=k(n);v(i);let d=await Fe({name:o,outDir:z(r.out)});R(d,i)}),t.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(o,r,n)=>{let i=k(n);v(i);let d=await Le({name:o,outDir:z(r.out),queue:r.queue});R(d,i)}),t.command("scaffold <name> [fields...]").description(`Generate a full CRUD module from field definitions
2040
+ Example: kick g scaffold Post title:string body:text published:boolean?
2041
+ Types: string, text, number, int, float, boolean, date, email, url, uuid, json, enum:a,b,c
2042
+ Append ? for optional fields: description:text?`).option("--no-entity","Skip entity and value object generation").option("--no-tests","Skip test file generation").option("--modules-dir <dir>","Modules directory").action(async(o,r,n,i)=>{let d=k(i);v(d),r.length===0&&(console.error(`
2043
+ Error: At least one field is required.
2044
+ Usage: kick g scaffold <name> <field:type> [field:type...]
2045
+ Example: kick g scaffold Post title:string body:text published:boolean
2046
+ `),process.exit(1));let c=await C(process.cwd()),a=n.modulesDir??c?.modulesDir??"src/modules",p=Ye(r),m=await Be({name:o,fields:p,modulesDir:z(a),noEntity:n.entity===!1,noTests:n.tests===!1});console.log(`
2047
+ Scaffolded ${o} with ${p.length} field(s):`);for(let u of p)console.log(` ${u.name}: ${u.type}${u.optional?" (optional)":""}`);R(m,d)}),t.command("config").description("Generate a kick.config.ts at the project root").option("--modules-dir <dir>","Modules directory path","src/modules").option("--repo <type>","Default repository type: inmemory | drizzle | prisma","inmemory").option("-f, --force","Overwrite existing kick.config.ts without prompting").action(async(o,r)=>{let n=k(r);v(n);let i=await Ge({outDir:z("."),modulesDir:o.modulesDir,defaultRepo:o.repo,force:o.force});R(i,n)})}s(Je,"registerGenerateCommand");import{cpSync as ko,existsSync as Ro,mkdirSync as Do}from"fs";import{resolve as Ve,join as Ze}from"path";import{execSync as xo}from"child_process";function E(e,t){xo(e,{cwd:t,stdio:"inherit"})}s(E,"runShellCommand");function Xe(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").action(t=>{let o=[];t.port&&o.push(`PORT=${t.port}`);let r=`npx vite-node --watch ${t.entry}`,n=o.length?`${o.join(" ")} ${r}`:r;console.log(`
2048
+ KickJS dev server starting...`),console.log(` Entry: ${t.entry}`),console.log(` HMR: enabled (vite-node)
2049
+ `);try{E(n)}catch{}}),e.command("build").description("Build for production via Vite").action(async()=>{console.log(`
2050
+ Building for production...
2051
+ `),E("npx vite build");let o=(await C(process.cwd()))?.copyDirs??[];if(o.length>0){console.log(`
2052
+ Copying directories to dist...`);for(let r of o){let n=typeof r=="string"?r:r.src,i=typeof r=="string"?Ze("dist",r):r.dest??Ze("dist",n),d=Ve(n),c=Ve(i);if(!Ro(d)){console.log(` \u26A0 Skipped ${n} (not found)`);continue}Do(c,{recursive:!0}),ko(d,c,{recursive:!0}),console.log(` \u2713 ${n} \u2192 ${i}`)}}console.log(`
2053
+ Build complete.
2054
+ `)}),e.command("start").description("Start production server").option("-e, --entry <file>","Entry file","dist/index.js").option("-p, --port <port>","Port number").action(t=>{let o=["NODE_ENV=production"];t.port&&o.push(`PORT=${t.port}`),E(`${o.join(" ")} node ${t.entry}`)}),e.command("dev:debug").description("Start dev server with Node.js inspector").option("-e, --entry <file>","Entry file","src/index.ts").option("-p, --port <port>","Port number").action(t=>{let o=t.port?`PORT=${t.port} `:"";try{E(`${o}npx vite-node --inspect --watch ${t.entry}`)}catch{}})}s(Xe,"registerRunCommands");import{platform as Oo,release as Io,arch as So}from"os";function et(e){e.command("info").description("Print system and framework info").action(()=>{console.log(`
3762
2055
  KickJS CLI
3763
2056
 
3764
2057
  System:
3765
- OS: ${platform()} ${release()} (${arch()})
2058
+ OS: ${Oo()} ${Io()} (${So()})
3766
2059
  Node: ${process.version}
3767
2060
 
3768
2061
  Packages:
@@ -3770,502 +2063,32 @@ function registerInfoCommand(program) {
3770
2063
  @forinda/kickjs-http workspace
3771
2064
  @forinda/kickjs-config workspace
3772
2065
  @forinda/kickjs-cli workspace
3773
- `);
3774
- });
3775
- }
3776
- __name(registerInfoCommand, "registerInfoCommand");
3777
-
3778
- // src/commands/custom.ts
3779
- function registerCustomCommands(program, config) {
3780
- if (!config?.commands?.length) return;
3781
- for (const cmd of config.commands) {
3782
- registerSingleCommand(program, cmd);
3783
- }
3784
- }
3785
- __name(registerCustomCommands, "registerCustomCommands");
3786
- function registerSingleCommand(program, def) {
3787
- const command = program.command(def.name).description(def.description);
3788
- if (def.aliases) {
3789
- for (const alias of def.aliases) {
3790
- command.alias(alias);
3791
- }
3792
- }
3793
- command.allowUnknownOption(true);
3794
- command.argument("[args...]", "Additional arguments passed to the command");
3795
- command.action((args) => {
3796
- const extraArgs = args.join(" ");
3797
- const steps = Array.isArray(def.steps) ? def.steps : [
3798
- def.steps
3799
- ];
3800
- for (const step of steps) {
3801
- const finalCmd = extraArgs ? `${step} ${extraArgs}` : step;
3802
- console.log(` $ ${finalCmd}`);
3803
- try {
3804
- runShellCommand(finalCmd);
3805
- } catch (err) {
3806
- console.error(` Command failed: ${def.name}`);
3807
- process.exitCode = 1;
3808
- return;
3809
- }
3810
- }
3811
- });
3812
- }
3813
- __name(registerSingleCommand, "registerSingleCommand");
3814
-
3815
- // src/commands/inspect.ts
3816
- var esc = /* @__PURE__ */ __name((code) => `\x1B[${code}m`, "esc");
3817
- var reset = esc("0");
3818
- var bold = /* @__PURE__ */ __name((s) => `${esc("1")}${s}${reset}`, "bold");
3819
- var dim = /* @__PURE__ */ __name((s) => `${esc("2")}${s}${reset}`, "dim");
3820
- var green = /* @__PURE__ */ __name((s) => `${esc("32")}${s}${reset}`, "green");
3821
- var red = /* @__PURE__ */ __name((s) => `${esc("31")}${s}${reset}`, "red");
3822
- var yellow = /* @__PURE__ */ __name((s) => `${esc("33")}${s}${reset}`, "yellow");
3823
- var cyan = /* @__PURE__ */ __name((s) => `${esc("36")}${s}${reset}`, "cyan");
3824
- var magenta = /* @__PURE__ */ __name((s) => `${esc("35")}${s}${reset}`, "magenta");
3825
- var blue = /* @__PURE__ */ __name((s) => `${esc("34")}${s}${reset}`, "blue");
3826
- var METHOD_COLORS = {
3827
- GET: green,
3828
- POST: cyan,
3829
- PUT: yellow,
3830
- PATCH: magenta,
3831
- DELETE: red
3832
- };
3833
- function colorMethod(method) {
3834
- const fn = METHOD_COLORS[method] ?? dim;
3835
- return fn(method.padEnd(7));
3836
- }
3837
- __name(colorMethod, "colorMethod");
3838
- function formatUptime(seconds) {
3839
- const d = Math.floor(seconds / 86400);
3840
- const h = Math.floor(seconds % 86400 / 3600);
3841
- const m = Math.floor(seconds % 3600 / 60);
3842
- const s = seconds % 60;
3843
- const parts = [];
3844
- if (d) parts.push(`${d}d`);
3845
- if (h) parts.push(`${h}h`);
3846
- if (m) parts.push(`${m}m`);
3847
- parts.push(`${s}s`);
3848
- return parts.join(" ");
3849
- }
3850
- __name(formatUptime, "formatUptime");
3851
- async function fetchJson(url) {
3852
- const res = await fetch(url, {
3853
- signal: AbortSignal.timeout(5e3)
3854
- });
3855
- if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
3856
- return res.json();
3857
- }
3858
- __name(fetchJson, "fetchJson");
3859
- async function fetchEndpoint(base, path) {
3860
- try {
3861
- return await fetchJson(`${base}${path}`);
3862
- } catch {
3863
- return null;
3864
- }
3865
- }
3866
- __name(fetchEndpoint, "fetchEndpoint");
3867
- async function fetchAll(base) {
3868
- const [health, metrics, routes, container, ws] = await Promise.all([
3869
- fetchEndpoint(base, "/health"),
3870
- fetchEndpoint(base, "/metrics"),
3871
- fetchEndpoint(base, "/routes"),
3872
- fetchEndpoint(base, "/container"),
3873
- fetchEndpoint(base, "/ws")
3874
- ]);
3875
- return {
3876
- health,
3877
- metrics,
3878
- routes,
3879
- container,
3880
- ws
3881
- };
3882
- }
3883
- __name(fetchAll, "fetchAll");
3884
- function printSummary(base, data) {
3885
- const { health, metrics, routes, container, ws } = data;
3886
- const line = dim("\u2500".repeat(60));
3887
- console.log();
3888
- console.log(bold(` KickJS Inspector`) + dim(` \u2192 ${base}`));
3889
- console.log(line);
3890
- if (health) {
3891
- const statusText = health.status === "healthy" ? green("\u25CF healthy") : red("\u25CF " + health.status);
3892
- console.log(` ${bold("Health:")} ${statusText}`);
3893
- } else {
3894
- console.log(` ${bold("Health:")} ${red("\u25CF unreachable")}`);
3895
- }
3896
- if (metrics) {
3897
- const rate = ((metrics.errorRate ?? 0) * 100).toFixed(1);
3898
- const rateColor = metrics.errorRate > 0.1 ? red : metrics.errorRate > 0 ? yellow : green;
3899
- console.log(` ${bold("Uptime:")} ${formatUptime(metrics.uptimeSeconds)}`);
3900
- console.log(` ${bold("Requests:")} ${metrics.requests}`);
3901
- console.log(` ${bold("Errors:")} ${metrics.serverErrors} server, ${metrics.clientErrors ?? 0} client ${dim("(")}${rateColor(rate + "%")}${dim(")")}`);
3902
- }
3903
- if (container) {
3904
- console.log(` ${bold("DI:")} ${container.count} bindings`);
3905
- }
3906
- if (ws && ws.enabled) {
3907
- console.log(` ${bold("WS:")} ${ws.connections ?? 0} connections, ${ws.namespaces ?? 0} namespaces`);
3908
- }
3909
- if (routes?.routes?.length) {
3910
- console.log();
3911
- console.log(bold(" Routes"));
3912
- console.log(line);
3913
- console.log(` ${dim("METHOD")} ${dim("PATH".padEnd(36))} ${dim("CONTROLLER")}`);
3914
- for (const r of routes.routes) {
3915
- const path = r.path.length > 36 ? r.path.slice(0, 33) + "..." : r.path.padEnd(36);
3916
- console.log(` ${colorMethod(r.method)} ${path} ${blue(r.controller)}.${dim(r.handler)}`);
3917
- }
3918
- }
3919
- console.log(line);
3920
- console.log();
3921
- }
3922
- __name(printSummary, "printSummary");
3923
- function registerInspectCommand(program) {
3924
- program.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 (url, opts) => {
3925
- let base = url ?? "http://localhost:3000";
3926
- if (opts.port) {
3927
- try {
3928
- const parsed = new URL(base);
3929
- parsed.port = opts.port;
3930
- base = parsed.origin;
3931
- } catch {
3932
- base = `http://localhost:${opts.port}`;
3933
- }
3934
- }
3935
- const debugBase = `${base.replace(/\/$/, "")}/_debug`;
3936
- const run = /* @__PURE__ */ __name(async () => {
3937
- try {
3938
- const data = await fetchAll(debugBase);
3939
- if (opts.json) {
3940
- console.log(JSON.stringify(data, null, 2));
3941
- } else {
3942
- printSummary(base, data);
3943
- }
3944
- } catch (err) {
3945
- if (opts.json) {
3946
- console.log(JSON.stringify({
3947
- error: String(err)
3948
- }));
3949
- } else {
3950
- console.error(red(` \u2716 Could not connect to ${base}`));
3951
- console.error(dim(` ${err instanceof Error ? err.message : String(err)}`));
3952
- }
3953
- if (!opts.watch) process.exitCode = 1;
3954
- }
3955
- }, "run");
3956
- if (opts.watch) {
3957
- const poll = /* @__PURE__ */ __name(async () => {
3958
- process.stdout.write("\x1B[2J\x1B[H");
3959
- await run();
3960
- }, "poll");
3961
- await poll();
3962
- setInterval(poll, 5e3);
3963
- } else {
3964
- await run();
3965
- }
3966
- });
3967
- }
3968
- __name(registerInspectCommand, "registerInspectCommand");
3969
-
3970
- // src/commands/add.ts
3971
- import { execSync as execSync3 } from "child_process";
3972
- import { existsSync as existsSync4 } from "fs";
3973
- import { resolve as resolve6 } from "path";
3974
- var PACKAGE_REGISTRY = {
3975
- // Core (already installed by kick new)
3976
- core: {
3977
- pkg: "@forinda/kickjs-core",
3978
- peers: [],
3979
- description: "DI container, decorators, reactivity"
3980
- },
3981
- http: {
3982
- pkg: "@forinda/kickjs-http",
3983
- peers: [
3984
- "express"
3985
- ],
3986
- description: "Express 5, routing, middleware"
3987
- },
3988
- config: {
3989
- pkg: "@forinda/kickjs-config",
3990
- peers: [],
3991
- description: "Zod-based env validation"
3992
- },
3993
- cli: {
3994
- pkg: "@forinda/kickjs-cli",
3995
- peers: [],
3996
- description: "CLI tool and code generators",
3997
- dev: true
3998
- },
3999
- // API
4000
- swagger: {
4001
- pkg: "@forinda/kickjs-swagger",
4002
- peers: [],
4003
- description: "OpenAPI spec + Swagger UI + ReDoc"
4004
- },
4005
- graphql: {
4006
- pkg: "@forinda/kickjs-graphql",
4007
- peers: [
4008
- "graphql"
4009
- ],
4010
- description: "GraphQL resolvers + GraphiQL"
4011
- },
4012
- // Database
4013
- drizzle: {
4014
- pkg: "@forinda/kickjs-drizzle",
4015
- peers: [
4016
- "drizzle-orm"
4017
- ],
4018
- description: "Drizzle ORM adapter + query builder"
4019
- },
4020
- prisma: {
4021
- pkg: "@forinda/kickjs-prisma",
4022
- peers: [
4023
- "@prisma/client"
4024
- ],
4025
- description: "Prisma adapter + query builder"
4026
- },
4027
- // Real-time
4028
- ws: {
4029
- pkg: "@forinda/kickjs-ws",
4030
- peers: [
4031
- "socket.io"
4032
- ],
4033
- description: "WebSocket with @WsController decorators"
4034
- },
4035
- // Observability
4036
- otel: {
4037
- pkg: "@forinda/kickjs-otel",
4038
- peers: [
4039
- "@opentelemetry/api"
4040
- ],
4041
- description: "OpenTelemetry tracing + metrics"
4042
- },
4043
- // DevTools
4044
- devtools: {
4045
- pkg: "@forinda/kickjs-devtools",
4046
- peers: [],
4047
- description: "Development dashboard \u2014 routes, DI, metrics, health",
4048
- dev: true
4049
- },
4050
- // Auth
4051
- auth: {
4052
- pkg: "@forinda/kickjs-auth",
4053
- peers: [
4054
- "jsonwebtoken"
4055
- ],
4056
- description: "Authentication \u2014 JWT, API key, and custom strategies"
4057
- },
4058
- // Mailer
4059
- mailer: {
4060
- pkg: "@forinda/kickjs-mailer",
4061
- peers: [
4062
- "nodemailer"
4063
- ],
4064
- description: "Email sending \u2014 SMTP, Resend, SES, or custom provider"
4065
- },
4066
- // Cron
4067
- cron: {
4068
- pkg: "@forinda/kickjs-cron",
4069
- peers: [
4070
- "croner"
4071
- ],
4072
- description: "Cron job scheduling (production-grade with croner)"
4073
- },
4074
- // Queue
4075
- queue: {
4076
- pkg: "@forinda/kickjs-queue",
4077
- peers: [],
4078
- description: "Queue adapter (BullMQ/RabbitMQ/Kafka)"
4079
- },
4080
- "queue:bullmq": {
4081
- pkg: "@forinda/kickjs-queue",
4082
- peers: [
4083
- "bullmq",
4084
- "ioredis"
4085
- ],
4086
- description: "Queue with BullMQ + Redis"
4087
- },
4088
- "queue:rabbitmq": {
4089
- pkg: "@forinda/kickjs-queue",
4090
- peers: [
4091
- "amqplib"
4092
- ],
4093
- description: "Queue with RabbitMQ"
4094
- },
4095
- "queue:kafka": {
4096
- pkg: "@forinda/kickjs-queue",
4097
- peers: [
4098
- "kafkajs"
4099
- ],
4100
- description: "Queue with Kafka"
4101
- },
4102
- // Multi-tenancy
4103
- "multi-tenant": {
4104
- pkg: "@forinda/kickjs-multi-tenant",
4105
- peers: [],
4106
- description: "Tenant resolution middleware"
4107
- },
4108
- // Notifications
4109
- notifications: {
4110
- pkg: "@forinda/kickjs-notifications",
4111
- peers: [],
4112
- description: "Multi-channel notifications \u2014 email, Slack, Discord, webhook"
4113
- },
4114
- // Testing
4115
- testing: {
4116
- pkg: "@forinda/kickjs-testing",
4117
- peers: [],
4118
- description: "Test utilities and TestModule builder",
4119
- dev: true
4120
- }
4121
- };
4122
- function detectPackageManager() {
4123
- if (existsSync4(resolve6("pnpm-lock.yaml"))) return "pnpm";
4124
- if (existsSync4(resolve6("yarn.lock"))) return "yarn";
4125
- return "npm";
4126
- }
4127
- __name(detectPackageManager, "detectPackageManager");
4128
- function printPackageList() {
4129
- console.log("\n Available KickJS packages:\n");
4130
- const maxName = Math.max(...Object.keys(PACKAGE_REGISTRY).map((k) => k.length));
4131
- for (const [name, info] of Object.entries(PACKAGE_REGISTRY)) {
4132
- const padded = name.padEnd(maxName + 2);
4133
- const peers = info.peers.length ? ` (+ ${info.peers.join(", ")})` : "";
4134
- console.log(` ${padded} ${info.description}${peers}`);
4135
- }
4136
- console.log("\n Usage: kick add graphql drizzle otel");
4137
- console.log(" kick add queue:bullmq");
4138
- console.log();
4139
- }
4140
- __name(printPackageList, "printPackageList");
4141
- function registerListCommand(program) {
4142
- program.command("list").alias("ls").description("List all available KickJS packages").action(() => {
4143
- printPackageList();
4144
- });
4145
- }
4146
- __name(registerListCommand, "registerListCommand");
4147
- function registerAddCommand(program) {
4148
- program.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 all available packages").action(async (packages, opts) => {
4149
- if (opts.list || packages.length === 0) {
4150
- printPackageList();
4151
- return;
4152
- }
4153
- const pm = opts.pm ?? detectPackageManager();
4154
- const forceDevFlag = opts.dev;
4155
- const prodDeps = /* @__PURE__ */ new Set();
4156
- const devDeps = /* @__PURE__ */ new Set();
4157
- const unknown = [];
4158
- for (const name of packages) {
4159
- const entry = PACKAGE_REGISTRY[name];
4160
- if (!entry) {
4161
- unknown.push(name);
4162
- continue;
4163
- }
4164
- const target = forceDevFlag || entry.dev ? devDeps : prodDeps;
4165
- target.add(entry.pkg);
4166
- for (const peer of entry.peers) {
4167
- target.add(peer);
4168
- }
4169
- }
4170
- if (unknown.length > 0) {
4171
- console.log(`
4172
- Unknown packages: ${unknown.join(", ")}`);
4173
- console.log(' Run "kick add --list" to see available packages.\n');
4174
- if (prodDeps.size === 0 && devDeps.size === 0) return;
4175
- }
4176
- if (prodDeps.size > 0) {
4177
- const deps = Array.from(prodDeps);
4178
- const cmd = `${pm} add ${deps.join(" ")}`;
4179
- console.log(`
4180
- Installing ${deps.length} dependency(ies):`);
4181
- for (const dep of deps) console.log(` + ${dep}`);
4182
- console.log();
4183
- try {
4184
- execSync3(cmd, {
4185
- stdio: "inherit"
4186
- });
4187
- } catch {
4188
- console.log(`
2066
+ `)})}s(et,"registerInfoCommand");function tt(e,t){if(t?.commands?.length)for(let o of t.commands)To(e,o)}s(tt,"registerCustomCommands");function To(e,t){let o=e.command(t.name).description(t.description);if(t.aliases)for(let r of t.aliases)o.alias(r);o.allowUnknownOption(!0),o.argument("[args...]","Additional arguments passed to the command"),o.action(r=>{let n=r.join(" "),i=Array.isArray(t.steps)?t.steps:[t.steps];for(let d of i){let c=n?`${d} ${n}`:d;console.log(` $ ${c}`);try{E(c)}catch{console.error(` Command failed: ${t.name}`),process.exitCode=1;return}}})}s(To,"registerSingleCommand");var T=s(e=>`\x1B[${e}m`,"esc"),P=T("0"),S=s(e=>`${T("1")}${e}${P}`,"bold"),I=s(e=>`${T("2")}${e}${P}`,"dim"),De=s(e=>`${T("32")}${e}${P}`,"green"),K=s(e=>`${T("31")}${e}${P}`,"red"),ot=s(e=>`${T("33")}${e}${P}`,"yellow"),jo=s(e=>`${T("36")}${e}${P}`,"cyan"),Po=s(e=>`${T("35")}${e}${P}`,"magenta"),Eo=s(e=>`${T("34")}${e}${P}`,"blue"),Ao={GET:De,POST:jo,PUT:ot,PATCH:Po,DELETE:K};function Uo(e){return(Ao[e]??I)(e.padEnd(7))}s(Uo,"colorMethod");function zo(e){let t=Math.floor(e/86400),o=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),n=e%60,i=[];return t&&i.push(`${t}d`),o&&i.push(`${o}h`),r&&i.push(`${r}m`),i.push(`${n}s`),i.join(" ")}s(zo,"formatUptime");async function qo(e){let t=await fetch(e,{signal:AbortSignal.timeout(5e3)});if(!t.ok)throw new Error(`${t.status} ${t.statusText}`);return t.json()}s(qo,"fetchJson");async function H(e,t){try{return await qo(`${e}${t}`)}catch{return null}}s(H,"fetchEndpoint");async function _o(e){let[t,o,r,n,i]=await Promise.all([H(e,"/health"),H(e,"/metrics"),H(e,"/routes"),H(e,"/container"),H(e,"/ws")]);return{health:t,metrics:o,routes:r,container:n,ws:i}}s(_o,"fetchAll");function Mo(e,t){let{health:o,metrics:r,routes:n,container:i,ws:d}=t,c=I("\u2500".repeat(60));if(console.log(),console.log(S(" KickJS Inspector")+I(` \u2192 ${e}`)),console.log(c),o){let a=o.status==="healthy"?De("\u25CF healthy"):K("\u25CF "+o.status);console.log(` ${S("Health:")} ${a}`)}else console.log(` ${S("Health:")} ${K("\u25CF unreachable")}`);if(r){let a=((r.errorRate??0)*100).toFixed(1),p=r.errorRate>.1?K:r.errorRate>0?ot:De;console.log(` ${S("Uptime:")} ${zo(r.uptimeSeconds)}`),console.log(` ${S("Requests:")} ${r.requests}`),console.log(` ${S("Errors:")} ${r.serverErrors} server, ${r.clientErrors??0} client ${I("(")}${p(a+"%")}${I(")")}`)}if(i&&console.log(` ${S("DI:")} ${i.count} bindings`),d&&d.enabled&&console.log(` ${S("WS:")} ${d.connections??0} connections, ${d.namespaces??0} namespaces`),n?.routes?.length){console.log(),console.log(S(" Routes")),console.log(c),console.log(` ${I("METHOD")} ${I("PATH".padEnd(36))} ${I("CONTROLLER")}`);for(let a of n.routes){let p=a.path.length>36?a.path.slice(0,33)+"...":a.path.padEnd(36);console.log(` ${Uo(a.method)} ${p} ${Eo(a.controller)}.${I(a.handler)}`)}}console.log(c),console.log()}s(Mo,"printSummary");function rt(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(t,o)=>{let r=t??"http://localhost:3000";if(o.port)try{let d=new URL(r);d.port=o.port,r=d.origin}catch{r=`http://localhost:${o.port}`}let n=`${r.replace(/\/$/,"")}/_debug`,i=s(async()=>{try{let d=await _o(n);o.json?console.log(JSON.stringify(d,null,2)):Mo(r,d)}catch(d){o.json?console.log(JSON.stringify({error:String(d)})):(console.error(K(` \u2716 Could not connect to ${r}`)),console.error(I(` ${d instanceof Error?d.message:String(d)}`))),o.watch||(process.exitCode=1)}},"run");if(o.watch){let d=s(async()=>{process.stdout.write("\x1B[2J\x1B[H"),await i()},"poll");await d(),setInterval(d,5e3)}else await i()})}s(rt,"registerInspectCommand");import{execSync as it}from"child_process";import{existsSync as nt}from"fs";import{resolve as st}from"path";var Oe={core:{pkg:"@forinda/kickjs-core",peers:[],description:"DI container, decorators, reactivity"},http:{pkg:"@forinda/kickjs-http",peers:["express"],description:"Express 5, routing, middleware"},config:{pkg:"@forinda/kickjs-config",peers:[],description:"Zod-based env validation"},cli:{pkg:"@forinda/kickjs-cli",peers:[],description:"CLI tool and code generators",dev:!0},swagger:{pkg:"@forinda/kickjs-swagger",peers:[],description:"OpenAPI spec + Swagger UI + ReDoc"},graphql:{pkg:"@forinda/kickjs-graphql",peers:["graphql"],description:"GraphQL resolvers + GraphiQL"},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"},otel:{pkg:"@forinda/kickjs-otel",peers:["@opentelemetry/api"],description:"OpenTelemetry tracing + metrics"},devtools:{pkg:"@forinda/kickjs-devtools",peers:[],description:"Development dashboard \u2014 routes, DI, metrics, health",dev:!0},auth:{pkg:"@forinda/kickjs-auth",peers:["jsonwebtoken"],description:"Authentication \u2014 JWT, API key, and custom strategies"},mailer:{pkg:"@forinda/kickjs-mailer",peers:["nodemailer"],description:"Email sending \u2014 SMTP, Resend, SES, or custom provider"},cron:{pkg:"@forinda/kickjs-cron",peers:["croner"],description:"Cron job scheduling (production-grade with croner)"},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"},"multi-tenant":{pkg:"@forinda/kickjs-multi-tenant",peers:[],description:"Tenant resolution middleware"},notifications:{pkg:"@forinda/kickjs-notifications",peers:[],description:"Multi-channel notifications \u2014 email, Slack, Discord, webhook"},testing:{pkg:"@forinda/kickjs-testing",peers:[],description:"Test utilities and TestModule builder",dev:!0}};function bo(){return nt(st("pnpm-lock.yaml"))?"pnpm":nt(st("yarn.lock"))?"yarn":"npm"}s(bo,"detectPackageManager");function at(){console.log(`
2067
+ Available KickJS packages:
2068
+ `);let e=Math.max(...Object.keys(Oe).map(t=>t.length));for(let[t,o]of Object.entries(Oe)){let r=t.padEnd(e+2),n=o.peers.length?` (+ ${o.peers.join(", ")})`:"";console.log(` ${r} ${o.description}${n}`)}console.log(`
2069
+ Usage: kick add graphql drizzle otel`),console.log(" kick add queue:bullmq"),console.log()}s(at,"printPackageList");function dt(e){e.command("list").alias("ls").description("List all available KickJS packages").action(()=>{at()})}s(dt,"registerListCommand");function ct(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 all available packages").action(async(t,o)=>{if(o.list||t.length===0){at();return}let r=o.pm??bo(),n=o.dev,i=new Set,d=new Set,c=[];for(let a of t){let p=Oe[a];if(!p){c.push(a);continue}let m=n||p.dev?d:i;m.add(p.pkg);for(let u of p.peers)m.add(u)}if(!(c.length>0&&(console.log(`
2070
+ Unknown packages: ${c.join(", ")}`),console.log(` Run "kick add --list" to see available packages.
2071
+ `),i.size===0&&d.size===0))){if(i.size>0){let a=Array.from(i),p=`${r} add ${a.join(" ")}`;console.log(`
2072
+ Installing ${a.length} dependency(ies):`);for(let m of a)console.log(` + ${m}`);console.log();try{it(p,{stdio:"inherit"})}catch{console.log(`
4189
2073
  Installation failed. Run manually:
4190
- ${cmd}
4191
- `);
4192
- }
4193
- }
4194
- if (devDeps.size > 0) {
4195
- const deps = Array.from(devDeps);
4196
- const cmd = `${pm} add -D ${deps.join(" ")}`;
4197
- console.log(`
4198
- Installing ${deps.length} dev dependency(ies):`);
4199
- for (const dep of deps) console.log(` + ${dep} (dev)`);
4200
- console.log();
4201
- try {
4202
- execSync3(cmd, {
4203
- stdio: "inherit"
4204
- });
4205
- } catch {
4206
- console.log(`
2074
+ ${p}
2075
+ `)}}if(d.size>0){let a=Array.from(d),p=`${r} add -D ${a.join(" ")}`;console.log(`
2076
+ Installing ${a.length} dev dependency(ies):`);for(let m of a)console.log(` + ${m} (dev)`);console.log();try{it(p,{stdio:"inherit"})}catch{console.log(`
4207
2077
  Installation failed. Run manually:
4208
- ${cmd}
4209
- `);
4210
- }
4211
- }
4212
- console.log(" Done!\n");
4213
- });
4214
- }
4215
- __name(registerAddCommand, "registerAddCommand");
4216
-
4217
- // src/commands/tinker.ts
4218
- import { resolve as resolve7, join as join17 } from "path";
4219
- import { existsSync as existsSync5 } from "fs";
4220
- import { pathToFileURL } from "url";
4221
- import { fork } from "child_process";
4222
- function registerTinkerCommand(program) {
4223
- program.command("tinker").description("Interactive REPL with DI container and services loaded").option("-e, --entry <file>", "Entry file to load", "src/index.ts").action(async (opts) => {
4224
- const cwd = process.cwd();
4225
- const entryPath = resolve7(cwd, opts.entry);
4226
- if (!existsSync5(entryPath)) {
4227
- console.error(`
4228
- Error: ${opts.entry} not found.
4229
- `);
4230
- process.exit(1);
4231
- }
4232
- const tsxBin = findBin(cwd, "tsx");
4233
- if (!tsxBin) {
4234
- console.error("\n Error: tsx not found. Install it: pnpm add -D tsx\n");
4235
- process.exit(1);
4236
- }
4237
- const tinkerScript = generateTinkerScript(entryPath, opts.entry);
4238
- const tmpFile = join17(cwd, ".kick-tinker.mjs");
4239
- const { writeFileSync, unlinkSync } = await import("fs");
4240
- writeFileSync(tmpFile, tinkerScript, "utf-8");
4241
- try {
4242
- const child = fork(tmpFile, [], {
4243
- cwd,
4244
- execPath: tsxBin,
4245
- stdio: "inherit"
4246
- });
4247
- await new Promise((resolve8) => {
4248
- child.on("exit", () => resolve8());
4249
- });
4250
- } finally {
4251
- try {
4252
- unlinkSync(tmpFile);
4253
- } catch {
4254
- }
4255
- }
4256
- });
4257
- }
4258
- __name(registerTinkerCommand, "registerTinkerCommand");
4259
- function generateTinkerScript(entryPath, displayPath) {
4260
- const entryUrl = pathToFileURL(entryPath).href;
4261
- return `
2078
+ ${p}
2079
+ `)}}console.log(` Done!
2080
+ `)}})}s(ct,"registerAddCommand");import{resolve as pt,join as mt}from"path";import{existsSync as lt}from"fs";import{pathToFileURL as Qo}from"url";import{fork as Go}from"child_process";function ut(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 t=>{let o=process.cwd(),r=pt(o,t.entry);lt(r)||(console.error(`
2081
+ Error: ${t.entry} not found.
2082
+ `),process.exit(1));let n=Lo(o,"tsx");n||(console.error(`
2083
+ Error: tsx not found. Install it: pnpm add -D tsx
2084
+ `),process.exit(1));let i=Fo(r,t.entry),d=mt(o,".kick-tinker.mjs"),{writeFileSync:c,unlinkSync:a}=await import("fs");c(d,i,"utf-8");try{let p=Go(d,[],{cwd:o,execPath:n,stdio:"inherit"});await new Promise(m=>{p.on("exit",()=>m())})}finally{try{a(d)}catch{}}})}s(ut,"registerTinkerCommand");function Fo(e,t){let o=Qo(e).href;return`
4262
2085
  import 'reflect-metadata'
4263
2086
 
4264
2087
  // Prevent bootstrap() from starting the HTTP server
4265
2088
  process.env.KICK_TINKER = '1'
4266
2089
 
4267
2090
  console.log('\\n \u{1F527} KickJS Tinker')
4268
- console.log(' Loading: ${displayPath}\\n')
2091
+ console.log(' Loading: ${t}\\n')
4269
2092
 
4270
2093
  // Load core
4271
2094
  let Container, Logger, HttpException, HttpStatus
@@ -4283,7 +2106,7 @@ try {
4283
2106
 
4284
2107
  // Load entry to trigger decorator registration
4285
2108
  try {
4286
- await import('${entryUrl}')
2109
+ await import('${o}')
4287
2110
  } catch (err) {
4288
2111
  console.warn(' Warning: ' + err.message)
4289
2112
  console.warn(' Container may be partially initialized.\\n')
@@ -4312,44 +2135,4 @@ server.on('exit', () => {
4312
2135
  console.log('\\n Goodbye!\\n')
4313
2136
  process.exit(0)
4314
2137
  })
4315
- `;
4316
- }
4317
- __name(generateTinkerScript, "generateTinkerScript");
4318
- function findBin(startDir, name) {
4319
- let dir = startDir;
4320
- while (true) {
4321
- const candidate = join17(dir, "node_modules", ".bin", name);
4322
- if (existsSync5(candidate)) return candidate;
4323
- const parent = resolve7(dir, "..");
4324
- if (parent === dir) break;
4325
- dir = parent;
4326
- }
4327
- return null;
4328
- }
4329
- __name(findBin, "findBin");
4330
-
4331
- // src/cli.ts
4332
- var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
4333
- var pkg = JSON.parse(readFileSync2(join18(__dirname2, "..", "package.json"), "utf-8"));
4334
- async function main() {
4335
- const program = new Command();
4336
- program.name("kick").description("KickJS \u2014 A production-grade, decorator-driven Node.js framework").version(pkg.version);
4337
- const config = await loadKickConfig(process.cwd());
4338
- registerInitCommand(program);
4339
- registerGenerateCommand(program);
4340
- registerRunCommands(program);
4341
- registerInfoCommand(program);
4342
- registerInspectCommand(program);
4343
- registerAddCommand(program);
4344
- registerListCommand(program);
4345
- registerTinkerCommand(program);
4346
- registerCustomCommands(program, config);
4347
- program.showHelpAfterError();
4348
- await program.parseAsync(process.argv);
4349
- }
4350
- __name(main, "main");
4351
- main().catch((err) => {
4352
- console.error(err instanceof Error ? err.message : err);
4353
- process.exitCode = 1;
4354
- });
4355
- //# sourceMappingURL=cli.js.map
2138
+ `}s(Fo,"generateTinkerScript");function Lo(e,t){let o=e;for(;;){let r=mt(o,"node_modules",".bin",t);if(lt(r))return r;let n=pt(o,"..");if(n===o)break;o=n}return null}s(Lo,"findBin");var Wo=Bo(Ko(import.meta.url)),Jo=JSON.parse(Yo(Ho(Wo,"..","package.json"),"utf-8"));async function Vo(){let e=new No;e.name("kick").description("KickJS \u2014 A production-grade, decorator-driven Node.js framework").version(Jo.version);let t=await C(process.cwd());Pe(e),Je(e),Xe(e),et(e),rt(e),ct(e),dt(e),ut(e),tt(e,t),e.showHelpAfterError(),await e.parseAsync(process.argv)}s(Vo,"main");Vo().catch(e=>{console.error(e instanceof Error?e.message:e),process.exitCode=1});