@kanjijs/cli 0.2.0-beta.17 → 0.2.0-beta.19

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/README.md CHANGED
@@ -15,6 +15,7 @@ bunx @kanjijs/cli <command>
15
15
  ## Commands
16
16
 
17
17
  ### `new <project-name>`
18
+
18
19
  Scaffolds a new Kanjijs project with a predefined directory structure (bun + typescript).
19
20
 
20
21
  ```bash
@@ -22,22 +23,43 @@ kanjijs new my-app
22
23
  ```
23
24
 
24
25
  Features included out-of-the-box:
25
- * **Hono + Bun** set up.
26
- * **Observability**: `@kanjijs/logger` (JSON logs) + Request ID propagation.
27
- * **Security**: Rate Limiting enabled by default via `@kanjijs/throttler`.
28
- * **DI & Decorators**: Ready to use `@Controller` and `@Inject`.
26
+
27
+ - **Hono + Bun** set up.
28
+ - **Observability**: `@kanjijs/logger` (JSON logs) + Request ID propagation.
29
+ - **Security**: Rate Limiting enabled by default via `@kanjijs/throttler`.
30
+ - **DI & Decorators**: Ready to use `@Controller` and `@Inject`.
29
31
 
30
32
  ### `g resource <name>`
31
- Generates a new Resource Module (Controller + Service + Module).
33
+
34
+ Generates a new Resource Module (Controller + Service + Module + Test).
32
35
 
33
36
  ```bash
34
37
  kanjijs g resource products
38
+ kanjijs g resource products --repository # With Repository Pattern
39
+ ```
40
+
41
+ - scaffolds `products.module.ts`, `products.controller.ts`, `products.service.ts`
42
+ - **Full CRUD**: Generates `findAll`, `findOne`, `create`, `update`, and `remove` endpoints and methods.
43
+ - **Auto-generates** `products.controller.spec.ts` for unit testing with mocks for all CRUD methods.
44
+ - **Auto-updates** `src/app.module.ts` to import the new module! 🚀
45
+
46
+ ### `g schema <name>`
47
+
48
+ Generates a new Drizzle ORM Table definition.
49
+
50
+ ```bash
51
+ # Default: src/database/schema/users.ts (and updates index.ts)
52
+ kanjijs g schema users
53
+
54
+ # Co-located: src/modules/users/schemas/users.ts
55
+ kanjijs g schema users --module users
35
56
  ```
36
57
 
37
- * scaffolds `products.module.ts`, `products.controller.ts`, `products.service.ts`
38
- * **Auto-updates** `src/app.module.ts` to import the new module! 🚀
58
+ - Creates a `pgTable` with default columns (`id`, `createdAt`, `updatedAt`).
59
+ - **Auto-updates** the central barrel file (`src/database/schema/index.ts`) to export the new schema.
39
60
 
40
61
  ### `dev`
62
+
41
63
  Starts the development server via Bun in watch mode.
42
64
 
43
65
  ```bash
@@ -50,24 +72,28 @@ PORT=4000 kanjijs dev
50
72
 
51
73
  To hack on the CLI itself:
52
74
 
53
- 1. Clone the repo and install dependencies.
54
- 2. Link the package globally:
75
+ 1. Clone the repo and install dependencies.
76
+ 2. Link the package globally:
77
+
55
78
  ```bash
56
79
  cd packages/cli
57
80
  npm link
58
81
  ```
59
- 3. Run the builder in watch mode:
82
+
83
+ 3. Run the builder in watch mode:
84
+
60
85
  ```bash
61
86
  bun run watch
62
87
  ```
63
- 4. In another terminal, test your changes:
88
+
89
+ 4. In another terminal, test your changes:
90
+
64
91
  ```bash
65
92
  kanjijs g resource users
66
93
  ```
67
94
 
68
-
69
-
70
95
  ### `openapi`
96
+
71
97
  Generates an `openapi.json` file by inspecting your application metadata at runtime.
72
98
 
73
99
  ```bash
@@ -75,6 +101,7 @@ kanjijs openapi --out openapi.json
75
101
  ```
76
102
 
77
103
  ### `client`
104
+
78
105
  Generates a Type-Safe Client SDK (TypeScript) from your `openapi.json`.
79
106
  Uses [openapi-typescript-codegen](https://github.com/ferdikoomen/openapi-typescript-codegen).
80
107
 
package/dist/index.js CHANGED
@@ -20004,9 +20004,52 @@ var import_picocolors2 = __toESM(require_picocolors(), 1);
20004
20004
  import path2 from "path";
20005
20005
  class GenerateCommand extends Command2 {
20006
20006
  load(program2) {
20007
- program2.command("g <type> <name>").description("Generate a new resource (e.g. 'kanjijs g resource users')").alias("generate").option("-r, --repository", "Generate with Repository pattern").action(async (type, name, options) => {
20007
+ program2.command("g <type> <name>").description("Generate a new resource (e.g. 'kanjijs g resource users')").alias("generate").option("-r, --repository", "Generate with Repository pattern").option("-m, --module <name>", "Generate schema in a specific module").action(async (type, name, options) => {
20008
+ if (type === "schema") {
20009
+ const lowerName2 = name.toLowerCase();
20010
+ let schemaDir = path2.join(process.cwd(), "src", "database", "schema");
20011
+ let relativePath = `./${lowerName2}`;
20012
+ if (options.module) {
20013
+ const moduleName = options.module.toLowerCase();
20014
+ schemaDir = path2.join(process.cwd(), "src", "modules", moduleName, "schemas");
20015
+ relativePath = `../../modules/${moduleName}/schemas/${lowerName2}`;
20016
+ }
20017
+ await import_fs_extra.default.ensureDir(schemaDir);
20018
+ const schemaPath = path2.join(schemaDir, `${lowerName2}.ts`);
20019
+ if (await import_fs_extra.default.pathExists(schemaPath)) {
20020
+ console.error(import_picocolors2.default.red(`Error: Schema ${lowerName2} already exists at ${schemaPath}`));
20021
+ process.exit(1);
20022
+ }
20023
+ const schemaTpl = `import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
20024
+
20025
+ export const ${lowerName2} = pgTable("${lowerName2}", {
20026
+ id: serial("id").primaryKey(),
20027
+ createdAt: timestamp("created_at").defaultNow(),
20028
+ updatedAt: timestamp("updated_at").defaultNow(),
20029
+ });
20030
+ `;
20031
+ await import_fs_extra.default.writeFile(schemaPath, schemaTpl);
20032
+ console.log(import_picocolors2.default.green(`CREATED ${options.module ? `src/modules/${options.module}/schemas/` : `src/database/schema/`}${lowerName2}.ts`));
20033
+ const indexDir = path2.join(process.cwd(), "src", "database", "schema");
20034
+ await import_fs_extra.default.ensureDir(indexDir);
20035
+ const indexPath = path2.join(indexDir, "index.ts");
20036
+ const exportStatement = `export * from "${relativePath}";`;
20037
+ if (await import_fs_extra.default.pathExists(indexPath)) {
20038
+ const content = await import_fs_extra.default.readFile(indexPath, "utf-8");
20039
+ if (!content.includes(exportStatement)) {
20040
+ await import_fs_extra.default.appendFile(indexPath, `
20041
+ ${exportStatement}`);
20042
+ console.log(import_picocolors2.default.green(`UPDATED src/database/schema/index.ts`));
20043
+ }
20044
+ } else {
20045
+ await import_fs_extra.default.writeFile(indexPath, `${exportStatement}
20046
+ `);
20047
+ console.log(import_picocolors2.default.green(`CREATED src/database/schema/index.ts`));
20048
+ }
20049
+ return;
20050
+ }
20008
20051
  if (type !== "resource") {
20009
- console.error(import_picocolors2.default.red(`Unknown type: ${type}. Only 'resource' is supported.`));
20052
+ console.error(import_picocolors2.default.red(`Unknown type: ${type}. Supported: 'resource', 'schema'.`));
20010
20053
  process.exit(1);
20011
20054
  }
20012
20055
  const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
@@ -20022,6 +20065,26 @@ class GenerateCommand extends Command2 {
20022
20065
  let serviceMethods = ` findAll() {
20023
20066
  this.logger.info("Finding all ${lowerName}");
20024
20067
  return "This action returns all ${lowerName}";
20068
+ }
20069
+
20070
+ findOne(id: number) {
20071
+ this.logger.info(\`Finding ${lowerName} with id \${id}\`);
20072
+ return \`This action returns a #\${id} ${lowerName}\`;
20073
+ }
20074
+
20075
+ create(data: any) {
20076
+ this.logger.info("Creating new ${lowerName}");
20077
+ return "This action adds a new ${lowerName}";
20078
+ }
20079
+
20080
+ update(id: number, data: any) {
20081
+ this.logger.info(\`Updating ${lowerName} with id \${id}\`);
20082
+ return \`This action updates a #\${id} ${lowerName}\`;
20083
+ }
20084
+
20085
+ remove(id: number) {
20086
+ this.logger.info(\`Deleting ${lowerName} with id \${id}\`);
20087
+ return \`This action removes a #\${id} ${lowerName}\`;
20025
20088
  }`;
20026
20089
  if (options.repository) {
20027
20090
  const commonDir = path2.join(process.cwd(), "src", "common");
@@ -20121,8 +20184,21 @@ export class ${repositoryClass} extends BaseRepository<any> { // TODO: Replace '
20121
20184
  return this.repo.findAll();
20122
20185
  }
20123
20186
 
20187
+ async findOne(id: number) {
20188
+ this.logger.info(\`Finding ${lowerName} with id \${id}\`);
20189
+ return this.repo.findById(id);
20190
+ }
20191
+
20124
20192
  async create(data: any) {
20125
20193
  return this.repo.create(data);
20194
+ }
20195
+
20196
+ async update(id: number, data: any) {
20197
+ return this.repo.update(id, data);
20198
+ }
20199
+
20200
+ async remove(id: number) {
20201
+ return this.repo.delete(id);
20126
20202
  }`;
20127
20203
  }
20128
20204
  const serviceTpl = `import { Injectable } from "@kanjijs/core";
@@ -20138,7 +20214,7 @@ ${serviceMethods}
20138
20214
  `;
20139
20215
  await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.service.ts`), serviceTpl);
20140
20216
  console.log(import_picocolors2.default.white(`CREATE ${lowerName}.service.ts`));
20141
- const controllerTpl = `import { Controller, Get, Post, Body } from "@kanjijs/core";
20217
+ const controllerTpl = `import { Controller, Get, Post, Patch, Delete } from "@kanjijs/core";
20142
20218
  import { type Context } from "hono";
20143
20219
  import { ${className}Service } from "./${lowerName}.service";
20144
20220
  import { Contract } from "@kanjijs/contracts";
@@ -20150,6 +20226,12 @@ const Create${className}Contract = Contract.json({
20150
20226
  })
20151
20227
  });
20152
20228
 
20229
+ const Update${className}Contract = Contract.json({
20230
+ body: z.object({
20231
+ name: z.string().optional(),
20232
+ })
20233
+ });
20234
+
20153
20235
  @Controller("/${lowerName}")
20154
20236
  export class ${className}Controller {
20155
20237
  constructor(private readonly service: ${className}Service) {}
@@ -20159,12 +20241,31 @@ export class ${className}Controller {
20159
20241
  return c.json(this.service.findAll());
20160
20242
  }
20161
20243
 
20244
+ @Get("/:id")
20245
+ findOne(c: Context) {
20246
+ const id = Number(c.req.param("id"));
20247
+ return c.json(this.service.findOne(id));
20248
+ }
20249
+
20162
20250
  @Post("/", { contract: Create${className}Contract })
20163
20251
  create(c: Context) {
20164
20252
  // Infer keys from contract (Contract-first)
20165
20253
  const body = c.get("kanji.validated.body");
20166
20254
  return c.json(this.service.create(body));
20167
20255
  }
20256
+
20257
+ @Patch("/:id", { contract: Update${className}Contract })
20258
+ update(c: Context) {
20259
+ const id = Number(c.req.param("id"));
20260
+ const body = c.get("kanji.validated.body");
20261
+ return c.json(this.service.update(id, body));
20262
+ }
20263
+
20264
+ @Delete("/:id")
20265
+ remove(c: Context) {
20266
+ const id = Number(c.req.param("id"));
20267
+ return c.json(this.service.remove(id));
20268
+ }
20168
20269
  }
20169
20270
  `;
20170
20271
  await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.controller.ts`), controllerTpl);
@@ -20190,6 +20291,31 @@ export class ${className}Module {}
20190
20291
  `;
20191
20292
  await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.module.ts`), moduleTpl);
20192
20293
  console.log(import_picocolors2.default.white(`CREATE ${lowerName}.module.ts`));
20294
+ const testTpl = `import { Test } from "@kanjijs/testing";
20295
+ import { ${className}Controller } from "./${lowerName}.controller";
20296
+ import { ${className}Service } from "./${lowerName}.service";
20297
+
20298
+ test("${className}Controller", async () => {
20299
+ const moduleRef = await Test.createTestingModule({
20300
+ controllers: [${className}Controller],
20301
+ providers: [${className}Service],
20302
+ })
20303
+ .overrideProvider(${className}Service)
20304
+ .useValue({
20305
+ findAll: () => [],
20306
+ findOne: () => ({}),
20307
+ create: () => ({}),
20308
+ update: () => ({}),
20309
+ remove: () => ({}),
20310
+ })
20311
+ .compile();
20312
+
20313
+ const controller = moduleRef.get(${className}Controller);
20314
+ expect(controller).toBeDefined();
20315
+ });
20316
+ `;
20317
+ await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.controller.spec.ts`), testTpl);
20318
+ console.log(import_picocolors2.default.white(`CREATE ${lowerName}.controller.spec.ts`));
20193
20319
  const appModulePath = path2.join(process.cwd(), "src", "app.module.ts");
20194
20320
  if (await import_fs_extra.default.pathExists(appModulePath)) {
20195
20321
  let content = await import_fs_extra.default.readFile(appModulePath, "utf-8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanjijs/cli",
3
- "version": "0.2.0-beta.17",
3
+ "version": "0.2.0-beta.19",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "kanji": "./dist/index.js"
@@ -21,7 +21,7 @@
21
21
  "fs-extra": "^11.3.3",
22
22
  "picocolors": "^1.1.1",
23
23
  "openapi-typescript-codegen": "^0.25.0",
24
- "@kanjijs/openapi": "^0.2.0-beta.17"
24
+ "@kanjijs/openapi": "^0.2.0-beta.19"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/fs-extra": "^11.0.4"
@@ -7,12 +7,12 @@
7
7
  "start": "bun run src/main.ts"
8
8
  },
9
9
  "dependencies": {
10
- "@kanjijs/core": "^0.2.0-beta.14",
11
- "@kanjijs/platform-bun": "^0.2.0-beta.14",
12
- "@kanjijs/platform-hono": "^0.2.0-beta.14",
13
- "@kanjijs/auth": "^0.2.0-beta.14",
14
- "@kanjijs/logger": "^0.2.0-beta.14",
15
- "@kanjijs/throttler": "^0.2.0-beta.14",
10
+ "@kanjijs/core": "^0.2.0-beta.19",
11
+ "@kanjijs/platform-bun": "^0.2.0-beta.19",
12
+ "@kanjijs/platform-hono": "^0.2.0-beta.19",
13
+ "@kanjijs/auth": "^0.2.0-beta.19",
14
+ "@kanjijs/logger": "^0.2.0-beta.19",
15
+ "@kanjijs/throttler": "^0.2.0-beta.19",
16
16
  "hono": "^4.0.0",
17
17
  "reflect-metadata": "^0.2.0",
18
18
  "zod": "^3.0.0"