@ajke/cli 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Wilt Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # @ajke/cli
2
+
3
+ The official CLI for the [Ajke framework](../core/README.md). Scaffold new Cloudflare Workers projects and generate modules, services, and controllers.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @ajke/cli
9
+ ```
10
+
11
+ Or run without installing:
12
+
13
+ ```bash
14
+ npx @ajke/cli new my-app
15
+ ```
16
+
17
+ ---
18
+
19
+ ## Commands
20
+
21
+ ### `ajke new <project-name>`
22
+
23
+ Scaffold a new Ajke project with a Hello module, D1 database setup, and all config files pre-configured.
24
+
25
+ ```bash
26
+ ajke new my-app
27
+ cd my-app
28
+ pnpm dev # http://localhost:4001
29
+ ```
30
+
31
+ **What gets created:**
32
+
33
+ ```
34
+ my-app/
35
+ ├── src/
36
+ │ ├── index.ts
37
+ │ ├── app.module.ts
38
+ │ └── modules/app/hello/
39
+ │ ├── hello.module.ts
40
+ │ ├── hello.controller.ts
41
+ │ └── hello.service.ts
42
+ ├── src/database/
43
+ │ ├── schema.ts
44
+ │ └── connection.ts
45
+ ├── migrations/
46
+ ├── ajke.config.ts
47
+ ├── wrangler.jsonc
48
+ ├── tsconfig.json
49
+ ├── vite.config.ts
50
+ ├── vitest.config.ts
51
+ └── package.json
52
+ ```
53
+
54
+ ---
55
+
56
+ ### `ajke generate <subcommand> <name>`
57
+
58
+ Aliases: `g`
59
+
60
+ Scaffold files inside an existing project. Reads `ajke.config.ts` for the modules directory.
61
+
62
+ #### `ajke generate module <name>` (alias: `g m`)
63
+
64
+ Generates a full module: module, controller, service, DTO, entity, and test file.
65
+
66
+ ```bash
67
+ ajke generate module payment
68
+ ajke g m payment
69
+ ajke g m payment --no-test # skip test file
70
+ ajke g m payment --path src/modules/billing/payment
71
+ ```
72
+
73
+ **Output:**
74
+
75
+ ```
76
+ src/modules/app/payment/
77
+ ├── payment.module.ts
78
+ ├── payment.controller.ts
79
+ ├── payment.service.ts
80
+ ├── payment.dto.ts
81
+ ├── payment.entity.ts
82
+ └── payment.test.ts
83
+ ```
84
+
85
+ #### `ajke generate service <name>` (alias: `g s`)
86
+
87
+ ```bash
88
+ ajke generate service payment
89
+ ajke g s payment --path src/modules/app/payment
90
+ ```
91
+
92
+ #### `ajke generate controller <name>` (alias: `g c`)
93
+
94
+ ```bash
95
+ ajke generate controller payment
96
+ ajke g c payment --path src/modules/app/payment
97
+ ```
98
+
99
+ ---
100
+
101
+ ### `ajke add <service> [args]`
102
+
103
+ Add a Cloudflare service binding to `wrangler.jsonc`.
104
+
105
+ | Subcommand | Usage | Example |
106
+ |---|---|---|
107
+ | `d1` | `ajke add d1 <BINDING> <db-name>` | `ajke add d1 DB my-database` |
108
+ | `r2` | `ajke add r2 <BINDING> <bucket-name>` | `ajke add r2 ASSETS my-bucket` |
109
+ | `kv` | `ajke add kv <BINDING>` | `ajke add kv SESSION_KV` |
110
+ | `queue` | `ajke add queue <BINDING> <queue-name>` | `ajke add queue MAIL mail-queue` |
111
+ | `ai` | `ajke add ai <BINDING>` | `ajke add ai AI` |
112
+ | `durable-object` | `ajke add durable-object <BINDING> <ClassName>` | `ajke add durable-object CHAT ChatRoom` |
113
+ | `vectorize` | `ajke add vectorize <BINDING> <index-name> [dims]` | `ajke add vectorize VEC my-index 1536` |
114
+ | `browser` | `ajke add browser <BINDING>` | `ajke add browser BROWSER` |
115
+ | `hyperdrive` | `ajke add hyperdrive <BINDING> <connection-string>` | `ajke add hyperdrive HD postgres://...` |
116
+
117
+ ---
118
+
119
+ ## Config (`ajke.config.ts`)
120
+
121
+ Place this file in your project root to customise the CLI behaviour.
122
+
123
+ ```typescript
124
+ import { defineConfig } from "@ajke/core/config";
125
+
126
+ export default defineConfig({
127
+ // Where generated modules are placed (relative to project root)
128
+ modulesDir: "src/modules/app",
129
+
130
+ // Which files are scaffolded by `ajke generate module <name>`
131
+ // Remove "test" to skip test files, or pass --no-test per-command
132
+ generate: {
133
+ files: ["module", "controller", "service", "dto", "entity", "test"],
134
+ },
135
+ });
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Usage Inside a Project
141
+
142
+ When `@ajke/cli` is installed locally as a dev dependency, add a script to `package.json`:
143
+
144
+ ```json
145
+ {
146
+ "scripts": {
147
+ "ajke": "tsx node_modules/@ajke/cli/bin/ajke.ts"
148
+ }
149
+ }
150
+ ```
151
+
152
+ Then run:
153
+
154
+ ```bash
155
+ pnpm ajke generate module posts
156
+ pnpm ajke add d1 DB my-database
157
+ ```
158
+
159
+ ---
160
+
161
+ ## License
162
+
163
+ MIT
package/bin/ajke.ts ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env tsx
2
+ import { runNew } from "./commands/new.js";
3
+ import { runGenerate } from "./commands/generate.js";
4
+ import { runAdd } from "./commands/add.js";
5
+
6
+ const BANNER = `
7
+ █████╗ ██╗██╗ ██╗███████╗
8
+ ██╔══██╗ ██║██║ ██╔╝██╔════╝
9
+ ███████║ ██║█████╔╝ █████╗
10
+ ██╔══██║██ ██║██╔═██╗ ██╔══╝
11
+ ██║ ██║╚█████╔╝██║ ██╗███████╗
12
+ ╚═╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝╚══════╝
13
+ Ajke CLI — Cloudflare Workers & Hono Framework
14
+ `.trim();
15
+
16
+ const HELP = `
17
+ ${BANNER}
18
+
19
+ Usage:
20
+ pnpm ajke <command> [subcommand] [args...]
21
+
22
+ Commands:
23
+ new Create a new Ajke project
24
+ generate (g) Scaffold module, service, or controller files
25
+ add Add a Cloudflare service binding to wrangler.jsonc
26
+ help Show this help message
27
+
28
+ Generate subcommands:
29
+ module (m) Scaffold a full module (module + controller + service + dto)
30
+ service (s) Scaffold a service file
31
+ controller (c) Scaffold a controller file
32
+
33
+ Add subcommands:
34
+ d1 D1 database binding
35
+ r2 R2 bucket binding
36
+ kv KV namespace binding
37
+ queue Queue producer + consumer
38
+ ai Workers AI binding
39
+ durable-object Durable Object binding + migration
40
+ vectorize Vectorize index binding
41
+ browser Browser rendering binding
42
+ hyperdrive Hyperdrive binding
43
+
44
+ Examples:
45
+ pnpm ajke new my-app
46
+ pnpm ajke generate module payment
47
+ pnpm ajke g m payment
48
+ pnpm ajke add d1 PAYMENTS_DB payments-db
49
+ pnpm ajke add r2 ASSETS_BUCKET my-assets
50
+ pnpm ajke add kv SESSION_KV
51
+ pnpm ajke add queue MAIL_QUEUE mailer
52
+ pnpm ajke add ai AI
53
+ pnpm ajke add durable-object CHAT_ROOM ChatRoom
54
+ `.trim();
55
+
56
+ async function main(): Promise<void> {
57
+ const args = process.argv.slice(2);
58
+ const [command, ...rest] = args;
59
+
60
+ if (!command || command === "help" || command === "--help" || command === "-h") {
61
+ console.log(`\n${HELP}\n`);
62
+ process.exit(0);
63
+ }
64
+
65
+ const cmd = command.toLowerCase();
66
+
67
+ if (cmd === "new" || cmd === "n") {
68
+ runNew(rest);
69
+ } else if (cmd === "generate" || cmd === "g") {
70
+ await runGenerate(rest);
71
+ } else if (cmd === "add") {
72
+ runAdd(rest);
73
+ } else {
74
+ console.error(` ✗ Unknown command: "${command}"\n`);
75
+ console.log(HELP);
76
+ process.exit(1);
77
+ }
78
+ }
79
+
80
+ main();
@@ -0,0 +1,155 @@
1
+ import {
2
+ addD1,
3
+ addR2,
4
+ addKv,
5
+ addQueue,
6
+ addAi,
7
+ addDurableObject,
8
+ addVectorize,
9
+ addBrowser,
10
+ addHyperdrive,
11
+ } from "../utils/wrangler.js";
12
+
13
+ const USAGE = `
14
+ Usage:
15
+ pnpm ajke add d1 <BINDING_NAME> <database-name>
16
+ pnpm ajke add r2 <BINDING_NAME> <bucket-name>
17
+ pnpm ajke add kv <BINDING_NAME>
18
+ pnpm ajke add queue <BINDING_NAME> <queue-name>
19
+ pnpm ajke add ai <BINDING_NAME>
20
+ pnpm ajke add durable-object <BINDING_NAME> <ClassName>
21
+ pnpm ajke add vectorize <BINDING_NAME> <index-name> [dimensions]
22
+ pnpm ajke add browser <BINDING_NAME>
23
+ pnpm ajke add hyperdrive <BINDING_NAME> <connection-string>
24
+
25
+ Examples:
26
+ pnpm ajke add d1 PAYMENTS_DB payments-db
27
+ pnpm ajke add r2 ASSETS_BUCKET my-assets
28
+ pnpm ajke add kv SESSION_KV
29
+ pnpm ajke add queue MAIL_QUEUE mailer
30
+ pnpm ajke add ai AI
31
+ pnpm ajke add durable-object CHAT_ROOM ChatRoom
32
+ pnpm ajke add vectorize VECTOR_IDX my-index 1536
33
+ pnpm ajke add browser BROWSER
34
+ pnpm ajke add hyperdrive HYPERDRIVE postgres://user:pass@host/db
35
+ `.trim();
36
+
37
+ export function runAdd(args: string[]): void {
38
+ const [service, ...rest] = args;
39
+
40
+ if (!service) {
41
+ console.error(` ✗ Missing service type.\n\n${USAGE}`);
42
+ process.exit(1);
43
+ }
44
+
45
+ const svc = service.toLowerCase();
46
+
47
+ switch (svc) {
48
+ case "d1": {
49
+ const [binding, dbName] = rest;
50
+ if (!binding || !dbName) {
51
+ console.error(` ✗ Usage: pnpm ajke add d1 <BINDING_NAME> <database-name>`);
52
+ process.exit(1);
53
+ }
54
+ console.log(`\n Adding D1 binding to wrangler.jsonc...\n`);
55
+ addD1(binding, dbName);
56
+ break;
57
+ }
58
+
59
+ case "r2": {
60
+ const [binding, bucketName] = rest;
61
+ if (!binding || !bucketName) {
62
+ console.error(` ✗ Usage: pnpm ajke add r2 <BINDING_NAME> <bucket-name>`);
63
+ process.exit(1);
64
+ }
65
+ console.log(`\n Adding R2 binding to wrangler.jsonc...\n`);
66
+ addR2(binding, bucketName);
67
+ break;
68
+ }
69
+
70
+ case "kv": {
71
+ const [binding] = rest;
72
+ if (!binding) {
73
+ console.error(` ✗ Usage: pnpm ajke add kv <BINDING_NAME>`);
74
+ process.exit(1);
75
+ }
76
+ console.log(`\n Adding KV namespace binding to wrangler.jsonc...\n`);
77
+ addKv(binding);
78
+ break;
79
+ }
80
+
81
+ case "queue": {
82
+ const [binding, queueName] = rest;
83
+ if (!binding || !queueName) {
84
+ console.error(` ✗ Usage: pnpm ajke add queue <BINDING_NAME> <queue-name>`);
85
+ process.exit(1);
86
+ }
87
+ console.log(`\n Adding Queue binding to wrangler.jsonc...\n`);
88
+ addQueue(binding, queueName);
89
+ break;
90
+ }
91
+
92
+ case "ai": {
93
+ const [binding] = rest;
94
+ if (!binding) {
95
+ console.error(` ✗ Usage: pnpm ajke add ai <BINDING_NAME>`);
96
+ process.exit(1);
97
+ }
98
+ console.log(`\n Adding AI binding to wrangler.jsonc...\n`);
99
+ addAi(binding);
100
+ break;
101
+ }
102
+
103
+ case "durable-object":
104
+ case "do": {
105
+ const [binding, className] = rest;
106
+ if (!binding || !className) {
107
+ console.error(` ✗ Usage: pnpm ajke add durable-object <BINDING_NAME> <ClassName>`);
108
+ process.exit(1);
109
+ }
110
+ console.log(`\n Adding Durable Object binding to wrangler.jsonc...\n`);
111
+ addDurableObject(binding, className);
112
+ break;
113
+ }
114
+
115
+ case "vectorize":
116
+ case "vector": {
117
+ const [binding, indexName, rawDimensions] = rest;
118
+ if (!binding || !indexName) {
119
+ console.error(` ✗ Usage: pnpm ajke add vectorize <BINDING_NAME> <index-name> [dimensions]`);
120
+ process.exit(1);
121
+ }
122
+ const dimensions = rawDimensions ? parseInt(rawDimensions, 10) : 1536;
123
+ console.log(`\n Adding Vectorize binding to wrangler.jsonc...\n`);
124
+ addVectorize(binding, indexName, dimensions);
125
+ break;
126
+ }
127
+
128
+ case "browser": {
129
+ const [binding] = rest;
130
+ if (!binding) {
131
+ console.error(` ✗ Usage: pnpm ajke add browser <BINDING_NAME>`);
132
+ process.exit(1);
133
+ }
134
+ console.log(`\n Adding Browser rendering binding to wrangler.jsonc...\n`);
135
+ addBrowser(binding);
136
+ break;
137
+ }
138
+
139
+ case "hyperdrive": {
140
+ const [binding, connectionString] = rest;
141
+ if (!binding || !connectionString) {
142
+ console.error(` ✗ Usage: pnpm ajke add hyperdrive <BINDING_NAME> <connection-string>`);
143
+ process.exit(1);
144
+ }
145
+ console.log(`\n Adding Hyperdrive binding to wrangler.jsonc...\n`);
146
+ addHyperdrive(binding, connectionString);
147
+ break;
148
+ }
149
+
150
+ default: {
151
+ console.error(` ✗ Unknown service type: "${service}"\n\n${USAGE}`);
152
+ process.exit(1);
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,74 @@
1
+ import {
2
+ generateModule,
3
+ generateService,
4
+ generateController,
5
+ } from "../generators/module.generator.js";
6
+ import { loadConfig } from "../utils/config.js";
7
+
8
+ const USAGE = `
9
+ Usage:
10
+ pnpm ajke generate module <name> [--path <dir>] [--no-test]
11
+ pnpm ajke generate service <name> [--path <dir>]
12
+ pnpm ajke generate controller <name> [--path <dir>]
13
+
14
+ Aliases:
15
+ g m → generate module
16
+ g s → generate service
17
+ g c → generate controller
18
+
19
+ Flags:
20
+ --no-test Skip generating the .test.ts file for a module
21
+
22
+ Examples:
23
+ pnpm ajke generate module payment
24
+ pnpm ajke g m payment
25
+ pnpm ajke generate module payment --no-test
26
+ pnpm ajke generate service payment --path src/modules/app/payment
27
+ `.trim();
28
+
29
+ function parsePath(args: string[]): { args: string[]; path?: string } {
30
+ const idx = args.indexOf("--path");
31
+ if (idx === -1) return { args };
32
+ const path = args[idx + 1];
33
+ const cleaned = args.filter((_, i) => i !== idx && i !== idx + 1);
34
+ return { args: cleaned, path };
35
+ }
36
+
37
+ function parseNoTest(args: string[]): { args: string[]; noTest: boolean } {
38
+ const idx = args.indexOf("--no-test");
39
+ if (idx === -1) return { args, noTest: false };
40
+ return { args: args.filter((_, i) => i !== idx), noTest: true };
41
+ }
42
+
43
+ export async function runGenerate(args: string[]): Promise<void> {
44
+ const { args: withoutPath, path: targetPath } = parsePath(args);
45
+ const { args: cleanArgs, noTest } = parseNoTest(withoutPath);
46
+ const [subcommand, name] = cleanArgs;
47
+
48
+ if (!subcommand || !name) {
49
+ console.error(` ✗ Missing arguments.\n\n${USAGE}`);
50
+ process.exit(1);
51
+ }
52
+
53
+ const config = await loadConfig();
54
+ const sub = subcommand.toLowerCase();
55
+
56
+ if (sub === "module" || sub === "m") {
57
+ console.log(`\n Generating module "${name}"...\n`);
58
+ generateModule(name, {
59
+ dir: targetPath,
60
+ modulesDir: config.modulesDir,
61
+ defaultFiles: config.generate.files,
62
+ noTest,
63
+ });
64
+ } else if (sub === "service" || sub === "s") {
65
+ console.log(`\n Generating service "${name}"...\n`);
66
+ generateService(name, targetPath, config.modulesDir);
67
+ } else if (sub === "controller" || sub === "c") {
68
+ console.log(`\n Generating controller "${name}"...\n`);
69
+ generateController(name, targetPath, config.modulesDir);
70
+ } else {
71
+ console.error(` ✗ Unknown generate subcommand: "${subcommand}"\n\n${USAGE}`);
72
+ process.exit(1);
73
+ }
74
+ }