@forinda/kickjs-cli 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -38,6 +38,33 @@ type RepoTypeConfig = BuiltinRepoType$1 | CustomRepoType;
38
38
  * entirely (the route entries will keep `body: unknown`).
39
39
  */
40
40
  type SchemaValidator = 'zod' | false;
41
+ /**
42
+ * One entry in the typed `assetMap` config record (`assets-plan.md`).
43
+ * Each entry names a source directory whose files become addressable
44
+ * via the `assets.<name>.*` typed accessor at runtime.
45
+ */
46
+ interface AssetMapEntry {
47
+ /**
48
+ * Source directory, relative to project root. Required. The directory
49
+ * must exist when `kick build` runs — `loadKickConfig` warns when an
50
+ * entry points at a missing directory but doesn't fail the load
51
+ * (the typegen + build steps surface the error in context instead).
52
+ */
53
+ src: string;
54
+ /**
55
+ * Destination directory inside `dist/`. Defaults to `dist/<name>/`
56
+ * where `<name>` is the assetMap key. Override when the consumer of
57
+ * the assets expects a non-standard layout (e.g. an existing
58
+ * downstream tool reads from `dist/templates/...`).
59
+ */
60
+ dest?: string;
61
+ /**
62
+ * Glob pattern for which files to include. Defaults to `**\/*` (all
63
+ * files). Files that don't match are NOT copied — `assetMap` is
64
+ * selective by design (unlike `copyDirs` which copies everything).
65
+ */
66
+ glob?: string;
67
+ }
41
68
  /** Typegen settings — controls .kickjs/types/* generation */
42
69
  interface TypegenConfig {
43
70
  /**
@@ -150,14 +177,6 @@ interface KickConfig {
150
177
  * packageManager: 'pnpm'
151
178
  */
152
179
  packageManager?: PackageManager;
153
- /** @deprecated Use `modules.dir` instead */
154
- modulesDir?: string;
155
- /** @deprecated Use `modules.repo` instead */
156
- defaultRepo?: RepoTypeConfig;
157
- /** @deprecated Use `modules.schemaDir` instead */
158
- schemaDir?: string;
159
- /** @deprecated Use `modules.pluralize` instead */
160
- pluralize?: boolean;
161
180
  /**
162
181
  * Directories to copy to dist/ after build.
163
182
  * Useful for EJS templates, email templates, static assets, etc.
@@ -175,6 +194,48 @@ interface KickConfig {
175
194
  src: string;
176
195
  dest?: string;
177
196
  }>;
197
+ /**
198
+ * Build output settings. The asset manager + `kick build`'s copy
199
+ * steps honour these — adopters who use Vite's `build.outDir =
200
+ * 'out'` (or any non-default) should mirror the value here so
201
+ * `assets.x.y()` paths line up with where Vite actually wrote.
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * build: { outDir: 'out' }
206
+ * ```
207
+ */
208
+ build?: {
209
+ /**
210
+ * Output directory, relative to project root. Defaults to
211
+ * `'dist'`. The asset manager emits its manifest + copies
212
+ * assetMap entries into this directory (under a per-namespace
213
+ * subdirectory by default; override per-entry via `dest`).
214
+ */
215
+ outDir?: string;
216
+ };
217
+ /**
218
+ * Typed, addressable assets — see `assets-plan.md`. Each entry maps
219
+ * a logical namespace name to a source directory. The build pipeline
220
+ * auto-derives the necessary copy step + emits a manifest at
221
+ * `dist/.kickjs-assets.json`; the runtime exposes
222
+ * `import { assets } from '@forinda/kickjs'` so adopters can resolve
223
+ * paths without dev/prod branching.
224
+ *
225
+ * `copyDirs` is unchanged — `assetMap` is a separate, opt-in surface.
226
+ * Adopters who want raw directory copies keep using `copyDirs`; those
227
+ * who want typed addressable assets add `assetMap` entries.
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * assetMap: {
232
+ * mails: { src: 'src/templates/mails' },
233
+ * reports: { src: 'src/templates/reports', glob: '**\/*.{ejs,html}' },
234
+ * schemas: { src: 'src/schemas', glob: '**\/*.json' },
235
+ * }
236
+ * ```
237
+ */
238
+ assetMap?: Record<string, AssetMapEntry>;
178
239
  /**
179
240
  * Typegen settings — controls `.kickjs/types/*` generation including
180
241
  * the schema validator used for body type extraction.
@@ -309,6 +370,188 @@ interface InitProjectOptions {
309
370
  /** Scaffold a new KickJS project */
310
371
  declare function initProject(options: InitProjectOptions): Promise<void>;
311
372
  //#endregion
373
+ //#region src/generator-extension/define.d.ts
374
+ /**
375
+ * Plugin generator API per architecture.md §21.2.3 — lets first-party
376
+ * AND third-party packages ship `kick g <name>` scaffolders the same
377
+ * way the framework's built-in generators do.
378
+ *
379
+ * Plugins declare a discovery file in their `package.json`:
380
+ *
381
+ * ```json
382
+ * {
383
+ * "name": "@my-org/kickjs-cqrs",
384
+ * "kickjs": { "generators": "./dist/generators.js" }
385
+ * }
386
+ * ```
387
+ *
388
+ * The discovery file exports a typed manifest:
389
+ *
390
+ * ```ts
391
+ * import { defineGenerator } from '@forinda/kickjs-cli'
392
+ *
393
+ * export default [
394
+ * defineGenerator({
395
+ * name: 'command',
396
+ * description: 'Generate a CQRS command + handler',
397
+ * args: [{ name: 'name', required: true }],
398
+ * files: (ctx) => [
399
+ * {
400
+ * path: `src/modules/${ctx.kebab}/commands/create-${ctx.kebab}.command.ts`,
401
+ * content: `// generated command for ${ctx.pascal}`,
402
+ * },
403
+ * ],
404
+ * }),
405
+ * ]
406
+ * ```
407
+ *
408
+ * `kick g command Order` then dispatches against the registered
409
+ * generator and writes the returned files relative to `cwd`.
410
+ */
411
+ /**
412
+ * Resolved naming variants + project context handed to a generator's
413
+ * `files()` factory. Keys mirror `TemplateContext` so first-party
414
+ * generators that rely on the same shape can be migrated to plugin
415
+ * generators without rewriting their templates.
416
+ */
417
+ interface GeneratorContext {
418
+ /** Raw name passed on the command line (`kick g resolver UserPost` → `'UserPost'`). */
419
+ name: string;
420
+ /** PascalCase form (`UserPost`). */
421
+ pascal: string;
422
+ /** kebab-case form (`user-post`). */
423
+ kebab: string;
424
+ /** camelCase form (`userPost`). */
425
+ camel: string;
426
+ /** snake_case form (`user_post`). */
427
+ snake: string;
428
+ /** Pluralized PascalCase (`UserPosts`) — present when the project enables `pluralize`. */
429
+ pluralPascal?: string;
430
+ /** Pluralized kebab-case (`user-posts`). */
431
+ pluralKebab?: string;
432
+ /** Pluralized camelCase (`userPosts`). */
433
+ pluralCamel?: string;
434
+ /** Modules directory from `kick.config.ts` (default `'src/modules'`). */
435
+ modulesDir: string;
436
+ /** Working directory for the generator — usually `process.cwd()`. */
437
+ cwd: string;
438
+ /** Positional arguments passed AFTER the name (e.g. `kick g command Order extra1 extra2` → `['extra1', 'extra2']`). */
439
+ args: string[];
440
+ /** Flag values from the command line — booleans for switches, strings for `--key value`. */
441
+ flags: Record<string, string | boolean>;
442
+ }
443
+ /** A single output file the generator wants written. */
444
+ interface GeneratorFile {
445
+ /**
446
+ * Output path. Relative paths resolve against `ctx.cwd`; absolute
447
+ * paths are used as-is. Parent directories are created automatically.
448
+ */
449
+ path: string;
450
+ /** File contents written verbatim (UTF-8). */
451
+ content: string;
452
+ }
453
+ /** CLI argument descriptor surfaced in `kick g --list` help. */
454
+ interface GeneratorArg {
455
+ name: string;
456
+ required?: boolean;
457
+ description?: string;
458
+ }
459
+ /** CLI flag descriptor — boolean unless `takesValue: true`. */
460
+ interface GeneratorFlag {
461
+ name: string;
462
+ alias?: string;
463
+ description?: string;
464
+ takesValue?: boolean;
465
+ }
466
+ /**
467
+ * Spec returned by {@link defineGenerator}. Plugin discovery files
468
+ * export `GeneratorSpec[]` as their default export.
469
+ */
470
+ interface GeneratorSpec {
471
+ /**
472
+ * Dispatch name — `kick g <name>` looks for an exact match against
473
+ * this string after the built-in generators are checked.
474
+ */
475
+ name: string;
476
+ /** Description shown in `kick g --list` and `--help`. */
477
+ description: string;
478
+ /** Optional argument descriptors — informational, surfaced in help output. */
479
+ args?: readonly GeneratorArg[];
480
+ /** Optional flag descriptors — informational, surfaced in help output. */
481
+ flags?: readonly GeneratorFlag[];
482
+ /** Build the output files for one invocation. May return a Promise. */
483
+ files(ctx: GeneratorContext): GeneratorFile[] | Promise<GeneratorFile[]>;
484
+ }
485
+ /**
486
+ * Identity factory — returns the spec verbatim. Exists for type
487
+ * inference and forward-compatibility (future fields can be added with
488
+ * defaults).
489
+ *
490
+ * @example
491
+ * ```ts
492
+ * import { defineGenerator } from '@forinda/kickjs-cli'
493
+ *
494
+ * export default [
495
+ * defineGenerator({
496
+ * name: 'command',
497
+ * description: 'Generate a CQRS command + handler',
498
+ * files: (ctx) => [
499
+ * {
500
+ * path: `src/modules/${ctx.kebab}/commands/${ctx.kebab}.command.ts`,
501
+ * content: `// command for ${ctx.pascal}`,
502
+ * },
503
+ * ],
504
+ * }),
505
+ * ]
506
+ * ```
507
+ */
508
+ declare function defineGenerator(spec: GeneratorSpec): GeneratorSpec;
509
+ //#endregion
510
+ //#region src/generator-extension/discover.d.ts
511
+ /**
512
+ * One row in the discovered registry. `source` is the npm package name
513
+ * the generator came from — surfaced in error messages so adopters can
514
+ * see which plugin owns a given generator.
515
+ */
516
+ interface DiscoveredGenerator {
517
+ source: string;
518
+ spec: GeneratorSpec;
519
+ }
520
+ /**
521
+ * Plugin discovery result, kept around even when no generators were
522
+ * registered so callers can distinguish "no plugins installed" from
523
+ * "no plugins matched the requested name."
524
+ */
525
+ interface DiscoveryResult {
526
+ generators: DiscoveredGenerator[];
527
+ /** Packages whose `kickjs.generators` was loaded successfully. */
528
+ loaded: string[];
529
+ /**
530
+ * Packages we tried to load but failed — typically a missing entry
531
+ * file or a default export that wasn't an array of GeneratorSpec.
532
+ */
533
+ failed: Array<{
534
+ source: string;
535
+ reason: string;
536
+ }>;
537
+ }
538
+ //#endregion
539
+ //#region src/generator-extension/context.d.ts
540
+ /**
541
+ * Build a {@link GeneratorContext} from the raw name + invocation
542
+ * arguments. Centralises the case-transformation logic so every plugin
543
+ * generator sees the same shape regardless of how the name was typed
544
+ * on the command line (`Post` vs `post` vs `user_post`).
545
+ */
546
+ declare function buildGeneratorContext(input: {
547
+ name: string;
548
+ args?: string[];
549
+ flags?: Record<string, string | boolean>;
550
+ modulesDir?: string;
551
+ cwd?: string;
552
+ pluralize?: boolean;
553
+ }): GeneratorContext;
554
+ //#endregion
312
555
  //#region src/utils/naming.d.ts
313
556
  /** Convert a name to PascalCase */
314
557
  declare function toPascalCase(name: string): string;
@@ -323,5 +566,5 @@ declare function toKebabCase(name: string): string;
323
566
  */
324
567
  declare function pluralize(name: string): string;
325
568
  //#endregion
326
- export { type KickCommandDefinition, type KickConfig, defineConfig, generateAdapter, generateController, generateDto, generateGuard, generateMiddleware, generateModule, generateService, initProject, loadKickConfig, pluralize, toCamelCase, toKebabCase, toPascalCase };
569
+ export { type DiscoveredGenerator, type DiscoveryResult, type GeneratorArg, type GeneratorContext, type GeneratorFile, type GeneratorFlag, type GeneratorSpec, type KickCommandDefinition, type KickConfig, buildGeneratorContext, defineConfig, defineGenerator, generateAdapter, generateController, generateDto, generateGuard, generateMiddleware, generateModule, generateService, initProject, loadKickConfig, pluralize, toCamelCase, toKebabCase, toPascalCase };
327
570
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config.ts","../src/generators/module.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/utils/naming.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config.ts","../src/generators/module.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/generator-extension/define.ts","../src/generator-extension/discover.ts","../src/generator-extension/context.ts","../src/utils/naming.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-cli v3.2.0
2
+ * @forinda/kickjs-cli v4.0.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,13 +8,13 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import { dirname, join, resolve } from "node:path";
11
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
12
12
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
13
13
  import * as clack from "@clack/prompts";
14
14
  import pc from "picocolors";
15
15
  import pkg from "pluralize";
16
16
  import { execSync } from "node:child_process";
17
- import { readFileSync } from "node:fs";
17
+ import { existsSync, readFileSync } from "node:fs";
18
18
  import { fileURLToPath } from "node:url";
19
19
  /** Write a file, creating parent directories if needed. Skips writing in dry run mode. */
20
20
  async function writeFileSafe(filePath, content) {
@@ -1543,15 +1543,15 @@ function generateEntryFile(name, template, version, packages = []) {
1543
1543
  const gqlAdapters = [];
1544
1544
  if (packages.includes("devtools")) {
1545
1545
  gqlImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`);
1546
- gqlAdapters.push(` new DevToolsAdapter(),`);
1546
+ gqlAdapters.push(` DevToolsAdapter(),`);
1547
1547
  }
1548
1548
  if (packages.includes("otel")) {
1549
1549
  gqlImports.push(`import { OtelAdapter } from '@forinda/kickjs-otel'`);
1550
- gqlAdapters.push(` new OtelAdapter({ serviceName: '${name}' }),`);
1550
+ gqlAdapters.push(` OtelAdapter({ serviceName: '${name}' }),`);
1551
1551
  }
1552
1552
  if (packages.includes("swagger")) {
1553
1553
  gqlImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`);
1554
- gqlAdapters.push(` new SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`);
1554
+ gqlAdapters.push(` SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`);
1555
1555
  }
1556
1556
  return `import 'reflect-metadata'
1557
1557
  // Side-effect import — registers the extended env schema with kickjs
@@ -1584,15 +1584,15 @@ ${gqlAdapters.length ? gqlAdapters.join("\n") + "\n" : ""} new GraphQLAdapter
1584
1584
  const cqrsAdapters = [];
1585
1585
  if (packages.includes("otel")) {
1586
1586
  cqrsImports.push(`import { OtelAdapter } from '@forinda/kickjs-otel'`);
1587
- cqrsAdapters.push(` new OtelAdapter({ serviceName: '${name}' }),`);
1587
+ cqrsAdapters.push(` OtelAdapter({ serviceName: '${name}' }),`);
1588
1588
  }
1589
1589
  if (packages.includes("devtools")) {
1590
1590
  cqrsImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`);
1591
- cqrsAdapters.push(` new DevToolsAdapter(),`);
1591
+ cqrsAdapters.push(` DevToolsAdapter(),`);
1592
1592
  }
1593
1593
  if (packages.includes("swagger")) {
1594
1594
  cqrsImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`);
1595
- cqrsAdapters.push(` new SwaggerAdapter({\n info: { title: '${name}', version: '${version}' },\n }),`);
1595
+ cqrsAdapters.push(` SwaggerAdapter({\n info: { title: '${name}', version: '${version}' },\n }),`);
1596
1596
  }
1597
1597
  if (packages.includes("graphql")) {
1598
1598
  cqrsImports.push(`import { GraphQLAdapter } from '@forinda/kickjs-graphql'`);
@@ -1611,7 +1611,7 @@ ${cqrsImports.length ? cqrsImports.join("\n") + "\n" : ""}import { modules } fro
1611
1611
 
1612
1612
  // Export the app for the Vite plugin (dev mode)
1613
1613
  export const app = await bootstrap({
1614
- modules,${cqrsImports.length ? `\n adapters: [\n${cqrsAdapters.join("\n")}\n // Uncomment for WebSocket support:\n // new WsAdapter(),\n // Uncomment when Redis is available:\n // new QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],` : `\n adapters: [\n // Uncomment for WebSocket support:\n // new WsAdapter(),\n // Uncomment when Redis is available:\n // new QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],`}
1614
+ modules,${cqrsImports.length ? `\n adapters: [\n${cqrsAdapters.join("\n")}\n // Uncomment for WebSocket support:\n // WsAdapter(),\n // Uncomment when Redis is available:\n // QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],` : `\n adapters: [\n // Uncomment for WebSocket support:\n // WsAdapter(),\n // Uncomment when Redis is available:\n // QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],`}
1615
1615
  })
1616
1616
  `;
1617
1617
  }
@@ -1620,15 +1620,15 @@ export const app = await bootstrap({
1620
1620
  const adapters = [];
1621
1621
  if (packages.includes("swagger")) {
1622
1622
  imports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`);
1623
- adapters.push(` new SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`);
1623
+ adapters.push(` SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`);
1624
1624
  }
1625
1625
  if (packages.includes("devtools")) {
1626
1626
  imports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`);
1627
- adapters.push(` new DevToolsAdapter(),`);
1627
+ adapters.push(` DevToolsAdapter(),`);
1628
1628
  }
1629
1629
  if (packages.includes("otel")) {
1630
1630
  imports.push(`import { OtelAdapter } from '@forinda/kickjs-otel'`);
1631
- adapters.push(` new OtelAdapter({ serviceName: '${name}' }),`);
1631
+ adapters.push(` OtelAdapter({ serviceName: '${name}' }),`);
1632
1632
  }
1633
1633
  if (packages.includes("graphql")) {
1634
1634
  imports.push(`import { GraphQLAdapter } from '@forinda/kickjs-graphql'`);
@@ -1652,15 +1652,15 @@ export const app = await bootstrap({ modules${adapters.length ? `,\n adapters:
1652
1652
  const restAdapters = [];
1653
1653
  if (packages.includes("devtools")) {
1654
1654
  restImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`);
1655
- restAdapters.push(` new DevToolsAdapter(),`);
1655
+ restAdapters.push(` DevToolsAdapter(),`);
1656
1656
  }
1657
1657
  if (packages.includes("swagger")) {
1658
1658
  restImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`);
1659
- restAdapters.push(` new SwaggerAdapter({\n info: { title: '${name}', version: '${version}' },\n }),`);
1659
+ restAdapters.push(` SwaggerAdapter({\n info: { title: '${name}', version: '${version}' },\n }),`);
1660
1660
  }
1661
1661
  if (packages.includes("otel")) {
1662
1662
  restImports.push(`import { OtelAdapter } from '@forinda/kickjs-otel'`);
1663
- restAdapters.push(` new OtelAdapter({ serviceName: '${name}' }),`);
1663
+ restAdapters.push(` OtelAdapter({ serviceName: '${name}' }),`);
1664
1664
  }
1665
1665
  return `import 'reflect-metadata'
1666
1666
  // Side-effect import — registers the extended env schema with kickjs
@@ -3038,7 +3038,7 @@ generated \`KickRoutes\` namespace (refreshed on \`kick dev\` and \`kick typegen
3038
3038
  \`\`\`ts
3039
3039
  import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
3040
3040
 
3041
- @Controller('/users')
3041
+ @Controller()
3042
3042
  export class UserController {
3043
3043
  @Get('/')
3044
3044
  async findAll(ctx: Ctx<KickRoutes.UserController['findAll']>) {
@@ -3319,7 +3319,7 @@ Run tests:
3319
3319
  ## Decorators Reference
3320
3320
 
3321
3321
  ### Route Decorators
3322
- - \`@Controller('/path')\` — define controller prefix
3322
+ - \`@Controller()\` — mark a class as an HTTP controller (path comes from \`routes().path\`)
3323
3323
  - \`@Get('/'), @Post('/'), @Put('/'), @Delete('/'), @Patch('/')\` — HTTP methods
3324
3324
  - \`@Middleware(fn)\` — attach middleware
3325
3325
  - \`@Public()\` — skip authentication (requires @forinda/kickjs-auth)
@@ -3466,7 +3466,7 @@ Then:
3466
3466
  If not using generators:
3467
3467
 
3468
3468
  - [ ] Create \`src/modules/<name>/<name>.controller.ts\`
3469
- - [ ] Add \`@Controller('/path')\` decorator
3469
+ - [ ] Add \`@Controller()\` decorator
3470
3470
  - [ ] Add route handlers with \`@Get()\`, \`@Post()\`, etc.
3471
3471
  - [ ] Create module file implementing \`AppModule\` with \`routes()\` returning \`{ path, router: buildRoutes(Controller), controller }\`
3472
3472
  - [ ] Register module in \`src/modules/index.ts\` (\`AppModuleClass[]\` array)
@@ -3528,8 +3528,8 @@ import { AuthAdapter, JwtStrategy } from '@forinda/kickjs-auth'
3528
3528
  bootstrap({
3529
3529
  modules,
3530
3530
  adapters: [
3531
- new AuthAdapter({
3532
- strategies: [new JwtStrategy({ secret: process.env.JWT_SECRET! })],
3531
+ AuthAdapter({
3532
+ strategies: [JwtStrategy({ secret: process.env.JWT_SECRET! })],
3533
3533
  }),
3534
3534
  ],
3535
3535
  })
@@ -3559,7 +3559,7 @@ import { WsAdapter } from '@forinda/kickjs-ws'
3559
3559
 
3560
3560
  bootstrap({
3561
3561
  modules,
3562
- adapters: [new WsAdapter()],
3562
+ adapters: [WsAdapter()],
3563
3563
  })
3564
3564
  \`\`\`
3565
3565
 
@@ -3663,7 +3663,7 @@ These work anywhere — scripts, plain files, outside \`@Service\`/\`@Controller
3663
3663
  ### HTTP Routes
3664
3664
  | Decorator | Purpose |
3665
3665
  |-----------|---------|
3666
- | \`@Controller('/path')\` | Define route prefix |
3666
+ | \`@Controller()\` | Define route prefix |
3667
3667
  | \`@Get('/'), @Post('/')\` | HTTP method handlers |
3668
3668
  | \`@Middleware(fn)\` | Attach middleware |
3669
3669
  | \`@Public()\` | Skip auth (requires auth adapter) |
@@ -3804,7 +3804,7 @@ async function initProject(options) {
3804
3804
  }
3805
3805
  }
3806
3806
  try {
3807
- const { runTypegen } = await import("./typegen-C30frihW.mjs");
3807
+ const { runTypegen } = await import("./typegen-vI1eqGLK.mjs");
3808
3808
  await runTypegen({
3809
3809
  cwd: dir,
3810
3810
  allowDuplicates: true,
@@ -3902,7 +3902,10 @@ async function loadKickConfig(cwd) {
3902
3902
  try {
3903
3903
  const { pathToFileURL } = await import("node:url");
3904
3904
  const mod = await import(pathToFileURL(filepath).href);
3905
- return mod.default ?? mod;
3905
+ const config = mod.default ?? mod;
3906
+ const warnings = validateAssetMap(config, cwd);
3907
+ for (const warning of warnings) console.warn(` Warning: ${warning}`);
3908
+ return config;
3906
3909
  } catch (err) {
3907
3910
  if (filename.endsWith(".ts")) 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.`);
3908
3911
  continue;
@@ -3910,7 +3913,126 @@ async function loadKickConfig(cwd) {
3910
3913
  }
3911
3914
  return null;
3912
3915
  }
3916
+ /**
3917
+ * Validate `assetMap` entries on a loaded config. Returns a list of
3918
+ * human-readable warnings; the caller decides how to surface them
3919
+ * (typically `console.warn`). Never throws — `kick g` and other
3920
+ * unrelated commands should keep working even when the assetMap is
3921
+ * misconfigured.
3922
+ *
3923
+ * Checks:
3924
+ *
3925
+ * - Each entry's `src` is a non-empty string.
3926
+ * - The `src` directory exists on disk (otherwise the typegen + build
3927
+ * steps will fail later with cryptic errors).
3928
+ * - `dest` doesn't escape the project root (defensive — a `dest:
3929
+ * '../../etc'` typo could write files outside the workspace).
3930
+ * - The namespace key is a non-empty string and doesn't include a
3931
+ * `/` (would conflict with the `<namespace>/<key>` manifest format).
3932
+ */
3933
+ function validateAssetMap(config, cwd) {
3934
+ const warnings = [];
3935
+ if (!config?.assetMap) return warnings;
3936
+ const root = resolve(cwd);
3937
+ for (const [namespace, entry] of Object.entries(config.assetMap)) {
3938
+ if (!namespace || namespace.includes("/")) {
3939
+ warnings.push(`assetMap key '${namespace}' is invalid — must be a non-empty string without '/'`);
3940
+ continue;
3941
+ }
3942
+ if (typeof entry?.src !== "string" || entry.src.length === 0) {
3943
+ warnings.push(`assetMap.${namespace} is missing a non-empty 'src' field`);
3944
+ continue;
3945
+ }
3946
+ if (!existsSync(resolve(cwd, entry.src))) warnings.push(`assetMap.${namespace}.src ('${entry.src}') does not exist — typegen + build will fail`);
3947
+ if (entry.dest) {
3948
+ if (escapesRoot(resolve(cwd, entry.dest), root)) warnings.push(`assetMap.${namespace}.dest ('${entry.dest}') resolves outside the project root — refusing to copy`);
3949
+ }
3950
+ }
3951
+ return warnings;
3952
+ }
3953
+ /**
3954
+ * Returns true when `path` (absolute) resolves outside of `root`
3955
+ * (also absolute). Uses `path.relative` for accuracy:
3956
+ *
3957
+ * - The result is empty when paths are identical (inside).
3958
+ * - It starts with `..` when the path traverses outside the root.
3959
+ * - It's absolute (Windows: cross-drive) when there's no relative
3960
+ * path between them.
3961
+ *
3962
+ * Avoids the prefix-match pitfalls of `startsWith` (e.g. `/app`
3963
+ * matching `/app2/...`, or case-mismatches on macOS / Windows).
3964
+ */
3965
+ function escapesRoot(path, root) {
3966
+ const rel = relative(root, path);
3967
+ return rel === "" ? false : rel.startsWith("..") || isAbsolute(rel);
3968
+ }
3969
+ //#endregion
3970
+ //#region src/generator-extension/define.ts
3971
+ /**
3972
+ * Identity factory — returns the spec verbatim. Exists for type
3973
+ * inference and forward-compatibility (future fields can be added with
3974
+ * defaults).
3975
+ *
3976
+ * @example
3977
+ * ```ts
3978
+ * import { defineGenerator } from '@forinda/kickjs-cli'
3979
+ *
3980
+ * export default [
3981
+ * defineGenerator({
3982
+ * name: 'command',
3983
+ * description: 'Generate a CQRS command + handler',
3984
+ * files: (ctx) => [
3985
+ * {
3986
+ * path: `src/modules/${ctx.kebab}/commands/${ctx.kebab}.command.ts`,
3987
+ * content: `// command for ${ctx.pascal}`,
3988
+ * },
3989
+ * ],
3990
+ * }),
3991
+ * ]
3992
+ * ```
3993
+ */
3994
+ function defineGenerator(spec) {
3995
+ return spec;
3996
+ }
3997
+ //#endregion
3998
+ //#region src/generator-extension/context.ts
3999
+ /** Convert any string to snake_case (`UserPost` / `user-post` → `user_post`). */
4000
+ function toSnakeCase(name) {
4001
+ return toKebabCase(name).replace(/-/g, "_");
4002
+ }
4003
+ /**
4004
+ * Build a {@link GeneratorContext} from the raw name + invocation
4005
+ * arguments. Centralises the case-transformation logic so every plugin
4006
+ * generator sees the same shape regardless of how the name was typed
4007
+ * on the command line (`Post` vs `post` vs `user_post`).
4008
+ */
4009
+ function buildGeneratorContext(input) {
4010
+ const cwd = input.cwd ?? process.cwd();
4011
+ const usePlural = input.pluralize ?? true;
4012
+ const pascal = toPascalCase(input.name);
4013
+ const camel = toCamelCase(input.name);
4014
+ const kebab = toKebabCase(input.name);
4015
+ const snake = toSnakeCase(input.name);
4016
+ const ctx = {
4017
+ name: input.name,
4018
+ pascal,
4019
+ camel,
4020
+ kebab,
4021
+ snake,
4022
+ modulesDir: input.modulesDir ?? "src/modules",
4023
+ cwd,
4024
+ args: input.args ?? [],
4025
+ flags: input.flags ?? {}
4026
+ };
4027
+ if (usePlural) {
4028
+ const pluralKebab = pluralize(kebab);
4029
+ ctx.pluralKebab = pluralKebab;
4030
+ ctx.pluralPascal = toPascalCase(pluralKebab);
4031
+ ctx.pluralCamel = toCamelCase(pluralKebab);
4032
+ }
4033
+ return ctx;
4034
+ }
3913
4035
  //#endregion
3914
- export { defineConfig, generateAdapter, generateController, generateDto, generateGuard, generateMiddleware, generateModule, generateService, initProject, loadKickConfig, pluralize, toCamelCase, toKebabCase, toPascalCase };
4036
+ export { buildGeneratorContext, defineConfig, defineGenerator, generateAdapter, generateController, generateDto, generateGuard, generateMiddleware, generateModule, generateService, initProject, loadKickConfig, pluralize, toCamelCase, toKebabCase, toPascalCase };
3915
4037
 
3916
4038
  //# sourceMappingURL=index.mjs.map