@forinda/kickjs-cli 5.11.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-docs-Dmku65k2.mjs +12 -0
- package/dist/agent-docs-Dmku65k2.mjs.map +1 -0
- package/dist/{builtins-DOQNq7JT.mjs → builtins-G49e_Qsj.mjs} +2 -2
- package/dist/cli.mjs +151 -1376
- package/dist/config-DdtRfl33.mjs +13 -0
- package/dist/config-DdtRfl33.mjs.map +1 -0
- package/dist/doctor-SUUDEI1J.mjs +1221 -0
- package/dist/doctor-SUUDEI1J.mjs.map +1 -0
- package/dist/index.d.mts +663 -853
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{plugin-CcuuVb2P.mjs → plugin-Dg0Lk2Lp.mjs} +3 -3
- package/dist/{plugin-CcuuVb2P.mjs.map → plugin-Dg0Lk2Lp.mjs.map} +1 -1
- package/dist/{project-docs-CjnHf0Wd.mjs → project-docs-C-dA6-TO.mjs} +6 -36
- package/dist/project-docs-C-dA6-TO.mjs.map +1 -0
- package/dist/{project-root-SRoIXPwv.mjs → project-root-so4F5DRN.mjs} +3 -3
- package/dist/{project-root-SRoIXPwv.mjs.map → project-root-so4F5DRN.mjs.map} +1 -1
- package/dist/{rolldown-runtime-BOORVFz_.mjs → rolldown-runtime-DrKbExWn.mjs} +1 -1
- package/dist/run-plugins-B2_AT35s.mjs +636 -0
- package/dist/run-plugins-B2_AT35s.mjs.map +1 -0
- package/dist/typegen-5MX2F5iL.mjs +114 -0
- package/dist/typegen-5MX2F5iL.mjs.map +1 -0
- package/dist/types-C5PH0h7Z.mjs +11 -0
- package/package.json +13 -13
- package/dist/agent-docs-BuCTT-9g.mjs +0 -12
- package/dist/agent-docs-BuCTT-9g.mjs.map +0 -1
- package/dist/config-DW9HQc2u.mjs +0 -13
- package/dist/config-DW9HQc2u.mjs.map +0 -1
- package/dist/doctor-Cin9GYv9.mjs +0 -2076
- package/dist/doctor-Cin9GYv9.mjs.map +0 -1
- package/dist/project-docs-CjnHf0Wd.mjs.map +0 -1
- package/dist/run-plugins-fsoovaEF.mjs +0 -976
- package/dist/run-plugins-fsoovaEF.mjs.map +0 -1
- package/dist/typegen-CSLwAvgI.mjs +0 -114
- package/dist/typegen-CSLwAvgI.mjs.map +0 -1
- package/dist/types-BZ1L4hvm.mjs +0 -12
- package/dist/types-BZ1L4hvm.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { GeneratorArg, GeneratorContext, GeneratorFile, GeneratorFlag, GeneratorSpec, KickCliPlugin as KickCliPlugin$1, KickCliPluginContext as KickCliPluginContext$1, KickPluginConflictError, defineCliPlugin, defineGenerator } from "@forinda/kickjs-cli-kit";
|
|
4
3
|
//#region src/commands/doctor.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* `kick doctor` — pre-flight checks for a KickJS project's dev
|
|
@@ -101,911 +100,356 @@ declare function defineDoctorExtension(ext: DoctorExtension): DoctorExtension;
|
|
|
101
100
|
*/
|
|
102
101
|
declare function defineDoctorCheck(check: DoctorCheck): DoctorCheck;
|
|
103
102
|
//#endregion
|
|
104
|
-
//#region src/
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
*
|
|
118
|
-
* This is intentionally regex-based (not AST-based) to avoid the
|
|
119
|
-
* ts-morph / typescript compiler dependency. Pattern from
|
|
120
|
-
* `packages/vite/src/module-discovery.ts` which already uses regex
|
|
121
|
-
* to detect `*.module.ts` exports.
|
|
122
|
-
*
|
|
123
|
-
* ## Collision detection
|
|
124
|
-
*
|
|
125
|
-
* Two classes with the same name across different files is a collision.
|
|
126
|
-
* The scanner records all collisions in `ScanResult.collisions` so the
|
|
127
|
-
* caller (generator) can decide whether to hard-error or auto-namespace.
|
|
128
|
-
*
|
|
129
|
-
* @module @forinda/kickjs-cli/typegen/scanner
|
|
130
|
-
*/
|
|
131
|
-
/** Decorators that mark a class as DI-managed */
|
|
132
|
-
declare const DECORATOR_NAMES: readonly ['Service', 'Controller', 'Repository', 'Injectable', 'Component', 'Module'];
|
|
133
|
-
type DecoratorName = (typeof DECORATOR_NAMES)[number];
|
|
134
|
-
/** A single discovered decorated class */
|
|
135
|
-
interface DiscoveredClass {
|
|
136
|
-
/** Class name (e.g., 'UserService') */
|
|
137
|
-
className: string;
|
|
138
|
-
/** Decorator that marked it (e.g., 'Service') */
|
|
139
|
-
decorator: DecoratorName;
|
|
140
|
-
/** Absolute file path */
|
|
141
|
-
filePath: string;
|
|
142
|
-
/** Path relative to scan root, with forward slashes */
|
|
143
|
-
relativePath: string;
|
|
144
|
-
/** True if exported as `default` */
|
|
145
|
-
isDefault: boolean;
|
|
146
|
-
}
|
|
147
|
-
/** A single route handler discovered on a controller class */
|
|
148
|
-
interface DiscoveredRoute {
|
|
149
|
-
/** Owning controller class name (e.g. 'UserController') */
|
|
150
|
-
controller: string;
|
|
151
|
-
/** Handler method name on the controller (e.g. 'getUser') */
|
|
152
|
-
method: string;
|
|
153
|
-
/** HTTP verb (uppercase) */
|
|
154
|
-
httpMethod: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
155
|
-
/** Route path including parameter placeholders (e.g. '/:id/posts/:postId') */
|
|
156
|
-
path: string;
|
|
157
|
-
/** URL path parameter names extracted from `:placeholder` segments */
|
|
158
|
-
pathParams: string[];
|
|
159
|
-
/**
|
|
160
|
-
* Whitelisted query field names extracted from `@ApiQueryParams({...})`.
|
|
161
|
-
* `null` means no `@ApiQueryParams` was found on this method (so the
|
|
162
|
-
* generator emits an unconstrained `query` shape). An empty array means
|
|
163
|
-
* the decorator existed but no fields could be statically extracted
|
|
164
|
-
* (e.g. an opaque imported config).
|
|
165
|
-
*/
|
|
166
|
-
queryFilterable: string[] | null;
|
|
167
|
-
querySortable: string[] | null;
|
|
168
|
-
querySearchable: string[] | null;
|
|
169
|
-
/**
|
|
170
|
-
* Schema identifiers referenced from the route decorator's second arg
|
|
171
|
-
* (e.g. `@Post('/', { body: createTaskSchema })`). `null` means no
|
|
172
|
-
* such reference; the value carries the identifier and the resolved
|
|
173
|
-
* import source (relative module path) if known.
|
|
174
|
-
*/
|
|
175
|
-
bodySchema: SchemaRef | null;
|
|
176
|
-
querySchema: SchemaRef | null;
|
|
177
|
-
paramsSchema: SchemaRef | null;
|
|
178
|
-
/** Absolute file path of the controller */
|
|
179
|
-
filePath: string;
|
|
180
|
-
/** Path relative to scan root, with forward slashes */
|
|
181
|
-
relativePath: string;
|
|
182
|
-
}
|
|
183
|
-
/** A statically-resolved schema identifier reference */
|
|
184
|
-
interface SchemaRef {
|
|
185
|
-
/** The identifier as written (e.g. `createTaskSchema`) */
|
|
186
|
-
identifier: string;
|
|
103
|
+
//#region src/plugin/types.d.ts
|
|
104
|
+
/** CLI plugin context with the host config narrowed to `KickConfig`. */
|
|
105
|
+
type KickCliPluginContext = KickCliPluginContext$1<KickConfig>;
|
|
106
|
+
/** A CLI plugin with the host config narrowed to `KickConfig`. */
|
|
107
|
+
type KickCliPlugin = KickCliPlugin$1<KickConfig>;
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/config.d.ts
|
|
110
|
+
/** A custom command that developers can register via kick.config.ts */
|
|
111
|
+
interface KickCommandDefinition {
|
|
112
|
+
/** The command name (e.g. 'db:migrate', 'seed', 'proto:gen') */
|
|
113
|
+
name: string;
|
|
114
|
+
/** Description shown in --help */
|
|
115
|
+
description: string;
|
|
187
116
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
117
|
+
* Shell command(s) to run. Can be a single string or an array of
|
|
118
|
+
* sequential steps. Use {args} as a placeholder for CLI arguments.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* 'npx drizzle-kit migrate'
|
|
122
|
+
* ['npx drizzle-kit generate', 'npx drizzle-kit migrate']
|
|
191
123
|
*/
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
interface DiscoveredToken {
|
|
196
|
-
/** The literal string passed to `createToken()` */
|
|
197
|
-
name: string;
|
|
198
|
-
/** The const variable name on the LHS, if any */
|
|
199
|
-
variable: string | null;
|
|
200
|
-
/** Absolute file path */
|
|
201
|
-
filePath: string;
|
|
202
|
-
/** Path relative to scan root, with forward slashes */
|
|
203
|
-
relativePath: string;
|
|
204
|
-
}
|
|
205
|
-
/** An `@Inject('literal')` call discovered in source */
|
|
206
|
-
interface DiscoveredInject {
|
|
207
|
-
/** The literal string passed to `@Inject()` */
|
|
208
|
-
name: string;
|
|
209
|
-
/** Absolute file path */
|
|
210
|
-
filePath: string;
|
|
211
|
-
/** Path relative to scan root, with forward slashes */
|
|
212
|
-
relativePath: string;
|
|
213
|
-
}
|
|
214
|
-
/** A name collision — same class name in two or more files */
|
|
215
|
-
interface ClassCollision {
|
|
216
|
-
/** The colliding class name */
|
|
217
|
-
className: string;
|
|
218
|
-
/** All files declaring the class */
|
|
219
|
-
classes: DiscoveredClass[];
|
|
124
|
+
steps: string | string[];
|
|
125
|
+
/** Optional aliases (e.g. ['migrate'] for 'db:migrate') */
|
|
126
|
+
aliases?: string[];
|
|
220
127
|
}
|
|
128
|
+
/** Project pattern — controls what generators produce and which deps are installed */
|
|
129
|
+
type ProjectPattern = 'rest' | 'minimal';
|
|
130
|
+
/** Package manager used for `kick add` and other dep-installing commands */
|
|
131
|
+
type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';
|
|
221
132
|
/**
|
|
222
|
-
*
|
|
223
|
-
* generator uses this to emit a `KickEnv` + `NodeJS.ProcessEnv`
|
|
224
|
-
* augmentation that flows through to `@Value` and `process.env`.
|
|
133
|
+
* Built-in repository type with first-class code generation support.
|
|
225
134
|
*
|
|
226
|
-
* `
|
|
135
|
+
* Only `inmemory` remains built-in (zero-dep, framework-owned). The
|
|
136
|
+
* `prisma` and `drizzle` ORM presets are **deprecated** — they now
|
|
137
|
+
* scaffold a generic custom-repository stub like any other name (see
|
|
138
|
+
* {@link DEPRECATED_REPO_TYPES}). Bring your own DB by passing a name:
|
|
139
|
+
* `repo: { name: 'postgres' }`.
|
|
227
140
|
*/
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
relativePath: string;
|
|
141
|
+
type BuiltinRepoType$1 = 'inmemory';
|
|
142
|
+
/** Custom repository type — generates a stub with TODO markers */
|
|
143
|
+
interface CustomRepoType {
|
|
144
|
+
name: string;
|
|
233
145
|
}
|
|
146
|
+
/** Repository type — built-in string or custom object */
|
|
147
|
+
type RepoTypeConfig = BuiltinRepoType$1 | CustomRepoType;
|
|
234
148
|
/**
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
* and declares a string-literal `name` field.
|
|
149
|
+
* Supported schema validators for `kick typegen` body/query/params
|
|
150
|
+
* type extraction.
|
|
238
151
|
*
|
|
239
|
-
*
|
|
240
|
-
* `
|
|
241
|
-
*
|
|
152
|
+
* - `'zod'` — emits `import('zod').infer<typeof schema>` (default)
|
|
153
|
+
* - `'kickjs-schema'` — emits `InferSchemaOutput<typeof schema>` from
|
|
154
|
+
* `@forinda/kickjs-schema`, works with Zod, Valibot, Yup, and any
|
|
155
|
+
* Standard Schema v1 or KickSchema adapter
|
|
156
|
+
* - `false` — disables schema-driven body typing (fields stay `unknown`)
|
|
242
157
|
*/
|
|
243
|
-
|
|
244
|
-
/** Whether this is a plugin (`definePlugin`) or adapter (`defineAdapter` / class) */
|
|
245
|
-
kind: 'plugin' | 'adapter';
|
|
246
|
-
/** The string literal passed as `name` (the value `dependsOn` references) */
|
|
247
|
-
name: string;
|
|
248
|
-
/** Absolute file path */
|
|
249
|
-
filePath: string;
|
|
250
|
-
/** Path relative to scan root, with forward slashes */
|
|
251
|
-
relativePath: string;
|
|
252
|
-
}
|
|
158
|
+
type SchemaValidator = 'zod' | 'kickjs-schema' | false;
|
|
253
159
|
/**
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* plugin, which emits the `ContextKeys` augmentation so `dependsOn`
|
|
258
|
-
* typo-checking is automatic and complete.
|
|
160
|
+
* One entry in the typed `assetMap` config record (`assets-plan.md`).
|
|
161
|
+
* Each entry names a source directory whose files become addressable
|
|
162
|
+
* via the `assets.<name>.*` typed accessor at runtime.
|
|
259
163
|
*/
|
|
260
|
-
interface
|
|
261
|
-
/**
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
164
|
+
interface AssetMapEntry {
|
|
165
|
+
/**
|
|
166
|
+
* Source directory, relative to project root. Required. The directory
|
|
167
|
+
* must exist when `kick build` runs — `loadKickConfig` warns when an
|
|
168
|
+
* entry points at a missing directory but doesn't fail the load
|
|
169
|
+
* (the typegen + build steps surface the error in context instead).
|
|
170
|
+
*/
|
|
171
|
+
src: string;
|
|
172
|
+
/**
|
|
173
|
+
* Destination directory inside `dist/`. Defaults to `dist/<name>/`
|
|
174
|
+
* where `<name>` is the assetMap key. Override when the consumer of
|
|
175
|
+
* the assets expects a non-standard layout (e.g. an existing
|
|
176
|
+
* downstream tool reads from `dist/templates/...`).
|
|
177
|
+
*/
|
|
178
|
+
dest?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Glob pattern for which files to include. Defaults to `**\/*` (all
|
|
181
|
+
* files). Files that don't match are NOT copied — `assetMap` is
|
|
182
|
+
* selective by design (unlike `copyDirs` which copies everything).
|
|
183
|
+
*/
|
|
184
|
+
glob?: string;
|
|
185
|
+
/**
|
|
186
|
+
* How file extensions feed into manifest keys. Default `'auto'`.
|
|
187
|
+
*
|
|
188
|
+
* - `'strip'` — drop the extension. `pages/index.pug` →
|
|
189
|
+
* `'pages/index'`. Two siblings with the same basename collide;
|
|
190
|
+
* last-walk-order wins, others are silently dropped. Opt-in
|
|
191
|
+
* only — kept for backward compatibility with projects that
|
|
192
|
+
* relied on this contract.
|
|
193
|
+
* - `'with-extension'` — keep every extension on every key. Best
|
|
194
|
+
* when the namespace holds extension siblings (`index.pug` +
|
|
195
|
+
* `index.html` + `index.css` in `src/pages/`); every file
|
|
196
|
+
* reaches the manifest under its full path.
|
|
197
|
+
* - `'auto'` — strip when basenames are unique, keep extensions
|
|
198
|
+
* on collision groups. Singleton files keep their short key;
|
|
199
|
+
* `pages/index.{pug,html,css}` becomes
|
|
200
|
+
* `pages/index.pug` / `pages/index.html` / `pages/index.css`.
|
|
201
|
+
* No data loss; non-colliding namespaces stay on the short
|
|
202
|
+
* keys they had before.
|
|
203
|
+
*
|
|
204
|
+
* @default 'auto'
|
|
205
|
+
*/
|
|
206
|
+
keys?: 'auto' | 'strip' | 'with-extension';
|
|
267
207
|
}
|
|
268
208
|
/**
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
209
|
+
* Database settings consumed by `kick db generate`, `kick db migrate`,
|
|
210
|
+
* and the M4.C composite-type detection gate. Mirrors the runtime
|
|
211
|
+
* `DbConfig` shape from `@forinda/kickjs-db` — duplicated here so
|
|
212
|
+
* adopters who only install `@forinda/kickjs-cli` can set the block
|
|
213
|
+
* without pulling `@forinda/kickjs-db` types into their kick.config.ts
|
|
214
|
+
* resolution. The CLI loads kick.config.ts through `loadKickConfig`,
|
|
215
|
+
* then `resolveDbConfig` re-reads this block from the same module and
|
|
216
|
+
* normalises defaults (`schemaPath` / `migrationsDir` / `dialect`).
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```ts
|
|
220
|
+
* defineConfig({
|
|
221
|
+
* db: {
|
|
222
|
+
* schemaPath: 'src/db/schema.ts',
|
|
223
|
+
* migrationsDir: 'db/migrations',
|
|
224
|
+
* dialect: 'postgres',
|
|
225
|
+
* connectionString: process.env.DATABASE_URL,
|
|
226
|
+
* },
|
|
227
|
+
* })
|
|
228
|
+
* ```
|
|
272
229
|
*/
|
|
273
|
-
interface
|
|
274
|
-
/** The literal string passed as the first arg to `defineAugmentation` */
|
|
275
|
-
name: string;
|
|
276
|
-
/** Optional `description` extracted from the second-arg object literal */
|
|
277
|
-
description: string | null;
|
|
278
|
-
/** Optional `example` extracted from the second-arg object literal */
|
|
279
|
-
example: string | null;
|
|
280
|
-
/** Absolute file path */
|
|
281
|
-
filePath: string;
|
|
282
|
-
/** Path relative to scan root, with forward slashes */
|
|
283
|
-
relativePath: string;
|
|
284
|
-
}
|
|
285
|
-
/**
|
|
286
|
-
* A decorated class whose file sits inside a module directory but
|
|
287
|
-
* isn't picked up by any of the module's `import.meta.glob(...)`
|
|
288
|
-
* patterns. Surfaced as a typegen warning per forinda/kick-js#235 §4
|
|
289
|
-
* so adopters notice silent registration drift before it bites them
|
|
290
|
-
* at runtime with a `MissingContributorError` or wrong code path.
|
|
291
|
-
*/
|
|
292
|
-
interface OrphanedClass {
|
|
293
|
-
/** The decorated class name */
|
|
294
|
-
className: string;
|
|
295
|
-
/** Absolute path of the class file */
|
|
296
|
-
filePath: string;
|
|
297
|
-
/** Path relative to scan root, with forward slashes */
|
|
298
|
-
relativePath: string;
|
|
299
|
-
/** Absolute path of the module file whose globs didn't match */
|
|
300
|
-
moduleFilePath: string;
|
|
301
|
-
/** The decorator name (`Service`, `Controller`, `Repository`, …) */
|
|
302
|
-
decorator: DecoratorName;
|
|
303
|
-
}
|
|
304
|
-
/** Aggregated scanner output */
|
|
305
|
-
interface ScanResult {
|
|
306
|
-
classes: DiscoveredClass[];
|
|
307
|
-
routes: DiscoveredRoute[];
|
|
308
|
-
tokens: DiscoveredToken[];
|
|
309
|
-
injects: DiscoveredInject[];
|
|
310
|
-
collisions: ClassCollision[];
|
|
311
|
-
/** Discovered env schema file (or null if none found at the configured path) */
|
|
312
|
-
env: DiscoveredEnv | null;
|
|
313
|
-
/** Plugins/adapters discovered via `defineAdapter`/`definePlugin`/`implements AppAdapter` */
|
|
314
|
-
pluginsAndAdapters: DiscoveredPluginOrAdapter[];
|
|
315
|
-
/** Augmentation interfaces declared via `defineAugmentation('Name', meta)` */
|
|
316
|
-
augmentations: DiscoveredAugmentation[];
|
|
317
|
-
/** Context keys from `define(Http)ContextDecorator({ key })` calls */
|
|
318
|
-
contextKeys: DiscoveredContextKey[];
|
|
230
|
+
interface KickDbConfigBlock {
|
|
319
231
|
/**
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
* Empty when every decorator file is matched. forinda/kick-js#235 §4.
|
|
232
|
+
* Path to the schema module (`pgEnum` / `table` declarations).
|
|
233
|
+
* Defaults to `'src/db/schema.ts'`.
|
|
323
234
|
*/
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
/** Options for the scanner */
|
|
327
|
-
interface ScanOptions {
|
|
328
|
-
/** Root directory to scan (e.g., absolute path to `src`) */
|
|
329
|
-
root: string;
|
|
330
|
-
/** Project root used to compute relative paths (e.g., process.cwd()) */
|
|
331
|
-
cwd: string;
|
|
332
|
-
/** Glob-like extensions to scan */
|
|
333
|
-
extensions?: string[];
|
|
334
|
-
/** Substrings that exclude a path (matched against relative path) */
|
|
335
|
-
exclude?: string[];
|
|
235
|
+
schemaPath?: string;
|
|
336
236
|
/**
|
|
337
|
-
*
|
|
338
|
-
* `'
|
|
339
|
-
* with a default export for the typegen to emit a typed `KickEnv`
|
|
340
|
-
* augmentation. If the file does not exist or doesn't match the
|
|
341
|
-
* expected shape, env typing is skipped silently.
|
|
237
|
+
* Where `kick db generate` writes migration directories. Defaults
|
|
238
|
+
* to `'db/migrations'`.
|
|
342
239
|
*/
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
//#region src/typegen/plugin.d.ts
|
|
347
|
-
interface TypegenLogger {
|
|
348
|
-
info(msg: string): void;
|
|
349
|
-
warn(msg: string): void;
|
|
350
|
-
error(msg: string): void;
|
|
351
|
-
}
|
|
352
|
-
interface TypegenContext {
|
|
353
|
-
cwd: string;
|
|
354
|
-
config: KickConfig;
|
|
355
|
-
/** Dynamic-import a TS module (Node loader). Used by plugins that need to
|
|
356
|
-
* read the adopter's schema / route map / asset registry at generate time. */
|
|
357
|
-
importTs<T = unknown>(absPath: string): Promise<T>;
|
|
358
|
-
/** Write under `cwd`. Caller passes a relPath (e.g. `.kickjs/types/foo.d.ts`). */
|
|
359
|
-
writeFile(relPath: string, contents: string): Promise<void>;
|
|
240
|
+
migrationsDir?: string;
|
|
241
|
+
/** SQL dialect. Defaults to `'postgres'`. */
|
|
242
|
+
dialect?: 'postgres' | 'sqlite' | 'mysql';
|
|
360
243
|
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
* The runner uses an order-independent cache key derived from the
|
|
366
|
-
* resolved options (`root`, `cwd`, `extensions`, `exclude`, `envFile`)
|
|
367
|
-
* — semantically equal options hit the cache regardless of how the
|
|
368
|
-
* caller built the literal. (We deliberately don't `JSON.stringify`
|
|
369
|
-
* the options for caching since that would be sensitive to property
|
|
370
|
-
* insertion order.) Plugins that don't need scanner data can ignore
|
|
371
|
-
* this method entirely.
|
|
372
|
-
*
|
|
373
|
-
* Implementation lives in the runner so test harnesses can inject
|
|
374
|
-
* a stub scanner; plugins only see the function.
|
|
244
|
+
* Postgres connection string for the built-in pgAdapter path. Read
|
|
245
|
+
* from the `DATABASE_URL` env var when omitted. Used by
|
|
246
|
+
* `kick db migrate*` and the M4.C composite-type gate at
|
|
247
|
+
* `kick db generate`.
|
|
375
248
|
*/
|
|
376
|
-
|
|
377
|
-
|
|
249
|
+
connectionString?: string;
|
|
250
|
+
/**
|
|
251
|
+
* Escape hatch: a factory returning a fully-constructed
|
|
252
|
+
* MigrationAdapter (typed loosely here so the CLI types don't pull
|
|
253
|
+
* `@forinda/kickjs-db` into adopter projects that don't import it).
|
|
254
|
+
* Takes precedence over `connectionString` when both are set.
|
|
255
|
+
*/
|
|
256
|
+
adapter?: () => unknown | Promise<unknown>;
|
|
378
257
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
id: string;
|
|
382
|
-
/** Glob patterns the Vite watcher subscribes to; change → re-run this plugin. */
|
|
383
|
-
inputs: string[];
|
|
258
|
+
/** Typegen settings — controls .kickjs/types/* generation */
|
|
259
|
+
interface TypegenConfig {
|
|
384
260
|
/**
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
* declaration files don't need the runtime-import dance.
|
|
388
|
-
*
|
|
389
|
-
* `kick/routes` overrides to `.ts` because it emits hoisted
|
|
390
|
-
* `import type {...} from '...'` lines at the top of the file. Inline
|
|
391
|
-
* `import('...').X` references inside `.d.ts` silently degrade to
|
|
392
|
-
* `unknown` under `moduleResolution: 'bundler'`; emitting `.ts`
|
|
393
|
-
* sidesteps that quirk and gets full type resolution.
|
|
394
|
-
*
|
|
395
|
-
* Adopter-supplied plugins should leave this unset unless they hit
|
|
396
|
-
* the same hoisted-import constraint.
|
|
261
|
+
* Source directory to scan for controllers and decorators.
|
|
262
|
+
* Defaults to `'src'`.
|
|
397
263
|
*/
|
|
398
|
-
|
|
264
|
+
srcDir?: string;
|
|
399
265
|
/**
|
|
400
|
-
*
|
|
401
|
-
*
|
|
266
|
+
* Output directory for generated `.d.ts` files.
|
|
267
|
+
* Defaults to `'.kickjs/types'`.
|
|
402
268
|
*/
|
|
403
|
-
|
|
404
|
-
}
|
|
405
|
-
interface TypegenPluginResult {
|
|
406
|
-
id: string;
|
|
407
|
-
status: 'written' | 'unchanged' | 'skipped' | 'error';
|
|
408
|
-
outFile?: string;
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Identity factory for {@link TypegenPlugin}. Returns the spec verbatim.
|
|
412
|
-
* Exists for type inference and forward-compatibility — future
|
|
413
|
-
* fields can be added with defaults without breaking adopters.
|
|
414
|
-
*
|
|
415
|
-
* Mirrors {@link defineGenerator} ergonomics. Use at the call site so
|
|
416
|
-
* the plugin's `generate(ctx)` body gets a fully-typed `ctx` without
|
|
417
|
-
* an explicit annotation:
|
|
418
|
-
*
|
|
419
|
-
* @example
|
|
420
|
-
* ```ts
|
|
421
|
-
* import { defineTypegen } from '@forinda/kickjs-cli'
|
|
422
|
-
*
|
|
423
|
-
* export const drizzleTypegen = defineTypegen({
|
|
424
|
-
* id: 'drizzle',
|
|
425
|
-
* inputs: ['src/db/schema.ts'],
|
|
426
|
-
* async generate(ctx) {
|
|
427
|
-
* const schema = await ctx.importTs(`${ctx.cwd}/src/db/schema.ts`)
|
|
428
|
-
* return `// declare module …`
|
|
429
|
-
* },
|
|
430
|
-
* })
|
|
431
|
-
* ```
|
|
432
|
-
*/
|
|
433
|
-
declare function defineTypegen(spec: TypegenPlugin): TypegenPlugin;
|
|
434
|
-
//#endregion
|
|
435
|
-
//#region src/generator-extension/define.d.ts
|
|
436
|
-
/**
|
|
437
|
-
* Plugin generator API per architecture.md §21.2.3 — lets first-party
|
|
438
|
-
* AND third-party packages ship `kick g <name>` scaffolders the same
|
|
439
|
-
* way the framework's built-in generators do.
|
|
440
|
-
*
|
|
441
|
-
* Plugins declare a discovery file in their `package.json`:
|
|
442
|
-
*
|
|
443
|
-
* ```json
|
|
444
|
-
* {
|
|
445
|
-
* "name": "@my-org/kickjs-cqrs",
|
|
446
|
-
* "kickjs": { "generators": "./dist/generators.js" }
|
|
447
|
-
* }
|
|
448
|
-
* ```
|
|
449
|
-
*
|
|
450
|
-
* The discovery file exports a typed manifest:
|
|
451
|
-
*
|
|
452
|
-
* ```ts
|
|
453
|
-
* import { defineGenerator } from '@forinda/kickjs-cli'
|
|
454
|
-
*
|
|
455
|
-
* export default [
|
|
456
|
-
* defineGenerator({
|
|
457
|
-
* name: 'command',
|
|
458
|
-
* description: 'Generate a CQRS command + handler',
|
|
459
|
-
* args: [{ name: 'name', required: true }],
|
|
460
|
-
* files: (ctx) => [
|
|
461
|
-
* {
|
|
462
|
-
* path: `src/modules/${ctx.kebab}/commands/create-${ctx.kebab}.command.ts`,
|
|
463
|
-
* content: `// generated command for ${ctx.pascal}`,
|
|
464
|
-
* },
|
|
465
|
-
* ],
|
|
466
|
-
* }),
|
|
467
|
-
* ]
|
|
468
|
-
* ```
|
|
469
|
-
*
|
|
470
|
-
* `kick g command Order` then dispatches against the registered
|
|
471
|
-
* generator and writes the returned files relative to `cwd`.
|
|
472
|
-
*/
|
|
473
|
-
/**
|
|
474
|
-
* Resolved naming variants + project context handed to a generator's
|
|
475
|
-
* `files()` factory. Keys mirror `TemplateContext` so first-party
|
|
476
|
-
* generators that rely on the same shape can be migrated to plugin
|
|
477
|
-
* generators without rewriting their templates.
|
|
478
|
-
*/
|
|
479
|
-
interface GeneratorContext {
|
|
480
|
-
/** Raw name passed on the command line (`kick g drizzle-typegen UserPost` → `'UserPost'`). */
|
|
481
|
-
name: string;
|
|
482
|
-
/** PascalCase form (`UserPost`). */
|
|
483
|
-
pascal: string;
|
|
484
|
-
/** kebab-case form (`user-post`). */
|
|
485
|
-
kebab: string;
|
|
486
|
-
/** camelCase form (`userPost`). */
|
|
487
|
-
camel: string;
|
|
488
|
-
/** snake_case form (`user_post`). */
|
|
489
|
-
snake: string;
|
|
490
|
-
/** Pluralized PascalCase (`UserPosts`) — present when the project enables `pluralize`. */
|
|
491
|
-
pluralPascal?: string;
|
|
492
|
-
/** Pluralized kebab-case (`user-posts`). */
|
|
493
|
-
pluralKebab?: string;
|
|
494
|
-
/** Pluralized camelCase (`userPosts`). */
|
|
495
|
-
pluralCamel?: string;
|
|
496
|
-
/** Modules directory from `kick.config.ts` (default `'src/modules'`). */
|
|
497
|
-
modulesDir: string;
|
|
269
|
+
outDir?: string;
|
|
498
270
|
/**
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
271
|
+
* Schema validator used to derive `body` types from route metadata.
|
|
272
|
+
*
|
|
273
|
+
* - `'zod'` — emit `z.infer<typeof <importedSchema>>` for any schema
|
|
274
|
+
* referenced as a named identifier in `@Get/@Post/...({ body, query, params })`.
|
|
275
|
+
* - `false` — disable schema-driven body typing.
|
|
276
|
+
*
|
|
277
|
+
* Future: `'joi' | 'yup' | 'json-schema'` plus a `{ name; module }`
|
|
278
|
+
* escape hatch for custom adapters.
|
|
279
|
+
*
|
|
280
|
+
* @default 'zod'
|
|
502
281
|
*/
|
|
503
|
-
|
|
282
|
+
schemaValidator?: SchemaValidator;
|
|
504
283
|
/**
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
284
|
+
* Path to the project's env schema file (relative to project root).
|
|
285
|
+
* Must default-export a `defineEnv(...)` schema for typegen to emit
|
|
286
|
+
* the typed `KickEnv` global registry.
|
|
287
|
+
*
|
|
288
|
+
* Set to `false` to disable env typing entirely.
|
|
508
289
|
*
|
|
509
|
-
*
|
|
510
|
-
* regardless of where the adopter typed the command. `kick g ...` from
|
|
511
|
-
* inside `src/modules/foo/` will still see `projectRoot` pointing at
|
|
512
|
-
* the directory that owns `kick.config.ts`.
|
|
290
|
+
* @default 'src/env.ts'
|
|
513
291
|
*/
|
|
514
|
-
|
|
515
|
-
/** Positional arguments passed AFTER the name (e.g. `kick g command Order extra1 extra2` → `['extra1', 'extra2']`). */
|
|
516
|
-
args: string[];
|
|
517
|
-
/** Flag values from the command line — booleans for switches, strings for `--key value`. */
|
|
518
|
-
flags: Record<string, string | boolean>;
|
|
519
|
-
}
|
|
520
|
-
/** A single output file the generator wants written. */
|
|
521
|
-
interface GeneratorFile {
|
|
292
|
+
envFile?: string | false;
|
|
522
293
|
/**
|
|
523
|
-
*
|
|
524
|
-
*
|
|
294
|
+
* Built-in or user typegen plugin ids to skip during `kick typegen`,
|
|
295
|
+
* `kick dev`, and `kick typegen --watch`.
|
|
296
|
+
*
|
|
297
|
+
* The plugin still loads and merge-time conflict detection still
|
|
298
|
+
* runs — only the `generate()` invocation is skipped — so adopters
|
|
299
|
+
* who want to hand-write `KickDbRegister` (manual typeof-schema
|
|
300
|
+
* augmentation) can disable `'kick/db'` and keep the rest:
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* typegen: {
|
|
304
|
+
* disable: ['kick/db'], // hand-written register.ts owns the type
|
|
305
|
+
* }
|
|
306
|
+
*
|
|
307
|
+
* Unrecognised ids are ignored — the list is treated as a wishlist,
|
|
308
|
+
* not a strict registry.
|
|
525
309
|
*/
|
|
526
|
-
|
|
527
|
-
/** File contents written verbatim (UTF-8). */
|
|
528
|
-
content: string;
|
|
529
|
-
}
|
|
530
|
-
/** CLI argument descriptor surfaced in `kick g --list` help. */
|
|
531
|
-
interface GeneratorArg {
|
|
532
|
-
name: string;
|
|
533
|
-
required?: boolean;
|
|
534
|
-
description?: string;
|
|
535
|
-
}
|
|
536
|
-
/** CLI flag descriptor — boolean unless `takesValue: true`. */
|
|
537
|
-
interface GeneratorFlag {
|
|
538
|
-
name: string;
|
|
539
|
-
alias?: string;
|
|
540
|
-
description?: string;
|
|
541
|
-
takesValue?: boolean;
|
|
310
|
+
disable?: string[];
|
|
542
311
|
}
|
|
543
|
-
/**
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
interface GeneratorSpec {
|
|
312
|
+
/** Module generation settings — controls how `kick g module` produces code */
|
|
313
|
+
interface ModuleConfig {
|
|
314
|
+
/** Where modules live (default: 'src/modules') */
|
|
315
|
+
dir?: string;
|
|
548
316
|
/**
|
|
549
|
-
*
|
|
550
|
-
*
|
|
317
|
+
* Default repository implementation for generators.
|
|
318
|
+
*
|
|
319
|
+
* Built-in types (string): `'drizzle'`, `'inmemory'`, `'prisma'`
|
|
320
|
+
* — generate fully working repository code.
|
|
321
|
+
*
|
|
322
|
+
* Custom types (object): `{ name: 'typeorm' }`
|
|
323
|
+
* — generate a stub repository with TODO markers.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* repo: 'prisma' // built-in
|
|
327
|
+
* repo: { name: 'typeorm' } // custom
|
|
551
328
|
*/
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
|
|
555
|
-
/** Optional argument descriptors — informational, surfaced in help output. */
|
|
556
|
-
args?: readonly GeneratorArg[];
|
|
557
|
-
/** Optional flag descriptors — informational, surfaced in help output. */
|
|
558
|
-
flags?: readonly GeneratorFlag[];
|
|
559
|
-
/** Build the output files for one invocation. May return a Promise. */
|
|
560
|
-
files(ctx: GeneratorContext): GeneratorFile[] | Promise<GeneratorFile[]>;
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Identity factory — returns the spec verbatim. Exists for type
|
|
564
|
-
* inference and forward-compatibility (future fields can be added with
|
|
565
|
-
* defaults).
|
|
566
|
-
*
|
|
567
|
-
* @example
|
|
568
|
-
* ```ts
|
|
569
|
-
* import { defineGenerator } from '@forinda/kickjs-cli'
|
|
570
|
-
*
|
|
571
|
-
* export default [
|
|
572
|
-
* defineGenerator({
|
|
573
|
-
* name: 'command',
|
|
574
|
-
* description: 'Generate a CQRS command + handler',
|
|
575
|
-
* files: (ctx) => [
|
|
576
|
-
* {
|
|
577
|
-
* path: `src/modules/${ctx.kebab}/commands/${ctx.kebab}.command.ts`,
|
|
578
|
-
* content: `// command for ${ctx.pascal}`,
|
|
579
|
-
* },
|
|
580
|
-
* ],
|
|
581
|
-
* }),
|
|
582
|
-
* ]
|
|
583
|
-
* ```
|
|
584
|
-
*/
|
|
585
|
-
declare function defineGenerator(spec: GeneratorSpec): GeneratorSpec;
|
|
586
|
-
//#endregion
|
|
587
|
-
//#region src/generator-extension/discover.d.ts
|
|
588
|
-
/**
|
|
589
|
-
* One row in the discovered registry. `source` is the npm package name
|
|
590
|
-
* the generator came from — surfaced in error messages so adopters can
|
|
591
|
-
* see which plugin owns a given generator.
|
|
592
|
-
*/
|
|
593
|
-
interface DiscoveredGenerator {
|
|
594
|
-
source: string;
|
|
595
|
-
spec: GeneratorSpec;
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* Plugin discovery result, kept around even when no generators were
|
|
599
|
-
* registered so callers can distinguish "no plugins installed" from
|
|
600
|
-
* "no plugins matched the requested name."
|
|
601
|
-
*/
|
|
602
|
-
interface DiscoveryResult {
|
|
603
|
-
generators: DiscoveredGenerator[];
|
|
604
|
-
/** Packages whose `kickjs.generators` was loaded successfully. */
|
|
605
|
-
loaded: string[];
|
|
606
|
-
/**
|
|
607
|
-
* Packages we tried to load but failed — typically a missing entry
|
|
608
|
-
* file or a default export that wasn't an array of GeneratorSpec.
|
|
609
|
-
*/
|
|
610
|
-
failed: Array<{
|
|
611
|
-
source: string;
|
|
612
|
-
reason: string;
|
|
613
|
-
}>;
|
|
614
|
-
}
|
|
615
|
-
//#endregion
|
|
616
|
-
//#region src/plugin/types.d.ts
|
|
617
|
-
/**
|
|
618
|
-
* Runtime context handed to `register()` — saves callbacks from
|
|
619
|
-
* re-loading config or guessing cwd. Forward-compatible: future fields
|
|
620
|
-
* land here without changing the callback signature.
|
|
621
|
-
*/
|
|
622
|
-
interface KickCliPluginContext {
|
|
329
|
+
repo?: RepoTypeConfig;
|
|
330
|
+
/** Schema output directory (e.g. 'src/db/schema' for Drizzle, 'prisma/' for Prisma) */
|
|
331
|
+
schemaDir?: string;
|
|
623
332
|
/**
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
* — `cwd` is whatever the adopter typed the command from, including
|
|
628
|
-
* arbitrary subdirectories.
|
|
333
|
+
* Whether to pluralize module names in generated code.
|
|
334
|
+
* When true (default), `kick g module user` creates `src/modules/users/`.
|
|
335
|
+
* When false, it creates `src/modules/user/` and uses singular names throughout.
|
|
629
336
|
*/
|
|
630
|
-
|
|
337
|
+
pluralize?: boolean;
|
|
631
338
|
/**
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
* a `kick.config.{ts,js,mjs,json}` is found, that directory; otherwise
|
|
635
|
-
* the nearest ancestor with a `package.json`; otherwise `cwd` itself.
|
|
339
|
+
* Import path for the Prisma generated client in `--repo prisma` templates.
|
|
340
|
+
* Must resolve within `src/` for path alias compatibility.
|
|
636
341
|
*
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
*
|
|
342
|
+
* @default '@prisma/client' (Prisma 5/6)
|
|
343
|
+
* @example
|
|
344
|
+
* prismaClientPath: '@/generated/prisma/client' // Prisma 7+
|
|
345
|
+
* prismaClientPath: './generated/prisma/client' // relative
|
|
640
346
|
*/
|
|
641
|
-
|
|
642
|
-
/** Resolved kick.config.ts (null if the project has none). */
|
|
643
|
-
config: KickConfig | null;
|
|
644
|
-
log: (msg: string) => void;
|
|
347
|
+
prismaClientPath?: string;
|
|
645
348
|
/**
|
|
646
|
-
*
|
|
647
|
-
*
|
|
648
|
-
* threaded through so `register()` callbacks (notably
|
|
649
|
-
* `kick/generate`) can register each plugin generator as a real
|
|
650
|
-
* Commander subcommand — without that, plugin generators only fire
|
|
651
|
-
* via the bare-action dispatch and are invisible to `kick g --help`.
|
|
349
|
+
* Module declaration style emitted by `kick g module` and the
|
|
350
|
+
* project scaffold.
|
|
652
351
|
*
|
|
653
|
-
*
|
|
654
|
-
*
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
name: string;
|
|
661
|
-
commands?: KickCommandDefinition[];
|
|
662
|
-
/** Programmatic command registration. Called once at CLI startup. */
|
|
663
|
-
register?: (program: Command, ctx: KickCliPluginContext) => void | Promise<void>;
|
|
664
|
-
typegens?: TypegenPlugin[];
|
|
665
|
-
generators?: GeneratorSpec[];
|
|
666
|
-
}
|
|
667
|
-
/** Identity helper — exists for type inference + parity with definePlugin. */
|
|
668
|
-
declare function defineCliPlugin(p: KickCliPlugin): KickCliPlugin;
|
|
669
|
-
declare class KickPluginConflictError extends Error {
|
|
670
|
-
constructor(kind: 'plugin' | 'command' | 'typegen' | 'generator', id: string, owners: string[]);
|
|
671
|
-
}
|
|
672
|
-
//#endregion
|
|
673
|
-
//#region src/config.d.ts
|
|
674
|
-
/** A custom command that developers can register via kick.config.ts */
|
|
675
|
-
interface KickCommandDefinition {
|
|
676
|
-
/** The command name (e.g. 'db:migrate', 'seed', 'proto:gen') */
|
|
677
|
-
name: string;
|
|
678
|
-
/** Description shown in --help */
|
|
679
|
-
description: string;
|
|
680
|
-
/**
|
|
681
|
-
* Shell command(s) to run. Can be a single string or an array of
|
|
682
|
-
* sequential steps. Use {args} as a placeholder for CLI arguments.
|
|
352
|
+
* - `'define'` (default) — `defineModule({ name, build: () => ({...}) })`
|
|
353
|
+
* factory form. Mirrors `defineAdapter` / `definePlugin` /
|
|
354
|
+
* `defineContextDecorator`.
|
|
355
|
+
* - `'class'` — legacy `class FooModule implements AppModule { ... }`
|
|
356
|
+
* form. Still fully supported by the framework loader; pin to this
|
|
357
|
+
* value for projects that prefer the class shape (existing-codebase
|
|
358
|
+
* consistency, class-decorator setups, etc).
|
|
683
359
|
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
*
|
|
360
|
+
* The framework runtime accepts both shapes regardless of this
|
|
361
|
+
* setting — the flag controls codegen output only. `kick g module`
|
|
362
|
+
* inserts the matching call form into `src/modules/index.ts`
|
|
363
|
+
* (`Module()` vs `Module`); `kick rm module` matches both.
|
|
364
|
+
*
|
|
365
|
+
* @default 'define'
|
|
687
366
|
*/
|
|
688
|
-
|
|
689
|
-
/** Optional aliases (e.g. ['migrate'] for 'db:migrate') */
|
|
690
|
-
aliases?: string[];
|
|
691
|
-
}
|
|
692
|
-
/** Project pattern — controls what generators produce and which deps are installed */
|
|
693
|
-
type ProjectPattern = 'rest' | 'ddd' | 'cqrs' | 'minimal';
|
|
694
|
-
/** Package manager used for `kick add` and other dep-installing commands */
|
|
695
|
-
type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';
|
|
696
|
-
/** Built-in repository types with first-class code generation support */
|
|
697
|
-
type BuiltinRepoType$1 = 'drizzle' | 'inmemory' | 'prisma';
|
|
698
|
-
/** Custom repository type — generates a stub with TODO markers */
|
|
699
|
-
interface CustomRepoType {
|
|
700
|
-
name: string;
|
|
367
|
+
style?: 'define' | 'class';
|
|
701
368
|
}
|
|
702
|
-
/**
|
|
703
|
-
|
|
704
|
-
/**
|
|
705
|
-
* Supported schema validators for `kick typegen` body/query/params
|
|
706
|
-
* type extraction.
|
|
707
|
-
*
|
|
708
|
-
* - `'zod'` — emits `import('zod').infer<typeof schema>` (default)
|
|
709
|
-
* - `'kickjs-schema'` — emits `InferSchemaOutput<typeof schema>` from
|
|
710
|
-
* `@forinda/kickjs-schema`, works with Zod, Valibot, Yup, and any
|
|
711
|
-
* Standard Schema v1 or KickSchema adapter
|
|
712
|
-
* - `false` — disables schema-driven body typing (fields stay `unknown`)
|
|
713
|
-
*/
|
|
714
|
-
type SchemaValidator = 'zod' | 'kickjs-schema' | false;
|
|
715
|
-
/**
|
|
716
|
-
* One entry in the typed `assetMap` config record (`assets-plan.md`).
|
|
717
|
-
* Each entry names a source directory whose files become addressable
|
|
718
|
-
* via the `assets.<name>.*` typed accessor at runtime.
|
|
719
|
-
*/
|
|
720
|
-
interface AssetMapEntry {
|
|
369
|
+
/** Configuration for the kick.config.ts file */
|
|
370
|
+
interface KickConfig {
|
|
721
371
|
/**
|
|
722
|
-
*
|
|
723
|
-
*
|
|
724
|
-
*
|
|
725
|
-
*
|
|
372
|
+
* Project pattern — controls default generator behavior.
|
|
373
|
+
* - 'rest' — Express + Swagger (default)
|
|
374
|
+
* - 'ddd' — Full DDD modules with use cases, entities, value objects
|
|
375
|
+
* - 'cqrs' — CQRS with commands, queries, events, WebSocket + queue
|
|
376
|
+
* - 'minimal' — Bare Express with no scaffolding
|
|
726
377
|
*/
|
|
727
|
-
|
|
378
|
+
pattern?: ProjectPattern;
|
|
728
379
|
/**
|
|
729
|
-
*
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
380
|
+
* Module generation settings — directory, repo type, pluralization, schema dir.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* modules: {
|
|
384
|
+
* dir: 'src/modules',
|
|
385
|
+
* repo: 'prisma',
|
|
386
|
+
* pluralize: false,
|
|
387
|
+
* schemaDir: 'prisma/',
|
|
388
|
+
* }
|
|
733
389
|
*/
|
|
734
|
-
|
|
390
|
+
modules?: ModuleConfig;
|
|
735
391
|
/**
|
|
736
|
-
*
|
|
737
|
-
*
|
|
738
|
-
*
|
|
392
|
+
* Package manager used by `kick add` (and any future dep-installing command)
|
|
393
|
+
* to install dependencies. When set, overrides lockfile auto-detection so
|
|
394
|
+
* commands always use the project's intended package manager.
|
|
395
|
+
*
|
|
396
|
+
* Priority (highest first):
|
|
397
|
+
* 1. `--pm` flag on the CLI
|
|
398
|
+
* 2. `packageManager` in kick.config
|
|
399
|
+
* 3. `packageManager` field in package.json (corepack convention)
|
|
400
|
+
* 4. Lockfile detection (pnpm-lock.yaml → pnpm, yarn.lock → yarn)
|
|
401
|
+
* 5. `'npm'`
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* packageManager: 'pnpm'
|
|
739
405
|
*/
|
|
740
|
-
|
|
406
|
+
packageManager?: PackageManager;
|
|
741
407
|
/**
|
|
742
|
-
*
|
|
408
|
+
* DI token scope prefix used by code generators. Every scaffolded
|
|
409
|
+
* `createToken<T>('<scope>/<area>/<key>')` substitutes this string
|
|
410
|
+
* for `<scope>`. Generators emit org-scoped tokens out of the box
|
|
411
|
+
* so adopter projects pass `kick-lint`'s `token-reserved-prefix`
|
|
412
|
+
* rule (which forbids the reserved `kick/` prefix on third-party
|
|
413
|
+
* code) without manual rename.
|
|
743
414
|
*
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
* - `'with-extension'` — keep every extension on every key. Best
|
|
750
|
-
* when the namespace holds extension siblings (`index.pug` +
|
|
751
|
-
* `index.html` + `index.css` in `src/pages/`); every file
|
|
752
|
-
* reaches the manifest under its full path.
|
|
753
|
-
* - `'auto'` — strip when basenames are unique, keep extensions
|
|
754
|
-
* on collision groups. Singleton files keep their short key;
|
|
755
|
-
* `pages/index.{pug,html,css}` becomes
|
|
756
|
-
* `pages/index.pug` / `pages/index.html` / `pages/index.css`.
|
|
757
|
-
* No data loss; non-colliding namespaces stay on the short
|
|
758
|
-
* keys they had before.
|
|
415
|
+
* Resolution order (highest first):
|
|
416
|
+
* 1. This field, when set
|
|
417
|
+
* 2. `package.json` `name` field — `@scope/pkg` → `'scope'`,
|
|
418
|
+
* bare `pkg` → `'pkg'`
|
|
419
|
+
* 3. Fallback `'app'`
|
|
759
420
|
*
|
|
760
|
-
* @
|
|
421
|
+
* @example
|
|
422
|
+
* tokenScope: 'mycorp'
|
|
423
|
+
* // → createToken<...>('mycorp/users/repository')
|
|
761
424
|
*/
|
|
762
|
-
|
|
763
|
-
}
|
|
764
|
-
/**
|
|
765
|
-
* Database settings consumed by `kick db generate`, `kick db migrate`,
|
|
766
|
-
* and the M4.C composite-type detection gate. Mirrors the runtime
|
|
767
|
-
* `DbConfig` shape from `@forinda/kickjs-db` — duplicated here so
|
|
768
|
-
* adopters who only install `@forinda/kickjs-cli` can set the block
|
|
769
|
-
* without pulling `@forinda/kickjs-db` types into their kick.config.ts
|
|
770
|
-
* resolution. The CLI loads kick.config.ts through `loadKickConfig`,
|
|
771
|
-
* then `resolveDbConfig` re-reads this block from the same module and
|
|
772
|
-
* normalises defaults (`schemaPath` / `migrationsDir` / `dialect`).
|
|
773
|
-
*
|
|
774
|
-
* @example
|
|
775
|
-
* ```ts
|
|
776
|
-
* defineConfig({
|
|
777
|
-
* db: {
|
|
778
|
-
* schemaPath: 'src/db/schema.ts',
|
|
779
|
-
* migrationsDir: 'db/migrations',
|
|
780
|
-
* dialect: 'postgres',
|
|
781
|
-
* connectionString: process.env.DATABASE_URL,
|
|
782
|
-
* },
|
|
783
|
-
* })
|
|
784
|
-
* ```
|
|
785
|
-
*/
|
|
786
|
-
interface KickDbConfigBlock {
|
|
425
|
+
tokenScope?: string;
|
|
787
426
|
/**
|
|
788
|
-
*
|
|
789
|
-
*
|
|
427
|
+
* Directories to copy to dist/ after build.
|
|
428
|
+
* Useful for EJS templates, email templates, static assets, etc.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```ts
|
|
432
|
+
* copyDirs: [
|
|
433
|
+
* 'src/views', // copies to dist/src/views
|
|
434
|
+
* { src: 'src/views', dest: 'dist/views' }, // custom dest
|
|
435
|
+
* 'src/emails',
|
|
436
|
+
* ]
|
|
437
|
+
* ```
|
|
790
438
|
*/
|
|
791
|
-
|
|
439
|
+
copyDirs?: Array<string | {
|
|
440
|
+
src: string;
|
|
441
|
+
dest?: string;
|
|
442
|
+
}>;
|
|
792
443
|
/**
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
*
|
|
801
|
-
*
|
|
802
|
-
* `kick db migrate*` and the M4.C composite-type gate at
|
|
803
|
-
* `kick db generate`.
|
|
804
|
-
*/
|
|
805
|
-
connectionString?: string;
|
|
806
|
-
/**
|
|
807
|
-
* Escape hatch: a factory returning a fully-constructed
|
|
808
|
-
* MigrationAdapter (typed loosely here so the CLI types don't pull
|
|
809
|
-
* `@forinda/kickjs-db` into adopter projects that don't import it).
|
|
810
|
-
* Takes precedence over `connectionString` when both are set.
|
|
811
|
-
*/
|
|
812
|
-
adapter?: () => unknown | Promise<unknown>;
|
|
813
|
-
}
|
|
814
|
-
/** Typegen settings — controls .kickjs/types/* generation */
|
|
815
|
-
interface TypegenConfig {
|
|
816
|
-
/**
|
|
817
|
-
* Source directory to scan for controllers and decorators.
|
|
818
|
-
* Defaults to `'src'`.
|
|
819
|
-
*/
|
|
820
|
-
srcDir?: string;
|
|
821
|
-
/**
|
|
822
|
-
* Output directory for generated `.d.ts` files.
|
|
823
|
-
* Defaults to `'.kickjs/types'`.
|
|
824
|
-
*/
|
|
825
|
-
outDir?: string;
|
|
826
|
-
/**
|
|
827
|
-
* Schema validator used to derive `body` types from route metadata.
|
|
828
|
-
*
|
|
829
|
-
* - `'zod'` — emit `z.infer<typeof <importedSchema>>` for any schema
|
|
830
|
-
* referenced as a named identifier in `@Get/@Post/...({ body, query, params })`.
|
|
831
|
-
* - `false` — disable schema-driven body typing.
|
|
832
|
-
*
|
|
833
|
-
* Future: `'joi' | 'yup' | 'json-schema'` plus a `{ name; module }`
|
|
834
|
-
* escape hatch for custom adapters.
|
|
835
|
-
*
|
|
836
|
-
* @default 'zod'
|
|
837
|
-
*/
|
|
838
|
-
schemaValidator?: SchemaValidator;
|
|
839
|
-
/**
|
|
840
|
-
* Path to the project's env schema file (relative to project root).
|
|
841
|
-
* Must default-export a `defineEnv(...)` schema for typegen to emit
|
|
842
|
-
* the typed `KickEnv` global registry.
|
|
843
|
-
*
|
|
844
|
-
* Set to `false` to disable env typing entirely.
|
|
845
|
-
*
|
|
846
|
-
* @default 'src/env.ts'
|
|
847
|
-
*/
|
|
848
|
-
envFile?: string | false;
|
|
849
|
-
/**
|
|
850
|
-
* Built-in or user typegen plugin ids to skip during `kick typegen`,
|
|
851
|
-
* `kick dev`, and `kick typegen --watch`.
|
|
852
|
-
*
|
|
853
|
-
* The plugin still loads and merge-time conflict detection still
|
|
854
|
-
* runs — only the `generate()` invocation is skipped — so adopters
|
|
855
|
-
* who want to hand-write `KickDbRegister` (manual typeof-schema
|
|
856
|
-
* augmentation) can disable `'kick/db'` and keep the rest:
|
|
857
|
-
*
|
|
858
|
-
* @example
|
|
859
|
-
* typegen: {
|
|
860
|
-
* disable: ['kick/db'], // hand-written register.ts owns the type
|
|
861
|
-
* }
|
|
862
|
-
*
|
|
863
|
-
* Unrecognised ids are ignored — the list is treated as a wishlist,
|
|
864
|
-
* not a strict registry.
|
|
865
|
-
*/
|
|
866
|
-
disable?: string[];
|
|
867
|
-
}
|
|
868
|
-
/** Module generation settings — controls how `kick g module` produces code */
|
|
869
|
-
interface ModuleConfig {
|
|
870
|
-
/** Where modules live (default: 'src/modules') */
|
|
871
|
-
dir?: string;
|
|
872
|
-
/**
|
|
873
|
-
* Default repository implementation for generators.
|
|
874
|
-
*
|
|
875
|
-
* Built-in types (string): `'drizzle'`, `'inmemory'`, `'prisma'`
|
|
876
|
-
* — generate fully working repository code.
|
|
877
|
-
*
|
|
878
|
-
* Custom types (object): `{ name: 'typeorm' }`
|
|
879
|
-
* — generate a stub repository with TODO markers.
|
|
880
|
-
*
|
|
881
|
-
* @example
|
|
882
|
-
* repo: 'prisma' // built-in
|
|
883
|
-
* repo: { name: 'typeorm' } // custom
|
|
884
|
-
*/
|
|
885
|
-
repo?: RepoTypeConfig;
|
|
886
|
-
/** Schema output directory (e.g. 'src/db/schema' for Drizzle, 'prisma/' for Prisma) */
|
|
887
|
-
schemaDir?: string;
|
|
888
|
-
/**
|
|
889
|
-
* Whether to pluralize module names in generated code.
|
|
890
|
-
* When true (default), `kick g module user` creates `src/modules/users/`.
|
|
891
|
-
* When false, it creates `src/modules/user/` and uses singular names throughout.
|
|
892
|
-
*/
|
|
893
|
-
pluralize?: boolean;
|
|
894
|
-
/**
|
|
895
|
-
* Import path for the Prisma generated client in `--repo prisma` templates.
|
|
896
|
-
* Must resolve within `src/` for path alias compatibility.
|
|
897
|
-
*
|
|
898
|
-
* @default '@prisma/client' (Prisma 5/6)
|
|
899
|
-
* @example
|
|
900
|
-
* prismaClientPath: '@/generated/prisma/client' // Prisma 7+
|
|
901
|
-
* prismaClientPath: './generated/prisma/client' // relative
|
|
902
|
-
*/
|
|
903
|
-
prismaClientPath?: string;
|
|
904
|
-
/**
|
|
905
|
-
* Module declaration style emitted by `kick g module` and the
|
|
906
|
-
* project scaffold.
|
|
907
|
-
*
|
|
908
|
-
* - `'define'` (default) — `defineModule({ name, build: () => ({...}) })`
|
|
909
|
-
* factory form. Mirrors `defineAdapter` / `definePlugin` /
|
|
910
|
-
* `defineContextDecorator`.
|
|
911
|
-
* - `'class'` — legacy `class FooModule implements AppModule { ... }`
|
|
912
|
-
* form. Still fully supported by the framework loader; pin to this
|
|
913
|
-
* value for projects that prefer the class shape (existing-codebase
|
|
914
|
-
* consistency, class-decorator setups, etc).
|
|
915
|
-
*
|
|
916
|
-
* The framework runtime accepts both shapes regardless of this
|
|
917
|
-
* setting — the flag controls codegen output only. `kick g module`
|
|
918
|
-
* inserts the matching call form into `src/modules/index.ts`
|
|
919
|
-
* (`Module()` vs `Module`); `kick rm module` matches both.
|
|
920
|
-
*
|
|
921
|
-
* @default 'define'
|
|
922
|
-
*/
|
|
923
|
-
style?: 'define' | 'class';
|
|
924
|
-
}
|
|
925
|
-
/** Configuration for the kick.config.ts file */
|
|
926
|
-
interface KickConfig {
|
|
927
|
-
/**
|
|
928
|
-
* Project pattern — controls default generator behavior.
|
|
929
|
-
* - 'rest' — Express + Swagger (default)
|
|
930
|
-
* - 'ddd' — Full DDD modules with use cases, entities, value objects
|
|
931
|
-
* - 'cqrs' — CQRS with commands, queries, events, WebSocket + queue
|
|
932
|
-
* - 'minimal' — Bare Express with no scaffolding
|
|
933
|
-
*/
|
|
934
|
-
pattern?: ProjectPattern;
|
|
935
|
-
/**
|
|
936
|
-
* Module generation settings — directory, repo type, pluralization, schema dir.
|
|
937
|
-
*
|
|
938
|
-
* @example
|
|
939
|
-
* modules: {
|
|
940
|
-
* dir: 'src/modules',
|
|
941
|
-
* repo: 'prisma',
|
|
942
|
-
* pluralize: false,
|
|
943
|
-
* schemaDir: 'prisma/',
|
|
944
|
-
* }
|
|
945
|
-
*/
|
|
946
|
-
modules?: ModuleConfig;
|
|
947
|
-
/**
|
|
948
|
-
* Package manager used by `kick add` (and any future dep-installing command)
|
|
949
|
-
* to install dependencies. When set, overrides lockfile auto-detection so
|
|
950
|
-
* commands always use the project's intended package manager.
|
|
951
|
-
*
|
|
952
|
-
* Priority (highest first):
|
|
953
|
-
* 1. `--pm` flag on the CLI
|
|
954
|
-
* 2. `packageManager` in kick.config
|
|
955
|
-
* 3. `packageManager` field in package.json (corepack convention)
|
|
956
|
-
* 4. Lockfile detection (pnpm-lock.yaml → pnpm, yarn.lock → yarn)
|
|
957
|
-
* 5. `'npm'`
|
|
958
|
-
*
|
|
959
|
-
* @example
|
|
960
|
-
* packageManager: 'pnpm'
|
|
961
|
-
*/
|
|
962
|
-
packageManager?: PackageManager;
|
|
963
|
-
/**
|
|
964
|
-
* DI token scope prefix used by code generators. Every scaffolded
|
|
965
|
-
* `createToken<T>('<scope>/<area>/<key>')` substitutes this string
|
|
966
|
-
* for `<scope>`. Generators emit org-scoped tokens out of the box
|
|
967
|
-
* so adopter projects pass `kick-lint`'s `token-reserved-prefix`
|
|
968
|
-
* rule (which forbids the reserved `kick/` prefix on third-party
|
|
969
|
-
* code) without manual rename.
|
|
970
|
-
*
|
|
971
|
-
* Resolution order (highest first):
|
|
972
|
-
* 1. This field, when set
|
|
973
|
-
* 2. `package.json` `name` field — `@scope/pkg` → `'scope'`,
|
|
974
|
-
* bare `pkg` → `'pkg'`
|
|
975
|
-
* 3. Fallback `'app'`
|
|
976
|
-
*
|
|
977
|
-
* @example
|
|
978
|
-
* tokenScope: 'mycorp'
|
|
979
|
-
* // → createToken<...>('mycorp/users/repository')
|
|
980
|
-
*/
|
|
981
|
-
tokenScope?: string;
|
|
982
|
-
/**
|
|
983
|
-
* Directories to copy to dist/ after build.
|
|
984
|
-
* Useful for EJS templates, email templates, static assets, etc.
|
|
985
|
-
*
|
|
986
|
-
* @example
|
|
987
|
-
* ```ts
|
|
988
|
-
* copyDirs: [
|
|
989
|
-
* 'src/views', // copies to dist/src/views
|
|
990
|
-
* { src: 'src/views', dest: 'dist/views' }, // custom dest
|
|
991
|
-
* 'src/emails',
|
|
992
|
-
* ]
|
|
993
|
-
* ```
|
|
994
|
-
*/
|
|
995
|
-
copyDirs?: Array<string | {
|
|
996
|
-
src: string;
|
|
997
|
-
dest?: string;
|
|
998
|
-
}>;
|
|
999
|
-
/**
|
|
1000
|
-
* Build output settings. The asset manager + `kick build`'s copy
|
|
1001
|
-
* steps honour these — adopters who use Vite's `build.outDir =
|
|
1002
|
-
* 'out'` (or any non-default) should mirror the value here so
|
|
1003
|
-
* `assets.x.y()` paths line up with where Vite actually wrote.
|
|
1004
|
-
*
|
|
1005
|
-
* @example
|
|
1006
|
-
* ```ts
|
|
1007
|
-
* build: { outDir: 'out' }
|
|
1008
|
-
* ```
|
|
444
|
+
* Build output settings. The asset manager + `kick build`'s copy
|
|
445
|
+
* steps honour these — adopters who use Vite's `build.outDir =
|
|
446
|
+
* 'out'` (or any non-default) should mirror the value here so
|
|
447
|
+
* `assets.x.y()` paths line up with where Vite actually wrote.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```ts
|
|
451
|
+
* build: { outDir: 'out' }
|
|
452
|
+
* ```
|
|
1009
453
|
*/
|
|
1010
454
|
build?: {
|
|
1011
455
|
/**
|
|
@@ -1158,7 +602,7 @@ declare function loadKickConfig(startDir: string): Promise<KickConfig | null>;
|
|
|
1158
602
|
type ModuleStyle = 'define' | 'class';
|
|
1159
603
|
//#endregion
|
|
1160
604
|
//#region src/generators/module.d.ts
|
|
1161
|
-
type BuiltinRepoType = '
|
|
605
|
+
type BuiltinRepoType = 'inmemory';
|
|
1162
606
|
type RepoType = BuiltinRepoType | (string & {});
|
|
1163
607
|
interface GenerateModuleOptions {
|
|
1164
608
|
name: string;
|
|
@@ -1193,9 +637,7 @@ interface GenerateModuleOptions {
|
|
|
1193
637
|
* Generate a module — structure depends on the project pattern.
|
|
1194
638
|
*
|
|
1195
639
|
* Patterns:
|
|
1196
|
-
* rest — flat folder: controller + service + DTOs + repo
|
|
1197
|
-
* ddd — nested DDD: presentation/ application/ domain/ infrastructure/
|
|
1198
|
-
* cqrs — commands, queries, events with WS/queue integration
|
|
640
|
+
* rest — flat folder: controller + service + DTOs + repo (default)
|
|
1199
641
|
* minimal — just controller + module index
|
|
1200
642
|
*/
|
|
1201
643
|
declare function generateModule(options: GenerateModuleOptions): Promise<string[]>;
|
|
@@ -1274,7 +716,7 @@ interface GenerateDtoOptions {
|
|
|
1274
716
|
declare function generateDto(options: GenerateDtoOptions): Promise<string[]>;
|
|
1275
717
|
//#endregion
|
|
1276
718
|
//#region src/generators/project.d.ts
|
|
1277
|
-
type ProjectTemplate = 'rest' | '
|
|
719
|
+
type ProjectTemplate = 'rest' | 'minimal';
|
|
1278
720
|
type SchemaLib = 'zod' | 'valibot' | 'yup';
|
|
1279
721
|
interface InitProjectOptions {
|
|
1280
722
|
name: string;
|
|
@@ -1309,6 +751,374 @@ declare function initProject(options: InitProjectOptions): Promise<void>;
|
|
|
1309
751
|
*/
|
|
1310
752
|
declare function findProjectRoot(startDir?: string): string;
|
|
1311
753
|
//#endregion
|
|
754
|
+
//#region src/typegen/scanner.d.ts
|
|
755
|
+
/**
|
|
756
|
+
* Static scanner for KickJS decorated classes and DI tokens.
|
|
757
|
+
*
|
|
758
|
+
* Walks `src/**\/*.ts` (excluding tests and node_modules) and extracts:
|
|
759
|
+
*
|
|
760
|
+
* - Decorated classes (`@Service`, `@Controller`, `@Repository`, etc.)
|
|
761
|
+
* - `createToken<T>('name')` definitions
|
|
762
|
+
* - `@Inject('literal')` calls
|
|
763
|
+
*
|
|
764
|
+
* The output feeds the type generator, which emits `.kickjs/types/*.d.ts`
|
|
765
|
+
* files used by the user's tsc to make `container.resolve()` and module
|
|
766
|
+
* discovery type-safe.
|
|
767
|
+
*
|
|
768
|
+
* This is intentionally regex-based (not AST-based) to avoid the
|
|
769
|
+
* ts-morph / typescript compiler dependency. Pattern from
|
|
770
|
+
* `packages/vite/src/module-discovery.ts` which already uses regex
|
|
771
|
+
* to detect `*.module.ts` exports.
|
|
772
|
+
*
|
|
773
|
+
* ## Collision detection
|
|
774
|
+
*
|
|
775
|
+
* Two classes with the same name across different files is a collision.
|
|
776
|
+
* The scanner records all collisions in `ScanResult.collisions` so the
|
|
777
|
+
* caller (generator) can decide whether to hard-error or auto-namespace.
|
|
778
|
+
*
|
|
779
|
+
* @module @forinda/kickjs-cli/typegen/scanner
|
|
780
|
+
*/
|
|
781
|
+
/** Decorators that mark a class as DI-managed */
|
|
782
|
+
declare const DECORATOR_NAMES: readonly ['Service', 'Controller', 'Repository', 'Injectable', 'Component', 'Module'];
|
|
783
|
+
type DecoratorName = (typeof DECORATOR_NAMES)[number];
|
|
784
|
+
/** A single discovered decorated class */
|
|
785
|
+
interface DiscoveredClass {
|
|
786
|
+
/** Class name (e.g., 'UserService') */
|
|
787
|
+
className: string;
|
|
788
|
+
/** Decorator that marked it (e.g., 'Service') */
|
|
789
|
+
decorator: DecoratorName;
|
|
790
|
+
/** Absolute file path */
|
|
791
|
+
filePath: string;
|
|
792
|
+
/** Path relative to scan root, with forward slashes */
|
|
793
|
+
relativePath: string;
|
|
794
|
+
/** True if exported as `default` */
|
|
795
|
+
isDefault: boolean;
|
|
796
|
+
}
|
|
797
|
+
/** A single route handler discovered on a controller class */
|
|
798
|
+
interface DiscoveredRoute {
|
|
799
|
+
/** Owning controller class name (e.g. 'UserController') */
|
|
800
|
+
controller: string;
|
|
801
|
+
/** Handler method name on the controller (e.g. 'getUser') */
|
|
802
|
+
method: string;
|
|
803
|
+
/** HTTP verb (uppercase) */
|
|
804
|
+
httpMethod: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
805
|
+
/** Route path including parameter placeholders (e.g. '/:id/posts/:postId') */
|
|
806
|
+
path: string;
|
|
807
|
+
/** URL path parameter names extracted from `:placeholder` segments */
|
|
808
|
+
pathParams: string[];
|
|
809
|
+
/**
|
|
810
|
+
* Whitelisted query field names extracted from `@ApiQueryParams({...})`.
|
|
811
|
+
* `null` means no `@ApiQueryParams` was found on this method (so the
|
|
812
|
+
* generator emits an unconstrained `query` shape). An empty array means
|
|
813
|
+
* the decorator existed but no fields could be statically extracted
|
|
814
|
+
* (e.g. an opaque imported config).
|
|
815
|
+
*/
|
|
816
|
+
queryFilterable: string[] | null;
|
|
817
|
+
querySortable: string[] | null;
|
|
818
|
+
querySearchable: string[] | null;
|
|
819
|
+
/**
|
|
820
|
+
* Schema identifiers referenced from the route decorator's second arg
|
|
821
|
+
* (e.g. `@Post('/', { body: createTaskSchema })`). `null` means no
|
|
822
|
+
* such reference; the value carries the identifier and the resolved
|
|
823
|
+
* import source (relative module path) if known.
|
|
824
|
+
*/
|
|
825
|
+
bodySchema: SchemaRef | null;
|
|
826
|
+
querySchema: SchemaRef | null;
|
|
827
|
+
paramsSchema: SchemaRef | null;
|
|
828
|
+
/** Absolute file path of the controller */
|
|
829
|
+
filePath: string;
|
|
830
|
+
/** Path relative to scan root, with forward slashes */
|
|
831
|
+
relativePath: string;
|
|
832
|
+
}
|
|
833
|
+
/** A statically-resolved schema identifier reference */
|
|
834
|
+
interface SchemaRef {
|
|
835
|
+
/** The identifier as written (e.g. `createTaskSchema`) */
|
|
836
|
+
identifier: string;
|
|
837
|
+
/**
|
|
838
|
+
* Resolved module specifier (relative path or bare module name) where
|
|
839
|
+
* the identifier is defined. `null` means the source could not be
|
|
840
|
+
* statically determined (the generator falls back to `unknown`).
|
|
841
|
+
*/
|
|
842
|
+
source: string | null;
|
|
843
|
+
}
|
|
844
|
+
/** A `createToken<T>('name')` call discovered in source */
|
|
845
|
+
interface DiscoveredToken {
|
|
846
|
+
/** The literal string passed to `createToken()` */
|
|
847
|
+
name: string;
|
|
848
|
+
/** The const variable name on the LHS, if any */
|
|
849
|
+
variable: string | null;
|
|
850
|
+
/** Absolute file path */
|
|
851
|
+
filePath: string;
|
|
852
|
+
/** Path relative to scan root, with forward slashes */
|
|
853
|
+
relativePath: string;
|
|
854
|
+
}
|
|
855
|
+
/** An `@Inject('literal')` call discovered in source */
|
|
856
|
+
interface DiscoveredInject {
|
|
857
|
+
/** The literal string passed to `@Inject()` */
|
|
858
|
+
name: string;
|
|
859
|
+
/** Absolute file path */
|
|
860
|
+
filePath: string;
|
|
861
|
+
/** Path relative to scan root, with forward slashes */
|
|
862
|
+
relativePath: string;
|
|
863
|
+
}
|
|
864
|
+
/** A name collision — same class name in two or more files */
|
|
865
|
+
interface ClassCollision {
|
|
866
|
+
/** The colliding class name */
|
|
867
|
+
className: string;
|
|
868
|
+
/** All files declaring the class */
|
|
869
|
+
classes: DiscoveredClass[];
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Information about a discovered env schema file. The typegen
|
|
873
|
+
* generator uses this to emit a `KickEnv` + `NodeJS.ProcessEnv`
|
|
874
|
+
* augmentation that flows through to `@Value` and `process.env`.
|
|
875
|
+
*
|
|
876
|
+
* `null` means no env file was found at the configured location.
|
|
877
|
+
*/
|
|
878
|
+
interface DiscoveredEnv {
|
|
879
|
+
/** Absolute path to the env schema file */
|
|
880
|
+
filePath: string;
|
|
881
|
+
/** Path relative to scan root, with forward slashes */
|
|
882
|
+
relativePath: string;
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* A plugin or adapter discovered in source — either via `defineAdapter({ name })`
|
|
886
|
+
* / `definePlugin({ name })` calls, or via a class that `implements AppAdapter`
|
|
887
|
+
* and declares a string-literal `name` field.
|
|
888
|
+
*
|
|
889
|
+
* The `name` here is the literal string passed to the framework (the value
|
|
890
|
+
* `dependsOn` references), NOT the symbol on the LHS. `defineAdapter` lets
|
|
891
|
+
* authors choose any name they want; the symbol is irrelevant at runtime.
|
|
892
|
+
*/
|
|
893
|
+
interface DiscoveredPluginOrAdapter {
|
|
894
|
+
/** Whether this is a plugin (`definePlugin`) or adapter (`defineAdapter` / class) */
|
|
895
|
+
kind: 'plugin' | 'adapter';
|
|
896
|
+
/** The string literal passed as `name` (the value `dependsOn` references) */
|
|
897
|
+
name: string;
|
|
898
|
+
/** Absolute file path */
|
|
899
|
+
filePath: string;
|
|
900
|
+
/** Path relative to scan root, with forward slashes */
|
|
901
|
+
relativePath: string;
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* A context key discovered from a `defineContextDecorator({ key })` or
|
|
905
|
+
* `defineHttpContextDecorator({ key })` call (including the curried
|
|
906
|
+
* `.withParams<P>()({ key })` form). Feeds the `kick/context` typegen
|
|
907
|
+
* plugin, which emits the `ContextKeys` augmentation so `dependsOn`
|
|
908
|
+
* typo-checking is automatic and complete.
|
|
909
|
+
*/
|
|
910
|
+
interface DiscoveredContextKey {
|
|
911
|
+
/** The literal `key:` value the contributor writes. */
|
|
912
|
+
key: string;
|
|
913
|
+
/** Absolute file path. */
|
|
914
|
+
filePath: string;
|
|
915
|
+
/** Path relative to scan root, with forward slashes. */
|
|
916
|
+
relativePath: string;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* A `defineAugmentation('Name', meta)` call discovered in source. Plugins
|
|
920
|
+
* call this to advertise an augmentable interface so the typegen can list
|
|
921
|
+
* every augmentation surface in one generated file.
|
|
922
|
+
*/
|
|
923
|
+
interface DiscoveredAugmentation {
|
|
924
|
+
/** The literal string passed as the first arg to `defineAugmentation` */
|
|
925
|
+
name: string;
|
|
926
|
+
/** Optional `description` extracted from the second-arg object literal */
|
|
927
|
+
description: string | null;
|
|
928
|
+
/** Optional `example` extracted from the second-arg object literal */
|
|
929
|
+
example: string | null;
|
|
930
|
+
/** Absolute file path */
|
|
931
|
+
filePath: string;
|
|
932
|
+
/** Path relative to scan root, with forward slashes */
|
|
933
|
+
relativePath: string;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* A decorated class whose file sits inside a module directory but
|
|
937
|
+
* isn't picked up by any of the module's `import.meta.glob(...)`
|
|
938
|
+
* patterns. Surfaced as a typegen warning per forinda/kick-js#235 §4
|
|
939
|
+
* so adopters notice silent registration drift before it bites them
|
|
940
|
+
* at runtime with a `MissingContributorError` or wrong code path.
|
|
941
|
+
*/
|
|
942
|
+
interface OrphanedClass {
|
|
943
|
+
/** The decorated class name */
|
|
944
|
+
className: string;
|
|
945
|
+
/** Absolute path of the class file */
|
|
946
|
+
filePath: string;
|
|
947
|
+
/** Path relative to scan root, with forward slashes */
|
|
948
|
+
relativePath: string;
|
|
949
|
+
/** Absolute path of the module file whose globs didn't match */
|
|
950
|
+
moduleFilePath: string;
|
|
951
|
+
/** The decorator name (`Service`, `Controller`, `Repository`, …) */
|
|
952
|
+
decorator: DecoratorName;
|
|
953
|
+
}
|
|
954
|
+
/** Aggregated scanner output */
|
|
955
|
+
interface ScanResult {
|
|
956
|
+
classes: DiscoveredClass[];
|
|
957
|
+
routes: DiscoveredRoute[];
|
|
958
|
+
tokens: DiscoveredToken[];
|
|
959
|
+
injects: DiscoveredInject[];
|
|
960
|
+
collisions: ClassCollision[];
|
|
961
|
+
/** Discovered env schema file (or null if none found at the configured path) */
|
|
962
|
+
env: DiscoveredEnv | null;
|
|
963
|
+
/** Plugins/adapters discovered via `defineAdapter`/`definePlugin`/`implements AppAdapter` */
|
|
964
|
+
pluginsAndAdapters: DiscoveredPluginOrAdapter[];
|
|
965
|
+
/** Augmentation interfaces declared via `defineAugmentation('Name', meta)` */
|
|
966
|
+
augmentations: DiscoveredAugmentation[];
|
|
967
|
+
/** Context keys from `define(Http)ContextDecorator({ key })` calls */
|
|
968
|
+
contextKeys: DiscoveredContextKey[];
|
|
969
|
+
/**
|
|
970
|
+
* Decorated classes that sit inside a module directory but aren't
|
|
971
|
+
* picked up by any of the module's `import.meta.glob(...)` patterns.
|
|
972
|
+
* Empty when every decorator file is matched. forinda/kick-js#235 §4.
|
|
973
|
+
*/
|
|
974
|
+
orphanedClasses: OrphanedClass[];
|
|
975
|
+
}
|
|
976
|
+
/** Options for the scanner */
|
|
977
|
+
interface ScanOptions {
|
|
978
|
+
/** Root directory to scan (e.g., absolute path to `src`) */
|
|
979
|
+
root: string;
|
|
980
|
+
/** Project root used to compute relative paths (e.g., process.cwd()) */
|
|
981
|
+
cwd: string;
|
|
982
|
+
/** Glob-like extensions to scan */
|
|
983
|
+
extensions?: string[];
|
|
984
|
+
/** Substrings that exclude a path (matched against relative path) */
|
|
985
|
+
exclude?: string[];
|
|
986
|
+
/**
|
|
987
|
+
* Path to the env schema file, relative to `cwd`. Defaults to
|
|
988
|
+
* `'src/env.ts'`. The file must contain a `defineEnv(...)` call
|
|
989
|
+
* with a default export for the typegen to emit a typed `KickEnv`
|
|
990
|
+
* augmentation. If the file does not exist or doesn't match the
|
|
991
|
+
* expected shape, env typing is skipped silently.
|
|
992
|
+
*/
|
|
993
|
+
envFile?: string;
|
|
994
|
+
/**
|
|
995
|
+
* Directory for the persistent per-file extraction cache. When set,
|
|
996
|
+
* unchanged files (matched by `mtimeMs:size` signature) are served
|
|
997
|
+
* from `<cacheDir>/scan.json` instead of being re-read and re-scanned.
|
|
998
|
+
* Omit to disable caching (every scan is a cold read — the original
|
|
999
|
+
* behaviour). Typically `<cwd>/.kickjs/cache`.
|
|
1000
|
+
*/
|
|
1001
|
+
cacheDir?: string;
|
|
1002
|
+
}
|
|
1003
|
+
//#endregion
|
|
1004
|
+
//#region src/typegen/plugin.d.ts
|
|
1005
|
+
interface TypegenLogger {
|
|
1006
|
+
info(msg: string): void;
|
|
1007
|
+
warn(msg: string): void;
|
|
1008
|
+
error(msg: string): void;
|
|
1009
|
+
}
|
|
1010
|
+
interface TypegenContext {
|
|
1011
|
+
cwd: string;
|
|
1012
|
+
config: KickConfig;
|
|
1013
|
+
/** Dynamic-import a TS module (Node loader). Used by plugins that need to
|
|
1014
|
+
* read the adopter's schema / route map / asset registry at generate time. */
|
|
1015
|
+
importTs<T = unknown>(absPath: string): Promise<T>;
|
|
1016
|
+
/** Write under `cwd`. Caller passes a relPath (e.g. `.kickjs/types/foo.d.ts`). */
|
|
1017
|
+
writeFile(relPath: string, contents: string): Promise<void>;
|
|
1018
|
+
/**
|
|
1019
|
+
* Run `scanProject` once per typegen pass, memoizing the result so
|
|
1020
|
+
* multiple plugins (`kick/routes`, `kick/env`, future adopter plugins)
|
|
1021
|
+
* share a single fs walk + AST extraction.
|
|
1022
|
+
*
|
|
1023
|
+
* The runner uses an order-independent cache key derived from the
|
|
1024
|
+
* resolved options (`root`, `cwd`, `extensions`, `exclude`, `envFile`)
|
|
1025
|
+
* — semantically equal options hit the cache regardless of how the
|
|
1026
|
+
* caller built the literal. (We deliberately don't `JSON.stringify`
|
|
1027
|
+
* the options for caching since that would be sensitive to property
|
|
1028
|
+
* insertion order.) Plugins that don't need scanner data can ignore
|
|
1029
|
+
* this method entirely.
|
|
1030
|
+
*
|
|
1031
|
+
* Implementation lives in the runner so test harnesses can inject
|
|
1032
|
+
* a stub scanner; plugins only see the function.
|
|
1033
|
+
*/
|
|
1034
|
+
getScanResult(opts: ScanOptions): Promise<ScanResult>;
|
|
1035
|
+
log: TypegenLogger;
|
|
1036
|
+
}
|
|
1037
|
+
interface TypegenPlugin {
|
|
1038
|
+
/** Stable id — used as filename: `.kickjs/types/${id}<outExtension>` (slashes → `__`). */
|
|
1039
|
+
id: string;
|
|
1040
|
+
/** Glob patterns the Vite watcher subscribes to; change → re-run this plugin. */
|
|
1041
|
+
inputs: string[];
|
|
1042
|
+
/**
|
|
1043
|
+
* Output filename extension. Default `.d.ts` — the right choice for
|
|
1044
|
+
* pure module-augmentation plugins (kick/db, kick/assets) since
|
|
1045
|
+
* declaration files don't need the runtime-import dance.
|
|
1046
|
+
*
|
|
1047
|
+
* `kick/routes` overrides to `.ts` because it emits hoisted
|
|
1048
|
+
* `import type {...} from '...'` lines at the top of the file. Inline
|
|
1049
|
+
* `import('...').X` references inside `.d.ts` silently degrade to
|
|
1050
|
+
* `unknown` under `moduleResolution: 'bundler'`; emitting `.ts`
|
|
1051
|
+
* sidesteps that quirk and gets full type resolution.
|
|
1052
|
+
*
|
|
1053
|
+
* Adopter-supplied plugins should leave this unset unless they hit
|
|
1054
|
+
* the same hoisted-import constraint.
|
|
1055
|
+
*/
|
|
1056
|
+
outExtension?: string;
|
|
1057
|
+
/**
|
|
1058
|
+
* Return the augmentation source (without banner — runner prepends).
|
|
1059
|
+
* Return null to skip emission (e.g. no schema file present).
|
|
1060
|
+
*/
|
|
1061
|
+
generate(ctx: TypegenContext): Promise<string | null>;
|
|
1062
|
+
}
|
|
1063
|
+
interface TypegenPluginResult {
|
|
1064
|
+
id: string;
|
|
1065
|
+
status: 'written' | 'unchanged' | 'skipped' | 'error';
|
|
1066
|
+
outFile?: string;
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Identity factory for {@link TypegenPlugin}. Returns the spec verbatim.
|
|
1070
|
+
* Exists for type inference and forward-compatibility — future
|
|
1071
|
+
* fields can be added with defaults without breaking adopters.
|
|
1072
|
+
*
|
|
1073
|
+
* Mirrors {@link defineGenerator} ergonomics. Use at the call site so
|
|
1074
|
+
* the plugin's `generate(ctx)` body gets a fully-typed `ctx` without
|
|
1075
|
+
* an explicit annotation:
|
|
1076
|
+
*
|
|
1077
|
+
* @example
|
|
1078
|
+
* ```ts
|
|
1079
|
+
* import { defineTypegen } from '@forinda/kickjs-cli'
|
|
1080
|
+
*
|
|
1081
|
+
* export const drizzleTypegen = defineTypegen({
|
|
1082
|
+
* id: 'drizzle',
|
|
1083
|
+
* inputs: ['src/db/schema.ts'],
|
|
1084
|
+
* async generate(ctx) {
|
|
1085
|
+
* const schema = await ctx.importTs(`${ctx.cwd}/src/db/schema.ts`)
|
|
1086
|
+
* return `// declare module …`
|
|
1087
|
+
* },
|
|
1088
|
+
* })
|
|
1089
|
+
* ```
|
|
1090
|
+
*/
|
|
1091
|
+
declare function defineTypegen(spec: TypegenPlugin): TypegenPlugin;
|
|
1092
|
+
//#endregion
|
|
1093
|
+
//#region src/generator-extension/discover.d.ts
|
|
1094
|
+
/**
|
|
1095
|
+
* One row in the discovered registry. `source` is the npm package name
|
|
1096
|
+
* the generator came from — surfaced in error messages so adopters can
|
|
1097
|
+
* see which plugin owns a given generator.
|
|
1098
|
+
*/
|
|
1099
|
+
interface DiscoveredGenerator {
|
|
1100
|
+
source: string;
|
|
1101
|
+
spec: GeneratorSpec;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Plugin discovery result, kept around even when no generators were
|
|
1105
|
+
* registered so callers can distinguish "no plugins installed" from
|
|
1106
|
+
* "no plugins matched the requested name."
|
|
1107
|
+
*/
|
|
1108
|
+
interface DiscoveryResult {
|
|
1109
|
+
generators: DiscoveredGenerator[];
|
|
1110
|
+
/** Packages whose `kickjs.generators` was loaded successfully. */
|
|
1111
|
+
loaded: string[];
|
|
1112
|
+
/**
|
|
1113
|
+
* Packages we tried to load but failed — typically a missing entry
|
|
1114
|
+
* file or a default export that wasn't an array of GeneratorSpec.
|
|
1115
|
+
*/
|
|
1116
|
+
failed: Array<{
|
|
1117
|
+
source: string;
|
|
1118
|
+
reason: string;
|
|
1119
|
+
}>;
|
|
1120
|
+
}
|
|
1121
|
+
//#endregion
|
|
1312
1122
|
//#region src/generator-extension/context.d.ts
|
|
1313
1123
|
/**
|
|
1314
1124
|
* Build a {@link GeneratorContext} from the raw name + invocation
|