@forinda/kickjs-cli 7.1.1 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +4 -3
  2. package/dist/agent-docs-B-HVS3nj.mjs +11 -0
  3. package/dist/agent-docs-C0JyMGzA.mjs +12 -0
  4. package/dist/agent-docs-C0JyMGzA.mjs.map +1 -0
  5. package/dist/{build-1OHeIhGr.mjs → build-7MjB7FQe.mjs} +2 -2
  6. package/dist/{build-BJd10HpG.mjs → build-BPVxGl5d.mjs} +3 -3
  7. package/dist/{build-BJd10HpG.mjs.map → build-BPVxGl5d.mjs.map} +1 -1
  8. package/dist/{builtins-BJ1fZBKi.mjs → builtins-BDNTBw86.mjs} +2 -2
  9. package/dist/{builtins-CK8nnKx3.mjs → builtins-C5IYbrWs.mjs} +328 -311
  10. package/dist/cli.mjs +2 -2
  11. package/dist/{config-B26M-H3G.mjs → config-CCjq5XrS.mjs} +4 -4
  12. package/dist/config-CCjq5XrS.mjs.map +1 -0
  13. package/dist/{config-CcRRKBhg.mjs → config-CTshIQFs.mjs} +3 -3
  14. package/dist/{doctor-bVCuDQuM.mjs → doctor-CabZwXIc.mjs} +244 -227
  15. package/dist/doctor-CabZwXIc.mjs.map +1 -0
  16. package/dist/{fullstack-Bhe_l1v6.mjs → fullstack-CpsuJwAC.mjs} +4 -4
  17. package/dist/{fullstack-Bhe_l1v6.mjs.map → fullstack-CpsuJwAC.mjs.map} +1 -1
  18. package/dist/{fullstack-BfgB5r1W.mjs → fullstack-U0G1tXX-.mjs} +3 -3
  19. package/dist/index.d.mts +30 -12
  20. package/dist/index.d.mts.map +1 -1
  21. package/dist/index.mjs +2 -2
  22. package/dist/{plugin-LY_08MMA.mjs → plugin-CLk1hucT.mjs} +2 -2
  23. package/dist/{plugin-_eznyEJd.mjs → plugin-CgNrMHOr.mjs} +3 -3
  24. package/dist/{plugin-_eznyEJd.mjs.map → plugin-CgNrMHOr.mjs.map} +1 -1
  25. package/dist/{project-BRcVX0aF.mjs → project-BDmhnlq7.mjs} +12 -8
  26. package/dist/project-BDmhnlq7.mjs.map +1 -0
  27. package/dist/{project-C7RF1-Gb.mjs → project-N9-eInMo.mjs} +11 -7
  28. package/dist/{project-docs-BJSgVODJ.mjs → project-docs-BAkYVe21.mjs} +72 -16
  29. package/dist/project-docs-BAkYVe21.mjs.map +1 -0
  30. package/dist/{project-docs-YvI9sWW2.mjs → project-docs-Ce8bB21w.mjs} +71 -15
  31. package/dist/{project-root-Cd1GKxvd.mjs → project-root-C_w_fm3Z.mjs} +2 -2
  32. package/dist/{project-root-DpJw0zEd.mjs → project-root-k6lK049j.mjs} +3 -3
  33. package/dist/{project-root-DpJw0zEd.mjs.map → project-root-k6lK049j.mjs.map} +1 -1
  34. package/dist/{prompts-CE8oxMS2.mjs → prompts-DgwJdGWX.mjs} +2 -2
  35. package/dist/{prompts-CE8oxMS2.mjs.map → prompts-DgwJdGWX.mjs.map} +1 -1
  36. package/dist/{rolldown-runtime-D-Y-59yJ.mjs → rolldown-runtime-CEoUVQPP.mjs} +1 -1
  37. package/dist/{run-plugins-CHv-_CI_.mjs → run-plugins-CA8eTAtB.mjs} +57 -57
  38. package/dist/run-plugins-CA8eTAtB.mjs.map +1 -0
  39. package/dist/{typegen-BDYmZKFF.mjs → typegen-XbrxqReA.mjs} +5 -5
  40. package/dist/{typegen-BDYmZKFF.mjs.map → typegen-XbrxqReA.mjs.map} +1 -1
  41. package/dist/{typegen-DiD6n0_q.mjs → typegen-n6mBEphY.mjs} +4 -4
  42. package/dist/{types-BvRcbnr7.mjs → types-qTFFy76T.mjs} +1 -1
  43. package/package.json +5 -5
  44. package/dist/agent-docs-Bxp34Ss6.mjs +0 -11
  45. package/dist/agent-docs-CMj7Zyj4.mjs +0 -12
  46. package/dist/agent-docs-CMj7Zyj4.mjs.map +0 -1
  47. package/dist/config-B26M-H3G.mjs.map +0 -1
  48. package/dist/doctor-bVCuDQuM.mjs.map +0 -1
  49. package/dist/project-BRcVX0aF.mjs.map +0 -1
  50. package/dist/project-docs-BJSgVODJ.mjs.map +0 -1
  51. package/dist/run-plugins-CHv-_CI_.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-cli v7.1.1
2
+ * @forinda/kickjs-cli v8.0.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import{l as e,s as t}from"./project-docs-BJSgVODJ.mjs";import{a as n,n as r,r as i,t as a,u as o}from"./prompts-CE8oxMS2.mjs";import{i as s,s as c}from"./project-BRcVX0aF.mjs";import{a as l}from"./config-B26M-H3G.mjs";import{t as u}from"./project-root-DpJw0zEd.mjs";import{createRequire as d}from"node:module";import f,{dirname as p,join as m,relative as h,resolve as g}from"node:path";import{existsSync as _,readFileSync as v,readdirSync as y,statSync as b}from"node:fs";import{readFile as x,writeFile as S}from"node:fs/promises";import C from"pluralize";import{pathToFileURL as ee}from"node:url";import{defineGenerator as te}from"@forinda/kickjs-cli-kit";function w(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function T(e){let t=w(e);return t.charAt(0).toLowerCase()+t.slice(1)}function E(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function D(e){return C.plural(e)}function O(e){return C.plural(e)}function k(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function ne(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function re(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).toLowerCase()}function ie(e,t,n){let r={inmemory:`InMemory${e}Repository`,drizzle:`Drizzle${e}Repository`,prisma:`Prisma${e}Repository`},i={inmemory:`in-memory-${t}`,drizzle:`drizzle-${t}`,prisma:`prisma-${t}`};return{repoClass:r[n]??`${ne(n)}${e}Repository`,repoFile:i[n]??`${re(n)}-${t}`}}function A(e){return e??`define`}function j(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,{repoClass:o,repoFile:s}=ie(t,n,i),c=A(a),l=`/**
11
+ import{l as e,s as t}from"./project-docs-BAkYVe21.mjs";import{a as n,n as r,r as i,t as a,u as o}from"./prompts-DgwJdGWX.mjs";import{i as s,s as c}from"./project-BDmhnlq7.mjs";import{o as l}from"./config-CCjq5XrS.mjs";import{t as u}from"./project-root-k6lK049j.mjs";import{createRequire as d}from"node:module";import f,{dirname as p,join as m,relative as h,resolve as g}from"node:path";import{existsSync as _,readFileSync as v,readdirSync as y,statSync as b}from"node:fs";import{readFile as x,writeFile as S}from"node:fs/promises";import C from"pluralize";import{pathToFileURL as w}from"node:url";import{defineGenerator as T}from"@forinda/kickjs-cli-kit";function E(e){return e.replace(/[-_\s]+(.)?/g,(e,t)=>t?t.toUpperCase():``).replace(/^(.)/,e=>e.toUpperCase())}function D(e){let t=E(e);return t.charAt(0).toLowerCase()+t.slice(1)}function O(e){return e.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()}function k(e){return C.plural(e)}function A(e){return C.plural(e)}function j(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const ee={inmemory:`in-memory`,drizzle:`Drizzle`,prisma:`Prisma`};function te(e){return e.charAt(0).toUpperCase()+e.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase())}function ne(e){return ee[e]??te(e)}function re(e){return`create${e}Repository`}function M(e){return e??`define`}function N(e){let{pascal:t,kebab:n,plural:r=``,repo:i,style:a}=e,o=re(t),s=M(a),c=`/**
12
12
  * ${t} Module
13
13
  *
14
14
  * REST module with a flat folder structure.
@@ -17,11 +17,14 @@ import{l as e,s as t}from"./project-docs-BJSgVODJ.mjs";import{a as n,n as r,r as
17
17
  * Structure:
18
18
  * ${n}.controller.ts — HTTP routes (CRUD)
19
19
  * ${n}.service.ts — Business logic
20
- * ${n}.repository.ts — Repository interface
21
- * ${s}.repository.ts — Repository implementation
20
+ * ${n}.repository.ts — Repository: factory, contract, token
22
21
  * dtos/ — Request/response schemas
23
- */`,u=`import { ${t.toUpperCase()}_REPOSITORY } from './${n}.repository'
24
- import { ${o} } from './${s}.repository'
22
+ *
23
+ * The repository is backed by an in-memory Map so this module works as
24
+ * generated. Swap in ${ne(i)} by replacing the factory body in
25
+ * ${n}.repository.ts — the contract is whatever that factory returns, so
26
+ * nothing else has to change.
27
+ */`,l=`import { ${t.toUpperCase()}_REPOSITORY, ${o} } from './${n}.repository'
25
28
  import { ${t}Controller } from './${n}.controller'
26
29
 
27
30
  // Eagerly load decorated classes so @Controller()/@Service()/@Repository() decorators
@@ -30,7 +33,7 @@ import { ${t}Controller } from './${n}.controller'
30
33
  import.meta.glob(
31
34
  ['./**/*.controller.ts', './**/*.service.ts', './**/*.repository.ts', '!./**/*.test.ts'],
32
35
  { eager: true },
33
- )`,d=` /**
36
+ )`,u=` /**
34
37
  * Declare HTTP routes for this module. Return value shape:
35
38
  *
36
39
  * - \`path\` — URL prefix for this route set.
@@ -44,18 +47,16 @@ import.meta.glob(
44
47
  * { path: '/${r}', version: 1, controller: ${t}V1Controller },
45
48
  * { path: '/${r}', version: 2, controller: ${t}V2Controller },
46
49
  * ]
47
- */`;return c===`class`?`${l}
50
+ */`;return s===`class`?`${c}
48
51
  import { Container, type AppModule, type ModuleRoutes } from '@forinda/kickjs'
49
- ${u}
52
+ ${l}
50
53
 
51
54
  export class ${t}Module implements AppModule {
52
55
  register(container: Container): void {
53
- container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
54
- container.resolve(${o}),
55
- )
56
+ container.registerFactory(${t.toUpperCase()}_REPOSITORY, () => ${o}())
56
57
  }
57
58
 
58
- ${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
59
+ ${u.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
59
60
  routes(): ModuleRoutes {
60
61
  return {
61
62
  path: '/${r}',
@@ -63,20 +64,20 @@ ${d.replace(/^ {4}/gm,` `).replace(/^ {6}/gm,` `)}
63
64
  }
64
65
  }
65
66
  }
66
- `:`${l}
67
+ `:`${c}
67
68
  import { defineModule } from '@forinda/kickjs'
68
- ${u}
69
+ ${l}
69
70
 
70
71
  export const ${t}Module = defineModule({
71
72
  name: '${t}Module',
72
73
  build: () => ({
73
74
  register(container) {
74
75
  container.registerFactory(${t.toUpperCase()}_REPOSITORY, () =>
75
- container.resolve(${o}),
76
+ ${o}(),
76
77
  )
77
78
  },
78
79
 
79
- ${d}
80
+ ${u}
80
81
  routes() {
81
82
  return {
82
83
  path: '/${r}',
@@ -85,7 +86,7 @@ ${d}
85
86
  },
86
87
  }),
87
88
  })
88
- `}function ae(e){let{pascal:t,kebab:n,plural:r=``,style:i}=e,a=A(i),o=` /**
89
+ `}function P(e){let{pascal:t,kebab:n,plural:r=``,style:i}=e,a=M(i),o=` /**
89
90
  * Declare HTTP routes. Return value shape:
90
91
  *
91
92
  * - \`path\` — URL prefix for this route set.
@@ -125,7 +126,7 @@ ${o}
125
126
  },
126
127
  }),
127
128
  })
128
- `}function M(e){let{pascal:t,kebab:n}=e,r=e.swagger?`import { ApiTags } from '@forinda/kickjs-swagger'
129
+ `}function F(e){let{pascal:t,kebab:n}=e,r=e.swagger?`import { ApiTags } from '@forinda/kickjs-swagger'
129
130
  `:``,i=e.swagger?` @ApiTags('${e.pascal}')\n`:``,a=t.charAt(0).toLowerCase()+t.slice(1);return`import {
130
131
  Controller,
131
132
  Get,
@@ -197,7 +198,7 @@ ${i} async remove(ctx: Ctx<KickRoutes.${t}Controller['remove']>) {
197
198
  return reply.noContent()
198
199
  }
199
200
  }
200
- `}function N(e){let{pascal:t}=e;return`import { z } from 'zod'
201
+ `}function ie(e){let{pascal:t}=e;return`import { z } from 'zod'
201
202
 
202
203
  /**
203
204
  * Create ${t} DTO — Zod schema for validating POST request bodies.
@@ -213,243 +214,196 @@ export const create${t}Schema = z.object({
213
214
  })
214
215
 
215
216
  export type Create${t}DTO = z.infer<typeof create${t}Schema>
216
- `}function oe(e){let{pascal:t}=e;return`import { z } from 'zod'
217
+ `}function ae(e){let{pascal:t}=e;return`import { z } from 'zod'
217
218
 
218
219
  export const update${t}Schema = z.object({
219
220
  name: z.string().min(1).max(200).optional(),
220
221
  })
221
222
 
222
223
  export type Update${t}DTO = z.infer<typeof update${t}Schema>
223
- `}function se(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
224
+ `}function oe(e){let{pascal:t}=e;return`export interface ${t}ResponseDTO {
224
225
  id: string
225
226
  name: string
226
227
  createdAt: string
227
228
  updatedAt: string
228
229
  }
229
- `}function P(e){let{pascal:t,kebab:n,dtoPrefix:r=`../../application/dtos`,tokenScope:i=`app`}=e;return`/**
230
- * ${t} Repository Interface
230
+ `}function I(e){let{pascal:t,kebab:n,repoType:r,dtoPrefix:i=`./dtos`,tokenScope:a=`app`}=e,o=r&&r!==`inmemory`?r:``,s=`create${t}Repository`,c=` const store = new Map<string, ${t}ResponseDTO>()
231
+
232
+ return {
233
+ async findById(id: string): Promise<${t}ResponseDTO | null> {
234
+ return store.get(id) ?? null
235
+ },
236
+
237
+ async findAll(): Promise<${t}ResponseDTO[]> {
238
+ return [...store.values()]
239
+ },
240
+
241
+ async findPaginated(
242
+ parsed: ParsedQuery,
243
+ ): Promise<{ data: ${t}ResponseDTO[]; total: number }> {
244
+ const all = [...store.values()]
245
+ const { offset, limit } = parsed.pagination
246
+ return { data: all.slice(offset, offset + limit), total: all.length }
247
+ },
248
+
249
+ async create(dto: Create${t}DTO): Promise<${t}ResponseDTO> {
250
+ const now = new Date().toISOString()
251
+ const entity = { id: randomUUID(), ...dto, createdAt: now, updatedAt: now } as ${t}ResponseDTO
252
+ store.set(entity.id, entity)
253
+ return entity
254
+ },
255
+
256
+ async update(id: string, dto: Update${t}DTO): Promise<${t}ResponseDTO> {
257
+ const existing = store.get(id)
258
+ if (!existing) throw HttpException.notFound('${t} not found')
259
+ const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }
260
+ store.set(id, updated)
261
+ return updated
262
+ },
263
+
264
+ async delete(id: string): Promise<void> {
265
+ if (!store.has(id)) throw HttpException.notFound('${t} not found')
266
+ store.delete(id)
267
+ },
268
+ }`;return`/**
269
+ * ${t} repository.
231
270
  *
232
- * Defines the contract for data access.
233
- * The interface declares what operations are available;
234
- * implementations (in-memory, Drizzle, Prisma) fulfill the contract.
271
+ * ${o?`Backed by an in-memory Map so the module works as generated. Replace the
272
+ * body with your ${o} queries — the contract stays whatever this factory
273
+ * returns.`:`Backed by an in-memory Map: a working store with no dependencies.`}
235
274
  *
236
- * To swap implementations, change the factory in the module's register() method.
275
+ * The factory's return type IS the contract: \`${t}Repository\` is derived
276
+ * from it, so the implementation and its interface cannot drift apart. To swap
277
+ * stores, write another factory returning a compatible shape and bind that one
278
+ * in the module — nothing else has to change.
237
279
  */
238
- import { createToken } from '@forinda/kickjs'
239
- import type { ${t}ResponseDTO } from '${r}/${n}-response.dto'
240
- import type { Create${t}DTO } from '${r}/create-${n}.dto'
241
- import type { Update${t}DTO } from '${r}/update-${n}.dto'
280
+ import { randomUUID } from 'node:crypto'
281
+ import { createToken, HttpException } from '@forinda/kickjs'
242
282
  import type { ParsedQuery } from '@forinda/kickjs'
283
+ import type { ${t}ResponseDTO } from '${i}/${n}-response.dto'
284
+ import type { Create${t}DTO } from '${i}/create-${n}.dto'
285
+ import type { Update${t}DTO } from '${i}/update-${n}.dto'
243
286
 
244
- export interface I${t}Repository {
245
- findById(id: string): Promise<${t}ResponseDTO | null>
246
- findAll(): Promise<${t}ResponseDTO[]>
247
- findPaginated(parsed: ParsedQuery): Promise<{ data: ${t}ResponseDTO[]; total: number }>
248
- create(dto: Create${t}DTO): Promise<${t}ResponseDTO>
249
- update(id: string, dto: Update${t}DTO): Promise<${t}ResponseDTO>
250
- delete(id: string): Promise<void>
287
+ export function ${s}() {
288
+ ${c}
251
289
  }
252
290
 
291
+ /** The contract, derived from the factory rather than declared beside it. */
292
+ export type ${t}Repository = ReturnType<typeof ${s}>
293
+
253
294
  /**
254
- * Collision-safe DI token bound to \`I${t}Repository\`.
295
+ * Collision-safe DI token bound to \`${t}Repository\`.
255
296
  * \`container.resolve(${t.toUpperCase()}_REPOSITORY)\` and
256
297
  * \`@Inject(${t.toUpperCase()}_REPOSITORY)\` both return the typed
257
- * interface — no manual generic, no \`any\` cast.
298
+ * contract — no manual generic, no \`any\` cast.
258
299
  *
259
- * The \`'${i}/'\` prefix matches the project scope so
300
+ * The \`'${a}/'\` prefix matches the project scope so
260
301
  * \`kick-lint\`'s \`token-reserved-prefix\` rule never fires —
261
302
  * adopters must NOT use the reserved \`'kick/'\` namespace.
262
303
  */
263
- export const ${t.toUpperCase()}_REPOSITORY = createToken<I${t}Repository>('${i}/${t}/repository')
264
- `}function F(e){let{pascal:t,kebab:n,repoPrefix:r=`../../domain/repositories`,dtoPrefix:i=`../../application/dtos`}=e;return`/**
265
- * In-Memory ${t} Repository
266
- *
267
- * Implements the repository interface using a Map.
268
- * Useful for prototyping and testing. Replace with a database implementation
269
- * (Drizzle, Prisma, etc.) for production use.
270
- *
271
- * @Repository() registers this class in the DI container as a singleton.
272
- */
273
- import { randomUUID } from 'node:crypto'
274
- import { Repository, HttpException } from '@forinda/kickjs'
275
- import type { ParsedQuery } from '@forinda/kickjs'
276
- import type { I${t}Repository } from '${r}/${n}.repository'
277
- import type { ${t}ResponseDTO } from '${i}/${n}-response.dto'
278
- import type { Create${t}DTO } from '${i}/create-${n}.dto'
279
- import type { Update${t}DTO } from '${i}/update-${n}.dto'
280
-
281
- @Repository()
282
- export class InMemory${t}Repository implements I${t}Repository {
283
- private store = new Map<string, ${t}ResponseDTO>()
284
-
285
- async findById(id: string): Promise<${t}ResponseDTO | null> {
286
- return this.store.get(id) ?? null
287
- }
288
-
289
- async findAll(): Promise<${t}ResponseDTO[]> {
290
- return Array.from(this.store.values())
291
- }
292
-
293
- async findPaginated(parsed: ParsedQuery): Promise<{ data: ${t}ResponseDTO[]; total: number }> {
294
- const all = Array.from(this.store.values())
295
- const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)
296
- return { data, total: all.length }
297
- }
298
-
299
- async create(dto: Create${t}DTO): Promise<${t}ResponseDTO> {
300
- const now = new Date().toISOString()
301
- const entity: ${t}ResponseDTO = {
302
- id: randomUUID(),
303
- ...dto,
304
- createdAt: now,
305
- updatedAt: now,
306
- }
307
- this.store.set(entity.id, entity)
308
- return entity
309
- }
304
+ export const ${t.toUpperCase()}_REPOSITORY = createToken<${t}Repository>('${a}/${t}/repository')
305
+ `}function se(e){let{pascal:t,kebab:n,plural:r=``,style:i=`define`,testHarness:a=!1}=e,o=i===`define`?`${t}Module()`:`${t}Module`;return`${a?`import { describe, it, expect, beforeEach } from 'vitest'
306
+ import request from 'supertest'
307
+ import { Container } from '@forinda/kickjs'
308
+ import { createTestApp } from '@forinda/kickjs-testing'
310
309
 
311
- async update(id: string, dto: Update${t}DTO): Promise<${t}ResponseDTO> {
312
- const existing = this.store.get(id)
313
- if (!existing) throw HttpException.notFound('${t} not found')
314
- const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }
315
- this.store.set(id, updated)
316
- return updated
317
- }
310
+ import { ${t}Module } from '../${n}.module'
318
311
 
319
- async delete(id: string): Promise<void> {
320
- if (!this.store.has(id)) throw HttpException.notFound('${t} not found')
321
- this.store.delete(id)
322
- }
323
- }
324
- `}function I(e){let{pascal:t,kebab:n,repoType:r=``,repoPrefix:i=`../../domain/repositories`,dtoPrefix:a=`../../application/dtos`}=e,o=r.charAt(0).toUpperCase()+r.slice(1).replace(/-([a-z])/g,(e,t)=>t.toUpperCase());return`/**
325
- * ${o} ${t} Repository
326
- *
327
- * Stub implementation for a custom '${r}' repository.
328
- * Implements the repository interface using an in-memory Map as a placeholder.
312
+ /**
313
+ * Where this module is mounted.
329
314
  *
330
- * TODO: Replace the in-memory Map with your ${r} data-access logic.
331
- * See I${t}Repository for the interface contract.
315
+ * \`createTestApp\` builds its own Application with the framework defaults —
316
+ * apiPrefix \`/api\` and defaultVersion \`1\` — whatever your \`bootstrap()\` uses,
317
+ * so this is correct as generated. If your app configures them differently,
318
+ * pass the same values in \`boot()\` below and update this to match, so the test
319
+ * exercises the paths production actually serves:
332
320
  *
333
- * @Repository() registers this class in the DI container as a singleton.
321
+ * defaultVersion: false → '/api/${r}'
322
+ * apiPrefix: '/v1' → '/v1/v1/${r}'
334
323
  */
335
- import { randomUUID } from 'node:crypto'
336
- import { Repository, HttpException } from '@forinda/kickjs'
337
- import type { ParsedQuery } from '@forinda/kickjs'
338
- import type { I${t}Repository } from '${i}/${n}.repository'
339
- import type { ${t}ResponseDTO } from '${a}/${n}-response.dto'
340
- import type { Create${t}DTO } from '${a}/create-${n}.dto'
341
- import type { Update${t}DTO } from '${a}/update-${n}.dto'
342
-
343
- @Repository()
344
- export class ${o}${t}Repository implements I${t}Repository {
345
- // TODO: Replace with your ${r} client/connection
346
- private store = new Map<string, ${t}ResponseDTO>()
347
-
348
- async findById(id: string): Promise<${t}ResponseDTO | null> {
349
- // TODO: Implement with ${r}
350
- return this.store.get(id) ?? null
351
- }
352
-
353
- async findAll(): Promise<${t}ResponseDTO[]> {
354
- // TODO: Implement with ${r}
355
- return Array.from(this.store.values())
356
- }
357
-
358
- async findPaginated(parsed: ParsedQuery): Promise<{ data: ${t}ResponseDTO[]; total: number }> {
359
- // TODO: Implement with ${r}
360
- const all = Array.from(this.store.values())
361
- const data = all.slice(parsed.pagination.offset, parsed.pagination.offset + parsed.pagination.limit)
362
- return { data, total: all.length }
363
- }
364
-
365
- async create(dto: Create${t}DTO): Promise<${t}ResponseDTO> {
366
- // TODO: Implement with ${r}
367
- const now = new Date().toISOString()
368
- const entity: ${t}ResponseDTO = {
369
- id: randomUUID(),
370
- ...dto,
371
- createdAt: now,
372
- updatedAt: now,
373
- }
374
- this.store.set(entity.id, entity)
375
- return entity
376
- }
377
-
378
- async update(id: string, dto: Update${t}DTO): Promise<${t}ResponseDTO> {
379
- // TODO: Implement with ${r}
380
- const existing = this.store.get(id)
381
- if (!existing) throw HttpException.notFound('${t} not found')
382
- const updated = { ...existing, ...dto, updatedAt: new Date().toISOString() }
383
- this.store.set(id, updated)
384
- return updated
385
- }
386
-
387
- async delete(id: string): Promise<void> {
388
- // TODO: Implement with ${r}
389
- if (!this.store.has(id)) throw HttpException.notFound('${t} not found')
390
- this.store.delete(id)
391
- }
392
- }
393
- `}function ce(e){let{pascal:t,kebab:n,plural:r=``}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
324
+ const BASE = '/api/v1/${r}'
325
+ `:`import { describe, it, beforeEach } from 'vitest'
394
326
  import { Container } from '@forinda/kickjs'
395
327
 
328
+ // Install \`@forinda/kickjs-testing\` and \`supertest\` to boot this module in a
329
+ // test: \`kick add @forinda/kickjs-testing\`. Until then every case below is a
330
+ // todo — the reporter lists them as outstanding rather than counting them as
331
+ // coverage.
332
+ `}
396
333
  describe('${t}Controller', () => {
397
334
  beforeEach(() => {
398
335
  Container.reset()
399
336
  })
400
337
 
401
- it('should be defined', () => {
402
- expect(true).toBe(true)
403
- })
404
-
405
- describe('POST /${r}', () => {
406
- it('should create a new ${n}', async () => {
407
- // TODO: Set up test module, call create endpoint, assert 201
408
- expect(true).toBe(true)
338
+ ${a?` /**
339
+ * Boot the module through the real pipeline.
340
+ *
341
+ * Drive \`app.handle\` rather than an Express app: it is the Application's own
342
+ * Node listener, so this test runs on whichever runtime the app is
343
+ * configured with.
344
+ */
345
+ async function boot() {
346
+ const { app, container } = await createTestApp({
347
+ modules: [${o}],
348
+ // Mirror your bootstrap() here if it overrides either, and update BASE:
349
+ // apiPrefix: '/api',
350
+ // defaultVersion: 1,
409
351
  })
410
- })
352
+ return { app, container, agent: request(app.handle.bind(app)) }
353
+ }
411
354
 
412
355
  describe('GET /${r}', () => {
413
- it('should return paginated ${r}', async () => {
414
- // TODO: Set up test module, call list endpoint, assert { data, meta }
415
- expect(true).toBe(true)
356
+ it('returns an empty page before anything is created', async () => {
357
+ const { agent } = await boot()
358
+ const res = await agent.get(BASE)
359
+
360
+ expect(res.status).toBe(200)
361
+ expect(res.body).toMatchObject({ data: [], meta: { page: 1 } })
416
362
  })
363
+
364
+ it.todo('returns created ${r}, paginated')
365
+ })`:` describe('GET /${r}', () => {
366
+ // Boot the module with createTestApp and assert { data, meta }.
367
+ it.todo('returns an empty page before anything is created')
368
+ it.todo('returns created ${r}, paginated')
369
+ })`}
370
+
371
+ // The cases below are scaffolding, not coverage. Each is \`it.todo\` so the
372
+ // reporter lists it as outstanding instead of counting it as a pass. Replace
373
+ // the todo with a real test as you implement each endpoint.
374
+
375
+ describe('POST /${r}', () => {
376
+ // Post a valid body, assert 201 and the created ${n} in the response.
377
+ it.todo('creates a ${n}')
378
+ // Post an invalid body, assert 422 and the validation detail.
379
+ it.todo('rejects an invalid body')
417
380
  })
418
381
 
419
382
  describe('GET /${r}/:id', () => {
420
- it('should return a ${n} by id', async () => {
421
- // TODO: Create a ${n}, then fetch by id, assert match
422
- expect(true).toBe(true)
423
- })
424
-
425
- it('should return 404 for non-existent ${n}', async () => {
426
- // TODO: Fetch non-existent id, assert 404
427
- expect(true).toBe(true)
428
- })
383
+ // Create one, fetch it by id, assert the payload matches.
384
+ it.todo('returns a ${n} by id')
385
+ // Fetch an id that does not exist, assert 404.
386
+ it.todo('returns 404 for an unknown id')
429
387
  })
430
388
 
431
389
  describe('PUT /${r}/:id', () => {
432
- it('should update an existing ${n}', async () => {
433
- // TODO: Create, update, assert changes
434
- expect(true).toBe(true)
435
- })
390
+ // Create, update, assert the change is reflected on a subsequent read.
391
+ it.todo('updates an existing ${n}')
436
392
  })
437
393
 
438
394
  describe('DELETE /${r}/:id', () => {
439
- it('should delete a ${n}', async () => {
440
- // TODO: Create, delete, assert gone
441
- expect(true).toBe(true)
442
- })
395
+ // Create, delete, assert a subsequent read is 404.
396
+ it.todo('deletes a ${n}')
443
397
  })
444
398
  })
445
- `}function le(e){let{pascal:t,kebab:n,plural:r=``,repoPrefix:i=`../infrastructure/repositories/in-memory-${n}.repository`}=e;return`import { describe, it, expect, beforeEach } from 'vitest'
446
- import { InMemory${t}Repository } from '${i}'
399
+ `}function ce(e){let{pascal:t,kebab:n,plural:r=``,repoPrefix:i=`../${n}.repository`}=e,a=`create${t}Repository`;return`import { describe, it, expect, beforeEach } from 'vitest'
400
+ import { ${a}, type ${t}Repository } from '${i}'
447
401
 
448
- describe('InMemory${t}Repository', () => {
449
- let repo: InMemory${t}Repository
402
+ describe('${t} repository', () => {
403
+ let repo: ${t}Repository
450
404
 
451
405
  beforeEach(() => {
452
- repo = new InMemory${t}Repository()
406
+ repo = ${a}()
453
407
  })
454
408
 
455
409
  it('should create and retrieve a ${n}', async () => {
@@ -506,7 +460,7 @@ describe('InMemory${t}Repository', () => {
506
460
  })
507
461
  `}function L(e){let{pascal:t,kebab:n}=e;return`import { Service, Inject, HttpException } from '@forinda/kickjs'
508
462
  import type { ParsedQuery } from '@forinda/kickjs'
509
- import { ${t.toUpperCase()}_REPOSITORY, type I${t}Repository } from './${n}.repository'
463
+ import { ${t.toUpperCase()}_REPOSITORY, type ${t}Repository } from './${n}.repository'
510
464
  import type { ${t}ResponseDTO } from './dtos/${n}-response.dto'
511
465
  import type { Create${t}DTO } from './dtos/create-${n}.dto'
512
466
  import type { Update${t}DTO } from './dtos/update-${n}.dto'
@@ -514,7 +468,7 @@ import type { Update${t}DTO } from './dtos/update-${n}.dto'
514
468
  @Service()
515
469
  export class ${t}Service {
516
470
  constructor(
517
- @Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: I${t}Repository,
471
+ @Inject(${t.toUpperCase()}_REPOSITORY) private readonly repo: ${t}Repository,
518
472
  ) {}
519
473
 
520
474
  async findById(id: string): Promise<${t}ResponseDTO | null> {
@@ -548,7 +502,7 @@ export const ${t.toUpperCase()}_QUERY_CONFIG: QueryFieldConfig = {
548
502
  sortable: ['name', 'createdAt'],
549
503
  searchable: ['name'],
550
504
  }
551
- `}async function ue(e){let{pascal:t,kebab:n,plural:r,style:i,write:a}=e;await a(`${n}.module.ts`,ae({pascal:t,kebab:n,plural:r,style:i})),await a(`${n}.controller.ts`,`import { Controller, Get, Post, Put, Delete, reply, type Ctx } from '@forinda/kickjs'
505
+ `}async function le(e){let{pascal:t,kebab:n,plural:r,style:i,write:a}=e;await a(`${n}.module.ts`,P({pascal:t,kebab:n,plural:r,style:i})),await a(`${n}.controller.ts`,`import { Controller, Get, Post, Put, Delete, reply, type Ctx } from '@forinda/kickjs'
552
506
 
553
507
  // \`Ctx<KickRoutes.${t}Controller['<method>']>\` is generated by
554
508
  // \`kick typegen\` (auto-run on \`kick dev\`).
@@ -588,7 +542,7 @@ export class ${t}Controller {
588
542
  return reply.noContent()
589
543
  }
590
544
  }
591
- `)}async function de(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,tokenScope:s,style:c,write:l}=e,u=e.swagger??!1;await l(`${n}.module.ts`,j({pascal:t,kebab:n,plural:r,repo:a,style:c})),await l(`${n}.constants.ts`,R({pascal:t,kebab:n})),await l(`${n}.controller.ts`,M({pascal:t,kebab:n,plural:r,pluralPascal:i,swagger:u})),await l(`${n}.service.ts`,L({pascal:t,kebab:n})),await l(`dtos/create-${n}.dto.ts`,N({pascal:t,kebab:n})),await l(`dtos/update-${n}.dto.ts`,oe({pascal:t,kebab:n})),await l(`dtos/${n}-response.dto.ts`,se({pascal:t,kebab:n})),await l(`${n}.repository.ts`,P({pascal:t,kebab:n,dtoPrefix:`./dtos`,tokenScope:s}));let d=a===`inmemory`,f=d?`in-memory-${n}`:`${E(a)}-${n}`,p=d?F({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`}):I({pascal:t,kebab:n,repoType:a,repoPrefix:`.`,dtoPrefix:`./dtos`});await l(`${f}.repository.ts`,p),o||(a!==`inmemory`&&await l(`in-memory-${n}.repository.ts`,F({pascal:t,kebab:n,repoPrefix:`.`,dtoPrefix:`./dtos`})),await l(`__tests__/${n}.controller.test.ts`,ce({pascal:t,kebab:n,plural:r})),await l(`__tests__/${n}.repository.test.ts`,le({pascal:t,kebab:n,plural:r,repoPrefix:`../in-memory-${n}.repository`})))}function fe(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function pe(n){let{name:r,modulesDir:s,noEntity:c,noTests:l,repo:u=`inmemory`,force:d,dryRun:f}=n,p=n.pluralize!==!1,h=n.pattern??`rest`;n.minimal&&(h=`minimal`);let g=E(r),_=w(r),v=p?D(g):g,y=p?O(_):_,b=m(s,v),x=[],S=d??!1,C={kebab:g,pascal:_,plural:v,pluralPascal:y,moduleDir:b,repo:u,noEntity:c??!1,noTests:l??!1,tokenScope:n.tokenScope??`app`,swagger:n.swagger??!1,style:n.style??`define`,write:async(n,r)=>{let s=m(b,n);if(f){x.push(s);return}if(!S&&await t(s)&&!await a({message:`File exists: ${o.dim(n)}. Overwrite?`,initialValue:!1})){i.warn(`Skipped: ${n}`);return}await e(s,r),x.push(s)},files:x};switch(h){case`minimal`:await ue(C);break;default:await de(C);break}return f||await z(s,_,v,g,C.style),x}async function z(n,r,i,a,o=`define`){let s=m(n,`index.ts`),c=await t(s),l=`./${i}/${a}.module`,u=o===`class`?`${r}Module`:`${r}Module()`;if(!c){await e(s,o===`class`?`import type { AppModuleEntry } from '@forinda/kickjs'
545
+ `)}async function ue(e){let{pascal:t,kebab:n,plural:r,pluralPascal:i,repo:a,noTests:o,tokenScope:s,style:c,testHarness:l,write:u}=e,d=e.swagger??!1;await u(`${n}.module.ts`,N({pascal:t,kebab:n,plural:r,repo:a,style:c})),await u(`${n}.constants.ts`,R({pascal:t,kebab:n})),await u(`${n}.controller.ts`,F({pascal:t,kebab:n,plural:r,pluralPascal:i,swagger:d})),await u(`${n}.service.ts`,L({pascal:t,kebab:n})),await u(`dtos/create-${n}.dto.ts`,ie({pascal:t,kebab:n})),await u(`dtos/update-${n}.dto.ts`,ae({pascal:t,kebab:n})),await u(`dtos/${n}-response.dto.ts`,oe({pascal:t,kebab:n})),await u(`${n}.repository.ts`,I({pascal:t,kebab:n,repoType:a,dtoPrefix:`./dtos`,tokenScope:s})),o||(await u(`__tests__/${n}.controller.test.ts`,se({pascal:t,kebab:n,plural:r,style:c,testHarness:l})),await u(`__tests__/${n}.repository.test.ts`,ce({pascal:t,kebab:n,plural:r,repoPrefix:`../${n}.repository`})))}function de(e){return e?typeof e==`string`?e:e.name:`inmemory`}async function fe(n){let{name:r,modulesDir:s,noEntity:c,noTests:l,repo:u=`inmemory`,force:d,dryRun:f}=n,p=n.pluralize!==!1,h=n.pattern??`rest`;n.minimal&&(h=`minimal`);let g=O(r),_=E(r),v=p?k(g):g,y=p?A(_):_,b=m(s,v),x=[],S=d??!1,C={kebab:g,pascal:_,plural:v,pluralPascal:y,moduleDir:b,repo:u,noEntity:c??!1,noTests:l??!1,tokenScope:n.tokenScope??`app`,swagger:n.swagger??!1,testHarness:n.testHarness??!1,style:n.style??`define`,write:async(n,r)=>{let s=m(b,n);if(f){x.push(s);return}if(!S&&await t(s)&&!await a({message:`File exists: ${o.dim(n)}. Overwrite?`,initialValue:!1})){i.warn(`Skipped: ${n}`);return}await e(s,r),x.push(s)},files:x};switch(h){case`minimal`:await le(C);break;default:await ue(C);break}return f||await z(s,_,v,g,C.style),x}async function z(n,r,i,a,o=`define`){let s=m(n,`index.ts`),c=await t(s),l=`./${i}/${a}.module`,u=o===`class`?`${r}Module`:`${r}Module()`;if(!c){await e(s,o===`class`?`import type { AppModuleEntry } from '@forinda/kickjs'
592
546
  import { ${r}Module } from '${l}'
593
547
 
594
548
  export const modules: AppModuleEntry[] = [${u}]
@@ -596,11 +550,11 @@ export const modules: AppModuleEntry[] = [${u}]
596
550
  import { ${r}Module } from '${l}'
597
551
 
598
552
  export const modules = defineModules().mount(${u})
599
- `);return}let d=await x(s,`utf-8`),f=`import { ${r}Module } from '${l}'`,p=k(l);if(!RegExp(`^import\\s*\\{[^}]*\\b${k(r)}Module\\b[^}]*\\}\\s*from\\s*['"]${p}['"]`,`m`).test(d)){let e=d.lastIndexOf(`import `);if(e!==-1){let t=d.indexOf(`
553
+ `);return}let d=await x(s,`utf-8`),f=`import { ${r}Module } from '${l}'`,p=j(l);if(!RegExp(`^import\\s*\\{[^}]*\\b${j(r)}Module\\b[^}]*\\}\\s*from\\s*['"]${p}['"]`,`m`).test(d)){let e=d.lastIndexOf(`import `);if(e!==-1){let t=d.indexOf(`
600
554
  `,e);d=d.slice(0,t+1)+f+`
601
555
  `+d.slice(t+1)}else d=f+`
602
- `+d}let h=V(d);if(h){let e=d.slice(h.rhsStart,h.rhsEnd+1);RegExp(`\\b${k(r)}Module\\b`).test(e)||(d=B(d,u))}else d=B(d,u);await S(s,d,`utf-8`)}function B(e,t){let n=V(e);if(!n)return e;if(n.shape===`array`){let r=e.slice(n.rhsStart+1,n.rhsEnd),i=r.trim(),a;if(!i)a=`[${t}]`;else{let e=i.endsWith(`,`)?``:`,`;a=`[${r.trimEnd()}${e} ${t}]`}return e.slice(0,n.rhsStart)+a+e.slice(n.rhsEnd+1)}return`${e.slice(0,n.chainEnd)}\n .mount(${t})${e.slice(n.chainEnd)}`}function V(e){let t=/export\s+const\s+modules\b[^=]*=/.exec(e);if(!t)return null;let n=t.index+t[0].length;for(;n<e.length&&/\s/.test(e[n]??``);)n++;if(e[n]===`[`){let t=he(e,n);return t===-1?null:{shape:`array`,rhsStart:n,rhsEnd:t}}if(e.slice(n,n+13)===`defineModules`){let t=me(e,n);return t===-1?null:{shape:`chain`,rhsStart:n,rhsEnd:t-1,chainEnd:t}}return null}function me(e,t=0){let n=/defineModules\s*\(/g;n.lastIndex=t;let r=n.exec(e);if(!r)return-1;let i=r.index+r[0].length-1;if(e[i]!==`(`||(i=U(e,i),i===-1))return-1;for(i++;;){let t=i;for(;t<e.length&&/\s/.test(e[t]??``);)t++;if(e[t]!==`.`||e.slice(t,t+6)!==`.mount`)break;for(t+=6;t<e.length&&/\s/.test(e[t]??``);)t++;if(e[t]!==`(`)break;let n=U(e,t);if(n===-1)break;i=n+1}return i}function H(e,t){let n=e.slice(t,t+2);if(n===`//`){for(t+=2;t<e.length&&e[t]!==`
603
- `;)t++;return t}if(n===`/*`){for(t+=2;t+1<e.length&&(e[t]!==`*`||e[t+1]!==`/`);)t++;return t+2}return t}function he(e,t){if(e[t]!==`[`)return-1;let n=1,r=t+1;for(;r<e.length;){let t=e.slice(r,r+2);if(t===`//`||t===`/*`){r=H(e,r);continue}let i=e[r]??``;if(i===`'`||i===`"`||i==="`"){let t=i;for(r++;r<e.length&&e[r]!==t;)e[r]===`\\`&&r++,r++;r<e.length&&r++;continue}if(i===`[`)n++;else if(i===`]`&&(n--,n===0))return r;r++}return-1}function U(e,t){if(e[t]!==`(`)return-1;let n=1,r=t+1;for(;r<e.length;){let t=e.slice(r,r+2);if(t===`//`||t===`/*`){r=H(e,r);continue}let i=e[r]??``;if(i===`'`||i===`"`||i==="`"){let t=i;for(r++;r<e.length&&e[r]!==t;)e[r]===`\\`&&r++,r++;r<e.length&&r++;continue}if(i===`(`)n++;else if(i===`)`&&(n--,n===0))return r;r++}return-1}async function ge(t){let{name:n,outDir:r}=t,i=E(n),a=w(n),o=[],s=m(r,`${i}.adapter.ts`);return await e(s,`import {
556
+ `+d}let h=V(d);if(h){let e=d.slice(h.rhsStart,h.rhsEnd+1);RegExp(`\\b${j(r)}Module\\b`).test(e)||(d=B(d,u))}else d=B(d,u);await S(s,d,`utf-8`)}function B(e,t){let n=V(e);if(!n)return e;if(n.shape===`array`){let r=e.slice(n.rhsStart+1,n.rhsEnd),i=r.trim(),a;if(!i)a=`[${t}]`;else{let e=i.endsWith(`,`)?``:`,`;a=`[${r.trimEnd()}${e} ${t}]`}return e.slice(0,n.rhsStart)+a+e.slice(n.rhsEnd+1)}return`${e.slice(0,n.chainEnd)}\n .mount(${t})${e.slice(n.chainEnd)}`}function V(e){let t=/export\s+const\s+modules\b[^=]*=/.exec(e);if(!t)return null;let n=t.index+t[0].length;for(;n<e.length&&/\s/.test(e[n]??``);)n++;if(e[n]===`[`){let t=me(e,n);return t===-1?null:{shape:`array`,rhsStart:n,rhsEnd:t}}if(e.slice(n,n+13)===`defineModules`){let t=pe(e,n);return t===-1?null:{shape:`chain`,rhsStart:n,rhsEnd:t-1,chainEnd:t}}return null}function pe(e,t=0){let n=/defineModules\s*\(/g;n.lastIndex=t;let r=n.exec(e);if(!r)return-1;let i=r.index+r[0].length-1;if(e[i]!==`(`||(i=U(e,i),i===-1))return-1;for(i++;;){let t=i;for(;t<e.length&&/\s/.test(e[t]??``);)t++;if(e[t]!==`.`||e.slice(t,t+6)!==`.mount`)break;for(t+=6;t<e.length&&/\s/.test(e[t]??``);)t++;if(e[t]!==`(`)break;let n=U(e,t);if(n===-1)break;i=n+1}return i}function H(e,t){let n=e.slice(t,t+2);if(n===`//`){for(t+=2;t<e.length&&e[t]!==`
557
+ `;)t++;return t}if(n===`/*`){for(t+=2;t+1<e.length&&(e[t]!==`*`||e[t+1]!==`/`);)t++;return t+2}return t}function me(e,t){if(e[t]!==`[`)return-1;let n=1,r=t+1;for(;r<e.length;){let t=e.slice(r,r+2);if(t===`//`||t===`/*`){r=H(e,r);continue}let i=e[r]??``;if(i===`'`||i===`"`||i==="`"){let t=i;for(r++;r<e.length&&e[r]!==t;)e[r]===`\\`&&r++,r++;r<e.length&&r++;continue}if(i===`[`)n++;else if(i===`]`&&(n--,n===0))return r;r++}return-1}function U(e,t){if(e[t]!==`(`)return-1;let n=1,r=t+1;for(;r<e.length;){let t=e.slice(r,r+2);if(t===`//`||t===`/*`){r=H(e,r);continue}let i=e[r]??``;if(i===`'`||i===`"`||i==="`"){let t=i;for(r++;r<e.length&&e[r]!==t;)e[r]===`\\`&&r++,r++;r<e.length&&r++;continue}if(i===`(`)n++;else if(i===`)`&&(n--,n===0))return r;r++}return-1}async function he(t){let{name:n,outDir:r}=t,i=O(n),a=E(n),o=[],s=m(r,`${i}.adapter.ts`);return await e(s,`import {
604
558
  defineAdapter,
605
559
  type AdapterContext,
606
560
  type AdapterMiddleware,
@@ -658,7 +612,9 @@ export const ${a}Adapter = defineAdapter<${a}AdapterConfig>({
658
612
 
659
613
  return {
660
614
  /**
661
- * Express middleware entries the Application mounts at named phases.
615
+ * Connect-style middleware entries the Application mounts at named
616
+ * phases. Works on every runtime — Express, Fastify and h3 all accept
617
+ * the connect signature through the runtime seam.
662
618
  *
663
619
  * \`phase\` controls where each handler sits in the pipeline:
664
620
  * 'beforeGlobal' | 'afterGlobal' | 'beforeRoutes' | 'afterRoutes'.
@@ -773,13 +729,74 @@ export const ${a}Adapter = defineAdapter<${a}AdapterConfig>({
773
729
  // Example: await this.pool.end()
774
730
  // Example: clearInterval(this.heartbeatTimer)
775
731
  },
732
+
733
+ /**
734
+ * Reports this adapter's backing service to \`GET /health/ready\`.
735
+ *
736
+ * The Application aggregates every adapter's check through
737
+ * \`Promise.allSettled\` and serves the combined result, so contributing
738
+ * one here is what makes your dependency visible to the built-in
739
+ * readiness endpoint — no route of your own required.
740
+ *
741
+ * Keep it cheap and time-boxed: readiness is polled by orchestrators.
742
+ *
743
+ * Delete this hook if your adapter has nothing to report.
744
+ */
745
+ async onHealthCheck(): Promise<{ name: string; status: 'up' | 'down' }> {
746
+ // Example:
747
+ // try {
748
+ // await _config.client.ping()
749
+ // return { name: '${i}', status: 'up' }
750
+ // } catch {
751
+ // return { name: '${i}', status: 'down' }
752
+ // }
753
+ return { name: '${i}', status: 'up' }
754
+ },
755
+
756
+ /**
757
+ * Snapshot for the DevTools topology view (\`/_debug\`).
758
+ *
759
+ * Must be CHEAP — the topology endpoint polls on a short interval, so
760
+ * \`state\` and \`metrics\` should be counters and flags already in memory,
761
+ * never a database round trip. Async is allowed for that reason, not as
762
+ * an invitation to do work.
763
+ *
764
+ * Delete this hook if the adapter has nothing worth showing.
765
+ */
766
+ // introspect(): IntrospectionSnapshot {
767
+ // return {
768
+ // protocolVersion: 1,
769
+ // state: { connected: true },
770
+ // metrics: { handled: 0 },
771
+ // tokens: [],
772
+ // }
773
+ // },
774
+
775
+ /**
776
+ * DevTools panels this adapter contributes.
777
+ *
778
+ * Import \`defineDevtoolsTab\` from \`@forinda/kickjs-devtools-kit\`;
779
+ * \`@forinda/kickjs\` deliberately does not depend on the kit at runtime.
780
+ *
781
+ * Delete this hook unless the adapter ships a panel.
782
+ */
783
+ // devtoolsTabs() {
784
+ // return [
785
+ // defineDevtoolsTab({
786
+ // id: '${i}',
787
+ // title: '${a}',
788
+ // kind: 'html',
789
+ // render: () => '<p>${a} adapter</p>',
790
+ // }),
791
+ // ]
792
+ // },
776
793
  }
777
794
  },
778
795
  })
779
- `),o.push(s),o}const _e={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,contributor:`contributors`};function W(e){let{type:t,outDir:n,moduleName:r,modulesDir:i=`src/modules`,defaultDir:a,shouldPluralize:o=!0}=e;if(n)return g(n);if(r){let e=_e,n=E(r),a=o?D(n):n,s=e[t]??``,c=m(i,a);return g(s?m(c,s):c)}return g(a)}async function ve(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=t.runtime===`express`,s=t.runtime?`this project runs on ${t.runtime} and has no
780
- // \`express\``:"this project declares no `runtime` in kick.config.ts, so it\n // may have no `express`",c=W({type:`middleware`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/middleware`,pattern:a,shouldPluralize:t.pluralize??!0}),l=E(n),u=T(n),d=[],f=m(c,`${l}.middleware.ts`);return await e(f,`${o?`import type { Request, Response, NextFunction } from 'express'`:`import type { IncomingMessage, ServerResponse } from 'node:http'`}
796
+ `),o.push(s),o}const ge={controller:``,service:``,dto:`dtos`,guard:`guards`,middleware:`middleware`,contributor:`contributors`};function W(e){let{type:t,outDir:n,moduleName:r,modulesDir:i=`src/modules`,defaultDir:a,shouldPluralize:o=!0}=e;if(n)return g(n);if(r){let e=ge,n=O(r),a=o?k(n):n,s=e[t]??``,c=m(i,a);return g(s?m(c,s):c)}return g(a)}async function _e(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=t.runtime===`express`,s=t.runtime?`this project runs on ${t.runtime} and has no
797
+ // \`express\``:"this project declares no `runtime` in kick.config.ts, so it\n // may have no `express`",c=W({type:`middleware`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/middleware`,pattern:a,shouldPluralize:t.pluralize??!0}),l=O(n),u=D(n),d=[],f=m(c,`${l}.middleware.ts`);return await e(f,`${o?`import type { Request, Response, NextFunction } from 'express'`:`import type { IncomingMessage, ServerResponse } from 'node:http'`}
781
798
 
782
- export interface ${w(n)}Options {
799
+ export interface ${E(n)}Options {
783
800
  // Add configuration options here. The factory below closes over the
784
801
  // resolved options object; pass them at the call site —
785
802
  // \`${u}({ foo: 'bar' })\` — and the closure preserves them across
@@ -787,10 +804,10 @@ export interface ${w(n)}Options {
787
804
  }
788
805
 
789
806
  /**
790
- * ${w(n)} middleware.
807
+ * ${E(n)} middleware.
791
808
  *
792
809
  * Usage in bootstrap (fires on every request):
793
- * middleware: [${u}()]
810
+ * middlewares: [${u}()]
794
811
  *
795
812
  * Usage with adapter — phase controls *when* the handler runs:
796
813
  *
@@ -819,7 +836,7 @@ export interface ${w(n)}Options {
819
836
  * Usage with @Middleware decorator:
820
837
  * @Middleware(${u}())
821
838
  */
822
- export function ${u}(options: ${w(n)}Options = {}) {
839
+ export function ${u}(options: ${E(n)}Options = {}) {
823
840
  ${o?" // Typed from `express` because that is this project's runtime\n // (`runtime: 'express'` in kick.config.ts). Global middleware is\n // connect-style on every engine, but only Express hands the handler its own\n // request / response objects — so `req.originalUrl`, `req.query` and\n // `res.json()` are all available here. Switching `runtime` later means\n // re-typing this signature from `node:http`.\n return (req: Request, res: Response, next: NextFunction) => {":` // Typed from \`node:http\`, not \`express\`. Global middleware is connect-style
824
841
  // on every engine, but ${s} dependency to import types from. Under Fastify the handler
825
842
  // receives \`request.raw\` / a reply driver, and under h3 the node objects, so
@@ -832,7 +849,7 @@ ${o?" // Typed from `express` because that is this project's runtime\n // (`ru
832
849
  next()
833
850
  }
834
851
  }
835
- `),d.push(f),d}async function ye(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=W({type:`guard`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/guards`,pattern:a,shouldPluralize:t.pluralize??!0}),s=E(n),c=T(n),l=w(n),u=[],d=m(o,`${s}.guard.ts`);return await e(d,`import { Container, HttpException } from '@forinda/kickjs'
852
+ `),d.push(f),d}async function ve(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=W({type:`guard`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/guards`,pattern:a,shouldPluralize:t.pluralize??!0}),s=O(n),c=D(n),l=E(n),u=[],d=m(o,`${s}.guard.ts`);return await e(d,`import { Container, HttpException } from '@forinda/kickjs'
836
853
  import type { RequestContext } from '@forinda/kickjs'
837
854
 
838
855
  /**
@@ -872,7 +889,7 @@ export async function ${c}Guard(ctx: RequestContext, next: () => void): Promise<
872
889
  ctx.problem.unauthorized({ detail: 'Invalid or expired token' })
873
890
  }
874
891
  }
875
- `),u.push(d),u}async function be(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=W({type:`service`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/services`,pattern:a,shouldPluralize:t.pluralize??!0}),s=E(n),c=w(n),l=[],u=m(o,`${s}.service.ts`);return await e(u,`import { Service } from '@forinda/kickjs'
892
+ `),u.push(d),u}async function ye(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=W({type:`service`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/services`,pattern:a,shouldPluralize:t.pluralize??!0}),s=O(n),c=E(n),l=[],u=m(o,`${s}.service.ts`);return await e(u,`import { Service } from '@forinda/kickjs'
876
893
 
877
894
  @Service()
878
895
  export class ${c}Service {
@@ -881,7 +898,7 @@ export class ${c}Service {
881
898
  // @Inject(MY_REPO) private readonly repo: IMyRepository,
882
899
  // ) {}
883
900
  }
884
- `),l.push(u),l}async function xe(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=W({type:`controller`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/controllers`,pattern:a,shouldPluralize:t.pluralize??!0}),s=E(n),c=w(n),l=[],u=m(o,`${s}.controller.ts`);return await e(u,`import { Controller, Get, Post, Put, Delete, reply, type Ctx } from '@forinda/kickjs'
901
+ `),l.push(u),l}async function be(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=W({type:`controller`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/controllers`,pattern:a,shouldPluralize:t.pluralize??!0}),s=O(n),c=E(n),l=[],u=m(o,`${s}.controller.ts`);return await e(u,`import { Controller, Get, Post, Put, Delete, reply, type Ctx } from '@forinda/kickjs'
885
902
 
886
903
  // \`Ctx<KickRoutes.${c}Controller['<method>']>\` is generated by
887
904
  // \`kick typegen\` (auto-run on \`kick dev\`). After the first run, your IDE
@@ -923,7 +940,7 @@ export class ${c}Controller {
923
940
  return reply.noContent()
924
941
  }
925
942
  }
926
- `),l.push(u),l}async function Se(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=W({type:`dto`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/dtos`,pattern:a,shouldPluralize:t.pluralize??!0}),s=E(n),c=w(n),l=T(n),u=[],d=m(o,`${s}.dto.ts`);return await e(d,`import { z } from 'zod'
943
+ `),l.push(u),l}async function xe(t){let{name:n,moduleName:r,modulesDir:i,pattern:a}=t,o=W({type:`dto`,outDir:t.outDir,moduleName:r,modulesDir:i,defaultDir:`src/dtos`,pattern:a,shouldPluralize:t.pluralize??!0}),s=O(n),c=E(n),l=D(n),u=[],d=m(o,`${s}.dto.ts`);return await e(d,`import { z } from 'zod'
927
944
 
928
945
  export const ${l}Schema = z.object({
929
946
  // Define your schema fields here
@@ -931,20 +948,20 @@ export const ${l}Schema = z.object({
931
948
  })
932
949
 
933
950
  export type ${c}DTO = z.infer<typeof ${l}Schema>
934
- `),u.push(d),u}function Ce(){let e=(process.env.LOG_LEVEL??process.env.KICKJS_LOG_LEVEL??``).toLowerCase();return e===`debug`||e===`trace`}var we=class extends Error{drifted;constructor(e){let t=e.map(e=>` ${e.id} → ${e.outFile}`).join(`
935
- `);super(`kick typegen --check: ${e.length} generated file(s) are out of date:\n${t}\n Run \`kick typegen\` and commit the result.`),this.name=`TypegenDriftError`,this.drifted=e}};function Te(e){return e}function Ee(e){let t=new Map;return{report(n,r){let i=r instanceof Error?r.message:String(r);t.get(n)!==i&&(t.set(n,i),e(` kick typegen: ${n} pass failed (${i}) — types in .kickjs/types may be stale`))},clear(e){t.delete(e)}}}const De=`__kickjs_typegen_owner`;function Oe(e){let{cwd:t,config:n}=e,r=e.debounceMs??100,i=e.pipeline??{runTypegen:async e=>(await import(`./typegen-BDYmZKFF.mjs`).then(e=>e.n)).runTypegen(e),runAllPluginTypegens:async e=>(await import(`./run-plugins-CHv-_CI_.mjs`)).runAllPluginTypegens(e),writeTypegenArtifacts:async(e,t,n)=>(await import(`./typegen-BDYmZKFF.mjs`).then(e=>e.n)).writeTypegenArtifacts(e,t,n),buildAssets:async(e,t)=>(await import(`./build-BJd10HpG.mjs`).then(e=>e.n)).buildAssets(e,t)},a=n?.typegen?.schemaValidator??`zod`,o=n?.typegen?.envFile,s=f.resolve(t,n?.typegen?.outDir??`.kickjs/types`),c=n?.assetMap?Object.values(n.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>f.resolve(t,e)):[],l=!!n?.assetMap&&Object.keys(n.assetMap).length>0,u=e=>e.replaceAll(`\\`,`/`),d=c.map(u),p=e=>{let t=u(e);return d.some(e=>t===e||t.startsWith(`${e}/`))},m=Ee(e.emitWarning),h=null,g=!1,_=new Set,v=new Set,y=!1,b=!1;function x(r,c){i.runTypegen({cwd:t,silent:!0,allowDuplicates:!0,schemaValidator:a,envFile:o,srcDir:n?.typegen?.srcDir,outDir:n?.typegen?.outDir,assetMap:n?.assetMap,changedFiles:r,runPlugins:!1}).then(()=>m.clear(`scan`)).catch(e=>m.report(`scan`,e)),i.runAllPluginTypegens({cwd:t,config:n,silent:!0,changedFiles:r,watch:!0}).then(e=>i.writeTypegenArtifacts(s,e,!0)).then(()=>m.clear(`plugins`)).catch(e=>m.report(`plugins`,e)).finally(()=>e.onPassComplete?.()),c&&n&&i.buildAssets(n,{cwd:t,silent:!0}).catch(()=>{})}function S(){let e=y?void 0:{changed:[..._],removed:[...v]},t=b;_.clear(),v.clear(),y=!1,b=!1,x(e,t)}return{assetSrcRoots:c,handleWatchEvent(e,t){if(!g&&!u(t).includes(`/.kickjs/`)){if(e===`unlinkDir`)y=!0,l&&(b=!0);else{if(t.endsWith(`.d.ts`))return;let n=/\.(ts|tsx|mts|cts)$/.test(t),r=p(t);if(!n&&!r)return;r&&l&&(b=!0),n&&(e===`unlink`?(v.add(t),_.delete(t)):(_.add(t),v.delete(t)))}h&&clearTimeout(h),h=setTimeout(S,r)}},runOnce(){g||x(void 0,l)},dispose(){g=!0,h&&clearTimeout(h),h=null}}}function ke(e){return E(e).replace(/-/g,`_`)}function G(e){let t=e.cwd??process.cwd(),n=e.projectRoot??u(t),r=e.pluralize??!0,i=w(e.name),a=T(e.name),o=E(e.name),s=ke(e.name),c={name:e.name,pascal:i,camel:a,kebab:o,snake:s,modulesDir:e.modulesDir??`src/modules`,cwd:t,projectRoot:n,args:e.args??[],flags:e.flags??{}};if(r){let e=D(o);c.pluralKebab=e,c.pluralPascal=w(e),c.pluralCamel=T(e)}return c}function Ae(e,t){return g(e.cwd,t)}async function je(e){return import(ee(e).href)}const K=new Map;async function q(e){let t=K.get(e);if(t)return t;let n=Me(e);return K.set(e,n),n}async function Me(e){let t=g(e,`package.json`);if(!_(t))return{generators:[],loaded:[],failed:[]};let n=Ne(JSON.parse(await x(t,`utf-8`))),r=d(g(e,`package.json`)),i=[],a=[],o=[];for(let e of n){let t;try{t=r.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await x(t,`utf-8`))}catch(t){o.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let s=n.kickjs.generators,c=g(p(t),s);if(!_(c)){o.push({source:e,reason:`kickjs.generators points to missing file: ${s}`});continue}let l;try{l=await je(c)}catch(t){o.push({source:e,reason:`failed to import manifest: ${t}`});continue}let u=l.default;if(!Array.isArray(u)){o.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of u){if(!Pe(t)){o.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}i.push({source:e,spec:t})}a.push(e)}return{generators:i,loaded:a,failed:o}}function Ne(e){let t=new Set;for(let n of[e.dependencies,e.devDependencies,e.peerDependencies])if(n)for(let e of Object.keys(n))t.add(e);return Array.from(t)}function Pe(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function Fe(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return J(r.spec,r.source,e,n);let i=Le(await q(n),e.generatorName);return i?J(i.spec,i.source,e,n):null}async function Ie(e,t=[]){let n=await q(e),r=new Set(t.map(e=>e.spec.name)),i=n.generators.filter(e=>!r.has(e.spec.name));return{generators:[...t,...i],loaded:n.loaded,failed:n.failed}}function Le(e,t){return e.generators.find(e=>e.spec.name===t)}async function J(t,n,r,i){let a=G({name:r.itemName,args:r.args,flags:r.flags,modulesDir:r.modulesDir,pluralize:r.pluralize,cwd:i,projectRoot:r.projectRoot}),o=await t.files(a),s=[];for(let t of o){let n=Ae(a,t.path);await e(n,t.content),s.push(n)}return{files:s,source:n}}function Re(e){return e}function ze(e){return e}function Be(e){try{return JSON.parse(v(e,`utf-8`))}catch{return null}}function Y(e){try{return v(e,`utf-8`)}catch{return null}}function Ve(e){let t=e.replace(/\/\*[\s\S]*?\*\//g,``).replace(/(^|[^:"'\\])\/\/.*$/gm,`$1`).replace(/,(\s*[}\]])/g,`$1`);try{return JSON.parse(t)}catch{return null}}function He(e){let t=m(e,`tsconfig.json`);if(_(t))return X(t,new Set)}function X(e,t){if(t.has(e)||t.size>=16)return null;t.add(e);let n=Y(e);if(n===null)return null;let r=Ve(n);if(typeof r!=`object`||!r)return null;let i=Array.isArray(r.extends)?r.extends:typeof r.extends==`string`?[r.extends]:[],a={};for(let n of i){if(typeof n!=`string`)continue;let r=Ue(p(e),n);if(!r)continue;let i=X(r,t);i&&Object.assign(a,i.compilerOptions)}return r.compilerOptions={...a,...r.compilerOptions},r}function Ue(e,t){if(t.startsWith(`.`)||t.startsWith(`/`))return Z(g(e,t));let n=e;for(;;){let e=Z(m(n,`node_modules`,t));if(e)return e;let r=p(n);if(r===n)break;n=r}return null}function Z(e){if(_(e)){try{if(b(e).isDirectory()){let t=m(e,`tsconfig.json`);return _(t)?t:null}}catch{return null}return e}let t=`${e}.json`;return _(t)?t:null}function We(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function Ge(){let e=process.version,t=Number.parseInt(e.replace(/^v/,``).split(`.`)[0],10);return Number.isNaN(t)||t<20?{name:`Node version`,status:`fail`,message:e,fix:`KickJS requires Node 20 or newer.
936
- Install a supported version via nvm / fnm / volta.`}:{name:`Node version`,status:`pass`,message:e}}function Ke(e){if(!e.pkg)return{name:`@forinda/kickjs installed`,status:`warn`,message:`no package.json`};let t={...e.pkg.dependencies,...e.pkg.peerDependencies};return t[`@forinda/kickjs`]?{name:`@forinda/kickjs installed`,status:`pass`,message:t[`@forinda/kickjs`]}:{name:`@forinda/kickjs installed`,status:`fail`,fix:"This directory does not look like a KickJS project — `@forinda/kickjs` is not in your package.json. Run `kick doctor` from the project root, or scaffold a fresh project with `kick new <name>`."}}function qe(e){if(!e.pkg)return null;let t={...e.pkg.dependencies,...e.pkg.peerDependencies};return t[`@forinda/kickjs`]&&!t.express?{name:`express installed`,status:`fail`,fix:"`@forinda/kickjs` declares `express` as a required peer dependency, but your package.json does not include it. Install: pnpm add express"}:t.express?{name:`express installed`,status:`pass`,message:t.express}:null}const Je={express:[],fastify:[`fastify`,`@fastify/middie`],h3:[`h3`]};function Ye(e){if(!e.pkg||e.runtime===`express`)return null;let t={...e.pkg.dependencies,...e.pkg.peerDependencies,...e.pkg.devDependencies},n=Je[e.runtime].filter(e=>!t[e]),r=`runtime engine (${e.runtime})`;return n.length>0?{name:r,status:`fail`,fix:`Resolved runtime '${e.runtime}' is missing engine peer(s): ${n.join(`, `)}.\nInstall: pnpm add ${n.join(` `)}`}:{name:r,status:`pass`}}function Xe(e){if(!e.pkg||!Ze(e.cwd))return null;let t=s[e.runtime],n=`upload driver (${e.runtime})`;return t.prod?{...e.pkg.dependencies,...e.pkg.peerDependencies,...e.pkg.devDependencies}[t.prod]?{name:n,status:`pass`,message:t.prod}:{name:n,status:`fail`,fix:`This project uses file uploads on the '${e.runtime}' runtime, which needs '${t.prod}'.\nInstall it: kick add upload (or pnpm add ${t.prod})`}:{name:n,status:`pass`,message:`native multipart`}}const Q=2e3;function Ze(e){let t=m(e,`src`);if(!_(t))return!1;let n=/@FileUpload\b|\bupload\.(single|array|none)\s*\(/,r=[t],i=0;for(;r.length>0&&i<Q;){let e=r.pop(),t;try{t=y(e,{withFileTypes:!0})}catch{continue}for(let a of t){if(i>=Q)break;let t=m(e,a.name);if(a.isDirectory()){a.name!==`node_modules`&&r.push(t);continue}if(/\.(ts|tsx|mts|cts)$/.test(a.name)&&(i++,n.test(Y(t)??``)))return!0}}return!1}function Qe(e){if(!e.pkg)return{name:`reflect-metadata installed`,status:`warn`,message:`no package.json`};let t={...e.pkg.dependencies,...e.pkg.peerDependencies,...e.pkg.devDependencies};return t[`reflect-metadata`]?{name:`reflect-metadata installed`,status:`pass`,message:t[`reflect-metadata`]}:{name:`reflect-metadata installed`,status:`fail`,fix:`KickJS decorators require the reflect-metadata polyfill.
951
+ `),u.push(d),u}function Se(){let e=(process.env.LOG_LEVEL??process.env.KICKJS_LOG_LEVEL??``).toLowerCase();return e===`debug`||e===`trace`}var Ce=class extends Error{drifted;constructor(e){let t=e.map(e=>` ${e.id} → ${e.outFile}`).join(`
952
+ `);super(`kick typegen --check: ${e.length} generated file(s) are out of date:\n${t}\n Run \`kick typegen\` and commit the result.`),this.name=`TypegenDriftError`,this.drifted=e}};function we(e){return e}function Te(e){let t=new Map;return{report(n,r){let i=r instanceof Error?r.message:String(r);t.get(n)!==i&&(t.set(n,i),e(` kick typegen: ${n} pass failed (${i}) — types in .kickjs/types may be stale`))},clear(e){t.delete(e)}}}const Ee=`__kickjs_typegen_owner`;function De(e){let{cwd:t,config:n}=e,r=e.debounceMs??100,i=e.pipeline??{runTypegen:async e=>(await import(`./typegen-XbrxqReA.mjs`).then(e=>e.n)).runTypegen(e),runAllPluginTypegens:async e=>(await import(`./run-plugins-CA8eTAtB.mjs`)).runAllPluginTypegens(e),writeTypegenArtifacts:async(e,t,n)=>(await import(`./typegen-XbrxqReA.mjs`).then(e=>e.n)).writeTypegenArtifacts(e,t,n),buildAssets:async(e,t)=>(await import(`./build-BPVxGl5d.mjs`).then(e=>e.n)).buildAssets(e,t)},a=n?.typegen?.schemaValidator??`zod`,o=n?.typegen?.envFile,s=f.resolve(t,n?.typegen?.outDir??`.kickjs/types`),c=n?.assetMap?Object.values(n.assetMap).map(e=>e?.src).filter(e=>typeof e==`string`&&e.length>0).map(e=>f.resolve(t,e)):[],l=!!n?.assetMap&&Object.keys(n.assetMap).length>0,u=e=>e.replaceAll(`\\`,`/`),d=c.map(u),p=e=>{let t=u(e);return d.some(e=>t===e||t.startsWith(`${e}/`))},m=Te(e.emitWarning),h=null,g=!1,_=new Set,v=new Set,y=!1,b=!1;function x(r,c){i.runTypegen({cwd:t,silent:!0,allowDuplicates:!0,schemaValidator:a,envFile:o,srcDir:n?.typegen?.srcDir,outDir:n?.typegen?.outDir,assetMap:n?.assetMap,changedFiles:r,runPlugins:!1}).then(()=>m.clear(`scan`)).catch(e=>m.report(`scan`,e)),i.runAllPluginTypegens({cwd:t,config:n,silent:!0,changedFiles:r,watch:!0}).then(e=>i.writeTypegenArtifacts(s,e,!0)).then(()=>m.clear(`plugins`)).catch(e=>m.report(`plugins`,e)).finally(()=>e.onPassComplete?.()),c&&n&&i.buildAssets(n,{cwd:t,silent:!0}).catch(()=>{})}function S(){let e=y?void 0:{changed:[..._],removed:[...v]},t=b;_.clear(),v.clear(),y=!1,b=!1,x(e,t)}return{assetSrcRoots:c,handleWatchEvent(e,t){if(!g&&!u(t).includes(`/.kickjs/`)){if(e===`unlinkDir`)y=!0,l&&(b=!0);else{if(t.endsWith(`.d.ts`))return;let n=/\.(ts|tsx|mts|cts)$/.test(t),r=p(t);if(!n&&!r)return;r&&l&&(b=!0),n&&(e===`unlink`?(v.add(t),_.delete(t)):(_.add(t),v.delete(t)))}h&&clearTimeout(h),h=setTimeout(S,r)}},runOnce(){g||x(void 0,l)},dispose(){g=!0,h&&clearTimeout(h),h=null}}}function Oe(e){return O(e).replace(/-/g,`_`)}function G(e){let t=e.cwd??process.cwd(),n=e.projectRoot??u(t),r=e.pluralize??!0,i=E(e.name),a=D(e.name),o=O(e.name),s=Oe(e.name),c={name:e.name,pascal:i,camel:a,kebab:o,snake:s,modulesDir:e.modulesDir??`src/modules`,cwd:t,projectRoot:n,args:e.args??[],flags:e.flags??{}};if(r){let e=k(o);c.pluralKebab=e,c.pluralPascal=E(e),c.pluralCamel=D(e)}return c}function ke(e,t){return g(e.cwd,t)}async function Ae(e){return import(w(e).href)}const K=new Map;async function q(e){let t=K.get(e);if(t)return t;let n=je(e);return K.set(e,n),n}async function je(e){let t=g(e,`package.json`);if(!_(t))return{generators:[],loaded:[],failed:[]};let n=Me(JSON.parse(await x(t,`utf-8`))),r=d(g(e,`package.json`)),i=[],a=[],o=[];for(let e of n){let t;try{t=r.resolve(`${e}/package.json`)}catch{continue}let n;try{n=JSON.parse(await x(t,`utf-8`))}catch(t){o.push({source:e,reason:`failed to parse package.json: ${t}`});continue}if(!n.kickjs?.generators)continue;let s=n.kickjs.generators,c=g(p(t),s);if(!_(c)){o.push({source:e,reason:`kickjs.generators points to missing file: ${s}`});continue}let l;try{l=await Ae(c)}catch(t){o.push({source:e,reason:`failed to import manifest: ${t}`});continue}let u=l.default;if(!Array.isArray(u)){o.push({source:e,reason:`manifest's default export is not an array of GeneratorSpec`});continue}for(let t of u){if(!Ne(t)){o.push({source:e,reason:`manifest entry is not a valid GeneratorSpec (missing name/files)`});continue}i.push({source:e,spec:t})}a.push(e)}return{generators:i,loaded:a,failed:o}}function Me(e){let t=new Set;for(let n of[e.dependencies,e.devDependencies,e.peerDependencies])if(n)for(let e of Object.keys(n))t.add(e);return Array.from(t)}function Ne(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.name==`string`&&typeof t.files==`function`}async function Pe(e,t=[]){let n=e.cwd??process.cwd(),r=t.find(t=>t.spec.name===e.generatorName);if(r)return J(r.spec,r.source,e,n);let i=Ie(await q(n),e.generatorName);return i?J(i.spec,i.source,e,n):null}async function Fe(e,t=[]){let n=await q(e),r=new Set(t.map(e=>e.spec.name)),i=n.generators.filter(e=>!r.has(e.spec.name));return{generators:[...t,...i],loaded:n.loaded,failed:n.failed}}function Ie(e,t){return e.generators.find(e=>e.spec.name===t)}async function J(t,n,r,i){let a=G({name:r.itemName,args:r.args,flags:r.flags,modulesDir:r.modulesDir,pluralize:r.pluralize,cwd:i,projectRoot:r.projectRoot}),o=await t.files(a),s=[];for(let t of o){let n=ke(a,t.path);await e(n,t.content),s.push(n)}return{files:s,source:n}}function Le(e){return e}function Re(e){return e}function ze(e){try{return JSON.parse(v(e,`utf-8`))}catch{return null}}function Y(e){try{return v(e,`utf-8`)}catch{return null}}function Be(e){let t=e.replace(/\/\*[\s\S]*?\*\//g,``).replace(/(^|[^:"'\\])\/\/.*$/gm,`$1`).replace(/,(\s*[}\]])/g,`$1`);try{return JSON.parse(t)}catch{return null}}function Ve(e){let t=m(e,`tsconfig.json`);if(_(t))return X(t,new Set)}function X(e,t){if(t.has(e)||t.size>=16)return null;t.add(e);let n=Y(e);if(n===null)return null;let r=Be(n);if(typeof r!=`object`||!r)return null;let i=Array.isArray(r.extends)?r.extends:typeof r.extends==`string`?[r.extends]:[],a={};for(let n of i){if(typeof n!=`string`)continue;let r=He(p(e),n);if(!r)continue;let i=X(r,t);i&&Object.assign(a,i.compilerOptions)}return r.compilerOptions={...a,...r.compilerOptions},r}function He(e,t){if(t.startsWith(`.`)||t.startsWith(`/`))return Z(g(e,t));let n=e;for(;;){let e=Z(m(n,`node_modules`,t));if(e)return e;let r=p(n);if(r===n)break;n=r}return null}function Z(e){if(_(e)){try{if(b(e).isDirectory()){let t=m(e,`tsconfig.json`);return _(t)?t:null}}catch{return null}return e}let t=`${e}.json`;return _(t)?t:null}function Ue(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function We(){let e=process.version,t=Number.parseInt(e.replace(/^v/,``).split(`.`)[0],10);return Number.isNaN(t)||t<20?{name:`Node version`,status:`fail`,message:e,fix:`KickJS requires Node 20 or newer.
953
+ Install a supported version via nvm / fnm / volta.`}:{name:`Node version`,status:`pass`,message:e}}function Ge(e){if(!e.pkg)return{name:`@forinda/kickjs installed`,status:`warn`,message:`no package.json`};let t={...e.pkg.dependencies,...e.pkg.peerDependencies};return t[`@forinda/kickjs`]?{name:`@forinda/kickjs installed`,status:`pass`,message:t[`@forinda/kickjs`]}:{name:`@forinda/kickjs installed`,status:`fail`,fix:"This directory does not look like a KickJS project — `@forinda/kickjs` is not in your package.json. Run `kick doctor` from the project root, or scaffold a fresh project with `kick new <name>`."}}function Ke(e){if(!e.pkg)return null;let t={...e.pkg.dependencies,...e.pkg.peerDependencies};return t[`@forinda/kickjs`]&&!t.express?{name:`express installed`,status:`fail`,fix:"`@forinda/kickjs` declares `express` as a required peer dependency, but your package.json does not include it. Install: pnpm add express"}:t.express?{name:`express installed`,status:`pass`,message:t.express}:null}const qe={express:[],fastify:[`fastify`,`@fastify/middie`],h3:[`h3`]};function Je(e){if(!e.pkg||e.runtime===`express`)return null;let t={...e.pkg.dependencies,...e.pkg.peerDependencies,...e.pkg.devDependencies},n=qe[e.runtime].filter(e=>!t[e]),r=`runtime engine (${e.runtime})`;return n.length>0?{name:r,status:`fail`,fix:`Resolved runtime '${e.runtime}' is missing engine peer(s): ${n.join(`, `)}.\nInstall: pnpm add ${n.join(` `)}`}:{name:r,status:`pass`}}function Ye(e){if(!e.pkg||!Xe(e.cwd))return null;let t=s[e.runtime],n=`upload driver (${e.runtime})`;return t.prod?{...e.pkg.dependencies,...e.pkg.peerDependencies,...e.pkg.devDependencies}[t.prod]?{name:n,status:`pass`,message:t.prod}:{name:n,status:`fail`,fix:`This project uses file uploads on the '${e.runtime}' runtime, which needs '${t.prod}'.\nInstall it: kick add upload (or pnpm add ${t.prod})`}:{name:n,status:`pass`,message:`native multipart`}}const Q=2e3;function Xe(e){let t=m(e,`src`);if(!_(t))return!1;let n=/@FileUpload\b|\bupload\.(single|array|none)\s*\(/,r=[t],i=0;for(;r.length>0&&i<Q;){let e=r.pop(),t;try{t=y(e,{withFileTypes:!0})}catch{continue}for(let a of t){if(i>=Q)break;let t=m(e,a.name);if(a.isDirectory()){a.name!==`node_modules`&&r.push(t);continue}if(/\.(ts|tsx|mts|cts)$/.test(a.name)&&(i++,n.test(Y(t)??``)))return!0}}return!1}function Ze(e){if(!e.pkg)return{name:`reflect-metadata installed`,status:`warn`,message:`no package.json`};let t={...e.pkg.dependencies,...e.pkg.peerDependencies,...e.pkg.devDependencies};return t[`reflect-metadata`]?{name:`reflect-metadata installed`,status:`pass`,message:t[`reflect-metadata`]}:{name:`reflect-metadata installed`,status:`fail`,fix:`KickJS decorators require the reflect-metadata polyfill.
937
954
  Install it: pnpm add reflect-metadata
938
955
  Then import it at the top of src/index.ts:
939
956
 
940
957
  import 'reflect-metadata'
941
- // ... rest of bootstrap`}}function $e(e){if(e.tsconfig===void 0)return[{name:`tsconfig.json present`,status:`fail`,fix:"Create a tsconfig.json with `experimentalDecorators: true` and `emitDecoratorMetadata: true`. `kick new` scaffolds one automatically."}];if(e.tsconfig===null)return[{name:`tsconfig.json readable`,status:`warn`,message:`could not parse tsconfig.json (or a config it extends)`,fix:"Check tsconfig.json for a syntax error, and that every path in its `extends` chain resolves. Decorator options could not be verified."}];let t=e.tsconfig.compilerOptions??{},n=[];return n.push(t.experimentalDecorators===!0?{name:`tsconfig: experimentalDecorators`,status:`pass`}:{name:`tsconfig: experimentalDecorators`,status:`fail`,fix:'Add `"experimentalDecorators": true` to compilerOptions in tsconfig.json. Without it, @Service / @Controller / @Get etc. don\'t register any metadata at compile time.'}),n.push(t.emitDecoratorMetadata===!0?{name:`tsconfig: emitDecoratorMetadata`,status:`pass`}:{name:`tsconfig: emitDecoratorMetadata`,status:`fail`,fix:'Add `"emitDecoratorMetadata": true` to compilerOptions in tsconfig.json. The DI container uses this metadata for constructor-parameter injection.'}),n}function et(e){let t=[`src/env.ts`,`src/env/index.ts`,`src/config/env.ts`,`src/config/index.ts`].map(t=>m(e.cwd,t)).filter(e=>_(e)).filter(e=>/\bloadEnv\s*\(/.test(Y(e)??``));if(t.length===0)return null;let n=[`src/index.ts`,`src/main.ts`].map(t=>m(e.cwd,t)).find(e=>_(e));if(!n)return{name:`env wiring`,status:`warn`,message:`env-init file exists but no src/index.ts or src/main.ts found`};let r=Y(n)??``,i=p(n),a=[];for(let e of t){let t=h(i,e).replace(/\\/g,`/`).replace(/\.ts$/,``),n=t.startsWith(`.`)?t:`./`+t,r=n.replace(/\/index$/,``);a.push(n,r);let o=e.replace(/\\/g,`/`).match(/\/src\/(.+?)(?:\.ts)?$/);if(o){let e=`@/`+o[1],t=e.replace(/\/index$/,``);a.push(e,t)}}let o=-1;for(let e of new Set(a)){let t=RegExp(`^import\\s+(?:.*?from\\s+)?['"]${We(e)}['"]`,`m`),n=r.match(t);n&&n.index!==void 0&&(o===-1||n.index<o)&&(o=n.index)}let s=r.search(/\bbootstrap\s*\(/),c=t.map(t=>h(e.cwd,t).replace(/\\/g,`/`)).join(`, `);return o===-1?{name:`env wiring`,status:`fail`,message:c,fix:`An env-init file (${c}) calls \`loadEnv(...)\` but \`${h(e.cwd,n).replace(/\\/g,`/`)}\` doesn't import it.\nWithout this, ConfigService.get('X') returns undefined while @Value('X') works via process.env fallback — a half-broken config you won't notice until something is missing.\n\nFix: add a side-effect import at the top of ${h(e.cwd,n).replace(/\\/g,`/`)} (above bootstrap()), pointing at one of the detected files. For example:\n\n import './env'\n // or\n import './config'\n // or, with the @/ alias:\n import '@/config/env'`}:s!==-1&&o>s?{name:`env wiring`,status:`warn`,message:`env-init imported AFTER bootstrap() — should be before`,fix:`Move the env import above the bootstrap() call so the schema runs before any service reads from ConfigService.`}:{name:`env wiring`,status:`pass`}}function tt(e,t=$){let n=0,r=0,i=[e];for(;i.length>0&&r<t;){let e=i.pop(),a;try{a=y(e,{withFileTypes:!0})}catch{continue}for(let o of a){if(r>=t)break;r++;let a=m(e,o.name);if(o.isDirectory()){i.push(a);continue}try{let e=b(a).mtimeMs;e>n&&(n=e)}catch{}}}return n}const $=2e3;function nt(e){let t=m(e.cwd,`.kickjs`,`types`);if(!_(t))return null;let n=tt(t);if(n===0)return null;let r=Date.now()-n,i=Math.floor(r/6e4);return i>60?{name:`typegen freshness`,status:`warn`,message:`last updated ${i} minutes ago`,fix:"Re-run `kick typegen` (or `kick dev`, which runs it on every reload) so generated types match the current code."}:{name:`typegen freshness`,status:`pass`,message:i===0?`just now`:`${i}m ago`}}const rt=[`vitest.config.ts`,`vitest.config.js`,`vitest.config.mts`,`vite.config.ts`,`jest.config.ts`,`jest.config.js`],it=[`.env.test`,`.env.test.local`],at=[`.env`,`.env.local`];function ot(e){let t=e.pkg?.scripts;if(t&&typeof t==`object`)for(let[e,n]of Object.entries(t)){if(!e.startsWith(`test`)||typeof n!=`string`)continue;let t=/KICKJS_ENV_FILE=(\S+)/.exec(n);if(t?.[1])return t[1]}return process.env.KICKJS_ENV_FILE??null}function st(e){if(!rt.some(t=>_(m(e.cwd,t))))return null;let t=at.filter(t=>_(m(e.cwd,t)));if(t.length===0)return null;let n=it.filter(t=>_(m(e.cwd,t)));if(n.length>0){let t=ot(e);return t&&t!==`off`&&t!==`none`?{name:`test env isolation`,status:`warn`,message:`${n.join(`, `)} present, but KICKJS_ENV_FILE=${t} overrides it`,fix:`KICKJS_ENV_FILE is checked before test-mode isolation, so ${n[0]} is\nignored and "${t}" is read instead. Drop the override to use the\ntest files, or set KICKJS_ENV_FILE=off if env comes from the runner.`}:{name:`test env isolation`,status:`pass`,message:`${n.join(`, `)} present`}}return{name:`test env isolation`,status:`warn`,message:`${t.join(`, `)} present, no .env.test`,fix:`Under NODE_ENV=test (or vitest's VITEST) KickJS reads .env.test / .env.test.local
958
+ // ... rest of bootstrap`}}function Qe(e){if(e.tsconfig===void 0)return[{name:`tsconfig.json present`,status:`fail`,fix:"Create a tsconfig.json with `experimentalDecorators: true` and `emitDecoratorMetadata: true`. `kick new` scaffolds one automatically."}];if(e.tsconfig===null)return[{name:`tsconfig.json readable`,status:`warn`,message:`could not parse tsconfig.json (or a config it extends)`,fix:"Check tsconfig.json for a syntax error, and that every path in its `extends` chain resolves. Decorator options could not be verified."}];let t=e.tsconfig.compilerOptions??{},n=[];return n.push(t.experimentalDecorators===!0?{name:`tsconfig: experimentalDecorators`,status:`pass`}:{name:`tsconfig: experimentalDecorators`,status:`fail`,fix:'Add `"experimentalDecorators": true` to compilerOptions in tsconfig.json. Without it, @Service / @Controller / @Get etc. don\'t register any metadata at compile time.'}),n.push(t.emitDecoratorMetadata===!0?{name:`tsconfig: emitDecoratorMetadata`,status:`pass`}:{name:`tsconfig: emitDecoratorMetadata`,status:`fail`,fix:'Add `"emitDecoratorMetadata": true` to compilerOptions in tsconfig.json. The DI container uses this metadata for constructor-parameter injection.'}),n}function $e(e){let t=[`src/env.ts`,`src/env/index.ts`,`src/config/env.ts`,`src/config/index.ts`].map(t=>m(e.cwd,t)).filter(e=>_(e)).filter(e=>/\bloadEnv\s*\(/.test(Y(e)??``));if(t.length===0)return null;let n=[`src/index.ts`,`src/main.ts`].map(t=>m(e.cwd,t)).find(e=>_(e));if(!n)return{name:`env wiring`,status:`warn`,message:`env-init file exists but no src/index.ts or src/main.ts found`};let r=Y(n)??``,i=p(n),a=[];for(let e of t){let t=h(i,e).replace(/\\/g,`/`).replace(/\.ts$/,``),n=t.startsWith(`.`)?t:`./`+t,r=n.replace(/\/index$/,``);a.push(n,r);let o=e.replace(/\\/g,`/`).match(/\/src\/(.+?)(?:\.ts)?$/);if(o){let e=`@/`+o[1],t=e.replace(/\/index$/,``);a.push(e,t)}}let o=-1;for(let e of new Set(a)){let t=RegExp(`^import\\s+(?:.*?from\\s+)?['"]${Ue(e)}['"]`,`m`),n=r.match(t);n&&n.index!==void 0&&(o===-1||n.index<o)&&(o=n.index)}let s=r.search(/\bbootstrap\s*\(/),c=t.map(t=>h(e.cwd,t).replace(/\\/g,`/`)).join(`, `);return o===-1?{name:`env wiring`,status:`fail`,message:c,fix:`An env-init file (${c}) calls \`loadEnv(...)\` but \`${h(e.cwd,n).replace(/\\/g,`/`)}\` doesn't import it.\nWithout this, ConfigService.get('X') returns undefined while @Value('X') works via process.env fallback — a half-broken config you won't notice until something is missing.\n\nFix: add a side-effect import at the top of ${h(e.cwd,n).replace(/\\/g,`/`)} (above bootstrap()), pointing at one of the detected files. For example:\n\n import './env'\n // or\n import './config'\n // or, with the @/ alias:\n import '@/config/env'`}:s!==-1&&o>s?{name:`env wiring`,status:`warn`,message:`env-init imported AFTER bootstrap() — should be before`,fix:`Move the env import above the bootstrap() call so the schema runs before any service reads from ConfigService.`}:{name:`env wiring`,status:`pass`}}function et(e,t=tt){let n=0,r=0,i=[e];for(;i.length>0&&r<t;){let e=i.pop(),a;try{a=y(e,{withFileTypes:!0})}catch{continue}for(let o of a){if(r>=t)break;r++;let a=m(e,o.name);if(o.isDirectory()){i.push(a);continue}try{let e=b(a).mtimeMs;e>n&&(n=e)}catch{}}}return n}const tt=2e3;function $(e){let t=m(e.cwd,`.kickjs`,`types`);if(!_(t))return null;let n=et(t);if(n===0)return null;let r=Date.now()-n,i=Math.floor(r/6e4);return i>60?{name:`typegen freshness`,status:`warn`,message:`last updated ${i} minutes ago`,fix:"Re-run `kick typegen` (or `kick dev`, which runs it on every reload) so generated types match the current code."}:{name:`typegen freshness`,status:`pass`,message:i===0?`just now`:`${i}m ago`}}const nt=[`vitest.config.ts`,`vitest.config.js`,`vitest.config.mts`,`vite.config.ts`,`jest.config.ts`,`jest.config.js`],rt=[`.env.test`,`.env.test.local`],it=[`.env`,`.env.local`];function at(e){let t=e.pkg?.scripts;if(t&&typeof t==`object`)for(let[e,n]of Object.entries(t)){if(!e.startsWith(`test`)||typeof n!=`string`)continue;let t=/KICKJS_ENV_FILE=(\S+)/.exec(n);if(t?.[1])return t[1]}return process.env.KICKJS_ENV_FILE??null}function ot(e){if(!nt.some(t=>_(m(e.cwd,t))))return null;let t=it.filter(t=>_(m(e.cwd,t)));if(t.length===0)return null;let n=rt.filter(t=>_(m(e.cwd,t)));if(n.length>0){let t=at(e);return t&&t!==`off`&&t!==`none`?{name:`test env isolation`,status:`warn`,message:`${n.join(`, `)} present, but KICKJS_ENV_FILE=${t} overrides it`,fix:`KICKJS_ENV_FILE is checked before test-mode isolation, so ${n[0]} is\nignored and "${t}" is read instead. Drop the override to use the\ntest files, or set KICKJS_ENV_FILE=off if env comes from the runner.`}:{name:`test env isolation`,status:`pass`,message:`${n.join(`, `)} present`}}return{name:`test env isolation`,status:`warn`,message:`${t.join(`, `)} present, no .env.test`,fix:`Under NODE_ENV=test (or vitest's VITEST) KickJS reads .env.test / .env.test.local
942
959
  INSTEAD of ${t.join(` and `)} — no layering, no fallback. Without one of\nthose files, every var your test config does not pin resolves from your
943
960
  development env — including DATABASE_URL and any third-party credentials.
944
961
 
945
962
  Create .env.test holding the whole environment the suite should run against
946
963
  (a var you leave out is then missing, not inherited), or set KICKJS_ENV_FILE=off
947
- and supply env from the test runner.`}}const ct=[()=>Ge(),Ke,qe,Ye,Xe,Qe,$e,et,st,nt];async function lt(e,t={}){let n={cwd:e,pkg:Be(m(e,`package.json`)),tsconfig:He(e),runtime:t.runtime??`express`},r=[...ct,...t.extraChecks??[]],i=[];for(let e of r){let t;try{t=await e(n)}catch(t){i.push({name:e.name||`doctor check`,status:`fail`,message:t instanceof Error?t.message:String(t)});continue}t!=null&&(Array.isArray(t)?i.push(...t):i.push(t))}return i}function ut(e){switch(e){case`pass`:return o.green(`✔`);case`warn`:return o.yellow(`⚠`);case`fail`:return o.red(`✖`)}}function dt(e){let t=ut(e.status),n=e.message?` ${o.dim(`(${e.message})`)}`:``;return`${t} ${e.name}${n}`}function ft(e){return e.split(`
964
+ and supply env from the test runner.`}}const st=[()=>We(),Ge,Ke,Je,Ye,Ze,Qe,$e,ot,$];async function ct(e,t={}){let n={cwd:e,pkg:ze(m(e,`package.json`)),tsconfig:Ve(e),runtime:t.runtime??`express`},r=[...st,...t.extraChecks??[]],i=[];for(let e of r){let t;try{t=await e(n)}catch(t){i.push({name:e.name||`doctor check`,status:`fail`,message:t instanceof Error?t.message:String(t)});continue}t!=null&&(Array.isArray(t)?i.push(...t):i.push(t))}return i}function lt(e){switch(e){case`pass`:return o.green(`✔`);case`warn`:return o.yellow(`⚠`);case`fail`:return o.red(`✖`)}}function ut(e){let t=lt(e.status),n=e.message?` ${o.dim(`(${e.message})`)}`:``;return`${t} ${e.name}${n}`}function dt(e){return e.split(`
948
965
  `).map(e=>` ${o.dim(`→`)} ${e}`).join(`
949
- `)}function pt(e){return e?.doctor?.checks??[]}function mt(e){e.command(`doctor`).description(`Pre-flight checks for your KickJS project (dev environment health)`).action(async()=>{let e=process.cwd(),t=pt(await l(e)),a=await c(e);r(`KickJS Doctor`);let s=await lt(e,{extraChecks:t,runtime:a});for(let e of s)i.message(dt(e)),e.fix&&e.status!==`pass`&&i.message(ft(e.fix));let u=s.filter(e=>e.status===`pass`).length,d=s.filter(e=>e.status===`warn`).length,f=s.filter(e=>e.status===`fail`).length,p=[o.green(`${u} passed`),d>0?o.yellow(`${d} warning${d===1?``:`s`}`):`${d} warnings`,f>0?o.red(`${f} error${f===1?``:`s`}`):`${f} errors`].join(`, `);f>0?(n(`${p} — fix the errors above before running the app`),process.exit(1)):n(d>0?`${p} — review the warnings`:o.green(`${p} — your environment looks good`))})}export{j as A,fe as C,F as D,I as E,E as F,w as I,D as M,O as N,P as O,T as P,pe as S,L as T,ve as _,Fe as a,z as b,De as c,Te as d,Ce as f,ye as g,be as h,Ie as i,k as j,M as k,Oe as l,xe as m,Re as n,G as o,Se as p,mt as r,te as s,ze as t,we as u,W as v,R as w,V as x,ge as y};
950
- //# sourceMappingURL=doctor-bVCuDQuM.mjs.map
966
+ `)}function ft(e){return e?.doctor?.checks??[]}function pt(e){e.command(`doctor`).description(`Pre-flight checks for your KickJS project (dev environment health)`).action(async()=>{let e=process.cwd(),t=ft(await l(e)),a=await c(e);r(`KickJS Doctor`);let s=await ct(e,{extraChecks:t,runtime:a});for(let e of s)i.message(ut(e)),e.fix&&e.status!==`pass`&&i.message(dt(e.fix));let u=s.filter(e=>e.status===`pass`).length,d=s.filter(e=>e.status===`warn`).length,f=s.filter(e=>e.status===`fail`).length,p=[o.green(`${u} passed`),d>0?o.yellow(`${d} warning${d===1?``:`s`}`):`${d} warnings`,f>0?o.red(`${f} error${f===1?``:`s`}`):`${f} errors`].join(`, `);f>0?(n(`${p} — fix the errors above before running the app`),process.exit(1)):n(d>0?`${p} — review the warnings`:o.green(`${p} — your environment looks good`))})}export{k as A,de as C,F as D,I as E,D as M,O as N,N as O,E as P,fe as S,L as T,_e as _,Pe as a,z as b,Ee as c,we as d,Se as f,ve as g,ye as h,Fe as i,A as j,j as k,De as l,be as m,Le as n,G as o,xe as p,pt as r,T as s,Re as t,Ce as u,W as v,R as w,V as x,he as y};
967
+ //# sourceMappingURL=doctor-CabZwXIc.mjs.map