@forinda/kickjs-cli 6.5.0 → 6.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/agent-docs-Du-kAfnd.mjs +12 -0
  2. package/dist/agent-docs-Du-kAfnd.mjs.map +1 -0
  3. package/dist/{build-RlkRhDyM.mjs → build-DGTo6Lbp.mjs} +3 -3
  4. package/dist/{build-RlkRhDyM.mjs.map → build-DGTo6Lbp.mjs.map} +1 -1
  5. package/dist/{builtins-Ce5uoK6s.mjs → builtins-C5YZMX7U.mjs} +2 -2
  6. package/dist/cli.mjs +52 -20
  7. package/dist/{config-xEbwXlmo.mjs → config-COL9zMB5.mjs} +3 -3
  8. package/dist/{config-xEbwXlmo.mjs.map → config-COL9zMB5.mjs.map} +1 -1
  9. package/dist/{doctor-DCFgdlJ3.mjs → doctor-BCMp4UCQ.mjs} +4 -4
  10. package/dist/{doctor-DCFgdlJ3.mjs.map → doctor-BCMp4UCQ.mjs.map} +1 -1
  11. package/dist/{fullstack-iuAdYQmC.mjs → fullstack-C8u97QC-.mjs} +5 -5
  12. package/dist/{fullstack-pPl60src.mjs → fullstack-Cqm_v3sf.mjs} +6 -6
  13. package/dist/fullstack-Cqm_v3sf.mjs.map +1 -0
  14. package/dist/index.d.mts +7 -0
  15. package/dist/index.d.mts.map +1 -1
  16. package/dist/index.mjs +2 -2
  17. package/dist/{plugin-B_40AQRr.mjs → plugin-BHLfFlki.mjs} +3 -3
  18. package/dist/{plugin-B_40AQRr.mjs.map → plugin-BHLfFlki.mjs.map} +1 -1
  19. package/dist/{project-D_8okjEO.mjs → project-YM9dH4O_.mjs} +5 -5
  20. package/dist/{project-D_8okjEO.mjs.map → project-YM9dH4O_.mjs.map} +1 -1
  21. package/dist/{project-docs-MYPeTYsJ.mjs → project-docs-VwSwcfVt.mjs} +31 -5
  22. package/dist/project-docs-VwSwcfVt.mjs.map +1 -0
  23. package/dist/{project-root-CWcfpTV4.mjs → project-root-DRTGL9O5.mjs} +3 -3
  24. package/dist/{project-root-CWcfpTV4.mjs.map → project-root-DRTGL9O5.mjs.map} +1 -1
  25. package/dist/{prompts-D0mIvDKz.mjs → prompts-BJywkbEr.mjs} +2 -2
  26. package/dist/{prompts-D0mIvDKz.mjs.map → prompts-BJywkbEr.mjs.map} +1 -1
  27. package/dist/{rolldown-runtime-wP2RwkAN.mjs → rolldown-runtime-BKn_X0JJ.mjs} +1 -1
  28. package/dist/{run-plugins-USrmMITe.mjs → run-plugins-C1wpiuKz.mjs} +11 -5
  29. package/dist/{run-plugins-USrmMITe.mjs.map → run-plugins-C1wpiuKz.mjs.map} +1 -1
  30. package/dist/{typegen-Do1rVRd1.mjs → typegen-Dh-JBQSr.mjs} +17 -17
  31. package/dist/typegen-Dh-JBQSr.mjs.map +1 -0
  32. package/dist/{types-CjDOGs_1.mjs → types-B2DQMqCA.mjs} +1 -1
  33. package/package.json +2 -2
  34. package/dist/agent-docs-Xlt6Fk-e.mjs +0 -12
  35. package/dist/agent-docs-Xlt6Fk-e.mjs.map +0 -1
  36. package/dist/fullstack-pPl60src.mjs.map +0 -1
  37. package/dist/project-docs-MYPeTYsJ.mjs.map +0 -1
  38. package/dist/typegen-Do1rVRd1.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"project-D_8okjEO.mjs","names":[],"sources":["../src/generators/templates/project-app.ts","../src/generators/templates/project-config.ts","../src/commands/add.ts","../src/generators/project.ts"],"sourcesContent":["type ProjectTemplate = 'rest' | 'minimal'\nexport type ProjectRuntime = 'express' | 'fastify' | 'h3'\n\n/** Per-runtime import source + factory name for the scaffolded `runtime:` option. */\nconst RUNTIME_FACTORY: Record<ProjectRuntime, { from: string; name: string }> = {\n express: { from: '@forinda/kickjs', name: 'expressRuntime' },\n fastify: { from: '@forinda/kickjs/fastify', name: 'fastifyRuntime' },\n h3: { from: '@forinda/kickjs/h3', name: 'h3Runtime' },\n}\n\n/**\n * Generate src/index.ts entry file with template-specific bootstrap.\n *\n * The runtime is always emitted explicitly (`runtime: expressRuntime()` etc.)\n * so the entry file is self-documenting and switching engines is a one-line\n * edit. Fastify / h3 parse bodies natively, so the REST template skips the\n * `express.json()` middleware (and the `express` import) under those engines.\n *\n * All templates export the app for the Vite plugin (dev mode).\n */\nexport function generateEntryFile(\n name: string,\n template: ProjectTemplate,\n version: string,\n packages: string[] = [],\n runtime: ProjectRuntime = 'express',\n): string {\n const factory = RUNTIME_FACTORY[runtime]\n const isExpress = runtime === 'express'\n\n switch (template) {\n case 'minimal': {\n const imports: string[] = []\n const adapters: string[] = []\n\n // The runtime factory comes from the core package for Express, or a\n // subpath for Fastify / h3.\n const kickImport = isExpress\n ? `import { bootstrap, ${factory.name} } from '@forinda/kickjs'`\n : `import { bootstrap } from '@forinda/kickjs'\\nimport { ${factory.name} } from '${factory.from}'`\n\n if (packages.includes('swagger')) {\n imports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`)\n adapters.push(` SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`)\n }\n if (packages.includes('devtools')) {\n imports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`)\n adapters.push(` DevToolsAdapter(),`)\n }\n const importsBlock = imports.length ? imports.join('\\n') + '\\n' : ''\n const adaptersBlock = adapters.length ? `,\\n adapters: [\\n${adapters.join('\\n')}\\n ]` : ''\n\n return `import 'reflect-metadata'\n// Side-effect import — registers the extended env schema with kickjs\n// **before** any controller / service / @Value gets resolved. Without\n// this line ConfigService.get('YOUR_KEY') returns undefined because the\n// cached schema would still be the base shape. See guide/configuration.\nimport './config'\n${kickImport}\n${importsBlock}import { modules } from './modules'\n\n// Export the app for the Vite plugin (dev mode)\nexport const app = await bootstrap({ modules, runtime: ${factory.name}()${adaptersBlock} })\n`\n }\n\n case 'rest':\n default: {\n // Build adapters based on user-selected packages\n const restImports: string[] = []\n const restAdapters: string[] = []\n\n if (packages.includes('devtools')) {\n restImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`)\n restAdapters.push(` DevToolsAdapter(),`)\n }\n if (packages.includes('swagger')) {\n restImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`)\n restAdapters.push(\n ` SwaggerAdapter({\\n info: { title: '${name}', version: '${version}' },\\n }),`,\n )\n }\n const restImportsBlock = restImports.length ? restImports.join('\\n') + '\\n' : ''\n const restAdaptersBlock = restAdapters.length\n ? `\\n adapters: [\\n${restAdapters.join('\\n')}\\n ],`\n : ''\n\n // Express needs `express.json()` for body parsing; Fastify / h3 parse\n // bodies natively, so adding it would consume the body stream twice.\n const kickNamed = ['bootstrap', 'requestId', 'requestLogger', 'helmet', 'cors']\n if (isExpress) kickNamed.push(factory.name)\n const kickImport = isExpress\n ? `import express from 'express'\\nimport {\\n ${kickNamed.join(',\\n ')},\\n} from '@forinda/kickjs'`\n : `import {\\n ${kickNamed.join(',\\n ')},\\n} from '@forinda/kickjs'\\nimport { ${factory.name} } from '${factory.from}'`\n const bodyParserLine = isExpress ? `\\n express.json(),` : ''\n\n return `import 'reflect-metadata'\n// Side-effect import — registers the extended env schema with kickjs\n// **before** any controller / service / @Value gets resolved. Without\n// this line ConfigService.get('YOUR_KEY') returns undefined because the\n// cached schema would still be the base shape. See guide/configuration.\nimport './config'\n${kickImport}\n${restImportsBlock}import { modules } from './modules'\n\n// Export the app for the Vite plugin (dev mode)\nexport const app = await bootstrap({\n modules,\n runtime: ${factory.name}(),${restAdaptersBlock}\n middleware: [\n helmet(),\n cors({ origin: '*' }),\n requestId(),\n requestLogger(),${bodyParserLine}\n ],\n})\n`\n }\n }\n}\n\n/** Generate src/modules/index.ts module registry */\nexport function generateModulesIndex(): string {\n return `import { defineModules } from '@forinda/kickjs'\nimport { HelloModule } from './hello/hello.module'\n\n// Remove HelloModule and run: kick g module <name>\n// \\`defineModules()\\` returns a chainable list — \\`kick g module\\` appends\n// \\`.mount(NewModule())\\` to the chain on every generation.\nexport const modules = defineModules().mount(HelloModule())\n`\n}\n\n/**\n * Generate `src/config/index.ts` — the project's typed env schema.\n *\n * Default-exports a `defineEnv(...)` schema so `kick typegen` can\n * infer it into the global `KickEnv` registry, and *also* calls\n * `loadEnv(envSchema)` as a module-load side effect so `ConfigService`\n * and `@Value()` see the extended shape from the very first DI\n * resolution. The companion `src/index.ts` template adds\n * `import './config'` immediately after `reflect-metadata` so the\n * registration runs before `bootstrap()` constructs anything.\n *\n * After typegen runs:\n *\n * @Value('DATABASE_URL') private url!: Env<'DATABASE_URL'>\n * process.env.DATABASE_URL // typed as string\n *\n * Both autocomplete and type-check at compile time.\n */\nexport function generateEnvFile(schemaLib: 'zod' | 'valibot' | 'yup' = 'zod'): string {\n if (schemaLib === 'valibot') {\n return `import { loadEnvFromSchema } from '@forinda/kickjs/config'\nimport { fromValibot } from '@forinda/kickjs-schema/valibot'\nimport * as v from 'valibot'\n\n/**\n * Project environment schema (Valibot).\n *\n * \\`fromValibot\\` wraps the Valibot schema as a \\`KickSchema\\` so the\n * env loader, validate middleware, and swagger spec generator all see\n * the same shape. The default export is the contract \\`kick typegen\\`\n * reads to populate \\`KickEnv\\` via \\`InferSchemaOutput<typeof _envSchema>\\`\n * — that's what makes \\`@Value('FOO')\\` autocomplete and\n * \\`process.env.FOO\\` typed.\n *\n * @example\n * DATABASE_URL: v.pipe(v.string(), v.url()),\n * JWT_SECRET: v.pipe(v.string(), v.minLength(32)),\n * REDIS_URL: v.optional(v.pipe(v.string(), v.url())),\n */\nconst envSchema = fromValibot(\n v.object({\n PORT: v.optional(v.pipe(v.string(), v.transform(Number)), '3000'),\n NODE_ENV: v.optional(v.picklist(['development', 'production', 'test']), 'development'),\n LOG_LEVEL: v.optional(v.string(), 'info'),\n // DATABASE_URL: v.pipe(v.string(), v.url()),\n }),\n)\n\n/**\n * IMPORTANT — side effect: register the schema with kickjs's env cache\n * **at module-load time**. \\`ConfigService\\` and \\`@Value()\\` both consume\n * this cache, and they will fall back to the base schema (or undefined)\n * if no extended schema has been registered before they're resolved.\n *\n * As long as \\`src/index.ts\\` imports this file (\\`import './config'\\`) at\n * the top — before \\`bootstrap()\\` runs — every controller and service\n * in the app sees the typed extended values.\n */\nexport const env = loadEnvFromSchema(envSchema)\n\nexport default envSchema\n`\n }\n\n if (schemaLib === 'yup') {\n return `import { loadEnvFromSchema } from '@forinda/kickjs/config'\nimport { fromYup } from '@forinda/kickjs-schema/yup'\nimport * as yup from 'yup'\n\n/**\n * Project environment schema (Yup).\n *\n * \\`fromYup\\` wraps the Yup schema as a \\`KickSchema\\` so the env loader,\n * validate middleware, and swagger spec generator all see the same\n * shape. The default export is the contract \\`kick typegen\\` reads to\n * populate \\`KickEnv\\` via \\`InferSchemaOutput<typeof _envSchema>\\`.\n *\n * Note: Yup's \\`.url()\\` defaults to http/https; database connection\n * strings like \\`postgres://\\` use \\`.matches(/^[a-z]+:\\\\/\\\\/.+/i)\\` or\n * a plain \\`.string().required()\\`.\n *\n * @example\n * DATABASE_URL: yup.string().required(),\n * JWT_SECRET: yup.string().min(32).required(),\n * REDIS_URL: yup.string().url().optional(),\n */\nconst envSchema = fromYup(\n yup.object({\n PORT: yup.number().default(3000),\n NODE_ENV: yup\n .string()\n .oneOf(['development', 'production', 'test'])\n .default('development'),\n LOG_LEVEL: yup.string().default('info'),\n // DATABASE_URL: yup.string().required(),\n }),\n)\n\n/**\n * IMPORTANT — side effect: register the schema with kickjs's env cache\n * **at module-load time**. \\`ConfigService\\` and \\`@Value()\\` both consume\n * this cache, and they will fall back to the base schema (or undefined)\n * if no extended schema has been registered before they're resolved.\n *\n * As long as \\`src/index.ts\\` imports this file (\\`import './config'\\`) at\n * the top — before \\`bootstrap()\\` runs — every controller and service\n * in the app sees the typed extended values.\n */\nexport const env = loadEnvFromSchema(envSchema)\n\nexport default envSchema\n`\n }\n\n // zod (default)\n return `import { loadEnvFromSchema } from '@forinda/kickjs/config'\nimport { fromZod } from '@forinda/kickjs-schema/zod'\nimport { z } from 'zod'\n\n/**\n * Project environment schema (Zod).\n *\n * \\`fromZod\\` wraps the Zod schema as a \\`KickSchema\\` so the env loader,\n * validate middleware, and swagger spec generator all see the same\n * shape. The default export is the contract \\`kick typegen\\` reads to\n * populate \\`KickEnv\\` via \\`InferSchemaOutput<typeof _envSchema>\\` —\n * that's what makes \\`@Value('FOO')\\` autocomplete and\n * \\`process.env.FOO\\` typed.\n *\n * @example\n * DATABASE_URL: z.string().url(),\n * JWT_SECRET: z.string().min(32),\n * REDIS_URL: z.string().url().optional(),\n */\nconst envSchema = fromZod(\n z.object({\n PORT: z.coerce.number().default(3000),\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n LOG_LEVEL: z.string().default('info'),\n // DATABASE_URL: z.string().url(),\n }),\n)\n\n/**\n * IMPORTANT — side effect: register the schema with kickjs's env cache\n * **at module-load time**. \\`ConfigService\\` and \\`@Value()\\` both consume\n * this cache, and they will fall back to the base schema (or undefined)\n * if no extended schema has been registered before they're resolved.\n *\n * As long as \\`src/index.ts\\` imports this file (\\`import './config'\\`) at\n * the top — before \\`bootstrap()\\` runs — every controller and service\n * in the app sees the typed extended values.\n */\nexport const env = loadEnvFromSchema(envSchema)\n\nexport default envSchema\n`\n}\n\n/** Generate src/modules/hello/hello.service.ts */\nexport function generateHelloService(): string {\n return `import { Service } from '@forinda/kickjs'\n\n@Service()\nexport class HelloService {\n greet(name: string) {\n return { message: \\`Hello \\${name} from KickJS!\\`, timestamp: new Date().toISOString() }\n }\n\n healthCheck() {\n return { status: 'ok', uptime: process.uptime() }\n }\n}\n`\n}\n\n/** Generate src/modules/hello/hello.controller.ts */\nexport function generateHelloController(): string {\n return `import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'\nimport { HelloService } from './hello.service'\n\n// \\`Ctx<KickRoutes.HelloController['<method>']>\\` is generated by\n// \\`kick typegen\\` (auto-run on \\`kick dev\\`). The first run after a fresh\n// scaffold creates \\`.kickjs/types/routes.ts\\` so this file typechecks.\n// See https://kickjs.app/guide/typegen.\n\n@Controller()\nexport class HelloController {\n @Autowired() private readonly helloService!: HelloService\n\n // Return-value handlers: the runtime sends the returned payload as\n // 200 json, and \\`kick typegen\\` infers the response type into\n // \\`KickRoutes.Api\\` — which is what makes the typed client\n // (@forinda/kickjs-client) end-to-end type-safe.\n @Get('/')\n index(_ctx: Ctx<KickRoutes.HelloController['index']>) {\n return this.helloService.greet('World')\n }\n\n @Get('/health')\n health(_ctx: Ctx<KickRoutes.HelloController['health']>) {\n return this.helloService.healthCheck()\n }\n}\n`\n}\n\n/** Generate src/modules/hello/hello.module.ts */\nexport function generateHelloModule(): string {\n return `import { defineModule } from '@forinda/kickjs'\nimport { HelloController } from './hello.controller'\n\nexport const HelloModule = defineModule({\n name: 'HelloModule',\n build: () => ({\n // \\`register(container)\\` is optional — only implement it when you need\n // to bind a token to a concrete implementation, e.g.\n // register(container) {\n // container.registerFactory(USER_REPOSITORY, () => container.resolve(InMemoryUserRepository))\n // }\n // The HelloService uses @Service() so the decorator handles registration.\n\n routes() {\n return {\n path: '/hello',\n controller: HelloController,\n }\n },\n }),\n})\n`\n}\n\n/** Generate kick.config.ts CLI configuration */\nexport function generateKickConfig(\n template: ProjectTemplate,\n defaultRepo: string = 'inmemory',\n packageManager: 'pnpm' | 'npm' | 'yarn' | 'bun' = 'pnpm',\n runtime: 'express' | 'fastify' | 'h3' = 'express',\n): string {\n // `inmemory` is the only built-in; every other name (incl. the\n // deprecated prisma/drizzle) is emitted as a `{ name }` custom repo.\n const repoValue = defaultRepo === 'inmemory' ? `'inmemory'` : `{ name: '${defaultRepo}' }`\n\n return `import { defineConfig } from '@forinda/kickjs-cli'\n\nexport default defineConfig({\n pattern: '${template}',\n // The HTTP engine this app boots on (matches \\`bootstrap({ runtime })\\` in\n // src/index.ts). Dep-aware commands read it: \\`kick add upload\\` installs the\n // engine's multipart driver, \\`kick doctor\\` checks the engine peers, and\n // \\`kick typegen\\` flips the runtime escape-hatch types to this engine.\n runtime: '${runtime}',\n // Pinned so \\`kick add\\` and other dep-installing commands always use the\n // project's intended package manager, regardless of which lockfile exists.\n packageManager: '${packageManager}',\n modules: {\n dir: 'src/modules',\n repo: ${repoValue},\n pluralize: true,\n },\n\n // \\`kick typegen\\` populates \\`.kickjs/types/\\` so \\`Ctx<KickRoutes.X['method']>\\`\n // resolves to fully-typed params/body/query. Auto-runs on \\`kick dev\\`.\n // \\`'kickjs-schema'\\` routes inference through \\`InferSchemaOutput\\` so the\n // typegen works for any wrapped schema (Zod / Valibot / Yup). Switch\n // to \\`'zod'\\` if you ship Zod schemas without \\`fromZod()\\` wrapping, or\n // set \\`schemaValidator: false\\` to skip schema-driven body typing.\n typegen: {\n schemaValidator: 'kickjs-schema',\n },\n\n commands: [\n {\n name: 'test',\n description: 'Run tests with Vitest',\n steps: 'npx vitest run',\n },\n {\n name: 'format',\n description: 'Format code with Prettier',\n steps: 'npx prettier --write src/',\n },\n {\n name: 'format:check',\n description: 'Check formatting without writing',\n steps: 'npx prettier --check src/',\n },\n {\n name: 'ci:check',\n description: 'Run typecheck + format check',\n steps: ['npx tsc --noEmit', 'npx prettier --check src/'],\n aliases: ['verify'],\n },\n ],\n})\n`\n}\n","type ProjectTemplate = 'rest' | 'minimal'\n\n/**\n * Supported schema libraries — passed through to `fromZod` /\n * `fromValibot` / `fromYup` in the generated env file. `zod` is the\n * default for `--yes` because it has the deepest ecosystem\n * compatibility (OpenAPI generation, Standard Schema brand for\n * `kick typegen`).\n */\nexport type SchemaLib = 'zod' | 'valibot' | 'yup'\n\n/** Map of optional package names to their npm package identifiers */\nconst PACKAGE_DEPS: Record<string, string> = {\n swagger: '@forinda/kickjs-swagger',\n ws: '@forinda/kickjs-ws',\n queue: '@forinda/kickjs-queue',\n devtools: '@forinda/kickjs-devtools',\n}\n\n/** Schema-lib runtime dependency ranges. Pinned to a recent release. */\nconst SCHEMA_LIB_DEPS: Record<SchemaLib, { name: string; range: string }> = {\n zod: { name: 'zod', range: '^4.3.6' },\n valibot: { name: 'valibot', range: '^1.4.1' },\n yup: { name: 'yup', range: '^1.7.1' },\n}\n\n/**\n * Map of package name → semver range string (`^x.y.z`). Resolved\n * from `npm view <name> version` upstream so per-package independent\n * versioning is honoured at scaffold time. Every sibling\n * `@forinda/kickjs-*` package we might add to the new project must\n * appear here; missing keys throw during package.json generation\n * (loud failure beats silently shipping `^undefined`).\n */\nexport type SiblingVersions = Record<string, string>\n\nfunction take(versions: SiblingVersions, name: string): string {\n const v = versions[name]\n if (!v) {\n throw new Error(\n `generatePackageJson: missing resolved version for ${name}. ` +\n `Add it to SIBLING_PACKAGES in generators/project.ts.`,\n )\n }\n return v\n}\n\n/** Generate package.json with template-aware dependencies */\nexport function generatePackageJson(\n name: string,\n template: ProjectTemplate,\n versions: SiblingVersions,\n packages: string[] = [],\n schemaLib: SchemaLib = 'zod',\n runtime: 'express' | 'fastify' | 'h3' = 'express',\n): string {\n const schemaDep = SCHEMA_LIB_DEPS[schemaLib]\n const baseDeps: Record<string, string> = {\n '@forinda/kickjs': take(versions, '@forinda/kickjs'),\n // The schema-agnostic abstraction kickjs-schema wraps zod / valibot\n // / yup behind a single `KickSchema` interface — env validation,\n // body validation, and swagger spec generation all flow through\n // `detectSchema()`. Shipping it as a direct dep (rather than a peer)\n // keeps the new-project install one-step.\n '@forinda/kickjs-schema': take(versions, '@forinda/kickjs-schema'),\n // `dotenv` is an optional peer of @forinda/kickjs — scaffolded apps\n // get it pre-installed so `.env` files Just Work. Apps that load\n // env from the shell or a secret manager can drop this safely.\n dotenv: '^17.3.1',\n 'reflect-metadata': '^0.2.2',\n [schemaDep.name]: schemaDep.range,\n }\n\n // Engine peers for the chosen runtime (optional peers of @forinda/kickjs).\n if (runtime === 'express') {\n // Express is the engine itself.\n baseDeps.express = '^5.1.0'\n } else if (runtime === 'fastify') {\n baseDeps.fastify = '^5.0.0'\n baseDeps['@fastify/middie'] = '^9.0.0'\n // Static serving uses `serve-static` (no express dependency).\n baseDeps['serve-static'] = '^2.2.0'\n } else if (runtime === 'h3') {\n baseDeps.h3 = '^1.0.0'\n baseDeps['serve-static'] = '^2.2.0'\n }\n\n // Add user-selected optional packages — each looked up against\n // the resolved version map so they're independently up-to-date.\n for (const pkg of packages) {\n const dep = PACKAGE_DEPS[pkg]\n if (dep && !baseDeps[dep]) {\n baseDeps[dep] = take(versions, dep)\n }\n }\n\n return JSON.stringify(\n {\n name,\n // Project starts at 0.0.0 — adopters bump as they ship. Tying\n // the project version to the CLI version (the previous\n // behaviour) made every scaffolded app `5.4.0` on day one,\n // which broke npm publishing for adopters trying their first\n // release.\n version: '0.0.0',\n type: 'module',\n scripts: {\n // `kick dev` (not bare `vite`): it boots Vite itself AND owns the\n // typegen-on-save watcher. Plain `vite` gives working HMR but\n // frozen `.kickjs/types` — new routes silently lose their typing\n // until a manual `kick typegen`.\n dev: 'kick dev',\n 'dev:debug': 'kick dev:debug',\n build: 'kick build',\n start: 'kick start',\n test: 'vitest run',\n 'test:watch': 'vitest',\n typecheck: 'tsc --noEmit',\n typegen: 'kick typegen',\n lint: 'eslint src/',\n format: 'prettier --write src/',\n },\n dependencies: baseDeps,\n devDependencies: {\n '@forinda/kickjs-cli': take(versions, '@forinda/kickjs-cli'),\n '@forinda/kickjs-vite': take(versions, '@forinda/kickjs-vite'),\n '@swc/core': '^1.15.21',\n // Express types only when Express is the engine (it's the only runtime\n // that imports `express` in src/index.ts).\n ...(runtime === 'express' ? { '@types/express': '^5.0.6' } : {}),\n '@types/node': '^25.0.0',\n 'unplugin-swc': '^1.5.9',\n vite: '^8.0.3',\n vitest: '^4.1.2',\n typescript: '^6.0.3',\n prettier: '^3.8.1',\n },\n },\n null,\n 2,\n )\n}\n\n/**\n * Generate vite.config.ts with the KickJS Vite plugin.\n *\n * The plugin handles:\n * - SSR environment setup for backend Node.js code\n * - Virtual module generation (virtual:kickjs/app)\n * - Module auto-discovery (scans *.module.ts files)\n * - HMR with selective container invalidation\n * - Express mounting via configureServer() post-hook\n * - httpServer piping to adapters (WsAdapter, Socket.IO, etc.)\n */\nexport function generateViteConfig(): string {\n return `import { defineConfig } from 'vite'\nimport { resolve } from 'node:path'\nimport swc from 'unplugin-swc'\nimport { kickjsVitePlugin, envWatchPlugin } from '@forinda/kickjs-vite'\n\nexport default defineConfig({\n oxc: false,\n plugins: [\n swc.vite(),\n kickjsVitePlugin({ entry: 'src/index.ts' }),\n // Watches .env files and triggers a full reload on change so the\n // dev server picks up env tweaks without a manual restart.\n envWatchPlugin(),\n ],\n resolve: {\n alias: {\n '@': resolve(__dirname, 'src'),\n },\n },\n build: {\n target: 'node20',\n ssr: true,\n outDir: 'dist',\n sourcemap: true,\n rollupOptions: {\n input: resolve(__dirname, 'src/index.ts'),\n output: { format: 'esm' },\n },\n },\n})\n`\n}\n\n/** Generate tsconfig.json with decorator support */\nexport function generateTsConfig(): string {\n return JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['node', 'vite/client'],\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n sourceMap: true,\n declaration: true,\n experimentalDecorators: true,\n emitDecoratorMetadata: true,\n outDir: 'dist',\n // rootDir omitted so .kickjs/types/*.d.ts can sit outside src/\n paths: { '@/*': ['./src/*'] },\n },\n // .kickjs/types is generated by `kick typegen` and refreshed\n // automatically on `kick dev`. Including it here makes\n // `container.resolve()` and module discovery type-safe.\n // Both .d.ts and .ts are matched: registry/services/modules are\n // declarations, but routes.ts holds resolvable imports from your\n // controllers' Zod schemas (TS silently degrades inline `import('...')`\n // inside `.d.ts` files under `moduleResolution: 'bundler'`).\n include: ['src', '.kickjs/types/**/*.d.ts', '.kickjs/types/**/*.ts'],\n },\n null,\n 2,\n )\n}\n\n/** Generate .prettierrc with project formatting rules */\nexport function generatePrettierConfig(): string {\n return JSON.stringify(\n {\n semi: false,\n singleQuote: true,\n trailingComma: 'all',\n printWidth: 100,\n tabWidth: 2,\n },\n null,\n 2,\n )\n}\n\n/** Generate .editorconfig for consistent editor settings */\nexport function generateEditorConfig(): string {\n return `# https://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n`\n}\n\n/** Generate .gitignore with common Node.js patterns */\nexport function generateGitIgnore(): string {\n return `node_modules/\ndist/\n.env\ncoverage/\n.DS_Store\n*.tsbuildinfo\n.kickjs/\n`\n}\n\n/** Generate .gitattributes for consistent line endings */\nexport function generateGitAttributes(): string {\n return `# Auto-detect text files and normalise line endings to LF\n* text=auto eol=lf\n\n# Explicitly mark generated / binary files\n*.png binary\n*.jpg binary\n*.jpeg binary\n*.gif binary\n*.ico binary\n*.woff binary\n*.woff2 binary\n*.ttf binary\n*.eot binary\n\n# Lock files — treat as generated\npnpm-lock.yaml -diff linguist-generated\nyarn.lock -diff linguist-generated\npackage-lock.json -diff linguist-generated\n`\n}\n\n/** Generate .env file with default environment variables */\nexport function generateEnv(): string {\n return `PORT=3000\nNODE_ENV=development\n`\n}\n\n/** Generate .env.example file as a template */\nexport function generateEnvExample(): string {\n return `PORT=3000\nNODE_ENV=development\n`\n}\n\n/** Generate vitest.config.ts for test configuration */\nexport function generateVitestConfig(): string {\n return `import { defineConfig } from 'vitest/config'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n test: {\n globals: true,\n environment: 'node',\n include: ['src/**/*.test.ts'],\n },\n})\n`\n}\n","import { execSync } from 'node:child_process'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport type { Command } from 'commander'\nimport { loadKickConfig, PACKAGE_MANAGERS, type PackageManager } from '../config'\n\ninterface PackageEntry {\n pkg: string\n peers: string[]\n description: string\n dev?: boolean\n /**\n * `true` for packages every project needs (framework + Vite plugin +\n * CLI). `kick new` installs these regardless of options chosen, and\n * future package-removal flows refuse to drop them.\n */\n core?: boolean\n /**\n * Set when the package still installs but should no longer be the\n * default choice. The string is the migration hint shown both in\n * `kick add --list --all` and as a warning when the package is added.\n */\n deprecated?: string\n}\n\n/** Registry of KickJS packages and their required peer dependencies */\nexport const PACKAGE_REGISTRY: Record<string, PackageEntry> = {\n // Core (always installed by kick new — required for the framework to run)\n kickjs: {\n pkg: '@forinda/kickjs',\n peers: ['express'],\n description: 'Unified framework: DI, decorators, routing, middleware',\n core: true,\n },\n vite: {\n pkg: '@forinda/kickjs-vite',\n peers: ['vite'],\n description: 'Vite plugin: dev server, HMR, module discovery',\n dev: true,\n core: true,\n },\n cli: {\n pkg: '@forinda/kickjs-cli',\n peers: [],\n description: 'CLI tool and code generators',\n dev: true,\n core: true,\n },\n\n // Schema validation — the validator backing env + DTO + OpenAPI\n // schemas. `@forinda/kickjs-schema` (a core dep) wraps whichever one\n // you pick behind `KickSchema`, but the validator itself is an\n // optional peer of `@forinda/kickjs`, so it must be installed\n // explicitly or the app errors at startup (\"Cannot find module\n // 'zod'\"). `kick new` installs the chosen one; `kick add` lets an\n // existing project add/switch.\n zod: {\n pkg: 'zod',\n peers: [],\n description: 'Zod schema validation (env, DTOs, OpenAPI) — wrap with fromZod()',\n },\n valibot: {\n pkg: 'valibot',\n peers: [],\n description: 'Valibot schema validation — wrap with fromValibot()',\n },\n yup: {\n pkg: 'yup',\n peers: [],\n description: 'Yup schema validation — wrap with fromYup()',\n },\n\n // Auth — deprecated in favour of BYO (bring-your-own) auth composed\n // from context contributors. Still installable for existing projects;\n // JWT is the common path, so it co-installs jsonwebtoken.\n auth: {\n pkg: '@forinda/kickjs-auth',\n peers: ['jsonwebtoken'],\n description: 'JWT, API key, OAuth strategies, @Public, @Roles (+ optional argon2/bcryptjs)',\n deprecated:\n 'auth is moving to BYO — compose @LoadAuthUser/@RequireRole/@Public from defineContextDecorator (see the BYO Auth recipe in the docs)',\n },\n\n // AI — requires zod (^4) for tool/schema definitions.\n ai: {\n pkg: '@forinda/kickjs-ai',\n peers: ['zod'],\n description: 'AI toolkit — LLM providers, tool definitions from controllers',\n },\n\n // API\n swagger: {\n pkg: '@forinda/kickjs-swagger',\n peers: [],\n description: 'OpenAPI spec + Swagger UI + ReDoc',\n },\n // Database — the dialect adapters now ship as subpaths of\n // `@forinda/kickjs-db` (`/pg`, `/sqlite`, `/mysql`), so each `kick add`\n // pulls the core package plus the one driver you need.\n db: {\n pkg: '@forinda/kickjs-db',\n peers: [],\n description: 'kick/db core — schema DSL, migrations, KickDbClient, customType',\n },\n pg: {\n pkg: '@forinda/kickjs-db',\n peers: ['pg'],\n description: 'kick/db + PostgreSQL driver (use @forinda/kickjs-db/pg)',\n },\n sqlite: {\n pkg: '@forinda/kickjs-db',\n peers: ['better-sqlite3'],\n description: 'kick/db + SQLite driver (use @forinda/kickjs-db/sqlite)',\n },\n mysql: {\n pkg: '@forinda/kickjs-db',\n peers: ['mysql2'],\n description: 'kick/db + MySQL driver (use @forinda/kickjs-db/mysql)',\n },\n drizzle: {\n pkg: '@forinda/kickjs-drizzle',\n peers: ['drizzle-orm'],\n description: 'Drizzle ORM adapter + query builder',\n deprecated:\n 'early-adoption adapter, no longer maintained — wire Drizzle directly (BYO), or use @forinda/kickjs-db, the built-in Kick ORM (`kick add db` / pg / sqlite / mysql)',\n },\n prisma: {\n pkg: '@forinda/kickjs-prisma',\n peers: ['@prisma/client'],\n description: 'Prisma adapter + query builder',\n deprecated:\n 'early-adoption adapter, no longer maintained — wire Prisma directly (BYO), or use @forinda/kickjs-db, the built-in Kick ORM (`kick add db` / pg / sqlite / mysql)',\n },\n\n // Real-time\n ws: {\n pkg: '@forinda/kickjs-ws',\n peers: ['ws'],\n description: 'WebSocket with @WsController decorators',\n },\n\n // DevTools\n devtools: {\n pkg: '@forinda/kickjs-devtools',\n peers: [],\n description: 'Development dashboard — routes, DI, metrics, health',\n dev: true,\n },\n\n // Queue\n queue: {\n pkg: '@forinda/kickjs-queue',\n peers: [],\n description: 'Queue adapter (BullMQ/RabbitMQ/Kafka)',\n },\n 'queue:bullmq': {\n pkg: '@forinda/kickjs-queue',\n peers: ['bullmq', 'ioredis'],\n description: 'Queue with BullMQ + Redis',\n },\n 'queue:rabbitmq': {\n pkg: '@forinda/kickjs-queue',\n peers: ['amqplib'],\n description: 'Queue with RabbitMQ',\n },\n 'queue:kafka': {\n pkg: '@forinda/kickjs-queue',\n peers: ['kafkajs'],\n description: 'Queue with Kafka',\n },\n 'queue:redis-pubsub': {\n pkg: '@forinda/kickjs-queue',\n peers: ['ioredis'],\n description: 'Lightweight pub/sub via Redis (no persistence)',\n },\n\n // MCP — Model Context Protocol server\n mcp: {\n pkg: '@forinda/kickjs-mcp',\n peers: ['@modelcontextprotocol/sdk'],\n description: 'Model Context Protocol server — expose @Controller endpoints as AI tools',\n },\n\n // Testing\n testing: {\n pkg: '@forinda/kickjs-testing',\n peers: [],\n description: 'Test utilities and TestModule builder',\n dev: true,\n },\n}\n\n/**\n * Headline `kick add` packages shown after scaffolding — derived from\n * {@link PACKAGE_REGISTRY} so it can never advertise a deprecated package (the\n * old hardcoded list included auth / drizzle / prisma). Excludes core packages\n * (already installed), deprecated ones, `:` sub-variants (e.g. `queue:bullmq`),\n * and the db-dialect / schema-lib duplicates that clutter a one-line summary.\n * `kick add --list` shows the full catalog.\n */\nexport const AVAILABLE_ADD_PACKAGES = Object.entries(PACKAGE_REGISTRY)\n .filter(\n ([name, entry]) =>\n !entry.core &&\n !entry.deprecated &&\n !name.includes(':') &&\n !['pg', 'sqlite', 'mysql', 'zod', 'valibot', 'yup'].includes(name),\n )\n .map(([name]) => name)\n .join(', ')\n\n/**\n * The `upload` catalog name is special — file uploads ship inside\n * `@forinda/kickjs` itself, so there's no package to install. What an app\n * needs is the multipart DRIVER for its HTTP runtime, and that differs per\n * engine. `planAddPackages` resolves `upload` against the configured runtime\n * (see {@link KickConfig.runtime}); `kick doctor` validates the same mapping.\n */\nexport const UPLOAD_DRIVERS: Record<\n 'express' | 'fastify' | 'h3',\n { prod?: string; dev?: string; note: string }\n> = {\n express: {\n prod: 'multer',\n dev: '@types/multer',\n note: 'Express uploads use multer (memory/disk storage, ctx.file / ctx.files).',\n },\n fastify: {\n prod: '@fastify/multipart',\n note: 'Fastify uploads use @fastify/multipart (buffered into ctx.file / ctx.files).',\n },\n h3: {\n note: 'h3 parses multipart natively (readMultipartFormData) — no driver to install.',\n },\n}\n\nexport type AppRuntime = 'express' | 'fastify' | 'h3'\n\n/**\n * Resolve the project's HTTP runtime: the `runtime` field in kick.config\n * (authoritative — `kick new` writes it), falling back to sniffing installed\n * deps in the nearest package.json (`fastify` → fastify, `h3` → h3), else\n * `express` (the default engine). Lets `kick add upload` / `kick doctor` pick\n * the engine-correct multipart driver even in projects scaffolded before the\n * `runtime` field existed.\n */\nexport async function resolveAppRuntime(cwd = process.cwd()): Promise<AppRuntime> {\n const config = await loadKickConfig(cwd)\n const fromConfig = (config as { runtime?: AppRuntime } | null)?.runtime\n if (fromConfig === 'express' || fromConfig === 'fastify' || fromConfig === 'h3') {\n return fromConfig\n }\n return detectRuntimeFromDeps(cwd)\n}\n\n/** Sniff the runtime from installed deps when kick.config has no `runtime`. */\nexport function detectRuntimeFromDeps(cwd = process.cwd()): AppRuntime {\n const dir = findUp('package.json', cwd)\n if (dir) {\n try {\n const pkg = JSON.parse(readFileSync(resolve(dir, 'package.json'), 'utf-8'))\n const deps = { ...pkg.dependencies, ...pkg.devDependencies } as Record<string, unknown>\n if ('fastify' in deps) return 'fastify'\n if ('h3' in deps) return 'h3'\n } catch {\n // ignore — fall through to the default engine\n }\n }\n return 'express'\n}\n\n/**\n * Walk up from `fromDir` to filesystem root, returning the first\n * directory that contains `name`. Lets monorepo sub-packages pick up\n * lockfiles and `packageManager` fields living at the workspace root.\n */\nfunction findUp(name: string, fromDir = process.cwd()): string | null {\n let current = fromDir\n while (true) {\n if (existsSync(resolve(current, name))) return current\n const parent = dirname(current)\n if (parent === current) return null\n current = parent\n }\n}\n\nfunction detectFromLockfile(): PackageManager | null {\n if (findUp('pnpm-lock.yaml')) return 'pnpm'\n if (findUp('yarn.lock')) return 'yarn'\n if (findUp('bun.lockb') || findUp('bun.lock')) return 'bun'\n if (findUp('package-lock.json')) return 'npm'\n return null\n}\n\n/**\n * Read `packageManager` from the nearest ancestor `package.json` that\n * declares the field (corepack convention: `\"pnpm@10.0.0\"`). Climbs so\n * monorepo sub-packages inherit the workspace pm even when their own\n * package.json omits the field.\n */\nfunction packageManagerFromPackageJson(): PackageManager | null {\n let dir: string | null = process.cwd()\n while (dir) {\n const pkgPath = resolve(dir, 'package.json')\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n const field: unknown = pkg.packageManager\n if (typeof field === 'string') {\n const name = field.split('@')[0] as PackageManager\n if (PACKAGE_MANAGERS.includes(name)) return name\n }\n } catch {\n // ignore — keep climbing\n }\n }\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n return null\n}\n\nexport type PackageManagerSource = 'flag' | 'config' | 'package.json' | 'lockfile' | 'default'\n\n/**\n * Resolve which package manager to use, in priority order:\n * 1. `--pm` CLI flag\n * 2. `packageManager` in kick.config\n * 3. `packageManager` in nearest ancestor package.json (corepack)\n * 4. Nearest ancestor lockfile (pnpm-lock.yaml → yarn.lock → bun.lock → package-lock.json)\n * 5. `'npm'` fallback\n *\n * Returns the chosen pm plus the source for callers that want to log\n * the resolution path.\n */\nexport async function resolvePackageManagerWithSource(\n flagPm: string | undefined,\n): Promise<{ pm: PackageManager; source: PackageManagerSource }> {\n if (flagPm && PACKAGE_MANAGERS.includes(flagPm as PackageManager)) {\n return { pm: flagPm as PackageManager, source: 'flag' }\n }\n\n const config = await loadKickConfig(process.cwd())\n if (config?.packageManager && PACKAGE_MANAGERS.includes(config.packageManager)) {\n return { pm: config.packageManager, source: 'config' }\n }\n\n const fromPkg = packageManagerFromPackageJson()\n if (fromPkg) return { pm: fromPkg, source: 'package.json' }\n\n const fromLock = detectFromLockfile()\n if (fromLock) return { pm: fromLock, source: 'lockfile' }\n\n return { pm: 'npm', source: 'default' }\n}\n\n/** Convenience wrapper for callers that don't care about the source. */\nexport async function resolvePackageManager(flagPm: string | undefined): Promise<PackageManager> {\n const { pm } = await resolvePackageManagerWithSource(flagPm)\n return pm\n}\n\n/**\n * Print the package catalog. By default shows just the three core\n * packages every project always has — the optional list churns\n * (packages added, deprecated, removed) and a long enumeration in CLI\n * output / docs goes stale within a release. Pass `all = true` to dump\n * everything; that's what `kick add --list --all` triggers when an\n * adopter genuinely wants the live catalog.\n */\nexport function printPackageList(all = false): void {\n const entries = Object.entries(PACKAGE_REGISTRY)\n const maxName = Math.max(...entries.map(([k]) => k.length))\n const core = entries.filter(([, info]) => info.core)\n const optional = entries.filter(([, info]) => !info.core)\n\n const formatRow = ([name, info]: [string, PackageEntry]): string => {\n const padded = name.padEnd(maxName + 2)\n const peers = info.peers.length ? ` (+ ${info.peers.join(', ')})` : ''\n const deprecated = info.deprecated ? ` [DEPRECATED — ${info.deprecated}]` : ''\n return ` ${padded} ${info.description}${peers}${deprecated}`\n }\n\n console.log('\\n Core packages (always installed by `kick new`):\\n')\n for (const row of core) console.log(formatRow(row))\n\n if (all) {\n console.log('\\n Optional packages (add as needed):\\n')\n for (const row of optional) console.log(formatRow(row))\n } else {\n console.log(`\\n Plus ${optional.length} optional packages (auth, swagger, db, queue, …).`)\n console.log(' Run `kick add --list --all` for the full catalog.')\n }\n\n console.log('\\n Usage: kick add ai db swagger')\n console.log(' kick add queue:bullmq')\n console.log(' kick add upload # installs the multipart driver for your runtime')\n console.log()\n}\n\nexport interface AddPlan {\n prodDeps: string[]\n devDeps: string[]\n unknown: string[]\n /** Deprecation notices for requested entries — print, then install anyway. */\n warnings: string[]\n /** Informational notes (e.g. the upload driver chosen for the runtime). */\n notices: string[]\n}\n\n/**\n * Pure resolution step for `kick add` — maps requested catalog names to\n * the npm packages (plus peers) to install, split prod/dev. Kept free\n * of I/O so the catalog rules (dev defaults, deprecations, unknown\n * handling) are unit-testable without spawning a package manager.\n */\nexport function planAddPackages(\n packages: string[],\n forceDev: boolean,\n runtime: AppRuntime = 'express',\n): AddPlan {\n const prodDeps = new Set<string>()\n const devDeps = new Set<string>()\n const unknown: string[] = []\n const warnings: string[] = []\n const notices: string[] = []\n\n for (const name of packages) {\n // `upload` isn't a package — it's the runtime's multipart driver. File\n // uploads ship in @forinda/kickjs; only the engine backend needs adding.\n if (name === 'upload') {\n const driver = UPLOAD_DRIVERS[runtime]\n notices.push(`upload (${runtime}): ${driver.note}`)\n if (driver.prod) (forceDev ? devDeps : prodDeps).add(driver.prod)\n if (driver.dev) devDeps.add(driver.dev)\n continue\n }\n\n const entry = PACKAGE_REGISTRY[name]\n if (!entry) {\n unknown.push(name)\n continue\n }\n if (entry.deprecated) {\n warnings.push(`'${name}' (${entry.pkg}) is deprecated — ${entry.deprecated}`)\n }\n const target = forceDev || entry.dev ? devDeps : prodDeps\n target.add(entry.pkg)\n for (const peer of entry.peers) {\n target.add(peer)\n }\n }\n\n return { prodDeps: [...prodDeps], devDeps: [...devDeps], unknown, warnings, notices }\n}\n\nexport function registerListCommand(program: Command): void {\n program\n .command('list')\n .alias('ls')\n .description('List KickJS packages (core only; pair with --all for the full catalog)')\n .option('--all', 'Include the full optional catalog')\n .action((opts: { all?: boolean }) => {\n printPackageList(Boolean(opts.all))\n })\n}\n\nexport function registerAddCommand(program: Command): void {\n program\n .command('add [packages...]')\n .description('Add KickJS packages with their required dependencies')\n .option('--pm <manager>', 'Package manager override')\n .option('-D, --dev', 'Install as dev dependency')\n .option('--list', 'List packages (core only by default; pair with --all)')\n .option('--all', 'When listing, include the full optional catalog')\n .action(async (packages: string[], opts: any) => {\n // List mode\n if (opts.list || packages.length === 0) {\n printPackageList(Boolean(opts.all))\n return\n }\n\n const { pm, source } = await resolvePackageManagerWithSource(opts.pm)\n console.log(`\\n Using ${pm} (resolved from ${source})`)\n // Resolve the runtime so `kick add upload` installs the right multipart\n // driver (express → multer, fastify → @fastify/multipart, h3 → none).\n const runtime = await resolveAppRuntime(process.cwd())\n const { prodDeps, devDeps, unknown, warnings, notices } = planAddPackages(\n packages,\n Boolean(opts.dev),\n runtime,\n )\n\n for (const warning of warnings) {\n console.warn(`\\n WARNING: ${warning}`)\n }\n\n for (const notice of notices) {\n console.log(`\\n ${notice}`)\n }\n\n if (unknown.length > 0) {\n console.log(`\\n Unknown packages: ${unknown.join(', ')}`)\n console.log(' Run \"kick add --list\" to see available packages.\\n')\n if (prodDeps.length === 0 && devDeps.length === 0) return\n }\n\n // Install production dependencies\n if (prodDeps.length > 0) {\n const deps = prodDeps\n const cmd = `${pm} add ${deps.join(' ')}`\n console.log(`\\n Installing ${deps.length} dependency(ies):`)\n for (const dep of deps) console.log(` + ${dep}`)\n console.log()\n try {\n execSync(cmd, { stdio: 'inherit' })\n } catch {\n console.log(`\\n Installation failed. Run manually:\\n ${cmd}\\n`)\n }\n }\n\n // Install dev dependencies\n if (devDeps.length > 0) {\n const deps = devDeps\n const cmd = `${pm} add -D ${deps.join(' ')}`\n console.log(`\\n Installing ${deps.length} dev dependency(ies):`)\n for (const dep of deps) console.log(` + ${dep} (dev)`)\n console.log()\n try {\n execSync(cmd, { stdio: 'inherit' })\n } catch {\n console.log(`\\n Installation failed. Run manually:\\n ${cmd}\\n`)\n }\n }\n\n console.log(' Done!\\n')\n })\n}\n","import { join, dirname } from 'node:path'\nimport { execFileSync, execSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { writeFileSafe } from '../utils/fs'\nimport {\n generatePackageJson,\n generateViteConfig,\n generateTsConfig,\n generatePrettierConfig,\n generateEditorConfig,\n generateGitIgnore,\n generateGitAttributes,\n generateEnv,\n generateEnvExample,\n generateVitestConfig,\n} from './templates/project-config'\nimport {\n generateEntryFile,\n generateEnvFile,\n generateModulesIndex,\n generateKickConfig,\n generateHelloService,\n generateHelloController,\n generateHelloModule,\n} from './templates/project-app'\nimport { generateReadme } from './templates/project-docs'\nimport { AVAILABLE_ADD_PACKAGES } from '../commands/add'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst cliPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))\nconst CLI_VERSION_FALLBACK = `^${cliPkg.version}`\n\n/**\n * Sibling `@forinda/kickjs-*` packages whose versions are resolved\n * independently when scaffolding a new project. Each entry is queried\n * via `npm view <name> version`; failure falls back to the CLI's own\n * version (`CLI_VERSION_FALLBACK`).\n *\n * Per-package independent versioning landed with changesets — before\n * that, every sibling shipped in lockstep with the CLI so a single\n * pin was correct. Now `@forinda/kickjs@5.5.0` may pair with\n * `@forinda/kickjs-cli@5.4.2` and `@forinda/kickjs-swagger@5.3.1`;\n * pinning them all to the CLI's version under-installs adopters.\n */\nconst SIBLING_PACKAGES = [\n '@forinda/kickjs',\n '@forinda/kickjs-cli',\n '@forinda/kickjs-schema',\n '@forinda/kickjs-vite',\n '@forinda/kickjs-swagger',\n '@forinda/kickjs-ws',\n '@forinda/kickjs-queue',\n '@forinda/kickjs-devtools',\n '@forinda/kickjs-testing',\n '@forinda/kickjs-client',\n] as const\n\n/**\n * Resolve the latest published version of every sibling package via\n * `npm view <name> version` (via execFileSync — no shell, no\n * injection vector). Each query has a short timeout; failures fall\n * back to the CLI's own version with a `^` prefix so the scaffold\n * stays usable offline.\n */\nexport async function resolveSiblingVersions(): Promise<Record<string, string>> {\n const results = await Promise.all(\n SIBLING_PACKAGES.map(async (name) => {\n try {\n const out = execFileSync('npm', ['view', name, 'version'], {\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim()\n if (out && /^\\d+\\.\\d+\\.\\d+/.test(out)) {\n return [name, `^${out}`] as const\n }\n } catch {\n // Network failure / package not yet published / npm\n // unavailable. Fall back to CLI version below.\n }\n return [name, CLI_VERSION_FALLBACK] as const\n }),\n )\n return Object.fromEntries(results)\n}\n\n/**\n * Resolve the published version of a package at a given dist-tag\n * (`npm view <name>@<tag> version`). Returns `null` on any failure. Used\n * to pin `@forinda/kickjs` to the `alpha` channel when scaffolding a\n * Fastify / h3 app — the engine subpaths (`@forinda/kickjs/fastify`,\n * `/h3`) ship only on the alpha until the runtimes land in a stable\n * release, so the default `latest` resolution would install a kickjs\n * that doesn't export them (→ Vite \"./h3 is not exported\" at boot).\n * Returns the bare version; the caller applies a `^` range so the project\n * floats to newer alphas and auto-graduates to stable (a caret over a\n * prerelease matches same-tuple prereleases ≥ it, plus later stables `< next\n * major`).\n */\nfunction resolveVersionAtTag(name: string, tag: string): string | null {\n try {\n const out = execFileSync('npm', ['view', `${name}@${tag}`, 'version'], {\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim()\n return out && /^\\d+\\.\\d+\\.\\d+/.test(out) ? out : null\n } catch {\n return null\n }\n}\n\n/**\n * Whether the package at a given dist-tag exports a subpath (e.g. `./h3`).\n * Reads the `exports` map via `npm view <name>@<tag> exports --json`. Used to\n * gate the alpha-pin: if `latest` already ships the engine subpath, the runtime\n * has graduated to stable and we should NOT downgrade to an older alpha.\n * Returns `false` on any failure (missing field / network / unparseable) so the\n * caller treats \"unknown\" as \"not present\" and falls through to the alpha path.\n */\n/** Strip a leading range operator (`^1.2.3` / `~1.2.3` → `1.2.3`). */\nfunction stripRange(range: string | undefined): string {\n return (range ?? '').replace(/^[\\^~>=<\\s]+/, '')\n}\n\n/**\n * Compare the release cores (major.minor.patch, ignoring any `-prerelease`\n * suffix) of two versions: is `a` >= `b`? Used to guard the alpha-pin so a\n * package is never downgraded onto a stale prerelease whose stable line has\n * already moved past it. A coarse compare is enough here — we only need\n * \"is this alpha at least as new as the stable we'd otherwise install\".\n */\nfunction baseVersionGte(a: string, b: string): boolean {\n const core = (v: string): number[] =>\n stripRange(v)\n .split('-')[0]!\n .split('.')\n .map((n) => Number.parseInt(n, 10) || 0)\n const [a0 = 0, a1 = 0, a2 = 0] = core(a)\n const [b0 = 0, b1 = 0, b2 = 0] = core(b)\n if (a0 !== b0) return a0 > b0\n if (a1 !== b1) return a1 > b1\n return a2 >= b2\n}\n\nfunction tagExportsSubpath(name: string, tag: string, subpath: string): boolean {\n try {\n const out = execFileSync('npm', ['view', `${name}@${tag}`, 'exports', '--json'], {\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim()\n if (!out) return false\n const exportsMap = JSON.parse(out) as Record<string, unknown>\n return Object.prototype.hasOwnProperty.call(exportsMap, subpath)\n } catch {\n return false\n }\n}\n\ntype ProjectTemplate = 'rest' | 'minimal'\ntype SchemaLib = 'zod' | 'valibot' | 'yup'\n\ninterface InitProjectOptions {\n name: string\n directory: string\n packageManager?: 'pnpm' | 'npm' | 'yarn' | 'bun'\n initGit?: boolean\n installDeps?: boolean\n template?: ProjectTemplate\n defaultRepo?: string\n packages?: string[]\n /** Schema library to scaffold env / DTOs with. Defaults to `zod`. */\n schemaLib?: SchemaLib\n /** HTTP engine to scaffold. Defaults to `express`. */\n runtime?: 'express' | 'fastify' | 'h3'\n}\n\n/** Scaffold a new KickJS project */\nexport async function initProject(options: InitProjectOptions): Promise<void> {\n const {\n name,\n directory,\n packageManager = 'pnpm',\n template = 'rest',\n defaultRepo = 'inmemory',\n packages = [],\n schemaLib = 'zod',\n runtime = 'express',\n } = options\n const dir = directory\n\n const log = (msg: string) => console.log(` ${msg}`)\n\n console.log(`\\n Creating KickJS project: ${name}\\n`)\n\n // Resolve published version of every sibling kickjs package in\n // parallel. Per-package independent versioning means\n // `@forinda/kickjs@5.5.0` may pair with `@forinda/kickjs-cli@5.4.2`\n // and `@forinda/kickjs-swagger@5.3.1`; pinning every dep to the\n // CLI's own version under-installs adopters whenever a sibling\n // bumps independently. `npm view` fallback keeps the scaffold\n // working offline.\n log('Resolving package versions...')\n const versions = await resolveSiblingVersions()\n\n // The pluggable-runtimes work (Fastify / h3 engine subpaths, the\n // `kick/runtime` typegen, `kick add upload`, `kick doctor` runtime checks)\n // ships only on the `alpha` channel until it lands in a stable release. So a\n // non-Express scaffold needs the alpha of every package that carries runtime\n // behavior, not just `@forinda/kickjs`:\n // - `@forinda/kickjs` — the `./fastify` / `./h3` export subpaths the\n // app imports (stable lacks them → Vite boot\n // error `\"./h3\" is not exported`).\n // - `@forinda/kickjs-cli` — `--runtime`, `kick add upload`, `kick doctor`,\n // the `kick/runtime` typegen plugin.\n // - `@forinda/kickjs-vite` — the dev loop co-versioned with the above.\n // Gated on whether `@forinda/kickjs@latest` already exports the chosen engine\n // subpath: once that's true the runtimes are stable and we keep `latest` for\n // everything (self-retiring — no code change needed at graduation). Each pin\n // is guarded so it never DOWNGRADES (an alpha can be older than latest — e.g.\n // a package whose stable moved on past an old prerelease). Express is exempt.\n if (runtime !== 'express') {\n const subpath = `./${runtime}` // './fastify' | './h3'\n if (tagExportsSubpath('@forinda/kickjs', 'latest', subpath)) {\n log(`Using @forinda/kickjs@latest (stable ships the ${runtime} runtime)`)\n } else {\n const RUNTIME_PKGS = ['@forinda/kickjs', '@forinda/kickjs-cli', '@forinda/kickjs-vite']\n const pinned: string[] = []\n let kickjsPinned = false\n for (const pkg of RUNTIME_PKGS) {\n const alpha = resolveVersionAtTag(pkg, 'alpha')\n // Only switch when the alpha is newer-or-equal to the stable we'd\n // otherwise install — never downgrade onto a stale prerelease. Use a\n // `^` range (not an exact pin) so the project picks up newer alphas and\n // auto-graduates to the stable release once it ships.\n if (alpha && baseVersionGte(alpha, stripRange(versions[pkg]))) {\n versions[pkg] = `^${alpha}`\n pinned.push(`${pkg}@^${alpha}`)\n if (pkg === '@forinda/kickjs') kickjsPinned = true\n }\n }\n if (kickjsPinned) {\n log(`Using the alpha channel for the ${runtime} runtime: ${pinned.join(', ')}`)\n } else {\n log(\n `WARNING: could not resolve @forinda/kickjs@alpha — the ${runtime} runtime subpath ` +\n `may be missing. After install, run: ${packageManager} add @forinda/kickjs@alpha`,\n )\n }\n }\n }\n\n // ── package.json — template-aware deps ────────────────────────────\n await writeFileSafe(\n join(dir, 'package.json'),\n generatePackageJson(name, template, versions, packages, schemaLib, runtime),\n )\n\n // ── vite.config.ts — enables HMR + SWC for decorators ──────────────\n await writeFileSafe(join(dir, 'vite.config.ts'), generateViteConfig())\n\n // ── tsconfig.json ───────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'tsconfig.json'), generateTsConfig())\n\n // ── .prettierrc ─────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.prettierrc'), generatePrettierConfig())\n\n // ── .editorconfig ─────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.editorconfig'), generateEditorConfig())\n\n // ── .gitignore ──────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.gitignore'), generateGitIgnore())\n\n // ── .gitattributes ────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.gitattributes'), generateGitAttributes())\n\n // ── .env ────────────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.env'), generateEnv())\n\n await writeFileSafe(join(dir, '.env.example'), generateEnvExample())\n\n // ── src/config/index.ts — typed env schema (read by `kick typegen`) ─\n // Lives under `src/config/` so the framework's \"config\" concept has a\n // single, conventional home. Old projects with `src/env.ts` still\n // work — `detectEnvFile()` searches both locations.\n await writeFileSafe(join(dir, 'src/config/index.ts'), generateEnvFile(schemaLib))\n\n // ── src/index.ts — template-aware entry point ─────────────────────\n await writeFileSafe(\n join(dir, 'src/index.ts'),\n generateEntryFile(name, template, cliPkg.version, packages, runtime),\n )\n\n // ── src/modules/index.ts ────────────────────────────────────────────\n await writeFileSafe(join(dir, 'src/modules/index.ts'), generateModulesIndex())\n\n // ── src/modules/hello/ — sample module ─────────────────────────────\n await writeFileSafe(join(dir, 'src/modules/hello/hello.service.ts'), generateHelloService())\n await writeFileSafe(join(dir, 'src/modules/hello/hello.controller.ts'), generateHelloController())\n await writeFileSafe(join(dir, 'src/modules/hello/hello.module.ts'), generateHelloModule())\n\n // ── kick.config.ts — CLI configuration ─────────────────────────────\n await writeFileSafe(\n join(dir, 'kick.config.ts'),\n generateKickConfig(template, defaultRepo, packageManager, runtime),\n )\n\n // ── vitest.config.ts ────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'vitest.config.ts'), generateVitestConfig())\n\n // ── README.md ────────────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'README.md'), generateReadme(name, template, packageManager))\n\n // ── Agent docs ──────────────────────────────────────────────────────\n // Delegate to `generateAgentDocs()` so `kick new` emits the same\n // `.agents/` subfolder layout as `kick g agents -f`. Otherwise the\n // two paths drifted: kick new was writing the legacy flat layout\n // (root-level AGENTS.md + kickjs-skills.md) while kick g agents\n // emits the per-skill SKILL.md format under .agents/. `force: true`\n // because the project directory is fresh — no overwrite prompts\n // make sense during init.\n const { generateAgentDocs } = await import('./agent-docs')\n await generateAgentDocs({\n outDir: dir,\n name,\n pm: packageManager,\n template,\n only: 'all',\n force: true,\n })\n\n // ── Install Dependencies ────────────────────────────────────────────\n // Install BEFORE git init so the lockfile is included in the first commit.\n if (options.installDeps) {\n console.log(`\\n Installing dependencies with ${packageManager}...\\n`)\n try {\n execSync(`${packageManager} install`, { cwd: dir, stdio: 'inherit' })\n console.log('\\n Dependencies installed successfully!')\n } catch {\n console.log(`\\n Warning: ${packageManager} install failed. Run it manually.`)\n }\n }\n\n // ── Initial typegen ────────────────────────────────────────────────\n // Run typegen once so the freshly-scaffolded HelloController's\n // `Ctx<KickRoutes.HelloController['index']>` references resolve in\n // the user's editor immediately. Failures are non-fatal.\n try {\n const { runTypegen } = await import('../typegen')\n await runTypegen({ cwd: dir, allowDuplicates: true, silent: true })\n } catch {\n // First-run typegen errors are non-fatal — `kick dev` will retry.\n }\n\n // ── Git Init ─────────────────────────────────────────────────────────\n // Runs after install + typegen so lockfile and generated types are\n // included in the initial commit.\n if (options.initGit) {\n try {\n execSync('git init', { cwd: dir, stdio: 'pipe' })\n execSync('git branch -M main', { cwd: dir, stdio: 'pipe' })\n execSync('git add -A', { cwd: dir, stdio: 'pipe' })\n execSync('git commit -m \"chore: initial commit from kick new\"', {\n cwd: dir,\n stdio: 'pipe',\n })\n log('Git repository initialized')\n } catch {\n log('Warning: git init failed (git may not be installed)')\n }\n }\n\n console.log('\\n Project scaffolded successfully!')\n console.log()\n\n const needsCd = dir !== process.cwd()\n log('Next steps:')\n if (needsCd) log(` cd ${name}`)\n if (!options.installDeps) log(` ${packageManager} install`)\n\n const genHint: Record<string, string> = {\n rest: 'kick g module user',\n ddd: 'kick g module user --repo drizzle',\n cqrs: 'kick g module user --pattern cqrs',\n minimal: '# add your routes to src/index.ts',\n }\n log(` ${genHint[template] ?? genHint.rest}`)\n log(' kick dev')\n log('')\n log('Commands:')\n log(' kick dev Start dev server with Vite HMR')\n log(' kick build Production build via Vite')\n log(' kick start Run production build')\n log('')\n log('Generators:')\n log(' kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)')\n log(' kick g scaffold <n> <f..> CRUD module from field definitions')\n log(' kick g controller <name> Standalone controller')\n log(' kick g service <name> @Service() class')\n log(' kick g middleware <name> Express middleware')\n log(' kick g guard <name> Route guard (auth, roles, etc.)')\n log(' kick g adapter <name> AppAdapter with lifecycle hooks')\n log(' kick g dto <name> Zod DTO schema')\n log(' kick g config Generate kick.config.ts')\n log('')\n log('Add packages:')\n log(' kick add <pkg> Install a KickJS package + peers')\n log(' kick add --list Show all available packages')\n log('')\n log(`Available: ${AVAILABLE_ADD_PACKAGES}`)\n log('')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"project-YM9dH4O_.mjs","names":[],"sources":["../src/generators/templates/project-app.ts","../src/generators/templates/project-config.ts","../src/commands/add.ts","../src/generators/project.ts"],"sourcesContent":["type ProjectTemplate = 'rest' | 'minimal'\nexport type ProjectRuntime = 'express' | 'fastify' | 'h3'\n\n/** Per-runtime import source + factory name for the scaffolded `runtime:` option. */\nconst RUNTIME_FACTORY: Record<ProjectRuntime, { from: string; name: string }> = {\n express: { from: '@forinda/kickjs', name: 'expressRuntime' },\n fastify: { from: '@forinda/kickjs/fastify', name: 'fastifyRuntime' },\n h3: { from: '@forinda/kickjs/h3', name: 'h3Runtime' },\n}\n\n/**\n * Generate src/index.ts entry file with template-specific bootstrap.\n *\n * The runtime is always emitted explicitly (`runtime: expressRuntime()` etc.)\n * so the entry file is self-documenting and switching engines is a one-line\n * edit. Fastify / h3 parse bodies natively, so the REST template skips the\n * `express.json()` middleware (and the `express` import) under those engines.\n *\n * All templates export the app for the Vite plugin (dev mode).\n */\nexport function generateEntryFile(\n name: string,\n template: ProjectTemplate,\n version: string,\n packages: string[] = [],\n runtime: ProjectRuntime = 'express',\n): string {\n const factory = RUNTIME_FACTORY[runtime]\n const isExpress = runtime === 'express'\n\n switch (template) {\n case 'minimal': {\n const imports: string[] = []\n const adapters: string[] = []\n\n // The runtime factory comes from the core package for Express, or a\n // subpath for Fastify / h3.\n const kickImport = isExpress\n ? `import { bootstrap, ${factory.name} } from '@forinda/kickjs'`\n : `import { bootstrap } from '@forinda/kickjs'\\nimport { ${factory.name} } from '${factory.from}'`\n\n if (packages.includes('swagger')) {\n imports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`)\n adapters.push(` SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`)\n }\n if (packages.includes('devtools')) {\n imports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`)\n adapters.push(` DevToolsAdapter(),`)\n }\n const importsBlock = imports.length ? imports.join('\\n') + '\\n' : ''\n const adaptersBlock = adapters.length ? `,\\n adapters: [\\n${adapters.join('\\n')}\\n ]` : ''\n\n return `import 'reflect-metadata'\n// Side-effect import — registers the extended env schema with kickjs\n// **before** any controller / service / @Value gets resolved. Without\n// this line ConfigService.get('YOUR_KEY') returns undefined because the\n// cached schema would still be the base shape. See guide/configuration.\nimport './config'\n${kickImport}\n${importsBlock}import { modules } from './modules'\n\n// Export the app for the Vite plugin (dev mode)\nexport const app = await bootstrap({ modules, runtime: ${factory.name}()${adaptersBlock} })\n`\n }\n\n case 'rest':\n default: {\n // Build adapters based on user-selected packages\n const restImports: string[] = []\n const restAdapters: string[] = []\n\n if (packages.includes('devtools')) {\n restImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`)\n restAdapters.push(` DevToolsAdapter(),`)\n }\n if (packages.includes('swagger')) {\n restImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`)\n restAdapters.push(\n ` SwaggerAdapter({\\n info: { title: '${name}', version: '${version}' },\\n }),`,\n )\n }\n const restImportsBlock = restImports.length ? restImports.join('\\n') + '\\n' : ''\n const restAdaptersBlock = restAdapters.length\n ? `\\n adapters: [\\n${restAdapters.join('\\n')}\\n ],`\n : ''\n\n // Express needs `express.json()` for body parsing; Fastify / h3 parse\n // bodies natively, so adding it would consume the body stream twice.\n const kickNamed = ['bootstrap', 'requestId', 'requestLogger', 'helmet', 'cors']\n if (isExpress) kickNamed.push(factory.name)\n const kickImport = isExpress\n ? `import express from 'express'\\nimport {\\n ${kickNamed.join(',\\n ')},\\n} from '@forinda/kickjs'`\n : `import {\\n ${kickNamed.join(',\\n ')},\\n} from '@forinda/kickjs'\\nimport { ${factory.name} } from '${factory.from}'`\n const bodyParserLine = isExpress ? `\\n express.json(),` : ''\n\n return `import 'reflect-metadata'\n// Side-effect import — registers the extended env schema with kickjs\n// **before** any controller / service / @Value gets resolved. Without\n// this line ConfigService.get('YOUR_KEY') returns undefined because the\n// cached schema would still be the base shape. See guide/configuration.\nimport './config'\n${kickImport}\n${restImportsBlock}import { modules } from './modules'\n\n// Export the app for the Vite plugin (dev mode)\nexport const app = await bootstrap({\n modules,\n runtime: ${factory.name}(),${restAdaptersBlock}\n middleware: [\n helmet(),\n cors({ origin: '*' }),\n requestId(),\n requestLogger(),${bodyParserLine}\n ],\n})\n`\n }\n }\n}\n\n/** Generate src/modules/index.ts module registry */\nexport function generateModulesIndex(): string {\n return `import { defineModules } from '@forinda/kickjs'\nimport { HelloModule } from './hello/hello.module'\n\n// Remove HelloModule and run: kick g module <name>\n// \\`defineModules()\\` returns a chainable list — \\`kick g module\\` appends\n// \\`.mount(NewModule())\\` to the chain on every generation.\nexport const modules = defineModules().mount(HelloModule())\n`\n}\n\n/**\n * Generate `src/config/index.ts` — the project's typed env schema.\n *\n * Default-exports a `defineEnv(...)` schema so `kick typegen` can\n * infer it into the global `KickEnv` registry, and *also* calls\n * `loadEnv(envSchema)` as a module-load side effect so `ConfigService`\n * and `@Value()` see the extended shape from the very first DI\n * resolution. The companion `src/index.ts` template adds\n * `import './config'` immediately after `reflect-metadata` so the\n * registration runs before `bootstrap()` constructs anything.\n *\n * After typegen runs:\n *\n * @Value('DATABASE_URL') private url!: Env<'DATABASE_URL'>\n * process.env.DATABASE_URL // typed as string\n *\n * Both autocomplete and type-check at compile time.\n */\nexport function generateEnvFile(schemaLib: 'zod' | 'valibot' | 'yup' = 'zod'): string {\n if (schemaLib === 'valibot') {\n return `import { loadEnvFromSchema } from '@forinda/kickjs/config'\nimport { fromValibot } from '@forinda/kickjs-schema/valibot'\nimport * as v from 'valibot'\n\n/**\n * Project environment schema (Valibot).\n *\n * \\`fromValibot\\` wraps the Valibot schema as a \\`KickSchema\\` so the\n * env loader, validate middleware, and swagger spec generator all see\n * the same shape. The default export is the contract \\`kick typegen\\`\n * reads to populate \\`KickEnv\\` via \\`InferSchemaOutput<typeof _envSchema>\\`\n * — that's what makes \\`@Value('FOO')\\` autocomplete and\n * \\`process.env.FOO\\` typed.\n *\n * @example\n * DATABASE_URL: v.pipe(v.string(), v.url()),\n * JWT_SECRET: v.pipe(v.string(), v.minLength(32)),\n * REDIS_URL: v.optional(v.pipe(v.string(), v.url())),\n */\nconst envSchema = fromValibot(\n v.object({\n PORT: v.optional(v.pipe(v.string(), v.transform(Number)), '3000'),\n NODE_ENV: v.optional(v.picklist(['development', 'production', 'test']), 'development'),\n LOG_LEVEL: v.optional(v.string(), 'info'),\n // DATABASE_URL: v.pipe(v.string(), v.url()),\n }),\n)\n\n/**\n * IMPORTANT — side effect: register the schema with kickjs's env cache\n * **at module-load time**. \\`ConfigService\\` and \\`@Value()\\` both consume\n * this cache, and they will fall back to the base schema (or undefined)\n * if no extended schema has been registered before they're resolved.\n *\n * As long as \\`src/index.ts\\` imports this file (\\`import './config'\\`) at\n * the top — before \\`bootstrap()\\` runs — every controller and service\n * in the app sees the typed extended values.\n */\nexport const env = loadEnvFromSchema(envSchema)\n\nexport default envSchema\n`\n }\n\n if (schemaLib === 'yup') {\n return `import { loadEnvFromSchema } from '@forinda/kickjs/config'\nimport { fromYup } from '@forinda/kickjs-schema/yup'\nimport * as yup from 'yup'\n\n/**\n * Project environment schema (Yup).\n *\n * \\`fromYup\\` wraps the Yup schema as a \\`KickSchema\\` so the env loader,\n * validate middleware, and swagger spec generator all see the same\n * shape. The default export is the contract \\`kick typegen\\` reads to\n * populate \\`KickEnv\\` via \\`InferSchemaOutput<typeof _envSchema>\\`.\n *\n * Note: Yup's \\`.url()\\` defaults to http/https; database connection\n * strings like \\`postgres://\\` use \\`.matches(/^[a-z]+:\\\\/\\\\/.+/i)\\` or\n * a plain \\`.string().required()\\`.\n *\n * @example\n * DATABASE_URL: yup.string().required(),\n * JWT_SECRET: yup.string().min(32).required(),\n * REDIS_URL: yup.string().url().optional(),\n */\nconst envSchema = fromYup(\n yup.object({\n PORT: yup.number().default(3000),\n NODE_ENV: yup\n .string()\n .oneOf(['development', 'production', 'test'])\n .default('development'),\n LOG_LEVEL: yup.string().default('info'),\n // DATABASE_URL: yup.string().required(),\n }),\n)\n\n/**\n * IMPORTANT — side effect: register the schema with kickjs's env cache\n * **at module-load time**. \\`ConfigService\\` and \\`@Value()\\` both consume\n * this cache, and they will fall back to the base schema (or undefined)\n * if no extended schema has been registered before they're resolved.\n *\n * As long as \\`src/index.ts\\` imports this file (\\`import './config'\\`) at\n * the top — before \\`bootstrap()\\` runs — every controller and service\n * in the app sees the typed extended values.\n */\nexport const env = loadEnvFromSchema(envSchema)\n\nexport default envSchema\n`\n }\n\n // zod (default)\n return `import { loadEnvFromSchema } from '@forinda/kickjs/config'\nimport { fromZod } from '@forinda/kickjs-schema/zod'\nimport { z } from 'zod'\n\n/**\n * Project environment schema (Zod).\n *\n * \\`fromZod\\` wraps the Zod schema as a \\`KickSchema\\` so the env loader,\n * validate middleware, and swagger spec generator all see the same\n * shape. The default export is the contract \\`kick typegen\\` reads to\n * populate \\`KickEnv\\` via \\`InferSchemaOutput<typeof _envSchema>\\` —\n * that's what makes \\`@Value('FOO')\\` autocomplete and\n * \\`process.env.FOO\\` typed.\n *\n * @example\n * DATABASE_URL: z.string().url(),\n * JWT_SECRET: z.string().min(32),\n * REDIS_URL: z.string().url().optional(),\n */\nconst envSchema = fromZod(\n z.object({\n PORT: z.coerce.number().default(3000),\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n LOG_LEVEL: z.string().default('info'),\n // DATABASE_URL: z.string().url(),\n }),\n)\n\n/**\n * IMPORTANT — side effect: register the schema with kickjs's env cache\n * **at module-load time**. \\`ConfigService\\` and \\`@Value()\\` both consume\n * this cache, and they will fall back to the base schema (or undefined)\n * if no extended schema has been registered before they're resolved.\n *\n * As long as \\`src/index.ts\\` imports this file (\\`import './config'\\`) at\n * the top — before \\`bootstrap()\\` runs — every controller and service\n * in the app sees the typed extended values.\n */\nexport const env = loadEnvFromSchema(envSchema)\n\nexport default envSchema\n`\n}\n\n/** Generate src/modules/hello/hello.service.ts */\nexport function generateHelloService(): string {\n return `import { Service } from '@forinda/kickjs'\n\n@Service()\nexport class HelloService {\n greet(name: string) {\n return { message: \\`Hello \\${name} from KickJS!\\`, timestamp: new Date().toISOString() }\n }\n\n healthCheck() {\n return { status: 'ok', uptime: process.uptime() }\n }\n}\n`\n}\n\n/** Generate src/modules/hello/hello.controller.ts */\nexport function generateHelloController(): string {\n return `import { Controller, Get, Autowired, type Ctx } from '@forinda/kickjs'\nimport { HelloService } from './hello.service'\n\n// \\`Ctx<KickRoutes.HelloController['<method>']>\\` is generated by\n// \\`kick typegen\\` (auto-run on \\`kick dev\\`). The first run after a fresh\n// scaffold creates \\`.kickjs/types/routes.ts\\` so this file typechecks.\n// See https://kickjs.app/guide/typegen.\n\n@Controller()\nexport class HelloController {\n @Autowired() private readonly helloService!: HelloService\n\n // Return-value handlers: the runtime sends the returned payload as\n // 200 json, and \\`kick typegen\\` infers the response type into\n // \\`KickRoutes.Api\\` — which is what makes the typed client\n // (@forinda/kickjs-client) end-to-end type-safe.\n @Get('/')\n index(_ctx: Ctx<KickRoutes.HelloController['index']>) {\n return this.helloService.greet('World')\n }\n\n @Get('/health')\n health(_ctx: Ctx<KickRoutes.HelloController['health']>) {\n return this.helloService.healthCheck()\n }\n}\n`\n}\n\n/** Generate src/modules/hello/hello.module.ts */\nexport function generateHelloModule(): string {\n return `import { defineModule } from '@forinda/kickjs'\nimport { HelloController } from './hello.controller'\n\nexport const HelloModule = defineModule({\n name: 'HelloModule',\n build: () => ({\n // \\`register(container)\\` is optional — only implement it when you need\n // to bind a token to a concrete implementation, e.g.\n // register(container) {\n // container.registerFactory(USER_REPOSITORY, () => container.resolve(InMemoryUserRepository))\n // }\n // The HelloService uses @Service() so the decorator handles registration.\n\n routes() {\n return {\n path: '/hello',\n controller: HelloController,\n }\n },\n }),\n})\n`\n}\n\n/** Generate kick.config.ts CLI configuration */\nexport function generateKickConfig(\n template: ProjectTemplate,\n defaultRepo: string = 'inmemory',\n packageManager: 'pnpm' | 'npm' | 'yarn' | 'bun' = 'pnpm',\n runtime: 'express' | 'fastify' | 'h3' = 'express',\n): string {\n // `inmemory` is the only built-in; every other name (incl. the\n // deprecated prisma/drizzle) is emitted as a `{ name }` custom repo.\n const repoValue = defaultRepo === 'inmemory' ? `'inmemory'` : `{ name: '${defaultRepo}' }`\n\n return `import { defineConfig } from '@forinda/kickjs-cli'\n\nexport default defineConfig({\n pattern: '${template}',\n // The HTTP engine this app boots on (matches \\`bootstrap({ runtime })\\` in\n // src/index.ts). Dep-aware commands read it: \\`kick add upload\\` installs the\n // engine's multipart driver, \\`kick doctor\\` checks the engine peers, and\n // \\`kick typegen\\` flips the runtime escape-hatch types to this engine.\n runtime: '${runtime}',\n // Pinned so \\`kick add\\` and other dep-installing commands always use the\n // project's intended package manager, regardless of which lockfile exists.\n packageManager: '${packageManager}',\n modules: {\n dir: 'src/modules',\n repo: ${repoValue},\n pluralize: true,\n },\n\n // \\`kick typegen\\` populates \\`.kickjs/types/\\` so \\`Ctx<KickRoutes.X['method']>\\`\n // resolves to fully-typed params/body/query. Auto-runs on \\`kick dev\\`.\n // \\`'kickjs-schema'\\` routes inference through \\`InferSchemaOutput\\` so the\n // typegen works for any wrapped schema (Zod / Valibot / Yup). Switch\n // to \\`'zod'\\` if you ship Zod schemas without \\`fromZod()\\` wrapping, or\n // set \\`schemaValidator: false\\` to skip schema-driven body typing.\n typegen: {\n schemaValidator: 'kickjs-schema',\n },\n\n commands: [\n {\n name: 'test',\n description: 'Run tests with Vitest',\n steps: 'npx vitest run',\n },\n {\n name: 'format',\n description: 'Format code with Prettier',\n steps: 'npx prettier --write src/',\n },\n {\n name: 'format:check',\n description: 'Check formatting without writing',\n steps: 'npx prettier --check src/',\n },\n {\n name: 'ci:check',\n description: 'Run typecheck + format check',\n steps: ['npx tsc --noEmit', 'npx prettier --check src/'],\n aliases: ['verify'],\n },\n ],\n})\n`\n}\n","type ProjectTemplate = 'rest' | 'minimal'\n\n/**\n * Supported schema libraries — passed through to `fromZod` /\n * `fromValibot` / `fromYup` in the generated env file. `zod` is the\n * default for `--yes` because it has the deepest ecosystem\n * compatibility (OpenAPI generation, Standard Schema brand for\n * `kick typegen`).\n */\nexport type SchemaLib = 'zod' | 'valibot' | 'yup'\n\n/** Map of optional package names to their npm package identifiers */\nconst PACKAGE_DEPS: Record<string, string> = {\n swagger: '@forinda/kickjs-swagger',\n ws: '@forinda/kickjs-ws',\n queue: '@forinda/kickjs-queue',\n devtools: '@forinda/kickjs-devtools',\n}\n\n/** Schema-lib runtime dependency ranges. Pinned to a recent release. */\nconst SCHEMA_LIB_DEPS: Record<SchemaLib, { name: string; range: string }> = {\n zod: { name: 'zod', range: '^4.3.6' },\n valibot: { name: 'valibot', range: '^1.4.1' },\n yup: { name: 'yup', range: '^1.7.1' },\n}\n\n/**\n * Map of package name → semver range string (`^x.y.z`). Resolved\n * from `npm view <name> version` upstream so per-package independent\n * versioning is honoured at scaffold time. Every sibling\n * `@forinda/kickjs-*` package we might add to the new project must\n * appear here; missing keys throw during package.json generation\n * (loud failure beats silently shipping `^undefined`).\n */\nexport type SiblingVersions = Record<string, string>\n\nfunction take(versions: SiblingVersions, name: string): string {\n const v = versions[name]\n if (!v) {\n throw new Error(\n `generatePackageJson: missing resolved version for ${name}. ` +\n `Add it to SIBLING_PACKAGES in generators/project.ts.`,\n )\n }\n return v\n}\n\n/** Generate package.json with template-aware dependencies */\nexport function generatePackageJson(\n name: string,\n template: ProjectTemplate,\n versions: SiblingVersions,\n packages: string[] = [],\n schemaLib: SchemaLib = 'zod',\n runtime: 'express' | 'fastify' | 'h3' = 'express',\n): string {\n const schemaDep = SCHEMA_LIB_DEPS[schemaLib]\n const baseDeps: Record<string, string> = {\n '@forinda/kickjs': take(versions, '@forinda/kickjs'),\n // The schema-agnostic abstraction kickjs-schema wraps zod / valibot\n // / yup behind a single `KickSchema` interface — env validation,\n // body validation, and swagger spec generation all flow through\n // `detectSchema()`. Shipping it as a direct dep (rather than a peer)\n // keeps the new-project install one-step.\n '@forinda/kickjs-schema': take(versions, '@forinda/kickjs-schema'),\n // `dotenv` is an optional peer of @forinda/kickjs — scaffolded apps\n // get it pre-installed so `.env` files Just Work. Apps that load\n // env from the shell or a secret manager can drop this safely.\n dotenv: '^17.3.1',\n 'reflect-metadata': '^0.2.2',\n [schemaDep.name]: schemaDep.range,\n }\n\n // Engine peers for the chosen runtime (optional peers of @forinda/kickjs).\n if (runtime === 'express') {\n // Express is the engine itself.\n baseDeps.express = '^5.1.0'\n } else if (runtime === 'fastify') {\n baseDeps.fastify = '^5.0.0'\n baseDeps['@fastify/middie'] = '^9.0.0'\n // Static serving uses `serve-static` (no express dependency).\n baseDeps['serve-static'] = '^2.2.0'\n } else if (runtime === 'h3') {\n baseDeps.h3 = '^1.0.0'\n baseDeps['serve-static'] = '^2.2.0'\n }\n\n // Add user-selected optional packages — each looked up against\n // the resolved version map so they're independently up-to-date.\n for (const pkg of packages) {\n const dep = PACKAGE_DEPS[pkg]\n if (dep && !baseDeps[dep]) {\n baseDeps[dep] = take(versions, dep)\n }\n }\n\n return JSON.stringify(\n {\n name,\n // Project starts at 0.0.0 — adopters bump as they ship. Tying\n // the project version to the CLI version (the previous\n // behaviour) made every scaffolded app `5.4.0` on day one,\n // which broke npm publishing for adopters trying their first\n // release.\n version: '0.0.0',\n type: 'module',\n scripts: {\n // `kick dev` (not bare `vite`): it boots Vite itself AND owns the\n // typegen-on-save watcher. Plain `vite` gives working HMR but\n // frozen `.kickjs/types` — new routes silently lose their typing\n // until a manual `kick typegen`.\n dev: 'kick dev',\n 'dev:debug': 'kick dev:debug',\n build: 'kick build',\n start: 'kick start',\n test: 'vitest run',\n 'test:watch': 'vitest',\n typecheck: 'tsc --noEmit',\n typegen: 'kick typegen',\n lint: 'eslint src/',\n format: 'prettier --write src/',\n },\n dependencies: baseDeps,\n devDependencies: {\n '@forinda/kickjs-cli': take(versions, '@forinda/kickjs-cli'),\n '@forinda/kickjs-vite': take(versions, '@forinda/kickjs-vite'),\n '@swc/core': '^1.15.21',\n // Express types only when Express is the engine (it's the only runtime\n // that imports `express` in src/index.ts).\n ...(runtime === 'express' ? { '@types/express': '^5.0.6' } : {}),\n '@types/node': '^25.0.0',\n 'unplugin-swc': '^1.5.9',\n vite: '^8.0.3',\n vitest: '^4.1.2',\n typescript: '^6.0.3',\n prettier: '^3.8.1',\n },\n },\n null,\n 2,\n )\n}\n\n/**\n * Generate vite.config.ts with the KickJS Vite plugin.\n *\n * The plugin handles:\n * - SSR environment setup for backend Node.js code\n * - Virtual module generation (virtual:kickjs/app)\n * - Module auto-discovery (scans *.module.ts files)\n * - HMR with selective container invalidation\n * - Express mounting via configureServer() post-hook\n * - httpServer piping to adapters (WsAdapter, Socket.IO, etc.)\n */\nexport function generateViteConfig(): string {\n return `import { defineConfig } from 'vite'\nimport { resolve } from 'node:path'\nimport swc from 'unplugin-swc'\nimport { kickjsVitePlugin, envWatchPlugin } from '@forinda/kickjs-vite'\n\nexport default defineConfig({\n oxc: false,\n plugins: [\n swc.vite(),\n kickjsVitePlugin({ entry: 'src/index.ts' }),\n // Watches .env files and triggers a full reload on change so the\n // dev server picks up env tweaks without a manual restart.\n envWatchPlugin(),\n ],\n resolve: {\n alias: {\n '@': resolve(__dirname, 'src'),\n },\n },\n build: {\n target: 'node20',\n ssr: true,\n outDir: 'dist',\n sourcemap: true,\n rollupOptions: {\n input: resolve(__dirname, 'src/index.ts'),\n output: { format: 'esm' },\n },\n },\n})\n`\n}\n\n/** Generate tsconfig.json with decorator support */\nexport function generateTsConfig(): string {\n return JSON.stringify(\n {\n compilerOptions: {\n target: 'ES2022',\n module: 'ESNext',\n moduleResolution: 'bundler',\n lib: ['ES2022'],\n types: ['node', 'vite/client'],\n strict: true,\n esModuleInterop: true,\n skipLibCheck: true,\n sourceMap: true,\n declaration: true,\n experimentalDecorators: true,\n emitDecoratorMetadata: true,\n outDir: 'dist',\n // rootDir omitted so .kickjs/types/*.d.ts can sit outside src/\n paths: { '@/*': ['./src/*'] },\n },\n // .kickjs/types is generated by `kick typegen` and refreshed\n // automatically on `kick dev`. Including it here makes\n // `container.resolve()` and module discovery type-safe.\n // Both .d.ts and .ts are matched: registry/services/modules are\n // declarations, but routes.ts holds resolvable imports from your\n // controllers' Zod schemas (TS silently degrades inline `import('...')`\n // inside `.d.ts` files under `moduleResolution: 'bundler'`).\n include: ['src', '.kickjs/types/**/*.d.ts', '.kickjs/types/**/*.ts'],\n },\n null,\n 2,\n )\n}\n\n/** Generate .prettierrc with project formatting rules */\nexport function generatePrettierConfig(): string {\n return JSON.stringify(\n {\n semi: false,\n singleQuote: true,\n trailingComma: 'all',\n printWidth: 100,\n tabWidth: 2,\n },\n null,\n 2,\n )\n}\n\n/** Generate .editorconfig for consistent editor settings */\nexport function generateEditorConfig(): string {\n return `# https://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n`\n}\n\n/** Generate .gitignore with common Node.js patterns */\nexport function generateGitIgnore(): string {\n return `node_modules/\ndist/\n.env\ncoverage/\n.DS_Store\n*.tsbuildinfo\n.kickjs/\n`\n}\n\n/** Generate .gitattributes for consistent line endings */\nexport function generateGitAttributes(): string {\n return `# Auto-detect text files and normalise line endings to LF\n* text=auto eol=lf\n\n# Explicitly mark generated / binary files\n*.png binary\n*.jpg binary\n*.jpeg binary\n*.gif binary\n*.ico binary\n*.woff binary\n*.woff2 binary\n*.ttf binary\n*.eot binary\n\n# Lock files — treat as generated\npnpm-lock.yaml -diff linguist-generated\nyarn.lock -diff linguist-generated\npackage-lock.json -diff linguist-generated\n`\n}\n\n/** Generate .env file with default environment variables */\nexport function generateEnv(): string {\n return `PORT=3000\nNODE_ENV=development\n`\n}\n\n/** Generate .env.example file as a template */\nexport function generateEnvExample(): string {\n return `PORT=3000\nNODE_ENV=development\n`\n}\n\n/** Generate vitest.config.ts for test configuration */\nexport function generateVitestConfig(): string {\n return `import { defineConfig } from 'vitest/config'\nimport swc from 'unplugin-swc'\n\nexport default defineConfig({\n plugins: [swc.vite()],\n test: {\n globals: true,\n environment: 'node',\n include: ['src/**/*.test.ts'],\n },\n})\n`\n}\n","import { execSync } from 'node:child_process'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport type { Command } from 'commander'\nimport { loadKickConfig, PACKAGE_MANAGERS, type PackageManager } from '../config'\n\ninterface PackageEntry {\n pkg: string\n peers: string[]\n description: string\n dev?: boolean\n /**\n * `true` for packages every project needs (framework + Vite plugin +\n * CLI). `kick new` installs these regardless of options chosen, and\n * future package-removal flows refuse to drop them.\n */\n core?: boolean\n /**\n * Set when the package still installs but should no longer be the\n * default choice. The string is the migration hint shown both in\n * `kick add --list --all` and as a warning when the package is added.\n */\n deprecated?: string\n}\n\n/** Registry of KickJS packages and their required peer dependencies */\nexport const PACKAGE_REGISTRY: Record<string, PackageEntry> = {\n // Core (always installed by kick new — required for the framework to run)\n kickjs: {\n pkg: '@forinda/kickjs',\n peers: ['express'],\n description: 'Unified framework: DI, decorators, routing, middleware',\n core: true,\n },\n vite: {\n pkg: '@forinda/kickjs-vite',\n peers: ['vite'],\n description: 'Vite plugin: dev server, HMR, module discovery',\n dev: true,\n core: true,\n },\n cli: {\n pkg: '@forinda/kickjs-cli',\n peers: [],\n description: 'CLI tool and code generators',\n dev: true,\n core: true,\n },\n\n // Schema validation — the validator backing env + DTO + OpenAPI\n // schemas. `@forinda/kickjs-schema` (a core dep) wraps whichever one\n // you pick behind `KickSchema`, but the validator itself is an\n // optional peer of `@forinda/kickjs`, so it must be installed\n // explicitly or the app errors at startup (\"Cannot find module\n // 'zod'\"). `kick new` installs the chosen one; `kick add` lets an\n // existing project add/switch.\n zod: {\n pkg: 'zod',\n peers: [],\n description: 'Zod schema validation (env, DTOs, OpenAPI) — wrap with fromZod()',\n },\n valibot: {\n pkg: 'valibot',\n peers: [],\n description: 'Valibot schema validation — wrap with fromValibot()',\n },\n yup: {\n pkg: 'yup',\n peers: [],\n description: 'Yup schema validation — wrap with fromYup()',\n },\n\n // Auth — deprecated in favour of BYO (bring-your-own) auth composed\n // from context contributors. Still installable for existing projects;\n // JWT is the common path, so it co-installs jsonwebtoken.\n auth: {\n pkg: '@forinda/kickjs-auth',\n peers: ['jsonwebtoken'],\n description: 'JWT, API key, OAuth strategies, @Public, @Roles (+ optional argon2/bcryptjs)',\n deprecated:\n 'auth is moving to BYO — compose @LoadAuthUser/@RequireRole/@Public from defineContextDecorator (see the BYO Auth recipe in the docs)',\n },\n\n // AI — requires zod (^4) for tool/schema definitions.\n ai: {\n pkg: '@forinda/kickjs-ai',\n peers: ['zod'],\n description: 'AI toolkit — LLM providers, tool definitions from controllers',\n },\n\n // API\n swagger: {\n pkg: '@forinda/kickjs-swagger',\n peers: [],\n description: 'OpenAPI spec + Swagger UI + ReDoc',\n },\n // Database — the dialect adapters now ship as subpaths of\n // `@forinda/kickjs-db` (`/pg`, `/sqlite`, `/mysql`), so each `kick add`\n // pulls the core package plus the one driver you need.\n db: {\n pkg: '@forinda/kickjs-db',\n peers: [],\n description: 'kick/db core — schema DSL, migrations, KickDbClient, customType',\n },\n pg: {\n pkg: '@forinda/kickjs-db',\n peers: ['pg'],\n description: 'kick/db + PostgreSQL driver (use @forinda/kickjs-db/pg)',\n },\n sqlite: {\n pkg: '@forinda/kickjs-db',\n peers: ['better-sqlite3'],\n description: 'kick/db + SQLite driver (use @forinda/kickjs-db/sqlite)',\n },\n mysql: {\n pkg: '@forinda/kickjs-db',\n peers: ['mysql2'],\n description: 'kick/db + MySQL driver (use @forinda/kickjs-db/mysql)',\n },\n drizzle: {\n pkg: '@forinda/kickjs-drizzle',\n peers: ['drizzle-orm'],\n description: 'Drizzle ORM adapter + query builder',\n deprecated:\n 'early-adoption adapter, no longer maintained — wire Drizzle directly (BYO), or use @forinda/kickjs-db, the built-in Kick ORM (`kick add db` / pg / sqlite / mysql)',\n },\n prisma: {\n pkg: '@forinda/kickjs-prisma',\n peers: ['@prisma/client'],\n description: 'Prisma adapter + query builder',\n deprecated:\n 'early-adoption adapter, no longer maintained — wire Prisma directly (BYO), or use @forinda/kickjs-db, the built-in Kick ORM (`kick add db` / pg / sqlite / mysql)',\n },\n\n // Real-time\n ws: {\n pkg: '@forinda/kickjs-ws',\n peers: ['ws'],\n description: 'WebSocket with @WsController decorators',\n },\n\n // DevTools\n devtools: {\n pkg: '@forinda/kickjs-devtools',\n peers: [],\n description: 'Development dashboard — routes, DI, metrics, health',\n dev: true,\n },\n\n // Queue\n queue: {\n pkg: '@forinda/kickjs-queue',\n peers: [],\n description: 'Queue adapter (BullMQ/RabbitMQ/Kafka)',\n },\n 'queue:bullmq': {\n pkg: '@forinda/kickjs-queue',\n peers: ['bullmq', 'ioredis'],\n description: 'Queue with BullMQ + Redis',\n },\n 'queue:rabbitmq': {\n pkg: '@forinda/kickjs-queue',\n peers: ['amqplib'],\n description: 'Queue with RabbitMQ',\n },\n 'queue:kafka': {\n pkg: '@forinda/kickjs-queue',\n peers: ['kafkajs'],\n description: 'Queue with Kafka',\n },\n 'queue:redis-pubsub': {\n pkg: '@forinda/kickjs-queue',\n peers: ['ioredis'],\n description: 'Lightweight pub/sub via Redis (no persistence)',\n },\n\n // MCP — Model Context Protocol server\n mcp: {\n pkg: '@forinda/kickjs-mcp',\n peers: ['@modelcontextprotocol/sdk'],\n description: 'Model Context Protocol server — expose @Controller endpoints as AI tools',\n },\n\n // Testing\n testing: {\n pkg: '@forinda/kickjs-testing',\n peers: [],\n description: 'Test utilities and TestModule builder',\n dev: true,\n },\n}\n\n/**\n * Headline `kick add` packages shown after scaffolding — derived from\n * {@link PACKAGE_REGISTRY} so it can never advertise a deprecated package (the\n * old hardcoded list included auth / drizzle / prisma). Excludes core packages\n * (already installed), deprecated ones, `:` sub-variants (e.g. `queue:bullmq`),\n * and the db-dialect / schema-lib duplicates that clutter a one-line summary.\n * `kick add --list` shows the full catalog.\n */\nexport const AVAILABLE_ADD_PACKAGES = Object.entries(PACKAGE_REGISTRY)\n .filter(\n ([name, entry]) =>\n !entry.core &&\n !entry.deprecated &&\n !name.includes(':') &&\n !['pg', 'sqlite', 'mysql', 'zod', 'valibot', 'yup'].includes(name),\n )\n .map(([name]) => name)\n .join(', ')\n\n/**\n * The `upload` catalog name is special — file uploads ship inside\n * `@forinda/kickjs` itself, so there's no package to install. What an app\n * needs is the multipart DRIVER for its HTTP runtime, and that differs per\n * engine. `planAddPackages` resolves `upload` against the configured runtime\n * (see {@link KickConfig.runtime}); `kick doctor` validates the same mapping.\n */\nexport const UPLOAD_DRIVERS: Record<\n 'express' | 'fastify' | 'h3',\n { prod?: string; dev?: string; note: string }\n> = {\n express: {\n prod: 'multer',\n dev: '@types/multer',\n note: 'Express uploads use multer (memory/disk storage, ctx.file / ctx.files).',\n },\n fastify: {\n prod: '@fastify/multipart',\n note: 'Fastify uploads use @fastify/multipart (buffered into ctx.file / ctx.files).',\n },\n h3: {\n note: 'h3 parses multipart natively (readMultipartFormData) — no driver to install.',\n },\n}\n\nexport type AppRuntime = 'express' | 'fastify' | 'h3'\n\n/**\n * Resolve the project's HTTP runtime: the `runtime` field in kick.config\n * (authoritative — `kick new` writes it), falling back to sniffing installed\n * deps in the nearest package.json (`fastify` → fastify, `h3` → h3), else\n * `express` (the default engine). Lets `kick add upload` / `kick doctor` pick\n * the engine-correct multipart driver even in projects scaffolded before the\n * `runtime` field existed.\n */\nexport async function resolveAppRuntime(cwd = process.cwd()): Promise<AppRuntime> {\n const config = await loadKickConfig(cwd)\n const fromConfig = (config as { runtime?: AppRuntime } | null)?.runtime\n if (fromConfig === 'express' || fromConfig === 'fastify' || fromConfig === 'h3') {\n return fromConfig\n }\n return detectRuntimeFromDeps(cwd)\n}\n\n/** Sniff the runtime from installed deps when kick.config has no `runtime`. */\nexport function detectRuntimeFromDeps(cwd = process.cwd()): AppRuntime {\n const dir = findUp('package.json', cwd)\n if (dir) {\n try {\n const pkg = JSON.parse(readFileSync(resolve(dir, 'package.json'), 'utf-8'))\n const deps = { ...pkg.dependencies, ...pkg.devDependencies } as Record<string, unknown>\n if ('fastify' in deps) return 'fastify'\n if ('h3' in deps) return 'h3'\n } catch {\n // ignore — fall through to the default engine\n }\n }\n return 'express'\n}\n\n/**\n * Walk up from `fromDir` to filesystem root, returning the first\n * directory that contains `name`. Lets monorepo sub-packages pick up\n * lockfiles and `packageManager` fields living at the workspace root.\n */\nfunction findUp(name: string, fromDir = process.cwd()): string | null {\n let current = fromDir\n while (true) {\n if (existsSync(resolve(current, name))) return current\n const parent = dirname(current)\n if (parent === current) return null\n current = parent\n }\n}\n\nfunction detectFromLockfile(): PackageManager | null {\n if (findUp('pnpm-lock.yaml')) return 'pnpm'\n if (findUp('yarn.lock')) return 'yarn'\n if (findUp('bun.lockb') || findUp('bun.lock')) return 'bun'\n if (findUp('package-lock.json')) return 'npm'\n return null\n}\n\n/**\n * Read `packageManager` from the nearest ancestor `package.json` that\n * declares the field (corepack convention: `\"pnpm@10.0.0\"`). Climbs so\n * monorepo sub-packages inherit the workspace pm even when their own\n * package.json omits the field.\n */\nfunction packageManagerFromPackageJson(): PackageManager | null {\n let dir: string | null = process.cwd()\n while (dir) {\n const pkgPath = resolve(dir, 'package.json')\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n const field: unknown = pkg.packageManager\n if (typeof field === 'string') {\n const name = field.split('@')[0] as PackageManager\n if (PACKAGE_MANAGERS.includes(name)) return name\n }\n } catch {\n // ignore — keep climbing\n }\n }\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n return null\n}\n\nexport type PackageManagerSource = 'flag' | 'config' | 'package.json' | 'lockfile' | 'default'\n\n/**\n * Resolve which package manager to use, in priority order:\n * 1. `--pm` CLI flag\n * 2. `packageManager` in kick.config\n * 3. `packageManager` in nearest ancestor package.json (corepack)\n * 4. Nearest ancestor lockfile (pnpm-lock.yaml → yarn.lock → bun.lock → package-lock.json)\n * 5. `'npm'` fallback\n *\n * Returns the chosen pm plus the source for callers that want to log\n * the resolution path.\n */\nexport async function resolvePackageManagerWithSource(\n flagPm: string | undefined,\n): Promise<{ pm: PackageManager; source: PackageManagerSource }> {\n if (flagPm && PACKAGE_MANAGERS.includes(flagPm as PackageManager)) {\n return { pm: flagPm as PackageManager, source: 'flag' }\n }\n\n const config = await loadKickConfig(process.cwd())\n if (config?.packageManager && PACKAGE_MANAGERS.includes(config.packageManager)) {\n return { pm: config.packageManager, source: 'config' }\n }\n\n const fromPkg = packageManagerFromPackageJson()\n if (fromPkg) return { pm: fromPkg, source: 'package.json' }\n\n const fromLock = detectFromLockfile()\n if (fromLock) return { pm: fromLock, source: 'lockfile' }\n\n return { pm: 'npm', source: 'default' }\n}\n\n/** Convenience wrapper for callers that don't care about the source. */\nexport async function resolvePackageManager(flagPm: string | undefined): Promise<PackageManager> {\n const { pm } = await resolvePackageManagerWithSource(flagPm)\n return pm\n}\n\n/**\n * Print the package catalog. By default shows just the three core\n * packages every project always has — the optional list churns\n * (packages added, deprecated, removed) and a long enumeration in CLI\n * output / docs goes stale within a release. Pass `all = true` to dump\n * everything; that's what `kick add --list --all` triggers when an\n * adopter genuinely wants the live catalog.\n */\nexport function printPackageList(all = false): void {\n const entries = Object.entries(PACKAGE_REGISTRY)\n const maxName = Math.max(...entries.map(([k]) => k.length))\n const core = entries.filter(([, info]) => info.core)\n const optional = entries.filter(([, info]) => !info.core)\n\n const formatRow = ([name, info]: [string, PackageEntry]): string => {\n const padded = name.padEnd(maxName + 2)\n const peers = info.peers.length ? ` (+ ${info.peers.join(', ')})` : ''\n const deprecated = info.deprecated ? ` [DEPRECATED — ${info.deprecated}]` : ''\n return ` ${padded} ${info.description}${peers}${deprecated}`\n }\n\n console.log('\\n Core packages (always installed by `kick new`):\\n')\n for (const row of core) console.log(formatRow(row))\n\n if (all) {\n console.log('\\n Optional packages (add as needed):\\n')\n for (const row of optional) console.log(formatRow(row))\n } else {\n console.log(`\\n Plus ${optional.length} optional packages (auth, swagger, db, queue, …).`)\n console.log(' Run `kick add --list --all` for the full catalog.')\n }\n\n console.log('\\n Usage: kick add ai db swagger')\n console.log(' kick add queue:bullmq')\n console.log(' kick add upload # installs the multipart driver for your runtime')\n console.log()\n}\n\nexport interface AddPlan {\n prodDeps: string[]\n devDeps: string[]\n unknown: string[]\n /** Deprecation notices for requested entries — print, then install anyway. */\n warnings: string[]\n /** Informational notes (e.g. the upload driver chosen for the runtime). */\n notices: string[]\n}\n\n/**\n * Pure resolution step for `kick add` — maps requested catalog names to\n * the npm packages (plus peers) to install, split prod/dev. Kept free\n * of I/O so the catalog rules (dev defaults, deprecations, unknown\n * handling) are unit-testable without spawning a package manager.\n */\nexport function planAddPackages(\n packages: string[],\n forceDev: boolean,\n runtime: AppRuntime = 'express',\n): AddPlan {\n const prodDeps = new Set<string>()\n const devDeps = new Set<string>()\n const unknown: string[] = []\n const warnings: string[] = []\n const notices: string[] = []\n\n for (const name of packages) {\n // `upload` isn't a package — it's the runtime's multipart driver. File\n // uploads ship in @forinda/kickjs; only the engine backend needs adding.\n if (name === 'upload') {\n const driver = UPLOAD_DRIVERS[runtime]\n notices.push(`upload (${runtime}): ${driver.note}`)\n if (driver.prod) (forceDev ? devDeps : prodDeps).add(driver.prod)\n if (driver.dev) devDeps.add(driver.dev)\n continue\n }\n\n const entry = PACKAGE_REGISTRY[name]\n if (!entry) {\n unknown.push(name)\n continue\n }\n if (entry.deprecated) {\n warnings.push(`'${name}' (${entry.pkg}) is deprecated — ${entry.deprecated}`)\n }\n const target = forceDev || entry.dev ? devDeps : prodDeps\n target.add(entry.pkg)\n for (const peer of entry.peers) {\n target.add(peer)\n }\n }\n\n return { prodDeps: [...prodDeps], devDeps: [...devDeps], unknown, warnings, notices }\n}\n\nexport function registerListCommand(program: Command): void {\n program\n .command('list')\n .alias('ls')\n .description('List KickJS packages (core only; pair with --all for the full catalog)')\n .option('--all', 'Include the full optional catalog')\n .action((opts: { all?: boolean }) => {\n printPackageList(Boolean(opts.all))\n })\n}\n\nexport function registerAddCommand(program: Command): void {\n program\n .command('add [packages...]')\n .description('Add KickJS packages with their required dependencies')\n .option('--pm <manager>', 'Package manager override')\n .option('-D, --dev', 'Install as dev dependency')\n .option('--list', 'List packages (core only by default; pair with --all)')\n .option('--all', 'When listing, include the full optional catalog')\n .action(async (packages: string[], opts: any) => {\n // List mode\n if (opts.list || packages.length === 0) {\n printPackageList(Boolean(opts.all))\n return\n }\n\n const { pm, source } = await resolvePackageManagerWithSource(opts.pm)\n console.log(`\\n Using ${pm} (resolved from ${source})`)\n // Resolve the runtime so `kick add upload` installs the right multipart\n // driver (express → multer, fastify → @fastify/multipart, h3 → none).\n const runtime = await resolveAppRuntime(process.cwd())\n const { prodDeps, devDeps, unknown, warnings, notices } = planAddPackages(\n packages,\n Boolean(opts.dev),\n runtime,\n )\n\n for (const warning of warnings) {\n console.warn(`\\n WARNING: ${warning}`)\n }\n\n for (const notice of notices) {\n console.log(`\\n ${notice}`)\n }\n\n if (unknown.length > 0) {\n console.log(`\\n Unknown packages: ${unknown.join(', ')}`)\n console.log(' Run \"kick add --list\" to see available packages.\\n')\n if (prodDeps.length === 0 && devDeps.length === 0) return\n }\n\n // Install production dependencies\n if (prodDeps.length > 0) {\n const deps = prodDeps\n const cmd = `${pm} add ${deps.join(' ')}`\n console.log(`\\n Installing ${deps.length} dependency(ies):`)\n for (const dep of deps) console.log(` + ${dep}`)\n console.log()\n try {\n execSync(cmd, { stdio: 'inherit' })\n } catch {\n console.log(`\\n Installation failed. Run manually:\\n ${cmd}\\n`)\n }\n }\n\n // Install dev dependencies\n if (devDeps.length > 0) {\n const deps = devDeps\n const cmd = `${pm} add -D ${deps.join(' ')}`\n console.log(`\\n Installing ${deps.length} dev dependency(ies):`)\n for (const dep of deps) console.log(` + ${dep} (dev)`)\n console.log()\n try {\n execSync(cmd, { stdio: 'inherit' })\n } catch {\n console.log(`\\n Installation failed. Run manually:\\n ${cmd}\\n`)\n }\n }\n\n console.log(' Done!\\n')\n })\n}\n","import { join, dirname } from 'node:path'\nimport { execFileSync, execSync } from 'node:child_process'\nimport { readFileSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { writeFileSafe } from '../utils/fs'\nimport {\n generatePackageJson,\n generateViteConfig,\n generateTsConfig,\n generatePrettierConfig,\n generateEditorConfig,\n generateGitIgnore,\n generateGitAttributes,\n generateEnv,\n generateEnvExample,\n generateVitestConfig,\n} from './templates/project-config'\nimport {\n generateEntryFile,\n generateEnvFile,\n generateModulesIndex,\n generateKickConfig,\n generateHelloService,\n generateHelloController,\n generateHelloModule,\n} from './templates/project-app'\nimport { generateReadme } from './templates/project-docs'\nimport { AVAILABLE_ADD_PACKAGES } from '../commands/add'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\nconst cliPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))\nconst CLI_VERSION_FALLBACK = `^${cliPkg.version}`\n\n/**\n * Sibling `@forinda/kickjs-*` packages whose versions are resolved\n * independently when scaffolding a new project. Each entry is queried\n * via `npm view <name> version`; failure falls back to the CLI's own\n * version (`CLI_VERSION_FALLBACK`).\n *\n * Per-package independent versioning landed with changesets — before\n * that, every sibling shipped in lockstep with the CLI so a single\n * pin was correct. Now `@forinda/kickjs@5.5.0` may pair with\n * `@forinda/kickjs-cli@5.4.2` and `@forinda/kickjs-swagger@5.3.1`;\n * pinning them all to the CLI's version under-installs adopters.\n */\nconst SIBLING_PACKAGES = [\n '@forinda/kickjs',\n '@forinda/kickjs-cli',\n '@forinda/kickjs-schema',\n '@forinda/kickjs-vite',\n '@forinda/kickjs-swagger',\n '@forinda/kickjs-ws',\n '@forinda/kickjs-queue',\n '@forinda/kickjs-devtools',\n '@forinda/kickjs-testing',\n '@forinda/kickjs-client',\n] as const\n\n/**\n * Resolve the latest published version of every sibling package via\n * `npm view <name> version` (via execFileSync — no shell, no\n * injection vector). Each query has a short timeout; failures fall\n * back to the CLI's own version with a `^` prefix so the scaffold\n * stays usable offline.\n */\nexport async function resolveSiblingVersions(): Promise<Record<string, string>> {\n const results = await Promise.all(\n SIBLING_PACKAGES.map(async (name) => {\n try {\n const out = execFileSync('npm', ['view', name, 'version'], {\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim()\n if (out && /^\\d+\\.\\d+\\.\\d+/.test(out)) {\n return [name, `^${out}`] as const\n }\n } catch {\n // Network failure / package not yet published / npm\n // unavailable. Fall back to CLI version below.\n }\n return [name, CLI_VERSION_FALLBACK] as const\n }),\n )\n return Object.fromEntries(results)\n}\n\n/**\n * Resolve the published version of a package at a given dist-tag\n * (`npm view <name>@<tag> version`). Returns `null` on any failure. Used\n * to pin `@forinda/kickjs` to the `alpha` channel when scaffolding a\n * Fastify / h3 app — the engine subpaths (`@forinda/kickjs/fastify`,\n * `/h3`) ship only on the alpha until the runtimes land in a stable\n * release, so the default `latest` resolution would install a kickjs\n * that doesn't export them (→ Vite \"./h3 is not exported\" at boot).\n * Returns the bare version; the caller applies a `^` range so the project\n * floats to newer alphas and auto-graduates to stable (a caret over a\n * prerelease matches same-tuple prereleases ≥ it, plus later stables `< next\n * major`).\n */\nfunction resolveVersionAtTag(name: string, tag: string): string | null {\n try {\n const out = execFileSync('npm', ['view', `${name}@${tag}`, 'version'], {\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim()\n return out && /^\\d+\\.\\d+\\.\\d+/.test(out) ? out : null\n } catch {\n return null\n }\n}\n\n/**\n * Whether the package at a given dist-tag exports a subpath (e.g. `./h3`).\n * Reads the `exports` map via `npm view <name>@<tag> exports --json`. Used to\n * gate the alpha-pin: if `latest` already ships the engine subpath, the runtime\n * has graduated to stable and we should NOT downgrade to an older alpha.\n * Returns `false` on any failure (missing field / network / unparseable) so the\n * caller treats \"unknown\" as \"not present\" and falls through to the alpha path.\n */\n/** Strip a leading range operator (`^1.2.3` / `~1.2.3` → `1.2.3`). */\nfunction stripRange(range: string | undefined): string {\n return (range ?? '').replace(/^[\\^~>=<\\s]+/, '')\n}\n\n/**\n * Compare the release cores (major.minor.patch, ignoring any `-prerelease`\n * suffix) of two versions: is `a` >= `b`? Used to guard the alpha-pin so a\n * package is never downgraded onto a stale prerelease whose stable line has\n * already moved past it. A coarse compare is enough here — we only need\n * \"is this alpha at least as new as the stable we'd otherwise install\".\n */\nfunction baseVersionGte(a: string, b: string): boolean {\n const core = (v: string): number[] =>\n stripRange(v)\n .split('-')[0]!\n .split('.')\n .map((n) => Number.parseInt(n, 10) || 0)\n const [a0 = 0, a1 = 0, a2 = 0] = core(a)\n const [b0 = 0, b1 = 0, b2 = 0] = core(b)\n if (a0 !== b0) return a0 > b0\n if (a1 !== b1) return a1 > b1\n return a2 >= b2\n}\n\nfunction tagExportsSubpath(name: string, tag: string, subpath: string): boolean {\n try {\n const out = execFileSync('npm', ['view', `${name}@${tag}`, 'exports', '--json'], {\n encoding: 'utf-8',\n timeout: 5000,\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .trim()\n if (!out) return false\n const exportsMap = JSON.parse(out) as Record<string, unknown>\n return Object.prototype.hasOwnProperty.call(exportsMap, subpath)\n } catch {\n return false\n }\n}\n\ntype ProjectTemplate = 'rest' | 'minimal'\ntype SchemaLib = 'zod' | 'valibot' | 'yup'\n\ninterface InitProjectOptions {\n name: string\n directory: string\n packageManager?: 'pnpm' | 'npm' | 'yarn' | 'bun'\n initGit?: boolean\n installDeps?: boolean\n template?: ProjectTemplate\n defaultRepo?: string\n packages?: string[]\n /** Schema library to scaffold env / DTOs with. Defaults to `zod`. */\n schemaLib?: SchemaLib\n /** HTTP engine to scaffold. Defaults to `express`. */\n runtime?: 'express' | 'fastify' | 'h3'\n}\n\n/** Scaffold a new KickJS project */\nexport async function initProject(options: InitProjectOptions): Promise<void> {\n const {\n name,\n directory,\n packageManager = 'pnpm',\n template = 'rest',\n defaultRepo = 'inmemory',\n packages = [],\n schemaLib = 'zod',\n runtime = 'express',\n } = options\n const dir = directory\n\n const log = (msg: string) => console.log(` ${msg}`)\n\n console.log(`\\n Creating KickJS project: ${name}\\n`)\n\n // Resolve published version of every sibling kickjs package in\n // parallel. Per-package independent versioning means\n // `@forinda/kickjs@5.5.0` may pair with `@forinda/kickjs-cli@5.4.2`\n // and `@forinda/kickjs-swagger@5.3.1`; pinning every dep to the\n // CLI's own version under-installs adopters whenever a sibling\n // bumps independently. `npm view` fallback keeps the scaffold\n // working offline.\n log('Resolving package versions...')\n const versions = await resolveSiblingVersions()\n\n // The pluggable-runtimes work (Fastify / h3 engine subpaths, the\n // `kick/runtime` typegen, `kick add upload`, `kick doctor` runtime checks)\n // ships only on the `alpha` channel until it lands in a stable release. So a\n // non-Express scaffold needs the alpha of every package that carries runtime\n // behavior, not just `@forinda/kickjs`:\n // - `@forinda/kickjs` — the `./fastify` / `./h3` export subpaths the\n // app imports (stable lacks them → Vite boot\n // error `\"./h3\" is not exported`).\n // - `@forinda/kickjs-cli` — `--runtime`, `kick add upload`, `kick doctor`,\n // the `kick/runtime` typegen plugin.\n // - `@forinda/kickjs-vite` — the dev loop co-versioned with the above.\n // Gated on whether `@forinda/kickjs@latest` already exports the chosen engine\n // subpath: once that's true the runtimes are stable and we keep `latest` for\n // everything (self-retiring — no code change needed at graduation). Each pin\n // is guarded so it never DOWNGRADES (an alpha can be older than latest — e.g.\n // a package whose stable moved on past an old prerelease). Express is exempt.\n if (runtime !== 'express') {\n const subpath = `./${runtime}` // './fastify' | './h3'\n if (tagExportsSubpath('@forinda/kickjs', 'latest', subpath)) {\n log(`Using @forinda/kickjs@latest (stable ships the ${runtime} runtime)`)\n } else {\n const RUNTIME_PKGS = ['@forinda/kickjs', '@forinda/kickjs-cli', '@forinda/kickjs-vite']\n const pinned: string[] = []\n let kickjsPinned = false\n for (const pkg of RUNTIME_PKGS) {\n const alpha = resolveVersionAtTag(pkg, 'alpha')\n // Only switch when the alpha is newer-or-equal to the stable we'd\n // otherwise install — never downgrade onto a stale prerelease. Use a\n // `^` range (not an exact pin) so the project picks up newer alphas and\n // auto-graduates to the stable release once it ships.\n if (alpha && baseVersionGte(alpha, stripRange(versions[pkg]))) {\n versions[pkg] = `^${alpha}`\n pinned.push(`${pkg}@^${alpha}`)\n if (pkg === '@forinda/kickjs') kickjsPinned = true\n }\n }\n if (kickjsPinned) {\n log(`Using the alpha channel for the ${runtime} runtime: ${pinned.join(', ')}`)\n } else {\n log(\n `WARNING: could not resolve @forinda/kickjs@alpha — the ${runtime} runtime subpath ` +\n `may be missing. After install, run: ${packageManager} add @forinda/kickjs@alpha`,\n )\n }\n }\n }\n\n // ── package.json — template-aware deps ────────────────────────────\n await writeFileSafe(\n join(dir, 'package.json'),\n generatePackageJson(name, template, versions, packages, schemaLib, runtime),\n )\n\n // ── vite.config.ts — enables HMR + SWC for decorators ──────────────\n await writeFileSafe(join(dir, 'vite.config.ts'), generateViteConfig())\n\n // ── tsconfig.json ───────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'tsconfig.json'), generateTsConfig())\n\n // ── .prettierrc ─────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.prettierrc'), generatePrettierConfig())\n\n // ── .editorconfig ─────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.editorconfig'), generateEditorConfig())\n\n // ── .gitignore ──────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.gitignore'), generateGitIgnore())\n\n // ── .gitattributes ────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.gitattributes'), generateGitAttributes())\n\n // ── .env ────────────────────────────────────────────────────────────\n await writeFileSafe(join(dir, '.env'), generateEnv())\n\n await writeFileSafe(join(dir, '.env.example'), generateEnvExample())\n\n // ── src/config/index.ts — typed env schema (read by `kick typegen`) ─\n // Lives under `src/config/` so the framework's \"config\" concept has a\n // single, conventional home. Old projects with `src/env.ts` still\n // work — `detectEnvFile()` searches both locations.\n await writeFileSafe(join(dir, 'src/config/index.ts'), generateEnvFile(schemaLib))\n\n // ── src/index.ts — template-aware entry point ─────────────────────\n await writeFileSafe(\n join(dir, 'src/index.ts'),\n generateEntryFile(name, template, cliPkg.version, packages, runtime),\n )\n\n // ── src/modules/index.ts ────────────────────────────────────────────\n await writeFileSafe(join(dir, 'src/modules/index.ts'), generateModulesIndex())\n\n // ── src/modules/hello/ — sample module ─────────────────────────────\n await writeFileSafe(join(dir, 'src/modules/hello/hello.service.ts'), generateHelloService())\n await writeFileSafe(join(dir, 'src/modules/hello/hello.controller.ts'), generateHelloController())\n await writeFileSafe(join(dir, 'src/modules/hello/hello.module.ts'), generateHelloModule())\n\n // ── kick.config.ts — CLI configuration ─────────────────────────────\n await writeFileSafe(\n join(dir, 'kick.config.ts'),\n generateKickConfig(template, defaultRepo, packageManager, runtime),\n )\n\n // ── vitest.config.ts ────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'vitest.config.ts'), generateVitestConfig())\n\n // ── README.md ────────────────────────────────────────────────────────\n await writeFileSafe(join(dir, 'README.md'), generateReadme(name, template, packageManager))\n\n // ── Agent docs ──────────────────────────────────────────────────────\n // Delegate to `generateAgentDocs()` so `kick new` emits the same\n // `.agents/` subfolder layout as `kick g agents -f`. Otherwise the\n // two paths drifted: kick new was writing the legacy flat layout\n // (root-level AGENTS.md + kickjs-skills.md) while kick g agents\n // emits the per-skill SKILL.md format under .agents/. `force: true`\n // because the project directory is fresh — no overwrite prompts\n // make sense during init.\n const { generateAgentDocs } = await import('./agent-docs')\n await generateAgentDocs({\n outDir: dir,\n name,\n pm: packageManager,\n template,\n only: 'all',\n force: true,\n })\n\n // ── Install Dependencies ────────────────────────────────────────────\n // Install BEFORE git init so the lockfile is included in the first commit.\n if (options.installDeps) {\n console.log(`\\n Installing dependencies with ${packageManager}...\\n`)\n try {\n execSync(`${packageManager} install`, { cwd: dir, stdio: 'inherit' })\n console.log('\\n Dependencies installed successfully!')\n } catch {\n console.log(`\\n Warning: ${packageManager} install failed. Run it manually.`)\n }\n }\n\n // ── Initial typegen ────────────────────────────────────────────────\n // Run typegen once so the freshly-scaffolded HelloController's\n // `Ctx<KickRoutes.HelloController['index']>` references resolve in\n // the user's editor immediately. Failures are non-fatal.\n try {\n const { runTypegen } = await import('../typegen')\n await runTypegen({ cwd: dir, allowDuplicates: true, silent: true })\n } catch {\n // First-run typegen errors are non-fatal — `kick dev` will retry.\n }\n\n // ── Git Init ─────────────────────────────────────────────────────────\n // Runs after install + typegen so lockfile and generated types are\n // included in the initial commit.\n if (options.initGit) {\n try {\n execSync('git init', { cwd: dir, stdio: 'pipe' })\n execSync('git branch -M main', { cwd: dir, stdio: 'pipe' })\n execSync('git add -A', { cwd: dir, stdio: 'pipe' })\n execSync('git commit -m \"chore: initial commit from kick new\"', {\n cwd: dir,\n stdio: 'pipe',\n })\n log('Git repository initialized')\n } catch {\n log('Warning: git init failed (git may not be installed)')\n }\n }\n\n console.log('\\n Project scaffolded successfully!')\n console.log()\n\n const needsCd = dir !== process.cwd()\n log('Next steps:')\n if (needsCd) log(` cd ${name}`)\n if (!options.installDeps) log(` ${packageManager} install`)\n\n const genHint: Record<string, string> = {\n rest: 'kick g module user',\n ddd: 'kick g module user --repo drizzle',\n cqrs: 'kick g module user --pattern cqrs',\n minimal: '# add your routes to src/index.ts',\n }\n log(` ${genHint[template] ?? genHint.rest}`)\n log(' kick dev')\n log('')\n log('Commands:')\n log(' kick dev Start dev server with Vite HMR')\n log(' kick build Production build via Vite')\n log(' kick start Run production build')\n log('')\n log('Generators:')\n log(' kick g module <name> Full DDD module (controller, DTOs, use-cases, repo)')\n log(' kick g scaffold <n> <f..> CRUD module from field definitions')\n log(' kick g controller <name> Standalone controller')\n log(' kick g service <name> @Service() class')\n log(' kick g middleware <name> Express middleware')\n log(' kick g guard <name> Route guard (auth, roles, etc.)')\n log(' kick g adapter <name> AppAdapter with lifecycle hooks')\n log(' kick g dto <name> Zod DTO schema')\n log(' kick g config Generate kick.config.ts')\n log('')\n log('Add packages:')\n log(' kick add <pkg> Install a KickJS package + peers')\n log(' kick add --list Show all available packages')\n log('')\n log(`Available: ${AVAILABLE_ADD_PACKAGES}`)\n log('')\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-cli v6.5.0
2
+ * @forinda/kickjs-cli v6.6.1
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{createRequire as e}from"node:module";import{dirname as t,extname as n,join as r}from"node:path";import{existsSync as i}from"node:fs";import{access as a,mkdir as o,readFile as s,writeFile as c}from"node:fs/promises";let l=!1;function u(e){l=e}const d=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function f(e,r){l||(await o(t(e),{recursive:!0}),await c(e,r,`utf-8`),d.has(n(e))&&await h(e,r).catch(()=>{}))}let p;async function m(t){if(p!==void 0)return p;try{p=await import(e(r(t,`package.json`)).resolve(`oxfmt`))}catch{p=null}return p}async function h(e,t){let n=await m(process.cwd());if(!n)return;let r=await _(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await c(e,i.code,`utf-8`)}const g=new Map;async function _(e){let n=t(e),a=n;if(g.has(a))return g.get(a);for(;;){let e=r(n,`.oxfmtrc.json`);if(i(e))try{let t=await s(e,`utf-8`),n=JSON.parse(t);return delete n.$schema,delete n.ignorePatterns,g.set(a,n),n}catch{return g.set(a,null),null}let o=t(n);if(o===n)return g.set(a,null),null;n=o}}async function v(e){try{return await a(e),!0}catch{return!1}}function y(e,t,n){let r={rest:`REST API`,ddd:`Domain-Driven Design`,cqrs:`CQRS + Event-Driven`,minimal:`Minimal`},i=[`@forinda/kickjs`,`@forinda/kickjs-vite`];return t!==`minimal`&&i.push(`@forinda/kickjs-swagger`,`@forinda/kickjs-devtools`),`# ${e}
11
+ import{createRequire as e}from"node:module";import{dirname as t,extname as n,join as r}from"node:path";import{existsSync as i}from"node:fs";import{access as a,mkdir as o,readFile as s,writeFile as c}from"node:fs/promises";let l=!1;function u(e){l=e}const d=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.json`,`.md`]);async function f(e,r){l||(await o(t(e),{recursive:!0}),await c(e,r,`utf-8`),d.has(n(e))&&await h(e,r).catch(()=>{}))}let p;async function m(t){if(p!==void 0)return p;try{p=await import(e(r(t,`package.json`)).resolve(`oxfmt`))}catch{p=null}return p}async function h(e,t){let n=await m(process.cwd());if(!n)return;let r=await _(e);if(r===null)return;let i=await n.format(e,t,r);i.code!==t&&await c(e,i.code,`utf-8`)}const g=new Map;async function _(e){let n=t(e),a=n;if(g.has(a))return g.get(a);for(;;){let e=r(n,`.oxfmtrc.json`);if(i(e))try{let t=await s(e,`utf-8`),n=JSON.parse(t);return delete n.$schema,delete n.ignorePatterns,g.set(a,n),n}catch{return g.set(a,null),null}let o=t(n);if(o===n)return g.set(a,null),null;n=o}}async function v(e){try{return await a(e),!0}catch{return!1}}function y(e,t,n){let r={rest:`REST API`,minimal:`Minimal`,fullstack:`Fullstack (KickJS API + typed web app)`},i=[`@forinda/kickjs`,`@forinda/kickjs-vite`];return t!==`minimal`&&i.push(`@forinda/kickjs-swagger`,`@forinda/kickjs-devtools`),`# ${e}
12
12
 
13
13
  A **${r[t]??`REST API`}** built with [KickJS](https://kickjs.app/) — a decorator-driven Node.js framework for TypeScript that runs on Express, Fastify, or h3 (swap the engine in one line).
14
14
 
@@ -156,7 +156,24 @@ add tool-specific affordances on top.
156
156
  ## Before You Start
157
157
 
158
158
  1. Run \`${n} install\` to install dependencies
159
- 2. Run \`kick dev\` to verify the app starts
159
+ 2. Run \`kick dev\` to verify the app starts${t===`fullstack`?`
160
+
161
+ ## Fullstack workspace layout
162
+
163
+ This is a WORKSPACE root — the KickJS API lives in \`server/\`, the typed web
164
+ app in \`web/\`. Run both with \`${n===`pnpm`?`pnpm dev`:`${n} run dev:server + ${n} run dev:web`}\`.
165
+
166
+ The type loop (do not break it):
167
+ 1. \`server/\` handlers RETURN their payloads → \`kick typegen\` (auto under
168
+ \`kick dev\`) emits \`server/.kickjs/types/kick__routes.ts\` incl. the flat
169
+ \`KickRoutes.Api\` map with inferred response types.
170
+ 2. \`web/src/types/kick-routes.d.ts\` imports that file TYPE-ONLY.
171
+ 3. \`web/src/api.ts\` = \`createClient<KickApi>({ baseUrl: '/api/v1' })\`
172
+ — every call site is typed from the server's handlers.
173
+
174
+ Rules: kick commands (\`kick g\`, \`kick typegen\`, \`kick dev\`) run in
175
+ \`server/\`; never import server runtime code into \`web/\` (the d.ts bridge is
176
+ type-only); prefer return-value handlers so responses stay inferable.`:``}
160
177
  3. Read the [KickJS documentation](https://kickjs.app/) for framework details
161
178
 
162
179
  ## HTTP runtime — DON'T assume Express-only
@@ -171,6 +188,15 @@ any engine-specific code, **check which engine this project uses**:
171
188
 
172
189
  Rules that keep generated code correct on **every** engine:
173
190
 
191
+ - **Prefer return-value handlers.** \`return payload\` sends 200 json on every
192
+ engine and lets \`kick typegen\` infer the response type into
193
+ \`KickRoutes.Api\` (consumed by the \`@forinda/kickjs-client\` typed client);
194
+ \`reply(status, body)\` for non-200, \`reply.noContent()\` for 204. A declared
195
+ \`{ response: schema }\` on the route feeds BOTH the OpenAPI success response
196
+ and the typegen response type. \`ctx.json(...)\` stays fully supported but
197
+ infers \`unknown\`.
198
+ - **Lifecycle hooks:** \`@PostConstruct()\` after instantiation; \`@PreDestroy()\`
199
+ when a REQUEST-scoped service's request closes (release transactions/handles).
174
200
  - **Write to \`ctx\`, not the raw request/response.** \`ctx.json()\`, \`ctx.body\`,
175
201
  \`ctx.params\`, \`ctx.query\`, \`ctx.set/get\`, \`ctx.sse()\` are engine-neutral and
176
202
  work identically everywhere. \`ctx.req\` / \`ctx.res\` are the engine-native
@@ -708,7 +734,7 @@ routes() {
708
734
  - \`@Controller('/path')\` with a path argument combined with module \`routes().path\` — duplicates the prefix. The decorator path is OpenAPI metadata only.
709
735
  - \`TodosModule\` in \`bootstrap({ modules: [TodosModule] })\` instead of \`TodosModule()\` — passing the factory instead of the invoked instance.
710
736
  - \`routes()\` returning \`router: …\` when a \`controller:\` would do — controller form is required for OpenAPI/Swagger introspection.
711
- - Module not registered in \`src/modules/index.ts\`.`},{slug:`add-adapter`,frontmatterName:`kickjs-add-adapter`,description:`Use when wiring a single-concern lifecycle integration (Swagger, DevTools, Sentry, Redis client).`,body:"**Steps**:\n1. `kick g adapter <name>` to scaffold the boilerplate, OR install via `kick add <package>` for first-party adapters.\n2. The generated file uses `defineAdapter()` — never `class implements AppAdapter`.\n3. Add the adapter instance (note the parens) to `src/adapters/index.ts` — don't inline in `src/index.ts`.\n4. Pick the right hook and middleware phase deliberately.\n5. Verify with `kick dev` that the adapter's lifecycle logs fire.\n\n**Canonical shape** — factory closure owns instance state:\n\n```ts\nexport const RedisAdapter = defineAdapter<RedisConfig>({\n name: 'RedisAdapter',\n defaults: { url: 'redis://localhost' },\n build: (config) => {\n const client = createClient(config.url)\n return {\n beforeStart: ({ container }) => {\n container.registerInstance(REDIS_CLIENT, client)\n },\n afterStart: () => client.connect(),\n shutdown: () => client.quit(),\n }\n },\n})\n\n// In src/adapters/index.ts:\nexport const adapters = [RedisAdapter({ url: env.REDIS_URL })] // <-- note parens\n```\n\n**Lifecycle hook decision tree**:\n- `beforeMount` — register early routes that should bypass middleware (health, docs UI).\n- `beforeStart` — DI ready, server not listening yet. **Use this for `container.registerInstance(...)` calls** so they work under `createTestApp` too.\n- `afterStart` — server has `ctx.server` available. Only use for things that need a listening server (Socket.IO upgrades, port logging). **Doesn't fire under `createTestApp`.**\n- `shutdown` — runs concurrently via `Promise.allSettled`, so one failure doesn't block siblings (but errors are swallowed — log inside).\n\n**Middleware phases** (see `MiddlewarePhase` JSDoc):\n`beforeGlobal` | `afterGlobal` (default) | `beforeRoutes` | `afterRoutes` (fires only on fall-through — matched routes that respond skip it).\n\n**Multi-instance** — `.scoped('cache', { url: ... })` makes `name` become `RedisAdapter:cache`. **Deferred config** — `.async({ inject, useFactory })` for config that depends on DI-resolved services.\n\n**Red flags**:\n- `bootstrap({ adapters: [MyAdapter] })` — passed the factory, not the instance. Call it: `MyAdapter()`.\n- Inlining the adapter list directly in `src/index.ts` — entry file should stay thin.\n- Returning a plain object instead of going through `defineAdapter()` — type inference for `config` will be wrong.\n- Using `.async()` for an adapter that returns `middleware()` / `contributors()` / `beforeMount()` / `onRouteMount()` — those hooks have already run by the time `.async()` resolves and are silently skipped.\n- Cross-adapter ordering via array position when it's load-bearing — use `dependsOn: ['OtelAdapter']`; cycles throw `MountCycleError` at boot.\n- Using an adapter when the integration ships **modules + DI bindings + middleware** together → that's a plugin. Promote to `definePlugin()` (see `add-plugin` skill).\n\n**Nuances**:\n- `AdapterContext.server` is `undefined` outside `afterStart`.\n- `shutdown` errors are swallowed by `Promise.allSettled` — wrap in try/catch and log if you care."},{slug:`add-plugin`,frontmatterName:`kickjs-add-plugin`,description:`Use when scaffolding a feature that bundles modules + DI + middleware + adapters together (auth, monitoring suite, multi-tenant scaffolding).`,body:"**When plugin > adapter**: a plugin is the right answer when the integration ships **more than one** of: a module, a DI binding, middleware, or another adapter. If you have a single hook (`beforeStart`) and no other contributions, use `defineAdapter` instead.\n\n**Canonical shape**:\n\n```ts\nimport { definePlugin } from '@forinda/kickjs'\n\nexport const AuthPlugin = definePlugin({\n name: 'AuthPlugin',\n defaults: { tokenTtl: '1h' },\n build: (config, { name }) => ({\n modules: () => [AuthModule()],\n adapters: () => [JwtAdapter({ ttl: config.tokenTtl })],\n middleware: () => [requestIdMiddleware()],\n register(container) {\n container.registerFactory(TOKEN_SIGNER, () => createSigner(config))\n },\n contributors() {\n return [LoadCurrentUser.registration]\n },\n onReady({ server }) {\n log.info(`AuthPlugin listening on port ${server.address().port}`)\n },\n }),\n})\n\n// In bootstrap:\nbootstrap({ plugins: [AuthPlugin({ tokenTtl: env.TOKEN_TTL })] }) // <-- parens\n```\n\n**Inline plugin literal** — the canonical answer for one-off DI bindings. There's no top-level `register:` on `bootstrap` itself:\n\n```ts\nbootstrap({\n plugins: [{ name: 'vector-store', register(c) { c.registerInstance(VECTOR_STORE, store) } }],\n})\n```\n\n**Execution order** (memorize):\nplugin `register()` → plugin `middleware()` → plugin `modules()` + user modules → plugin `adapters()` + user adapters → server listens → plugin `onReady()`.\n\n**Static vs dynamic modules**: `modules()` returning an array is introspectable (Swagger, DevTools see it). `setup(registry)` is imperative — pick the latter when the module set depends on resolved config.\n\n**Multi-instance** — `.scoped('users', { url })`; derive unique DI tokens from `ctx.name` inside `build`:\n\n```ts\nbuild: (config, { name }) => ({\n register(c) {\n c.registerInstance(createToken(`cache/${name}`), client)\n },\n})\n```\n\n**Precedence**: plugin contributors land at `'adapter'` precedence — beat global, lose to module/class/method same-key.\n\n**Red flags**:\n- `bootstrap({ plugins: [AuthPlugin] })` — passed factory. Call it: `AuthPlugin()`.\n- Reaching for a plugin when an adapter would do (no modules, no DI bindings, no contributors) — overkill; use `defineAdapter()`.\n- `.async()` plugin that depends on `modules()` / `middleware()` / `adapters()` / `contributors()` — those are dropped. `.async()` only resolves `register()` + `onReady()`.\n- Confusing CLI plugins (`defineCliPlugin` from `@forinda/kickjs-cli`) with runtime plugins (`definePlugin` from `@forinda/kickjs`) — different surfaces, different registration sites.\n- `dependsOn: ['SomePlugin']` referring to a plugin not in the boot list — throws `MissingMountDepError` at boot.\n\n**Nuances**:\n- `definition` is `Object.freeze`'d metadata; useful for version checks (`compare(AuthPlugin.definition.version, '1.2.0')`) — not mountable."},{slug:`write-controller-test`,frontmatterName:`kickjs-write-controller-test`,description:`Use when adding a Vitest test that exercises an HTTP route or DI graph.`,body:"**Template** (copy/paste, adjust):\n\n```ts\nimport { describe, it, expect, beforeEach } from 'vitest'\nimport { Container } from '@forinda/kickjs'\nimport { createTestApp } from '@forinda/kickjs-testing'\n\nbeforeEach(() => {\n Container.reset() // isolated DI per test\n})\n\ndescribe('UserController', () => {\n it('returns users', async () => {\n const app = await createTestApp([UserModule])\n const res = await app.get('/api/v1/users')\n expect(res.status).toBe(200)\n })\n})\n```\n\n**Typed handler signature** — pair with `kick typegen` so `ctx.body` / `params` / `query` are typed by the route's Zod schema:\n\n```ts\n@Post('/', { body: createTodoSchema })\ncreate(ctx: Ctx<KickRoutes.TodoController['create']>) {\n // ctx.body is typed from createTodoSchema; ctx.params from the route\n ctx.created(await this.service.create(ctx.body))\n}\n```\n\n**Red flags**:\n- `new Container()` — wrong; use `Container.reset()` in `beforeEach` or `Container.create()` for fully isolated graphs.\n- `Container.getInstance().reset()` — wrong; same fix.\n- Sharing a container instance across `it()` blocks — leaks registrations between tests.\n- Injecting a `Scope.REQUEST` service into a `SINGLETON` — container throws at resolve. Singletons must resolve request-scoped services explicitly per call.\n- Calling `getRequestValue<string>('traceId')` — the generic slot is the **key** type, not the value type; widens key and bypasses typed lookup.\n- Asserting on `res.body.requestId` when `requestId()` middleware isn't mounted in the test app — value will be `undefined`.\n- Using `Scope.REQUEST` services in a test without mounting `requestScopeMiddleware()` — `getRequestValue` silently returns `undefined`; `getRequestStore` throws.\n\n**Nuances**:\n- `@Inject` and `@Autowired` are interchangeable — same runtime, same types; pick by readability.\n- `@Value('MISSING_KEY')` with no default **throws on property access**, not at construction — tests that exercise the getter will surface the missing-env issue."},{slug:`env-wiring-check`,frontmatterName:`kickjs-env-wiring-check`,description:`Use when ConfigService.get('SOME_KEY') returns undefined or @Value silently falls back to process.env.`,body:"**Diagnosis (in order)**:\n1. Open `src/index.ts`. The **first non-`reflect-metadata`** import MUST be `import './config'`.\n2. Open `src/config/index.ts`. It MUST call `loadEnv(envSchema)` as a top-level side effect — not just declare the schema:\n ```ts\n import { loadEnv, defineEnv } from '@forinda/kickjs'\n const envSchema = defineEnv((base) => base.extend({ DATABASE_URL: z.string().url() }))\n export const env = loadEnv(envSchema)\n ```\n3. The new key MUST be declared in the Zod schema. `@Value('NEW_KEY')` accepts any string at the type level and **falls back to raw `process.env`** when the schema doesn't know the key — silently skipping Zod coercion.\n4. After adding a key, re-run `kick typegen` (or restart `kick dev` if the typegen watcher missed it) so the global `KickEnv` augmentation picks it up.\n\n**Why `@Value` \"works\" but `ConfigService.get` doesn't**: `@Value` has the `process.env` fallback that masks missing-side-effect-import bugs; `ConfigService` has none. If `@Value('FOO')` returns a value but `ConfigService.get('FOO')` returns `undefined`, the side-effect import of `./config` is missing.\n\n**`reloadEnv` vs `resetEnvCache`** — distinct, frequently mixed up:\n- `reloadEnv()` — re-reads `process.env` against the **already registered** schema. Use in HMR plugins after `.env` file changes. Schema survives.\n- `resetEnvCache()` — drops the registered schema entirely. **Test-only.** Calling it between dev requests drops the project's keys.\n\n**Nuances**:\n- `loadEnv()` cache is **sticky**: once `loadEnv(extendedSchema)` runs anywhere, no-arg calls reuse it — but only if it actually ran. Schema downgrades silently if `src/config/index.ts` isn't imported.\n- `createConfigService(envSchema)` is deprecated; the typegen-driven `ConfigService` covers it.\n- `dotenv` is an **optional peer dep** in v5+ — projects upgrading from older versions may need to add it explicitly.\n- For HMR-friendly `.env` edits, add `envWatchPlugin()` to `vite.config.ts` — calls `reloadEnv()` automatically.\n\n**Fix recipe**: add the key to the schema; add `import './config'` as the first non-reflect-metadata import in `src/index.ts`; re-run `kick typegen`."},{slug:`bootstrap-export`,frontmatterName:`kickjs-bootstrap-export`,description:`Use when HMR is silently doing full restarts on every save, or createTestApp can't find the app handle.`,body:"**Check** `src/index.ts`'s last line:\n\n```ts\n// CORRECT — Vite plugin + createTestApp import the named `app` symbol\nexport const app = await bootstrap({ ... })\n\n// WRONG — HMR degrades to full restart, createTestApp loses the handle\nawait bootstrap({ ... })\n```\n\nThe Vite plugin imports the named `app` symbol via `virtual:kickjs/app`; testing helpers do too. Without the export, both fall back to slower paths (full restart on save, mock handle in tests) **without warning**.\n\n**Red flags**:\n- A bare `await bootstrap(...)` with no `export` — fix by adding `export const app =`.\n- Re-assigning `app` later in the file (`app = somethingElse`) — Vite imports by reference at module-load time; reassignments don't propagate.\n- Multiple files calling `bootstrap()` — only the entry should. Tests use `createTestApp` instead."},{slug:`thin-entry-file`,frontmatterName:`kickjs-thin-entry-file`,description:`Use when src/index.ts is accumulating module/middleware/plugin/adapter literals.`,body:`**Refactor target**:
737
+ - Module not registered in \`src/modules/index.ts\`.`},{slug:`add-adapter`,frontmatterName:`kickjs-add-adapter`,description:`Use when wiring a single-concern lifecycle integration (Swagger, DevTools, Sentry, Redis client).`,body:"**Steps**:\n1. `kick g adapter <name>` to scaffold the boilerplate, OR install via `kick add <package>` for first-party adapters.\n2. The generated file uses `defineAdapter()` — never `class implements AppAdapter`.\n3. Add the adapter instance (note the parens) to `src/adapters/index.ts` — don't inline in `src/index.ts`.\n4. Pick the right hook and middleware phase deliberately.\n5. Verify with `kick dev` that the adapter's lifecycle logs fire.\n\n**Canonical shape** — factory closure owns instance state:\n\n```ts\nexport const RedisAdapter = defineAdapter<RedisConfig>({\n name: 'RedisAdapter',\n defaults: { url: 'redis://localhost' },\n build: (config) => {\n const client = createClient(config.url)\n return {\n beforeStart: ({ container }) => {\n container.registerInstance(REDIS_CLIENT, client)\n },\n afterStart: () => client.connect(),\n shutdown: () => client.quit(),\n }\n },\n})\n\n// In src/adapters/index.ts:\nexport const adapters = [RedisAdapter({ url: env.REDIS_URL })] // <-- note parens\n```\n\n**Lifecycle hook decision tree**:\n- `beforeMount` — register early routes that should bypass middleware (health, docs UI).\n- `beforeStart` — DI ready, server not listening yet. **Use this for `container.registerInstance(...)` calls** so they work under `createTestApp` too.\n- `afterStart` — server has `ctx.server` available. Only use for things that need a listening server (Socket.IO upgrades, port logging). **Doesn't fire under `createTestApp`.**\n- `shutdown` — runs concurrently via `Promise.allSettled`, so one failure doesn't block siblings (but errors are swallowed — log inside).\n\n**Middleware phases** (see `MiddlewarePhase` JSDoc):\n`beforeGlobal` | `afterGlobal` (default) | `beforeRoutes` | `afterRoutes` (fires only on fall-through — matched routes that respond skip it).\n\n**Multi-instance** — `.scoped('cache', { url: ... })` makes `name` become `RedisAdapter:cache`. **Deferred config** — `.async({ inject, useFactory })` for config that depends on DI-resolved services.\n\n**Red flags**:\n- `bootstrap({ adapters: [MyAdapter] })` — passed the factory, not the instance. Call it: `MyAdapter()`.\n- Inlining the adapter list directly in `src/index.ts` — entry file should stay thin.\n- Returning a plain object instead of going through `defineAdapter()` — type inference for `config` will be wrong.\n- Using `.async()` for an adapter that returns `middleware()` / `contributors()` / `beforeMount()` / `onRouteMount()` — those hooks have already run by the time `.async()` resolves and are silently skipped.\n- Cross-adapter ordering via array position when it's load-bearing — use `dependsOn: ['OtelAdapter']`; cycles throw `MountCycleError` at boot.\n- Using an adapter when the integration ships **modules + DI bindings + middleware** together → that's a plugin. Promote to `definePlugin()` (see `add-plugin` skill).\n\n**Nuances**:\n- `AdapterContext.server` is `undefined` outside `afterStart`.\n- `shutdown` errors are swallowed by `Promise.allSettled` — wrap in try/catch and log if you care."},{slug:`add-plugin`,frontmatterName:`kickjs-add-plugin`,description:`Use when scaffolding a feature that bundles modules + DI + middleware + adapters together (auth, monitoring suite, multi-tenant scaffolding).`,body:"**When plugin > adapter**: a plugin is the right answer when the integration ships **more than one** of: a module, a DI binding, middleware, or another adapter. If you have a single hook (`beforeStart`) and no other contributions, use `defineAdapter` instead.\n\n**Canonical shape**:\n\n```ts\nimport { definePlugin } from '@forinda/kickjs'\n\nexport const AuthPlugin = definePlugin({\n name: 'AuthPlugin',\n defaults: { tokenTtl: '1h' },\n build: (config, { name }) => ({\n modules: () => [AuthModule()],\n adapters: () => [JwtAdapter({ ttl: config.tokenTtl })],\n middleware: () => [requestIdMiddleware()],\n register(container) {\n container.registerFactory(TOKEN_SIGNER, () => createSigner(config))\n },\n contributors() {\n return [LoadCurrentUser.registration]\n },\n onReady({ server }) {\n log.info(`AuthPlugin listening on port ${server.address().port}`)\n },\n }),\n})\n\n// In bootstrap:\nbootstrap({ plugins: [AuthPlugin({ tokenTtl: env.TOKEN_TTL })] }) // <-- parens\n```\n\n**Inline plugin literal** — the canonical answer for one-off DI bindings. There's no top-level `register:` on `bootstrap` itself:\n\n```ts\nbootstrap({\n plugins: [{ name: 'vector-store', register(c) { c.registerInstance(VECTOR_STORE, store) } }],\n})\n```\n\n**Execution order** (memorize):\nplugin `register()` → plugin `middleware()` → plugin `modules()` + user modules → plugin `adapters()` + user adapters → server listens → plugin `onReady()`.\n\n**Static vs dynamic modules**: `modules()` returning an array is introspectable (Swagger, DevTools see it). `setup(registry)` is imperative — pick the latter when the module set depends on resolved config.\n\n**Multi-instance** — `.scoped('users', { url })`; derive unique DI tokens from `ctx.name` inside `build`:\n\n```ts\nbuild: (config, { name }) => ({\n register(c) {\n c.registerInstance(createToken(`cache/${name}`), client)\n },\n})\n```\n\n**Precedence**: plugin contributors land at `'adapter'` precedence — beat global, lose to module/class/method same-key.\n\n**Red flags**:\n- `bootstrap({ plugins: [AuthPlugin] })` — passed factory. Call it: `AuthPlugin()`.\n- Reaching for a plugin when an adapter would do (no modules, no DI bindings, no contributors) — overkill; use `defineAdapter()`.\n- `.async()` plugin that depends on `modules()` / `middleware()` / `adapters()` / `contributors()` — those are dropped. `.async()` only resolves `register()` + `onReady()`.\n- Confusing CLI plugins (`defineCliPlugin` from `@forinda/kickjs-cli`) with runtime plugins (`definePlugin` from `@forinda/kickjs`) — different surfaces, different registration sites.\n- `dependsOn: ['SomePlugin']` referring to a plugin not in the boot list — throws `MissingMountDepError` at boot.\n\n**Nuances**:\n- `definition` is `Object.freeze`'d metadata; useful for version checks (`compare(AuthPlugin.definition.version, '1.2.0')`) — not mountable."},{slug:`write-controller-test`,frontmatterName:`kickjs-write-controller-test`,description:`Use when adding a Vitest test that exercises an HTTP route or DI graph.`,body:"**Template** (copy/paste, adjust):\n\n```ts\nimport { describe, it, expect, beforeEach } from 'vitest'\nimport { Container } from '@forinda/kickjs'\nimport { createTestApp } from '@forinda/kickjs-testing'\n\nbeforeEach(() => {\n Container.reset() // isolated DI per test\n})\n\ndescribe('UserController', () => {\n it('returns users', async () => {\n const app = await createTestApp([UserModule])\n const res = await app.get('/api/v1/users')\n expect(res.status).toBe(200)\n })\n})\n```\n\n**Typed handler signature** — pair with `kick typegen` so `ctx.body` / `params` / `query` are typed by the route's Zod schema:\n\n```ts\n@Post('/', { body: createTodoSchema })\nasync create(ctx: Ctx<KickRoutes.TodoController['create']>) {\n // ctx.body is typed from createTodoSchema; ctx.params from the route.\n // Returning (vs ctx.created) lets typegen infer the response type.\n return reply(201, await this.service.create(ctx.body))\n}\n```\n\n**Red flags**:\n- `new Container()` — wrong; use `Container.reset()` in `beforeEach` or `Container.create()` for fully isolated graphs.\n- `Container.getInstance().reset()` — wrong; same fix.\n- Sharing a container instance across `it()` blocks — leaks registrations between tests.\n- Injecting a `Scope.REQUEST` service into a `SINGLETON` — container throws at resolve. Singletons must resolve request-scoped services explicitly per call.\n- Calling `getRequestValue<string>('traceId')` — the generic slot is the **key** type, not the value type; widens key and bypasses typed lookup.\n- Asserting on `res.body.requestId` when `requestId()` middleware isn't mounted in the test app — value will be `undefined`.\n- Using `Scope.REQUEST` services in a test without mounting `requestScopeMiddleware()` — `getRequestValue` silently returns `undefined`; `getRequestStore` throws.\n\n**Nuances**:\n- `@Inject` and `@Autowired` are interchangeable — same runtime, same types; pick by readability.\n- `@Value('MISSING_KEY')` with no default **throws on property access**, not at construction — tests that exercise the getter will surface the missing-env issue."},{slug:`env-wiring-check`,frontmatterName:`kickjs-env-wiring-check`,description:`Use when ConfigService.get('SOME_KEY') returns undefined or @Value silently falls back to process.env.`,body:"**Diagnosis (in order)**:\n1. Open `src/index.ts`. The **first non-`reflect-metadata`** import MUST be `import './config'`.\n2. Open `src/config/index.ts`. It MUST call `loadEnv(envSchema)` as a top-level side effect — not just declare the schema:\n ```ts\n import { loadEnv, defineEnv } from '@forinda/kickjs'\n const envSchema = defineEnv((base) => base.extend({ DATABASE_URL: z.string().url() }))\n export const env = loadEnv(envSchema)\n ```\n3. The new key MUST be declared in the Zod schema. `@Value('NEW_KEY')` accepts any string at the type level and **falls back to raw `process.env`** when the schema doesn't know the key — silently skipping Zod coercion.\n4. After adding a key, re-run `kick typegen` (or restart `kick dev` if the typegen watcher missed it) so the global `KickEnv` augmentation picks it up.\n\n**Why `@Value` \"works\" but `ConfigService.get` doesn't**: `@Value` has the `process.env` fallback that masks missing-side-effect-import bugs; `ConfigService` has none. If `@Value('FOO')` returns a value but `ConfigService.get('FOO')` returns `undefined`, the side-effect import of `./config` is missing.\n\n**`reloadEnv` vs `resetEnvCache`** — distinct, frequently mixed up:\n- `reloadEnv()` — re-reads `process.env` against the **already registered** schema. Use in HMR plugins after `.env` file changes. Schema survives.\n- `resetEnvCache()` — drops the registered schema entirely. **Test-only.** Calling it between dev requests drops the project's keys.\n\n**Nuances**:\n- `loadEnv()` cache is **sticky**: once `loadEnv(extendedSchema)` runs anywhere, no-arg calls reuse it — but only if it actually ran. Schema downgrades silently if `src/config/index.ts` isn't imported.\n- `createConfigService(envSchema)` is deprecated; the typegen-driven `ConfigService` covers it.\n- `dotenv` is an **optional peer dep** in v5+ — projects upgrading from older versions may need to add it explicitly.\n- For HMR-friendly `.env` edits, add `envWatchPlugin()` to `vite.config.ts` — calls `reloadEnv()` automatically.\n\n**Fix recipe**: add the key to the schema; add `import './config'` as the first non-reflect-metadata import in `src/index.ts`; re-run `kick typegen`."},{slug:`bootstrap-export`,frontmatterName:`kickjs-bootstrap-export`,description:`Use when HMR is silently doing full restarts on every save, or createTestApp can't find the app handle.`,body:"**Check** `src/index.ts`'s last line:\n\n```ts\n// CORRECT — Vite plugin + createTestApp import the named `app` symbol\nexport const app = await bootstrap({ ... })\n\n// WRONG — HMR degrades to full restart, createTestApp loses the handle\nawait bootstrap({ ... })\n```\n\nThe Vite plugin imports the named `app` symbol via `virtual:kickjs/app`; testing helpers do too. Without the export, both fall back to slower paths (full restart on save, mock handle in tests) **without warning**.\n\n**Red flags**:\n- A bare `await bootstrap(...)` with no `export` — fix by adding `export const app =`.\n- Re-assigning `app` later in the file (`app = somethingElse`) — Vite imports by reference at module-load time; reassignments don't propagate.\n- Multiple files calling `bootstrap()` — only the entry should. Tests use `createTestApp` instead."},{slug:`thin-entry-file`,frontmatterName:`kickjs-thin-entry-file`,description:`Use when src/index.ts is accumulating module/middleware/plugin/adapter literals.`,body:`**Refactor target**:
712
738
 
713
739
  \`\`\`ts
714
740
  // src/modules/index.ts — fluent chain (default for \`modules.style: 'define'\`)
@@ -864,4 +890,4 @@ Codex / Cursor / Gemini / Claude Code without copy-pasting.
864
890
  CLI template. Hand-edited content is overwritten — keep customisation
865
891
  in \`.agents/COPILOT.local.md\`.
866
892
  `}export{S as a,u as c,C as i,f as l,b as n,y as o,w as r,v as s,x as t};
867
- //# sourceMappingURL=project-docs-MYPeTYsJ.mjs.map
893
+ //# sourceMappingURL=project-docs-VwSwcfVt.mjs.map