@forinda/kickjs-cli 2.2.4 → 2.3.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/cli.mjs +991 -84
- package/dist/index.d.mts +7 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +178 -59
- package/dist/index.mjs.map +1 -1
- package/dist/{typegen-zoIEma5k.mjs → typegen-CsTiIxmu.mjs} +2 -2
- package/dist/{typegen-zoIEma5k.mjs.map → typegen-CsTiIxmu.mjs.map} +1 -1
- package/package.json +7 -4
package/dist/index.d.mts
CHANGED
|
@@ -228,6 +228,7 @@ interface GenerateMiddlewareOptions {
|
|
|
228
228
|
moduleName?: string;
|
|
229
229
|
modulesDir?: string;
|
|
230
230
|
pattern?: ProjectPattern;
|
|
231
|
+
pluralize?: boolean;
|
|
231
232
|
}
|
|
232
233
|
declare function generateMiddleware(options: GenerateMiddlewareOptions): Promise<string[]>;
|
|
233
234
|
//#endregion
|
|
@@ -238,6 +239,7 @@ interface GenerateGuardOptions {
|
|
|
238
239
|
moduleName?: string;
|
|
239
240
|
modulesDir?: string;
|
|
240
241
|
pattern?: ProjectPattern;
|
|
242
|
+
pluralize?: boolean;
|
|
241
243
|
}
|
|
242
244
|
declare function generateGuard(options: GenerateGuardOptions): Promise<string[]>;
|
|
243
245
|
//#endregion
|
|
@@ -248,6 +250,7 @@ interface GenerateServiceOptions {
|
|
|
248
250
|
moduleName?: string;
|
|
249
251
|
modulesDir?: string;
|
|
250
252
|
pattern?: ProjectPattern;
|
|
253
|
+
pluralize?: boolean;
|
|
251
254
|
}
|
|
252
255
|
declare function generateService(options: GenerateServiceOptions): Promise<string[]>;
|
|
253
256
|
//#endregion
|
|
@@ -258,6 +261,7 @@ interface GenerateControllerOptions {
|
|
|
258
261
|
moduleName?: string;
|
|
259
262
|
modulesDir?: string;
|
|
260
263
|
pattern?: ProjectPattern;
|
|
264
|
+
pluralize?: boolean;
|
|
261
265
|
}
|
|
262
266
|
declare function generateController(options: GenerateControllerOptions): Promise<string[]>;
|
|
263
267
|
//#endregion
|
|
@@ -268,6 +272,7 @@ interface GenerateDtoOptions {
|
|
|
268
272
|
moduleName?: string;
|
|
269
273
|
modulesDir?: string;
|
|
270
274
|
pattern?: ProjectPattern;
|
|
275
|
+
pluralize?: boolean;
|
|
271
276
|
}
|
|
272
277
|
declare function generateDto(options: GenerateDtoOptions): Promise<string[]>;
|
|
273
278
|
//#endregion
|
|
@@ -294,7 +299,8 @@ declare function toCamelCase(name: string): string;
|
|
|
294
299
|
declare function toKebabCase(name: string): string;
|
|
295
300
|
/**
|
|
296
301
|
* Pluralize a kebab-case name for directory/file names.
|
|
297
|
-
*
|
|
302
|
+
* Uses the `pluralize` npm package for correct English pluralization
|
|
303
|
+
* including irregulars (person → people, status → statuses, child → children).
|
|
298
304
|
*/
|
|
299
305
|
declare function pluralize(name: string): string;
|
|
300
306
|
//#endregion
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config.ts","../src/generators/module.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/utils/naming.ts"],"mappings":";;;UAIiB,qBAAA;EAAqB;EAEpC,IAAA;EAFoC;EAIpC,WAAA;EAAA;;;;;AAeF;;;EANE,KAAA;EAMwB;EAJxB,OAAA;AAAA;;KAIU,cAAA;;KAGA,iBAAA;;UAKK,cAAA;EACf,IAAA;AAAA;;KAIU,cAAA,GAAiB,iBAAA,GAAkB,cAAA;;;AAS/C;;;;;KAAY,eAAA;;UAGK,aAAA;EAuBkB;;;;EAlBjC,MAAA;EA4BA;;;AAIF;EA3BE,MAAA;;;;;;;;;;;AAiEF;;EApDE,eAAA,GAAkB,eAAA;EA6DR;;;;;;;;;EAnDV,OAAA;AAAA;;UAIe,YAAA;EAiEf;EA/DA,GAAA;EAiEA;;;;;;;;;;;;;EAnDA,IAAA,GAAO,cAAA;EAuFL;EArFF,SAAA;EAqFQ;AAKV;;;;EApFE,SAAA;EAoF2B;;;;AA6B7B;;;;;EAvGE,gBAAA;AAAA;;UAIe,UAAA;;;;AC7GjB;;;;;EDsHE,OAAA,GAAU,cAAA;ECrHQ;;;;AAOnB;;;;;;;ED0HC,OAAA,GAAU,YAAA;ECnHV;EDuHA,UAAA;ECtHA;EDwHA,WAAA,GAAc,cAAA;ECtHd;EDwHA,SAAA;ECvHA;EDyHA,SAAA;ECrHA;;;AAwBF;;;;;;;;;;ED2GE,QAAA,GAAW,KAAA;IAAiB,GAAA;IAAa,IAAA;EAAA;;;;AE/J3C;;;;;;;;EF2KE,OAAA,GAAU,aAAA;;EAEV,QAAA,GAAW,qBAAA;;EAEX,KAAA;IACE,UAAA;IACA,MAAA;IACA,aAAA;IACA,MAAA;EAAA;AAAA;;iBAKY,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;;iBA6B5B,cAAA,CAAe,GAAA,WAAc,OAAA,CAAQ,UAAA;;;KChN/C,eAAA;AAAA,KACA,QAAA,GAAW,eAAA;AAAA,UASb,qBAAA;EACR,IAAA;EACA,UAAA;EACA,QAAA;EACA,OAAA;EACA,IAAA,GAAO,QAAA;EACP,OAAA;EACA,KAAA;EACA,OAAA,GAAU,cAAA;EACV,MAAA;EDVwB;ECYxB,SAAA;EDTyB;ECWzB,gBAAA;AAAA;;ADNF;;;;;AAKA;;;;iBCyBsB,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,OAAA;;;UCzD5D,sBAAA;EACR,IAAA;EACA,MAAA;AAAA;AAAA,iBAGoB,eAAA,CAAgB,OAAA,EAAS,sBAAA,GAAyB,OAAA;;;UCH9D,yBAAA;EACR,IAAA;EACA,MAAA;EACA,UAAA;EACA,UAAA;EACA,OAAA,GAAU,cAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config.ts","../src/generators/module.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/utils/naming.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v2.
|
|
2
|
+
* @forinda/kickjs-cli v2.3.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { dirname, join, resolve } from "node:path";
|
|
12
12
|
import { createInterface } from "node:readline";
|
|
13
13
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
14
|
+
import pkg from "pluralize";
|
|
14
15
|
import { execSync } from "node:child_process";
|
|
15
16
|
import { readFileSync } from "node:fs";
|
|
16
17
|
import { fileURLToPath } from "node:url";
|
|
@@ -45,26 +46,18 @@ function toKebabCase(name) {
|
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
47
48
|
* Pluralize a kebab-case name for directory/file names.
|
|
48
|
-
*
|
|
49
|
+
* Uses the `pluralize` npm package for correct English pluralization
|
|
50
|
+
* including irregulars (person → people, status → statuses, child → children).
|
|
49
51
|
*/
|
|
50
52
|
function pluralize(name) {
|
|
51
|
-
|
|
52
|
-
if (name.endsWith("x") || name.endsWith("z")) return name + "es";
|
|
53
|
-
if (name.endsWith("sh") || name.endsWith("ch")) return name + "es";
|
|
54
|
-
if (name.endsWith("y") && !/[aeiou]y$/.test(name)) return name.slice(0, -1) + "ies";
|
|
55
|
-
return name + "s";
|
|
53
|
+
return pkg.plural(name);
|
|
56
54
|
}
|
|
57
55
|
/**
|
|
58
56
|
* Pluralize a PascalCase name for class identifiers.
|
|
59
|
-
* If already plural (ends in 's'), returns as-is.
|
|
60
57
|
* Used for `List${pluralPascal}UseCase` to avoid `ListUserssUseCase`.
|
|
61
58
|
*/
|
|
62
59
|
function pluralizePascal(name) {
|
|
63
|
-
|
|
64
|
-
if (name.endsWith("x") || name.endsWith("z")) return name + "es";
|
|
65
|
-
if (name.endsWith("sh") || name.endsWith("ch")) return name + "es";
|
|
66
|
-
if (name.endsWith("y") && !/[aeiou]y$/i.test(name)) return name.slice(0, -1) + "ies";
|
|
67
|
-
return name + "s";
|
|
60
|
+
return pkg.plural(name);
|
|
68
61
|
}
|
|
69
62
|
//#endregion
|
|
70
63
|
//#region src/generators/templates/module-index.ts
|
|
@@ -2401,13 +2394,14 @@ const CQRS_FOLDER_MAP = {
|
|
|
2401
2394
|
* 3. Standalone default directory
|
|
2402
2395
|
*/
|
|
2403
2396
|
function resolveOutDir(options) {
|
|
2404
|
-
const { type, outDir, moduleName, modulesDir = "src/modules", defaultDir, pattern = "ddd" } = options;
|
|
2397
|
+
const { type, outDir, moduleName, modulesDir = "src/modules", defaultDir, pattern = "ddd", shouldPluralize = true } = options;
|
|
2405
2398
|
if (outDir) return resolve(outDir);
|
|
2406
2399
|
if (moduleName) {
|
|
2407
2400
|
const folderMap = pattern === "ddd" ? DDD_FOLDER_MAP : pattern === "cqrs" ? CQRS_FOLDER_MAP : FLAT_FOLDER_MAP;
|
|
2408
|
-
const
|
|
2401
|
+
const kebab = toKebabCase(moduleName);
|
|
2402
|
+
const folder = shouldPluralize ? pluralize(kebab) : kebab;
|
|
2409
2403
|
const subfolder = folderMap[type] ?? "";
|
|
2410
|
-
const base = join(modulesDir,
|
|
2404
|
+
const base = join(modulesDir, folder);
|
|
2411
2405
|
return resolve(subfolder ? join(base, subfolder) : base);
|
|
2412
2406
|
}
|
|
2413
2407
|
return resolve(defaultDir);
|
|
@@ -2422,7 +2416,8 @@ async function generateMiddleware(options) {
|
|
|
2422
2416
|
moduleName,
|
|
2423
2417
|
modulesDir,
|
|
2424
2418
|
defaultDir: "src/middleware",
|
|
2425
|
-
pattern
|
|
2419
|
+
pattern,
|
|
2420
|
+
shouldPluralize: options.pluralize ?? true
|
|
2426
2421
|
});
|
|
2427
2422
|
const kebab = toKebabCase(name);
|
|
2428
2423
|
const camel = toCamelCase(name);
|
|
@@ -2466,7 +2461,8 @@ async function generateGuard(options) {
|
|
|
2466
2461
|
moduleName,
|
|
2467
2462
|
modulesDir,
|
|
2468
2463
|
defaultDir: "src/guards",
|
|
2469
|
-
pattern
|
|
2464
|
+
pattern,
|
|
2465
|
+
shouldPluralize: options.pluralize ?? true
|
|
2470
2466
|
});
|
|
2471
2467
|
const kebab = toKebabCase(name);
|
|
2472
2468
|
const camel = toCamelCase(name);
|
|
@@ -2523,7 +2519,8 @@ async function generateService(options) {
|
|
|
2523
2519
|
moduleName,
|
|
2524
2520
|
modulesDir,
|
|
2525
2521
|
defaultDir: "src/services",
|
|
2526
|
-
pattern
|
|
2522
|
+
pattern,
|
|
2523
|
+
shouldPluralize: options.pluralize ?? true
|
|
2527
2524
|
});
|
|
2528
2525
|
const kebab = toKebabCase(name);
|
|
2529
2526
|
const pascal = toPascalCase(name);
|
|
@@ -2552,7 +2549,8 @@ async function generateController(options) {
|
|
|
2552
2549
|
moduleName,
|
|
2553
2550
|
modulesDir,
|
|
2554
2551
|
defaultDir: "src/controllers",
|
|
2555
|
-
pattern
|
|
2552
|
+
pattern,
|
|
2553
|
+
shouldPluralize: options.pluralize ?? true
|
|
2556
2554
|
});
|
|
2557
2555
|
const kebab = toKebabCase(name);
|
|
2558
2556
|
const pascal = toPascalCase(name);
|
|
@@ -2593,7 +2591,8 @@ async function generateDto(options) {
|
|
|
2593
2591
|
moduleName,
|
|
2594
2592
|
modulesDir,
|
|
2595
2593
|
defaultDir: "src/dtos",
|
|
2596
|
-
pattern
|
|
2594
|
+
pattern,
|
|
2595
|
+
shouldPluralize: options.pluralize ?? true
|
|
2597
2596
|
});
|
|
2598
2597
|
const kebab = toKebabCase(name);
|
|
2599
2598
|
const pascal = toPascalCase(name);
|
|
@@ -3006,18 +3005,30 @@ export class UserService {
|
|
|
3006
3005
|
|
|
3007
3006
|
### Modules
|
|
3008
3007
|
|
|
3009
|
-
|
|
3008
|
+
Modules implement \`AppModule\` and wire controllers via \`buildRoutes()\`:
|
|
3010
3009
|
|
|
3011
3010
|
\`\`\`ts
|
|
3012
|
-
import {
|
|
3011
|
+
import { type AppModule, type ModuleRoutes, buildRoutes } from '@forinda/kickjs'
|
|
3013
3012
|
import { UserController } from './user.controller'
|
|
3014
|
-
import { UserService } from './user.service'
|
|
3015
3013
|
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3014
|
+
export class UserModule implements AppModule {
|
|
3015
|
+
routes(): ModuleRoutes {
|
|
3016
|
+
return {
|
|
3017
|
+
path: '/users',
|
|
3018
|
+
router: buildRoutes(UserController),
|
|
3019
|
+
controller: UserController,
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
\`\`\`
|
|
3024
|
+
|
|
3025
|
+
Register all modules in \`src/modules/index.ts\`:
|
|
3026
|
+
|
|
3027
|
+
\`\`\`ts
|
|
3028
|
+
import type { AppModuleClass } from '@forinda/kickjs'
|
|
3029
|
+
import { UserModule } from './user/user.module'
|
|
3030
|
+
|
|
3031
|
+
export const modules: AppModuleClass[] = [UserModule]
|
|
3021
3032
|
\`\`\`
|
|
3022
3033
|
|
|
3023
3034
|
### RequestContext
|
|
@@ -3128,6 +3139,86 @@ Hot-reload of \`.env\` changes during dev is wired up automatically via
|
|
|
3128
3139
|
\`envWatchPlugin()\` in \`vite.config.ts\` — edit \`.env\`, the dev server
|
|
3129
3140
|
reloads, and the next \`config.get()\` re-parses with the new values.
|
|
3130
3141
|
|
|
3142
|
+
### Standalone Env Utilities (No DI Required)
|
|
3143
|
+
|
|
3144
|
+
These functions work anywhere — scripts, CLI tools, plain files, outside \`@Service\`/\`@Controller\`:
|
|
3145
|
+
|
|
3146
|
+
\`\`\`ts
|
|
3147
|
+
import { defineEnv, loadEnv, getEnv, reloadEnv, resetEnvCache, baseEnvSchema } from '@forinda/kickjs/config'
|
|
3148
|
+
import { z } from 'zod'
|
|
3149
|
+
|
|
3150
|
+
// Define and parse schema
|
|
3151
|
+
const schema = defineEnv((base) =>
|
|
3152
|
+
base.extend({ DATABASE_URL: z.string().url() })
|
|
3153
|
+
)
|
|
3154
|
+
const env = loadEnv(schema) // Parse + validate process.env
|
|
3155
|
+
console.log(env.PORT) // 3000 (coerced to number)
|
|
3156
|
+
console.log(env.DATABASE_URL) // validated URL string
|
|
3157
|
+
|
|
3158
|
+
// Get single value
|
|
3159
|
+
const port = getEnv('PORT') // typed after kick typegen
|
|
3160
|
+
|
|
3161
|
+
// Reload after .env changes (HMR calls this automatically)
|
|
3162
|
+
reloadEnv()
|
|
3163
|
+
|
|
3164
|
+
// Reset cache in tests that swap schemas
|
|
3165
|
+
resetEnvCache()
|
|
3166
|
+
\`\`\`
|
|
3167
|
+
|
|
3168
|
+
| Function | Purpose |
|
|
3169
|
+
|----------|---------|
|
|
3170
|
+
| \`defineEnv(fn)\` | Extend base schema with custom Zod keys |
|
|
3171
|
+
| \`loadEnv(schema?)\` | Parse \`process.env\`, validate, cache, return typed object |
|
|
3172
|
+
| \`getEnv(key, schema?)\` | Get single validated env value |
|
|
3173
|
+
| \`reloadEnv()\` | Re-read \`.env\` from disk, re-parse with same schema |
|
|
3174
|
+
| \`resetEnvCache()\` | Clear parsed cache AND registered schema (for tests) |
|
|
3175
|
+
| \`baseEnvSchema\` | Base Zod schema: \`PORT\`, \`NODE_ENV\`, \`LOG_LEVEL\` |
|
|
3176
|
+
|
|
3177
|
+
## Standalone Utilities (No DI Required)
|
|
3178
|
+
|
|
3179
|
+
These utilities work outside decorated classes:
|
|
3180
|
+
|
|
3181
|
+
### Logger
|
|
3182
|
+
|
|
3183
|
+
\`\`\`ts
|
|
3184
|
+
import { Logger, createLogger } from '@forinda/kickjs'
|
|
3185
|
+
|
|
3186
|
+
const log = Logger.for('MyScript') // Static factory
|
|
3187
|
+
log.info('Processing started')
|
|
3188
|
+
log.error('Something failed')
|
|
3189
|
+
|
|
3190
|
+
const log2 = createLogger('Worker') // Function form
|
|
3191
|
+
\`\`\`
|
|
3192
|
+
|
|
3193
|
+
### Injection Tokens
|
|
3194
|
+
|
|
3195
|
+
\`\`\`ts
|
|
3196
|
+
import { createToken } from '@forinda/kickjs'
|
|
3197
|
+
|
|
3198
|
+
// Type-safe DI tokens for factory/interface binding
|
|
3199
|
+
const DB_URL = createToken<string>('config.database.url')
|
|
3200
|
+
const FEATURE_FLAGS = createToken<FeatureFlags>('app.features')
|
|
3201
|
+
\`\`\`
|
|
3202
|
+
|
|
3203
|
+
### Reactivity
|
|
3204
|
+
|
|
3205
|
+
\`\`\`ts
|
|
3206
|
+
import { ref, computed, watch, reactive } from '@forinda/kickjs'
|
|
3207
|
+
|
|
3208
|
+
const count = ref(0)
|
|
3209
|
+
const doubled = computed(() => count.value * 2)
|
|
3210
|
+
const stop = watch(() => count.value, (val) => console.log(val))
|
|
3211
|
+
count.value++ // logs 1
|
|
3212
|
+
\`\`\`
|
|
3213
|
+
|
|
3214
|
+
### HTTP Errors
|
|
3215
|
+
|
|
3216
|
+
\`\`\`ts
|
|
3217
|
+
import { HttpException, HttpStatus } from '@forinda/kickjs'
|
|
3218
|
+
|
|
3219
|
+
throw new HttpException(HttpStatus.NOT_FOUND, 'User not found')
|
|
3220
|
+
\`\`\`
|
|
3221
|
+
|
|
3131
3222
|
## Testing
|
|
3132
3223
|
|
|
3133
3224
|
Tests live in \`src/**/*.test.ts\`:
|
|
@@ -3162,7 +3253,6 @@ Run tests:
|
|
|
3162
3253
|
- \`@Roles('admin', 'user')\` — role-based access control
|
|
3163
3254
|
|
|
3164
3255
|
### DI Decorators
|
|
3165
|
-
- \`@Module({ controllers, providers, imports })\` — define module
|
|
3166
3256
|
- \`@Service()\` — singleton service (DI-registered)
|
|
3167
3257
|
- \`@Repository()\` — repository (semantic alias for @Service)
|
|
3168
3258
|
- \`@Autowired()\` — property injection
|
|
@@ -3240,7 +3330,7 @@ ${template === "ddd" ? `\`\`\`
|
|
|
3240
3330
|
├── <name>.repository.ts # Data access (@Repository)
|
|
3241
3331
|
├── <name>.dto.ts # Request/response schemas (Zod)
|
|
3242
3332
|
├── <name>.entity.ts # Domain entity (optional)
|
|
3243
|
-
└── <name>.module.ts # Module definition (
|
|
3333
|
+
└── <name>.module.ts # Module definition (implements AppModule)
|
|
3244
3334
|
\`\`\`
|
|
3245
3335
|
` : template === "cqrs" ? `\`\`\`
|
|
3246
3336
|
<name>/
|
|
@@ -3254,7 +3344,7 @@ ${template === "ddd" ? `\`\`\`
|
|
|
3254
3344
|
│ └── <name>-created.event.ts
|
|
3255
3345
|
├── <name>.controller.ts # HTTP routes
|
|
3256
3346
|
├── <name>.repository.ts # Data access
|
|
3257
|
-
└── <name>.module.ts # Module definition
|
|
3347
|
+
└── <name>.module.ts # Module definition (implements AppModule)
|
|
3258
3348
|
\`\`\`
|
|
3259
3349
|
` : template === "graphql" ? `\`\`\`
|
|
3260
3350
|
resolvers/
|
|
@@ -3267,7 +3357,7 @@ resolvers/
|
|
|
3267
3357
|
├── <name>.controller.ts # HTTP routes (@Controller)
|
|
3268
3358
|
├── <name>.service.ts # Business logic (@Service)
|
|
3269
3359
|
├── <name>.dto.ts # Request/response schemas (Zod)
|
|
3270
|
-
└── <name>.module.ts # Module definition (
|
|
3360
|
+
└── <name>.module.ts # Module definition (implements AppModule)
|
|
3271
3361
|
\`\`\`
|
|
3272
3362
|
` : `\`\`\`
|
|
3273
3363
|
src/
|
|
@@ -3303,8 +3393,8 @@ If not using generators:
|
|
|
3303
3393
|
- [ ] Create \`src/modules/<name>/<name>.controller.ts\`
|
|
3304
3394
|
- [ ] Add \`@Controller('/path')\` decorator
|
|
3305
3395
|
- [ ] Add route handlers with \`@Get()\`, \`@Post()\`, etc.
|
|
3306
|
-
- [ ] Create module file with
|
|
3307
|
-
- [ ] Register module in \`src/modules/index.ts\`
|
|
3396
|
+
- [ ] Create module file implementing \`AppModule\` with \`routes()\` returning \`{ path, router: buildRoutes(Controller), controller }\`
|
|
3397
|
+
- [ ] Register module in \`src/modules/index.ts\` (\`AppModuleClass[]\` array)
|
|
3308
3398
|
- [ ] Test with \`kick dev\`
|
|
3309
3399
|
|
|
3310
3400
|
### Manual Service
|
|
@@ -3312,7 +3402,7 @@ If not using generators:
|
|
|
3312
3402
|
- [ ] Create \`src/modules/<name>/<name>.service.ts\`
|
|
3313
3403
|
- [ ] Add \`@Service()\` decorator
|
|
3314
3404
|
- [ ] Inject dependencies with \`@Autowired()\`
|
|
3315
|
-
- [ ]
|
|
3405
|
+
- [ ] Inject via \`@Autowired()\` where needed
|
|
3316
3406
|
- [ ] Write unit tests
|
|
3317
3407
|
|
|
3318
3408
|
### New Middleware
|
|
@@ -3337,9 +3427,12 @@ Use \`kick add\` to install KickJS packages with correct peer dependencies:
|
|
|
3337
3427
|
### Generate CRUD Module
|
|
3338
3428
|
|
|
3339
3429
|
\`\`\`bash
|
|
3340
|
-
kick g scaffold user name:string email:string age:number
|
|
3430
|
+
kick g scaffold user name:string email:string:optional age:number
|
|
3341
3431
|
\`\`\`
|
|
3342
3432
|
|
|
3433
|
+
Append \`:optional\` for optional fields (shell-safe, no quoting needed).
|
|
3434
|
+
Quoted \`?\` syntax also works: \`"email:string?"\` or \`"email?:string"\`.
|
|
3435
|
+
|
|
3343
3436
|
This creates a full CRUD module with:
|
|
3344
3437
|
- Controller with GET, POST, PUT, DELETE routes
|
|
3345
3438
|
- Service with business logic
|
|
@@ -3455,7 +3548,17 @@ private config!: ConfigService
|
|
|
3455
3548
|
const port = this.config.get('PORT') // typed: number
|
|
3456
3549
|
\`\`\`
|
|
3457
3550
|
|
|
3458
|
-
3. **
|
|
3551
|
+
3. **Standalone utilities** (no DI — works in scripts, CLI, plain files):
|
|
3552
|
+
\`\`\`ts
|
|
3553
|
+
import { loadEnv, getEnv, reloadEnv, resetEnvCache } from '@forinda/kickjs/config'
|
|
3554
|
+
|
|
3555
|
+
const env = loadEnv(schema) // Parse + validate all vars
|
|
3556
|
+
const port = getEnv('PORT') // Single value lookup
|
|
3557
|
+
reloadEnv() // Re-read .env from disk
|
|
3558
|
+
resetEnvCache() // Full reset (for tests)
|
|
3559
|
+
\`\`\`
|
|
3560
|
+
|
|
3561
|
+
4. **Direct \`process.env\`** — avoid in app code; bypasses Zod
|
|
3459
3562
|
coercion and the typed \`KickEnv\` registry.
|
|
3460
3563
|
|
|
3461
3564
|
> **Pitfall**: never delete \`import './config'\` from \`src/index.ts\`.
|
|
@@ -3464,6 +3567,22 @@ const port = this.config.get('PORT') // typed: number
|
|
|
3464
3567
|
> \`@Value()\` only works because of its raw \`process.env\` fallback —
|
|
3465
3568
|
> Zod coercion + schema defaults are silently skipped.
|
|
3466
3569
|
|
|
3570
|
+
## Standalone Utilities (No DI Required)
|
|
3571
|
+
|
|
3572
|
+
These work anywhere — scripts, plain files, outside \`@Service\`/\`@Controller\`:
|
|
3573
|
+
|
|
3574
|
+
| Utility | Import | Example |
|
|
3575
|
+
|---------|--------|---------|
|
|
3576
|
+
| \`Logger.for(name)\` | \`@forinda/kickjs\` | \`const log = Logger.for('MyScript')\` |
|
|
3577
|
+
| \`createLogger(name)\` | \`@forinda/kickjs\` | \`const log = createLogger('Worker')\` |
|
|
3578
|
+
| \`createToken<T>(name)\` | \`@forinda/kickjs\` | \`const TOKEN = createToken<string>('db.url')\` |
|
|
3579
|
+
| \`ref(value)\` | \`@forinda/kickjs\` | \`const count = ref(0)\` |
|
|
3580
|
+
| \`computed(fn)\` | \`@forinda/kickjs\` | \`const doubled = computed(() => count.value * 2)\` |
|
|
3581
|
+
| \`watch(source, cb)\` | \`@forinda/kickjs\` | \`watch(() => count.value, (v) => log(v))\` |
|
|
3582
|
+
| \`reactive(obj)\` | \`@forinda/kickjs\` | \`const state = reactive({ count: 0 })\` |
|
|
3583
|
+
| \`HttpException\` | \`@forinda/kickjs\` | \`throw new HttpException(404, 'Not found')\` |
|
|
3584
|
+
| \`HttpStatus\` | \`@forinda/kickjs\` | \`HttpStatus.NOT_FOUND // 404\` |
|
|
3585
|
+
|
|
3467
3586
|
## Key Decorators
|
|
3468
3587
|
|
|
3469
3588
|
### HTTP Routes
|
|
@@ -3478,7 +3597,7 @@ const port = this.config.get('PORT') // typed: number
|
|
|
3478
3597
|
### Dependency Injection
|
|
3479
3598
|
| Decorator | Purpose |
|
|
3480
3599
|
|-----------|---------|
|
|
3481
|
-
|
|
|
3600
|
+
| \`AppModule\` interface | Define feature module (implements \`routes()\`) |
|
|
3482
3601
|
| \`@Service()\` | Register singleton service |
|
|
3483
3602
|
| \`@Repository()\` | Register repository |
|
|
3484
3603
|
| \`@Autowired()\` | Property injection |
|
|
@@ -3574,6 +3693,26 @@ async function initProject(options) {
|
|
|
3574
3693
|
await writeFileSafe(join(dir, "README.md"), generateReadme(name, template, packageManager));
|
|
3575
3694
|
await writeFileSafe(join(dir, "CLAUDE.md"), generateClaude(name, template, packageManager));
|
|
3576
3695
|
await writeFileSafe(join(dir, "AGENTS.md"), generateAgents(name, template, packageManager));
|
|
3696
|
+
if (options.installDeps) {
|
|
3697
|
+
console.log(`\n Installing dependencies with ${packageManager}...\n`);
|
|
3698
|
+
try {
|
|
3699
|
+
execSync(`${packageManager} install`, {
|
|
3700
|
+
cwd: dir,
|
|
3701
|
+
stdio: "inherit"
|
|
3702
|
+
});
|
|
3703
|
+
console.log("\n Dependencies installed successfully!");
|
|
3704
|
+
} catch {
|
|
3705
|
+
console.log(`\n Warning: ${packageManager} install failed. Run it manually.`);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
try {
|
|
3709
|
+
const { runTypegen } = await import("./typegen-CsTiIxmu.mjs");
|
|
3710
|
+
await runTypegen({
|
|
3711
|
+
cwd: dir,
|
|
3712
|
+
allowDuplicates: true,
|
|
3713
|
+
silent: true
|
|
3714
|
+
});
|
|
3715
|
+
} catch {}
|
|
3577
3716
|
if (options.initGit) try {
|
|
3578
3717
|
execSync("git init", {
|
|
3579
3718
|
cwd: dir,
|
|
@@ -3595,26 +3734,6 @@ async function initProject(options) {
|
|
|
3595
3734
|
} catch {
|
|
3596
3735
|
log("Warning: git init failed (git may not be installed)");
|
|
3597
3736
|
}
|
|
3598
|
-
if (options.installDeps) {
|
|
3599
|
-
console.log(`\n Installing dependencies with ${packageManager}...\n`);
|
|
3600
|
-
try {
|
|
3601
|
-
execSync(`${packageManager} install`, {
|
|
3602
|
-
cwd: dir,
|
|
3603
|
-
stdio: "inherit"
|
|
3604
|
-
});
|
|
3605
|
-
console.log("\n Dependencies installed successfully!");
|
|
3606
|
-
} catch {
|
|
3607
|
-
console.log(`\n Warning: ${packageManager} install failed. Run it manually.`);
|
|
3608
|
-
}
|
|
3609
|
-
}
|
|
3610
|
-
try {
|
|
3611
|
-
const { runTypegen } = await import("./typegen-zoIEma5k.mjs");
|
|
3612
|
-
await runTypegen({
|
|
3613
|
-
cwd: dir,
|
|
3614
|
-
allowDuplicates: true,
|
|
3615
|
-
silent: true
|
|
3616
|
-
});
|
|
3617
|
-
} catch {}
|
|
3618
3737
|
console.log("\n Project scaffolded successfully!");
|
|
3619
3738
|
console.log();
|
|
3620
3739
|
const needsCd = dir !== process.cwd();
|