@sohan_fahad/wilt 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -0
  3. package/bin/commands/add.ts +155 -0
  4. package/bin/commands/generate.ts +74 -0
  5. package/bin/commands/new.ts +457 -0
  6. package/bin/create-wilt-app.ts +3 -0
  7. package/bin/generators/module.generator.ts +266 -0
  8. package/bin/utils/config.ts +46 -0
  9. package/bin/utils/paths.ts +32 -0
  10. package/bin/utils/wrangler.ts +216 -0
  11. package/bin/wilt.ts +79 -0
  12. package/dist/lib/bin/create-wilt-app.js +413 -0
  13. package/dist/lib/bin/create-wilt-app.js.map +1 -0
  14. package/dist/lib/bin/wilt.js +1151 -0
  15. package/dist/lib/bin/wilt.js.map +1 -0
  16. package/dist/lib/chunk-EUXUH3YW.js +15 -0
  17. package/dist/lib/chunk-EUXUH3YW.js.map +1 -0
  18. package/dist/lib/chunk-FIEODUMV.js +234 -0
  19. package/dist/lib/chunk-FIEODUMV.js.map +1 -0
  20. package/dist/lib/chunk-MOVXD653.cjs +234 -0
  21. package/dist/lib/chunk-MOVXD653.cjs.map +1 -0
  22. package/dist/lib/chunk-ZBDE64SD.cjs +15 -0
  23. package/dist/lib/chunk-ZBDE64SD.cjs.map +1 -0
  24. package/dist/lib/config.cjs +10 -0
  25. package/dist/lib/config.cjs.map +1 -0
  26. package/dist/lib/config.d.cts +13 -0
  27. package/dist/lib/config.d.ts +13 -0
  28. package/dist/lib/config.js +10 -0
  29. package/dist/lib/config.js.map +1 -0
  30. package/dist/lib/index.cjs +974 -0
  31. package/dist/lib/index.cjs.map +1 -0
  32. package/dist/lib/index.d.cts +255 -0
  33. package/dist/lib/index.d.ts +255 -0
  34. package/dist/lib/index.js +974 -0
  35. package/dist/lib/index.js.map +1 -0
  36. package/dist/lib/middleware/index.cjs +10 -0
  37. package/dist/lib/middleware/index.cjs.map +1 -0
  38. package/dist/lib/middleware/index.d.cts +18 -0
  39. package/dist/lib/middleware/index.d.ts +18 -0
  40. package/dist/lib/middleware/index.js +10 -0
  41. package/dist/lib/middleware/index.js.map +1 -0
  42. package/package.json +78 -0
  43. package/src/wilt/README.md +285 -0
  44. package/src/wilt/config.ts +14 -0
  45. package/src/wilt/context/execution-context.ts +36 -0
  46. package/src/wilt/context/index.ts +1 -0
  47. package/src/wilt/decorators/core/exception-filters.decorator.ts +24 -0
  48. package/src/wilt/decorators/core/index.ts +6 -0
  49. package/src/wilt/decorators/core/injectable.decorator.ts +41 -0
  50. package/src/wilt/decorators/core/optional.decorator.ts +9 -0
  51. package/src/wilt/decorators/core/set-metadata.decorator.ts +20 -0
  52. package/src/wilt/decorators/core/use-guards.decorator.ts +14 -0
  53. package/src/wilt/decorators/core/use-interceptors.decorator.ts +16 -0
  54. package/src/wilt/decorators/http/controller.decorator.ts +230 -0
  55. package/src/wilt/decorators/http/header.decorator.ts +11 -0
  56. package/src/wilt/decorators/http/http-code.decorator.ts +8 -0
  57. package/src/wilt/decorators/http/index.ts +6 -0
  58. package/src/wilt/decorators/http/redirect.decorator.ts +13 -0
  59. package/src/wilt/decorators/http/route-mapping.decorator.ts +22 -0
  60. package/src/wilt/decorators/http/route-params.decorator.ts +60 -0
  61. package/src/wilt/decorators/index.ts +3 -0
  62. package/src/wilt/decorators/modules/global.decorator.ts +8 -0
  63. package/src/wilt/decorators/modules/index.ts +2 -0
  64. package/src/wilt/decorators/modules/module.decorator.ts +16 -0
  65. package/src/wilt/exceptions/http-exception.ts +17 -0
  66. package/src/wilt/exceptions/http-exceptions.ts +85 -0
  67. package/src/wilt/exceptions/index.ts +2 -0
  68. package/src/wilt/index.ts +11 -0
  69. package/src/wilt/injector/index.ts +1 -0
  70. package/src/wilt/injector/injector.ts +103 -0
  71. package/src/wilt/injector/module-compiler.ts +48 -0
  72. package/src/wilt/injector/module.factory.ts +74 -0
  73. package/src/wilt/interfaces/core/filter.interface.ts +5 -0
  74. package/src/wilt/interfaces/core/guard.interface.ts +5 -0
  75. package/src/wilt/interfaces/core/index.ts +5 -0
  76. package/src/wilt/interfaces/core/interceptor.interface.ts +9 -0
  77. package/src/wilt/interfaces/core/lifecycle.interface.ts +19 -0
  78. package/src/wilt/interfaces/core/pipe.interface.ts +9 -0
  79. package/src/wilt/interfaces/http/index.ts +1 -0
  80. package/src/wilt/interfaces/http/response.interface.ts +27 -0
  81. package/src/wilt/interfaces/index.ts +3 -0
  82. package/src/wilt/interfaces/modules/index.ts +1 -0
  83. package/src/wilt/interfaces/modules/module.interface.ts +17 -0
  84. package/src/wilt/middleware/error-handler.middleware.ts +63 -0
  85. package/src/wilt/middleware/index.ts +2 -0
  86. package/src/wilt/middleware/request-logger.middleware.ts +17 -0
  87. package/src/wilt/pipes/index.ts +3 -0
  88. package/src/wilt/pipes/validate.pipe.ts +79 -0
  89. package/src/wilt/pipes/zod-query.pipe.ts +42 -0
  90. package/src/wilt/pipes/zod-validate.pipe.ts +49 -0
  91. package/src/wilt/services/index.ts +1 -0
  92. package/src/wilt/services/reflector.service.ts +24 -0
  93. package/src/wilt/utils/apply-decorators.util.ts +17 -0
  94. package/src/wilt/utils/forward-ref.util.ts +14 -0
  95. package/src/wilt/utils/index.ts +22 -0
  96. package/src/wilt/utils/logger.util.ts +189 -0
  97. package/src/wilt/utils/response.util.ts +72 -0
@@ -0,0 +1,1151 @@
1
+ #!/usr/bin/env node
2
+
3
+ // bin/commands/new.ts
4
+ import { mkdirSync, writeFileSync, existsSync } from "fs";
5
+ import { join, resolve } from "path";
6
+ import { execSync } from "child_process";
7
+ function pascal(name) {
8
+ return name.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toUpperCase());
9
+ }
10
+ function kebab(name) {
11
+ return name.replace(/([A-Z])/g, "-$1").replace(/^-/, "").replace(/_/g, "-").toLowerCase();
12
+ }
13
+ function packageJsonTemplate(name) {
14
+ return JSON.stringify(
15
+ {
16
+ name: kebab(name),
17
+ version: "0.0.1",
18
+ private: true,
19
+ type: "module",
20
+ scripts: {
21
+ dev: "vite dev --host --port 4001",
22
+ "dev:cf": "wrangler dev --port 4001",
23
+ build: "vite build",
24
+ deploy: "vite build && wrangler deploy",
25
+ "cf-typegen": "wrangler types --env-interface CloudflareBindings",
26
+ test: "vitest --run",
27
+ "test:watch": "vitest --watch",
28
+ "db:gen": "drizzle-kit generate",
29
+ "db:migrate": "wrangler d1 migrations apply DB --local",
30
+ "db:migrate:remote": "wrangler d1 migrations apply DB --remote",
31
+ check: "tsc --noEmit",
32
+ wilt: "wilt"
33
+ },
34
+ dependencies: {
35
+ hono: "^4.12.0",
36
+ wilt: "^0.1.0",
37
+ "reflect-metadata": "^0.2.2",
38
+ tsyringe: "^4.10.0",
39
+ zod: "^4.0.0",
40
+ "drizzle-orm": "^0.45.0",
41
+ ulid: "^3.0.0"
42
+ },
43
+ devDependencies: {
44
+ "@cloudflare/vite-plugin": "^1.36.0",
45
+ "@cloudflare/workers-types": "^4.0.0",
46
+ "@cloudflare/vitest-pool-workers": "^0.16.0",
47
+ "@sohan_fahad/wilt": "^0.1.0",
48
+ "@types/node": "^22.0.0",
49
+ "drizzle-kit": "^0.31.0",
50
+ typescript: "^5.0.0",
51
+ vite: "^6.0.0",
52
+ vitest: "^4.1.0",
53
+ tsx: "^4.0.0",
54
+ wrangler: "^4.0.0"
55
+ }
56
+ },
57
+ null,
58
+ 2
59
+ );
60
+ }
61
+ function wranglerTemplate(name) {
62
+ return `{
63
+ "$schema": "node_modules/wrangler/config-schema.json",
64
+ "name": "${kebab(name)}",
65
+ "main": "./src/index.ts",
66
+ "compatibility_date": "2025-06-17",
67
+ "compatibility_flags": ["nodejs_compat"],
68
+ "vars": {
69
+ "MODE": "development"
70
+ },
71
+ "observability": { "enabled": true },
72
+ "d1_databases": [
73
+ {
74
+ "binding": "DB",
75
+ "database_name": "REPLACE_WITH_YOUR_D1_NAME",
76
+ "database_id": "00000000-0000-0000-0000-000000000001",
77
+ "migrations_dir": "migrations",
78
+ "preview_database_id": "00000000-0000-0000-0000-000000000002"
79
+ }
80
+ ],
81
+ "dev": { "port": 4001 }
82
+ }
83
+ `;
84
+ }
85
+ function tsconfigTemplate() {
86
+ return `{
87
+ "compilerOptions": {
88
+ "target": "ESNext",
89
+ "module": "ESNext",
90
+ "moduleResolution": "Bundler",
91
+ "strict": true,
92
+ "skipLibCheck": true,
93
+ "experimentalDecorators": true,
94
+ "emitDecoratorMetadata": true,
95
+ "lib": ["ESNext", "DOM"],
96
+ "types": ["vite/client", "@cloudflare/workers-types"],
97
+ "baseUrl": ".",
98
+ "paths": {
99
+ "@app/*": ["src/*"]
100
+ }
101
+ },
102
+ "include": ["src/**/*", "worker-configuration.d.ts"],
103
+ "exclude": ["node_modules", "dist"]
104
+ }
105
+ `;
106
+ }
107
+ function viteConfigTemplate() {
108
+ return `import { defineConfig } from "vite";
109
+ import { cloudflare } from "@cloudflare/vite-plugin";
110
+ import { fileURLToPath, URL } from "node:url";
111
+
112
+ export default defineConfig({
113
+ plugins: [cloudflare()],
114
+ resolve: {
115
+ alias: {
116
+ "@app": fileURLToPath(new URL("./src", import.meta.url)),
117
+ },
118
+ },
119
+ });
120
+ `;
121
+ }
122
+ function workerTypesTemplate() {
123
+ return `// Generated by \`pnpm cf-typegen\`. Do not edit manually.
124
+ /// <reference types="@cloudflare/vitest-pool-workers/types" />
125
+ interface CloudflareBindings {
126
+ DB: D1Database;
127
+ MODE: string;
128
+ }
129
+
130
+ declare namespace Cloudflare {
131
+ interface Env extends CloudflareBindings {
132
+ TEST_MIGRATIONS: string;
133
+ }
134
+ }
135
+ `;
136
+ }
137
+ function vitestConfigTemplate() {
138
+ return `import path from "path";
139
+ import { defineConfig } from "vitest/config";
140
+ import { cloudflarePool, cloudflareTest, readD1Migrations } from "@cloudflare/vitest-pool-workers";
141
+
142
+ export default defineConfig(async () => {
143
+ const migrations = await readD1Migrations("./migrations");
144
+ const poolOptions = {
145
+ wrangler: { configPath: "./wrangler.jsonc" },
146
+ miniflare: {
147
+ bindings: { TEST_MIGRATIONS: JSON.stringify(migrations) },
148
+ },
149
+ };
150
+ return {
151
+ plugins: [cloudflareTest(poolOptions)],
152
+ resolve: {
153
+ alias: { "@app": path.resolve("./src") },
154
+ },
155
+ test: {
156
+ pool: cloudflarePool(poolOptions),
157
+ },
158
+ };
159
+ });
160
+ `;
161
+ }
162
+ function indexTemplate() {
163
+ return `import "reflect-metadata";
164
+ import { app } from "./app.module";
165
+
166
+ export default {
167
+ async fetch(request: Request, env: CloudflareBindings, ctx: ExecutionContext): Promise<Response> {
168
+ return app.fetch(request, env, ctx);
169
+ },
170
+ };
171
+ `;
172
+ }
173
+ function appModuleTemplate(name) {
174
+ const p = pascal(name);
175
+ return `import { cors } from "hono/cors";
176
+ import { secureHeaders } from "hono/secure-headers";
177
+
178
+ import { Module, createModule } from "wilt";
179
+ import { requestLogger } from "wilt/middleware";
180
+ import { HelloModule } from "./modules/app/hello/hello.module";
181
+
182
+ @Module({
183
+ imports: [HelloModule],
184
+ })
185
+ export class AppModule {}
186
+
187
+ const app = createModule(AppModule, {
188
+ middlewares: [cors(), secureHeaders(), requestLogger],
189
+ });
190
+
191
+ app.get("/", (c) => c.json({ success: true, status: "healthy", timestamp: new Date().toISOString() }));
192
+ app.get("/health", (c) => c.json({ success: true, status: "healthy", timestamp: new Date().toISOString() }));
193
+
194
+ export { app };
195
+ `;
196
+ }
197
+ function helloModuleTemplate() {
198
+ return `import { Module } from "wilt";
199
+ import { HelloController } from "./hello.controller";
200
+ import { HelloService } from "./hello.service";
201
+
202
+ @Module({
203
+ controllers: [HelloController],
204
+ providers: [HelloService],
205
+ })
206
+ export class HelloModule {}
207
+ `;
208
+ }
209
+ function helloControllerTemplate() {
210
+ return `import type { Context } from "hono";
211
+ import { Controller, Get, Inject } from "wilt";
212
+ import { ResponseUtil } from "wilt";
213
+ import { HelloService } from "./hello.service";
214
+
215
+ @Controller("/hello")
216
+ export class HelloController {
217
+ constructor(@Inject(HelloService) private helloService: HelloService) {}
218
+
219
+ @Get()
220
+ async greet(c: Context) {
221
+ const message = this.helloService.greet();
222
+ return ResponseUtil.success(c, { message });
223
+ }
224
+ }
225
+ `;
226
+ }
227
+ function helloServiceTemplate() {
228
+ return `import { Injectable } from "wilt";
229
+
230
+ @Injectable()
231
+ export class HelloService {
232
+ greet() {
233
+ return "Hello from Wilt!";
234
+ }
235
+ }
236
+ `;
237
+ }
238
+ function dbSchemaTemplate() {
239
+ return `// Register all your entity tables here so Drizzle can build relations and
240
+ // drizzle-kit can generate migrations from a single source of truth.
241
+ export const schema = {};
242
+ `;
243
+ }
244
+ function dbConnectionTemplate() {
245
+ return `import { drizzle } from "drizzle-orm/d1";
246
+ import type { Context } from "hono";
247
+ import { schema } from "./schema";
248
+ export function getDb(c: Context) {
249
+ return drizzle(c.env.DB, { schema: schema });
250
+ }
251
+ `;
252
+ }
253
+ function gitignoreTemplate() {
254
+ return `dist/
255
+ node_modules/
256
+ .wrangler
257
+ .env
258
+ .env.production
259
+ .dev.vars
260
+ .DS_Store
261
+ coverage/
262
+ *.log
263
+ `;
264
+ }
265
+ function wiltConfigTemplate() {
266
+ return `import { defineConfig } from "wilt/config";
267
+
268
+ export default defineConfig({
269
+ // Directory where generated modules are placed (relative to project root)
270
+ modulesDir: "src/modules/app",
271
+
272
+ // Default files scaffolded by \`wilt generate module <name>\`
273
+ // Remove "test" to skip test files, or pass --no-test at the CLI
274
+ generate: {
275
+ files: ["module", "controller", "service", "dto", "entity", "test"],
276
+ },
277
+ });
278
+ `;
279
+ }
280
+ function envExampleTemplate() {
281
+ return `CLOUDFLARE_ACCOUNT_ID=
282
+ CLOUDFLARE_TOKEN=
283
+
284
+ CLOUDFLARE_DATABASE_ID=
285
+ `;
286
+ }
287
+ function drizzleConfigTemplate() {
288
+ return `import { defineConfig } from "drizzle-kit";
289
+
290
+ export default defineConfig({
291
+ schema: "./src/database/schema.ts",
292
+ out: "./migrations",
293
+ dialect: "sqlite",
294
+ driver: "d1-http",
295
+ dbCredentials: {
296
+ accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
297
+ databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
298
+ token: process.env.CLOUDFLARE_TOKEN!,
299
+ },
300
+ verbose: true,
301
+ strict: true,
302
+ });
303
+ `;
304
+ }
305
+ function migrationTemplate() {
306
+ return `-- Initial migration
307
+ -- Add your tables here
308
+ `;
309
+ }
310
+ function migrationMetaTemplate(name) {
311
+ return JSON.stringify(
312
+ {
313
+ version: "5",
314
+ dialect: "sqlite",
315
+ entries: [
316
+ {
317
+ idx: 0,
318
+ version: "5",
319
+ when: Date.now(),
320
+ tag: "0000_initial",
321
+ breakpoints: true
322
+ }
323
+ ]
324
+ },
325
+ null,
326
+ 2
327
+ );
328
+ }
329
+ function write(path, content) {
330
+ writeFileSync(path, content, "utf-8");
331
+ console.log(` \u2714 ${path.replace(process.cwd() + "/", "")}`);
332
+ }
333
+ function dir(path) {
334
+ mkdirSync(path, { recursive: true });
335
+ }
336
+ function runNew(args) {
337
+ const [name] = args;
338
+ if (!name) {
339
+ console.error(" \u2717 Usage: wilt new <project-name>");
340
+ process.exit(1);
341
+ }
342
+ const projectDir = resolve(process.cwd(), name);
343
+ if (existsSync(projectDir)) {
344
+ console.error(` \u2717 Directory already exists: ${projectDir}`);
345
+ process.exit(1);
346
+ }
347
+ console.log(`
348
+ Creating Wilt app "${name}"...
349
+ `);
350
+ dir(join(projectDir, "src/modules/app/hello"));
351
+ dir(join(projectDir, "src/database"));
352
+ dir(join(projectDir, "migrations/meta"));
353
+ write(join(projectDir, "package.json"), packageJsonTemplate(name));
354
+ write(join(projectDir, "wrangler.jsonc"), wranglerTemplate(name));
355
+ write(join(projectDir, "tsconfig.json"), tsconfigTemplate());
356
+ write(join(projectDir, "vite.config.ts"), viteConfigTemplate());
357
+ write(join(projectDir, "vitest.config.ts"), vitestConfigTemplate());
358
+ write(join(projectDir, "worker-configuration.d.ts"), workerTypesTemplate());
359
+ write(join(projectDir, "wilt.config.ts"), wiltConfigTemplate());
360
+ write(join(projectDir, ".gitignore"), gitignoreTemplate());
361
+ write(join(projectDir, ".env.example"), envExampleTemplate());
362
+ write(join(projectDir, "drizzle.config.ts"), drizzleConfigTemplate());
363
+ write(join(projectDir, "src/index.ts"), indexTemplate());
364
+ write(join(projectDir, "src/app.module.ts"), appModuleTemplate(name));
365
+ write(join(projectDir, "src/modules/app/hello/hello.module.ts"), helloModuleTemplate());
366
+ write(join(projectDir, "src/modules/app/hello/hello.controller.ts"), helloControllerTemplate());
367
+ write(join(projectDir, "src/modules/app/hello/hello.service.ts"), helloServiceTemplate());
368
+ write(join(projectDir, "src/database/schema.ts"), dbSchemaTemplate());
369
+ write(join(projectDir, "src/database/connection.ts"), dbConnectionTemplate());
370
+ write(join(projectDir, "migrations/0000_initial.sql"), migrationTemplate());
371
+ write(join(projectDir, "migrations/meta/_journal.json"), migrationMetaTemplate(name));
372
+ console.log("\n Installing dependencies...\n");
373
+ try {
374
+ const pm = detectPackageManager();
375
+ execSync(`${pm} install`, { cwd: projectDir, stdio: "inherit" });
376
+ } catch {
377
+ console.log(" \u26A0 Could not auto-install. Run `pnpm install` manually.\n");
378
+ }
379
+ console.log(`
380
+ \u2714 Project created!
381
+
382
+ Next steps:
383
+ cd ${name}
384
+ cp .env.example .env # fill in Cloudflare credentials
385
+ # Edit wrangler.jsonc: set database_name + database_id
386
+ pnpm db:migrate # apply local D1 migrations
387
+ pnpm dev # http://localhost:4001
388
+
389
+ Try it:
390
+ curl http://localhost:4001/health
391
+ curl http://localhost:4001/hello
392
+
393
+ Generate a new module:
394
+ pnpm wilt generate module posts
395
+ `);
396
+ }
397
+ function detectPackageManager() {
398
+ try {
399
+ execSync("pnpm --version", { stdio: "ignore" });
400
+ return "pnpm";
401
+ } catch {
402
+ }
403
+ try {
404
+ execSync("bun --version", { stdio: "ignore" });
405
+ return "bun";
406
+ } catch {
407
+ }
408
+ return "npm";
409
+ }
410
+
411
+ // bin/generators/module.generator.ts
412
+ import { writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
413
+ import { join as join3 } from "path";
414
+
415
+ // bin/utils/paths.ts
416
+ import { join as join2 } from "path";
417
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
418
+ var PROJECT_ROOT = process.cwd();
419
+ var SRC_DIR = join2(PROJECT_ROOT, "src");
420
+ var MODULES_DIR = join2(SRC_DIR, "modules");
421
+ var APP_MODULES_DIR = join2(MODULES_DIR, "app");
422
+ var WRANGLER_JSONC = join2(PROJECT_ROOT, "wrangler.jsonc");
423
+ var APP_MODULE_FILE = join2(SRC_DIR, "app.module.ts");
424
+ function wiltImportPath() {
425
+ return "wilt";
426
+ }
427
+ function ensureDir(dir2) {
428
+ if (!existsSync2(dir2)) {
429
+ mkdirSync2(dir2, { recursive: true });
430
+ }
431
+ }
432
+
433
+ // bin/generators/module.generator.ts
434
+ function toPascalCase(name) {
435
+ return name.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toUpperCase());
436
+ }
437
+ function toCamelCase(name) {
438
+ const pascal2 = toPascalCase(name);
439
+ return pascal2.charAt(0).toLowerCase() + pascal2.slice(1);
440
+ }
441
+ function toKebabCase(name) {
442
+ return name.replace(/([A-Z])/g, "-$1").replace(/^-/, "").replace(/_/g, "-").toLowerCase();
443
+ }
444
+ function pluralize(name) {
445
+ return name.endsWith("s") ? name : name + "s";
446
+ }
447
+ function moduleTemplate(name, wiltPath) {
448
+ const pascal2 = toPascalCase(name);
449
+ const kebab2 = toKebabCase(name);
450
+ return `import { Module } from "${wiltPath}";
451
+ import { ${pascal2}Controller } from "./${kebab2}.controller";
452
+ import { ${pascal2}Service } from "./${kebab2}.service";
453
+
454
+ @Module({
455
+ controllers: [${pascal2}Controller],
456
+ providers: [${pascal2}Service],
457
+ exports: [${pascal2}Service],
458
+ })
459
+ export class ${pascal2}Module {}
460
+ `;
461
+ }
462
+ function entityTemplate(name) {
463
+ const camel = toCamelCase(name);
464
+ const kebab2 = toKebabCase(name);
465
+ const pascal2 = toPascalCase(name);
466
+ const entityVar = pluralize(camel);
467
+ const tableName = pluralize(kebab2);
468
+ return `import { sqliteTable, integer } from "drizzle-orm/sqlite-core";
469
+
470
+ export const ${entityVar} = sqliteTable("${tableName}", {
471
+ id: integer("id").primaryKey({ autoIncrement: true }),
472
+ createdAt: integer("created_at", { mode: "timestamp" })
473
+ .$defaultFn(() => new Date())
474
+ .notNull(),
475
+ });
476
+
477
+ export type ${pascal2} = typeof ${entityVar}.$inferSelect;
478
+ export type New${pascal2} = typeof ${entityVar}.$inferInsert;
479
+ `;
480
+ }
481
+ function controllerTemplate(name, wiltPath) {
482
+ const pascal2 = toPascalCase(name);
483
+ const camel = toCamelCase(name);
484
+ const kebab2 = toKebabCase(name);
485
+ return `import type { Context } from "hono";
486
+ import { Controller, Get, Inject } from "${wiltPath}";
487
+ import { ResponseUtil } from "${wiltPath}";
488
+ import { ${pascal2}Service } from "./${kebab2}.service";
489
+
490
+ @Controller("/${kebab2}")
491
+ export class ${pascal2}Controller {
492
+ constructor(@Inject(${pascal2}Service) private ${camel}Service: ${pascal2}Service) {}
493
+
494
+ @Get()
495
+ async getAll(c: Context) {
496
+ const data = await this.${camel}Service.findAll();
497
+ return ResponseUtil.success(c, data);
498
+ }
499
+ }
500
+ `;
501
+ }
502
+ function serviceTemplate(name, wiltPath) {
503
+ const pascal2 = toPascalCase(name);
504
+ return `import { Injectable } from "${wiltPath}";
505
+ import { createDatabase } from "../../../database/connection";
506
+
507
+ @Injectable()
508
+ export class ${pascal2}Service {
509
+
510
+ private getDatabase(env: CloudflareBindings) {
511
+ return createDatabase(env.DB);
512
+ }
513
+
514
+ async findAll() {
515
+ return [];
516
+ }
517
+ }
518
+ `;
519
+ }
520
+ function testTemplate(name) {
521
+ const kebab2 = toKebabCase(name);
522
+ const pascal2 = toPascalCase(name);
523
+ const table = pluralize(kebab2.replace(/-/g, "_"));
524
+ return `import { describe, it, expect, beforeAll, afterEach } from "vitest";
525
+ import { SELF, env, applyD1Migrations } from "cloudflare:test";
526
+ import { ${pascal2}Service } from "./${kebab2}.service";
527
+
528
+ const BASE = "http://localhost";
529
+ const service = new ${pascal2}Service();
530
+
531
+ beforeAll(async () => {
532
+ await applyD1Migrations(env.DB, JSON.parse(env.TEST_MIGRATIONS));
533
+ });
534
+
535
+ afterEach(async () => {
536
+ await env.DB.prepare("DELETE FROM ${table}").run();
537
+ });
538
+
539
+ describe("${pascal2}Service", () => {
540
+ it("should be defined", () => {
541
+ expect(service).toBeDefined();
542
+ });
543
+ });
544
+
545
+ describe("GET /${kebab2}", () => {
546
+ it("returns an empty list", async () => {
547
+ const res = await SELF.fetch(\`\${BASE}/${kebab2}\`);
548
+ expect(res.status).toBe(200);
549
+ const { data } = await res.json() as { data: unknown[] };
550
+ expect(data).toEqual([]);
551
+ });
552
+ });
553
+ `;
554
+ }
555
+ function dtoTemplate(name) {
556
+ const pascal2 = toPascalCase(name);
557
+ return `import { z } from "zod";
558
+
559
+ export const Create${pascal2}Dto = z.object({
560
+ // TODO: define your fields
561
+ });
562
+
563
+ export const Update${pascal2}Dto = Create${pascal2}Dto.partial();
564
+
565
+ export type Create${pascal2}DtoType = z.infer<typeof Create${pascal2}Dto>;
566
+ export type Update${pascal2}DtoType = z.infer<typeof Update${pascal2}Dto>;
567
+ `;
568
+ }
569
+ function generateModule(name, options = {}) {
570
+ const kebab2 = toKebabCase(name);
571
+ const pascal2 = toPascalCase(name);
572
+ const basedir = options.modulesDir ?? APP_MODULES_DIR;
573
+ const moduleDir = options.dir ?? join3(basedir, kebab2);
574
+ let filesToGen = options.files ?? options.defaultFiles ?? ["module", "controller", "service", "dto", "entity", "test"];
575
+ if (options.noTest) filesToGen = filesToGen.filter((f) => f !== "test");
576
+ if (existsSync3(moduleDir)) {
577
+ console.error(` \u2717 Directory already exists: ${moduleDir}`);
578
+ process.exit(1);
579
+ }
580
+ ensureDir(moduleDir);
581
+ const wiltPath = wiltImportPath();
582
+ const hasEntity = filesToGen.includes("entity");
583
+ const fileMap = {
584
+ module: moduleTemplate(name, wiltPath),
585
+ controller: controllerTemplate(name, wiltPath),
586
+ service: serviceTemplate(name, wiltPath),
587
+ dto: dtoTemplate(name),
588
+ entity: entityTemplate(name),
589
+ test: testTemplate(name)
590
+ };
591
+ const extMap = {
592
+ module: `${kebab2}.module.ts`,
593
+ controller: `${kebab2}.controller.ts`,
594
+ service: `${kebab2}.service.ts`,
595
+ dto: `${kebab2}.dto.ts`,
596
+ entity: `${kebab2}.entity.ts`,
597
+ test: `${kebab2}.test.ts`
598
+ };
599
+ for (const file of filesToGen) {
600
+ const filePath = join3(moduleDir, extMap[file]);
601
+ writeFileSync2(filePath, fileMap[file], "utf-8");
602
+ console.log(` \u2714 Created ${filePath.replace(process.cwd() + "/", "")}`);
603
+ }
604
+ const relModuleDir = moduleDir.replace(process.cwd() + "/", "");
605
+ const entityLine = hasEntity ? `
606
+ 2. Add the entity to src/database/schema.ts:
607
+
608
+ import { ${pluralize(toCamelCase(name))} } from "@app/${relModuleDir.replace(/^src\//, "")}/${kebab2}.entity";
609
+
610
+ export const schema = {
611
+ ...existing,
612
+ ${pluralize(toCamelCase(name))},
613
+ };
614
+ ` : "";
615
+ console.log(`
616
+ Next steps \u2014
617
+
618
+ 1. Register the module in src/app.module.ts:
619
+
620
+ import { ${pascal2}Module } from "./${relModuleDir.replace(/^src\//, "")}/${kebab2}.module";
621
+
622
+ @Module({
623
+ imports: [..., ${pascal2}Module],
624
+ })
625
+ export class AppModule {}
626
+ ${entityLine} Then run \`pnpm db:gen\` to generate a migration.
627
+ `);
628
+ }
629
+ function generateService(name, dir2, modulesDir) {
630
+ const kebab2 = toKebabCase(name);
631
+ const basedir = modulesDir ?? APP_MODULES_DIR;
632
+ const targetDir = dir2 ?? join3(basedir, kebab2);
633
+ const wiltPath = wiltImportPath();
634
+ const filePath = join3(targetDir, `${kebab2}.service.ts`);
635
+ if (existsSync3(filePath)) {
636
+ console.error(` \u2717 File already exists: ${filePath}`);
637
+ process.exit(1);
638
+ }
639
+ ensureDir(targetDir);
640
+ writeFileSync2(filePath, serviceTemplate(name, wiltPath), "utf-8");
641
+ console.log(` \u2714 Created ${filePath.replace(process.cwd() + "/", "")}`);
642
+ }
643
+ function generateController(name, dir2, modulesDir) {
644
+ const kebab2 = toKebabCase(name);
645
+ const basedir = modulesDir ?? APP_MODULES_DIR;
646
+ const targetDir = dir2 ?? join3(basedir, kebab2);
647
+ const wiltPath = wiltImportPath();
648
+ const filePath = join3(targetDir, `${kebab2}.controller.ts`);
649
+ if (existsSync3(filePath)) {
650
+ console.error(` \u2717 File already exists: ${filePath}`);
651
+ process.exit(1);
652
+ }
653
+ ensureDir(targetDir);
654
+ writeFileSync2(filePath, controllerTemplate(name, wiltPath), "utf-8");
655
+ console.log(` \u2714 Created ${filePath.replace(process.cwd() + "/", "")}`);
656
+ }
657
+
658
+ // bin/utils/config.ts
659
+ import { existsSync as existsSync4 } from "fs";
660
+ import { join as join4, resolve as resolve2 } from "path";
661
+ import { createJiti } from "jiti";
662
+ async function loadConfig(cwd = process.cwd()) {
663
+ let userConfig = {};
664
+ for (const name of ["wilt.config.ts", "wilt.config.js", "wilt.config.mjs"]) {
665
+ const configPath = join4(cwd, name);
666
+ if (existsSync4(configPath)) {
667
+ try {
668
+ const jiti = createJiti(import.meta.url, { interopDefault: true });
669
+ const mod = await jiti.import(configPath);
670
+ userConfig = mod ?? {};
671
+ } catch {
672
+ }
673
+ break;
674
+ }
675
+ }
676
+ return {
677
+ modulesDir: resolve2(cwd, userConfig.modulesDir ?? "src/modules/app"),
678
+ srcDir: resolve2(cwd, userConfig.srcDir ?? "src"),
679
+ generate: {
680
+ files: userConfig.generate?.files ?? ["module", "controller", "service", "dto", "entity", "test"]
681
+ }
682
+ };
683
+ }
684
+
685
+ // bin/commands/generate.ts
686
+ var USAGE = `
687
+ Usage:
688
+ pnpm wilt generate module <name> [--path <dir>] [--no-test]
689
+ pnpm wilt generate service <name> [--path <dir>]
690
+ pnpm wilt generate controller <name> [--path <dir>]
691
+
692
+ Aliases:
693
+ g m \u2192 generate module
694
+ g s \u2192 generate service
695
+ g c \u2192 generate controller
696
+
697
+ Flags:
698
+ --no-test Skip generating the .test.ts file for a module
699
+
700
+ Examples:
701
+ pnpm wilt generate module payment
702
+ pnpm wilt g m payment
703
+ pnpm wilt generate module payment --no-test
704
+ pnpm wilt generate service payment --path src/modules/app/payment
705
+ `.trim();
706
+ function parsePath(args) {
707
+ const idx = args.indexOf("--path");
708
+ if (idx === -1) return { args };
709
+ const path = args[idx + 1];
710
+ const cleaned = args.filter((_, i) => i !== idx && i !== idx + 1);
711
+ return { args: cleaned, path };
712
+ }
713
+ function parseNoTest(args) {
714
+ const idx = args.indexOf("--no-test");
715
+ if (idx === -1) return { args, noTest: false };
716
+ return { args: args.filter((_, i) => i !== idx), noTest: true };
717
+ }
718
+ async function runGenerate(args) {
719
+ const { args: withoutPath, path: targetPath } = parsePath(args);
720
+ const { args: cleanArgs, noTest } = parseNoTest(withoutPath);
721
+ const [subcommand, name] = cleanArgs;
722
+ if (!subcommand || !name) {
723
+ console.error(` \u2717 Missing arguments.
724
+
725
+ ${USAGE}`);
726
+ process.exit(1);
727
+ }
728
+ const config = await loadConfig();
729
+ const sub = subcommand.toLowerCase();
730
+ if (sub === "module" || sub === "m") {
731
+ console.log(`
732
+ Generating module "${name}"...
733
+ `);
734
+ generateModule(name, {
735
+ dir: targetPath,
736
+ modulesDir: config.modulesDir,
737
+ defaultFiles: config.generate.files,
738
+ noTest
739
+ });
740
+ } else if (sub === "service" || sub === "s") {
741
+ console.log(`
742
+ Generating service "${name}"...
743
+ `);
744
+ generateService(name, targetPath, config.modulesDir);
745
+ } else if (sub === "controller" || sub === "c") {
746
+ console.log(`
747
+ Generating controller "${name}"...
748
+ `);
749
+ generateController(name, targetPath, config.modulesDir);
750
+ } else {
751
+ console.error(` \u2717 Unknown generate subcommand: "${subcommand}"
752
+
753
+ ${USAGE}`);
754
+ process.exit(1);
755
+ }
756
+ }
757
+
758
+ // bin/utils/wrangler.ts
759
+ import { readFileSync, writeFileSync as writeFileSync3 } from "fs";
760
+ function stripComments(text) {
761
+ return text.split("\n").map((line) => line.replace(/\s*\/\/.*$/, "")).join("\n");
762
+ }
763
+ function readWrangler() {
764
+ const raw = readFileSync(WRANGLER_JSONC, "utf-8");
765
+ return JSON.parse(stripComments(raw));
766
+ }
767
+ function writeWrangler(config) {
768
+ const header = `{
769
+ "$schema": "node_modules/wrangler/config-schema.json",`;
770
+ const body = JSON.stringify(config, null, 2);
771
+ const bodyWithoutSchema = body.replace(/^\{/, "").replace(/\s+"?\$schema"?\s*:\s*"[^"]*",?\n/, "\n");
772
+ writeFileSync3(WRANGLER_JSONC, header + bodyWithoutSchema, "utf-8");
773
+ }
774
+ function addD1(binding, databaseName) {
775
+ const config = readWrangler();
776
+ if (!config.d1_databases) config.d1_databases = [];
777
+ const alreadyExists = config.d1_databases.some(
778
+ (db) => db.binding === binding
779
+ );
780
+ if (alreadyExists) {
781
+ console.error(` \u2717 D1 binding "${binding}" already exists in wrangler.jsonc`);
782
+ process.exit(1);
783
+ }
784
+ config.d1_databases.push({
785
+ binding,
786
+ database_name: databaseName,
787
+ database_id: "00000000-0000-0000-0000-000000000000",
788
+ migrations_dir: "migrations"
789
+ });
790
+ writeWrangler(config);
791
+ console.log(` \u2714 Added D1 binding "${binding}" (database: ${databaseName})`);
792
+ console.log(` ! Remember to replace database_id with your actual D1 database ID.`);
793
+ }
794
+ function addR2(binding, bucketName) {
795
+ const config = readWrangler();
796
+ if (!config.r2_buckets) config.r2_buckets = [];
797
+ if (config.r2_buckets.some((b) => b.binding === binding)) {
798
+ console.error(` \u2717 R2 binding "${binding}" already exists in wrangler.jsonc`);
799
+ process.exit(1);
800
+ }
801
+ config.r2_buckets.push({
802
+ binding,
803
+ bucket_name: bucketName,
804
+ preview_bucket_name: `${bucketName}-preview`
805
+ });
806
+ writeWrangler(config);
807
+ console.log(` \u2714 Added R2 binding "${binding}" (bucket: ${bucketName})`);
808
+ }
809
+ function addKv(binding) {
810
+ const config = readWrangler();
811
+ if (!config.kv_namespaces) config.kv_namespaces = [];
812
+ if (config.kv_namespaces.some((k) => k.binding === binding)) {
813
+ console.error(` \u2717 KV binding "${binding}" already exists in wrangler.jsonc`);
814
+ process.exit(1);
815
+ }
816
+ config.kv_namespaces.push({
817
+ binding,
818
+ id: "00000000000000000000000000000000"
819
+ });
820
+ writeWrangler(config);
821
+ console.log(` \u2714 Added KV namespace binding "${binding}"`);
822
+ console.log(` ! Remember to replace id with your actual KV namespace ID.`);
823
+ }
824
+ function addQueue(binding, queueName) {
825
+ const config = readWrangler();
826
+ if (!config.queues) config.queues = { producers: [], consumers: [] };
827
+ if (!config.queues.producers) config.queues.producers = [];
828
+ if (!config.queues.consumers) config.queues.consumers = [];
829
+ if (config.queues.producers.some((p) => p.binding === binding)) {
830
+ console.error(` \u2717 Queue producer binding "${binding}" already exists in wrangler.jsonc`);
831
+ process.exit(1);
832
+ }
833
+ config.queues.producers.push({ binding, queue: queueName });
834
+ config.queues.consumers.push({
835
+ queue: queueName,
836
+ max_batch_size: 10,
837
+ max_batch_timeout: 10,
838
+ max_retries: 3
839
+ });
840
+ writeWrangler(config);
841
+ console.log(` \u2714 Added Queue binding "${binding}" (queue: ${queueName})`);
842
+ }
843
+ function addAi(binding) {
844
+ const config = readWrangler();
845
+ if (config.ai) {
846
+ console.error(` \u2717 AI binding already exists in wrangler.jsonc`);
847
+ process.exit(1);
848
+ }
849
+ config.ai = { binding };
850
+ writeWrangler(config);
851
+ console.log(` \u2714 Added AI binding "${binding}"`);
852
+ }
853
+ function addDurableObject(binding, className) {
854
+ const config = readWrangler();
855
+ if (!config.durable_objects) config.durable_objects = { bindings: [] };
856
+ if (!config.durable_objects.bindings) config.durable_objects.bindings = [];
857
+ if (config.durable_objects.bindings.some((b) => b.name === binding)) {
858
+ console.error(` \u2717 Durable Object binding "${binding}" already exists in wrangler.jsonc`);
859
+ process.exit(1);
860
+ }
861
+ config.durable_objects.bindings.push({
862
+ name: binding,
863
+ class_name: className
864
+ });
865
+ if (!config.migrations) config.migrations = [];
866
+ const existingClasses = config.migrations.flatMap(
867
+ (m) => m.new_sqlite_classes ?? m.new_classes ?? []
868
+ );
869
+ if (!existingClasses.includes(className)) {
870
+ const nextTag = `v${config.migrations.length + 1}`;
871
+ config.migrations.push({
872
+ tag: nextTag,
873
+ new_sqlite_classes: [className]
874
+ });
875
+ console.log(` ! Added migration entry "${nextTag}" for ${className}.`);
876
+ }
877
+ writeWrangler(config);
878
+ console.log(` \u2714 Added Durable Object binding "${binding}" (class: ${className})`);
879
+ }
880
+ function addVectorize(binding, indexName, dimensions = 1536) {
881
+ const config = readWrangler();
882
+ if (!config.vectorize) config.vectorize = [];
883
+ if (config.vectorize.some((v) => v.binding === binding)) {
884
+ console.error(` \u2717 Vectorize binding "${binding}" already exists in wrangler.jsonc`);
885
+ process.exit(1);
886
+ }
887
+ config.vectorize.push({
888
+ binding,
889
+ index_name: indexName,
890
+ dimensions,
891
+ metric: "cosine"
892
+ });
893
+ writeWrangler(config);
894
+ console.log(` \u2714 Added Vectorize binding "${binding}" (index: ${indexName}, dimensions: ${dimensions})`);
895
+ console.log(` ! Create the index with: wrangler vectorize create ${indexName} --dimensions=${dimensions} --metric=cosine`);
896
+ }
897
+ function addBrowser(binding) {
898
+ const config = readWrangler();
899
+ if (config.browser) {
900
+ console.error(` \u2717 Browser binding already exists in wrangler.jsonc`);
901
+ process.exit(1);
902
+ }
903
+ config.browser = { binding };
904
+ writeWrangler(config);
905
+ console.log(` \u2714 Added Browser rendering binding "${binding}"`);
906
+ }
907
+ function addHyperdrive(binding, connectionString) {
908
+ const config = readWrangler();
909
+ if (!config.hyperdrive) config.hyperdrive = [];
910
+ if (config.hyperdrive.some((h) => h.binding === binding)) {
911
+ console.error(` \u2717 Hyperdrive binding "${binding}" already exists in wrangler.jsonc`);
912
+ process.exit(1);
913
+ }
914
+ config.hyperdrive.push({
915
+ binding,
916
+ id: "00000000000000000000000000000000",
917
+ localConnectionString: connectionString
918
+ });
919
+ writeWrangler(config);
920
+ console.log(` \u2714 Added Hyperdrive binding "${binding}"`);
921
+ console.log(` ! Remember to replace id with your actual Hyperdrive config ID.`);
922
+ }
923
+
924
+ // bin/commands/add.ts
925
+ var USAGE2 = `
926
+ Usage:
927
+ pnpm wilt add d1 <BINDING_NAME> <database-name>
928
+ pnpm wilt add r2 <BINDING_NAME> <bucket-name>
929
+ pnpm wilt add kv <BINDING_NAME>
930
+ pnpm wilt add queue <BINDING_NAME> <queue-name>
931
+ pnpm wilt add ai <BINDING_NAME>
932
+ pnpm wilt add durable-object <BINDING_NAME> <ClassName>
933
+ pnpm wilt add vectorize <BINDING_NAME> <index-name> [dimensions]
934
+ pnpm wilt add browser <BINDING_NAME>
935
+ pnpm wilt add hyperdrive <BINDING_NAME> <connection-string>
936
+
937
+ Examples:
938
+ pnpm wilt add d1 PAYMENTS_DB payments-db
939
+ pnpm wilt add r2 ASSETS_BUCKET my-assets
940
+ pnpm wilt add kv SESSION_KV
941
+ pnpm wilt add queue MAIL_QUEUE mailer
942
+ pnpm wilt add ai AI
943
+ pnpm wilt add durable-object CHAT_ROOM ChatRoom
944
+ pnpm wilt add vectorize VECTOR_IDX my-index 1536
945
+ pnpm wilt add browser BROWSER
946
+ pnpm wilt add hyperdrive HYPERDRIVE postgres://user:pass@host/db
947
+ `.trim();
948
+ function runAdd(args) {
949
+ const [service, ...rest] = args;
950
+ if (!service) {
951
+ console.error(` \u2717 Missing service type.
952
+
953
+ ${USAGE2}`);
954
+ process.exit(1);
955
+ }
956
+ const svc = service.toLowerCase();
957
+ switch (svc) {
958
+ case "d1": {
959
+ const [binding, dbName] = rest;
960
+ if (!binding || !dbName) {
961
+ console.error(` \u2717 Usage: pnpm wilt add d1 <BINDING_NAME> <database-name>`);
962
+ process.exit(1);
963
+ }
964
+ console.log(`
965
+ Adding D1 binding to wrangler.jsonc...
966
+ `);
967
+ addD1(binding, dbName);
968
+ break;
969
+ }
970
+ case "r2": {
971
+ const [binding, bucketName] = rest;
972
+ if (!binding || !bucketName) {
973
+ console.error(` \u2717 Usage: pnpm wilt add r2 <BINDING_NAME> <bucket-name>`);
974
+ process.exit(1);
975
+ }
976
+ console.log(`
977
+ Adding R2 binding to wrangler.jsonc...
978
+ `);
979
+ addR2(binding, bucketName);
980
+ break;
981
+ }
982
+ case "kv": {
983
+ const [binding] = rest;
984
+ if (!binding) {
985
+ console.error(` \u2717 Usage: pnpm wilt add kv <BINDING_NAME>`);
986
+ process.exit(1);
987
+ }
988
+ console.log(`
989
+ Adding KV namespace binding to wrangler.jsonc...
990
+ `);
991
+ addKv(binding);
992
+ break;
993
+ }
994
+ case "queue": {
995
+ const [binding, queueName] = rest;
996
+ if (!binding || !queueName) {
997
+ console.error(` \u2717 Usage: pnpm wilt add queue <BINDING_NAME> <queue-name>`);
998
+ process.exit(1);
999
+ }
1000
+ console.log(`
1001
+ Adding Queue binding to wrangler.jsonc...
1002
+ `);
1003
+ addQueue(binding, queueName);
1004
+ break;
1005
+ }
1006
+ case "ai": {
1007
+ const [binding] = rest;
1008
+ if (!binding) {
1009
+ console.error(` \u2717 Usage: pnpm wilt add ai <BINDING_NAME>`);
1010
+ process.exit(1);
1011
+ }
1012
+ console.log(`
1013
+ Adding AI binding to wrangler.jsonc...
1014
+ `);
1015
+ addAi(binding);
1016
+ break;
1017
+ }
1018
+ case "durable-object":
1019
+ case "do": {
1020
+ const [binding, className] = rest;
1021
+ if (!binding || !className) {
1022
+ console.error(` \u2717 Usage: pnpm wilt add durable-object <BINDING_NAME> <ClassName>`);
1023
+ process.exit(1);
1024
+ }
1025
+ console.log(`
1026
+ Adding Durable Object binding to wrangler.jsonc...
1027
+ `);
1028
+ addDurableObject(binding, className);
1029
+ break;
1030
+ }
1031
+ case "vectorize":
1032
+ case "vector": {
1033
+ const [binding, indexName, rawDimensions] = rest;
1034
+ if (!binding || !indexName) {
1035
+ console.error(` \u2717 Usage: pnpm wilt add vectorize <BINDING_NAME> <index-name> [dimensions]`);
1036
+ process.exit(1);
1037
+ }
1038
+ const dimensions = rawDimensions ? parseInt(rawDimensions, 10) : 1536;
1039
+ console.log(`
1040
+ Adding Vectorize binding to wrangler.jsonc...
1041
+ `);
1042
+ addVectorize(binding, indexName, dimensions);
1043
+ break;
1044
+ }
1045
+ case "browser": {
1046
+ const [binding] = rest;
1047
+ if (!binding) {
1048
+ console.error(` \u2717 Usage: pnpm wilt add browser <BINDING_NAME>`);
1049
+ process.exit(1);
1050
+ }
1051
+ console.log(`
1052
+ Adding Browser rendering binding to wrangler.jsonc...
1053
+ `);
1054
+ addBrowser(binding);
1055
+ break;
1056
+ }
1057
+ case "hyperdrive": {
1058
+ const [binding, connectionString] = rest;
1059
+ if (!binding || !connectionString) {
1060
+ console.error(` \u2717 Usage: pnpm wilt add hyperdrive <BINDING_NAME> <connection-string>`);
1061
+ process.exit(1);
1062
+ }
1063
+ console.log(`
1064
+ Adding Hyperdrive binding to wrangler.jsonc...
1065
+ `);
1066
+ addHyperdrive(binding, connectionString);
1067
+ break;
1068
+ }
1069
+ default: {
1070
+ console.error(` \u2717 Unknown service type: "${service}"
1071
+
1072
+ ${USAGE2}`);
1073
+ process.exit(1);
1074
+ }
1075
+ }
1076
+ }
1077
+
1078
+ // bin/wilt.ts
1079
+ var BANNER = `
1080
+ \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
1081
+ \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D
1082
+ \u2588\u2588\u2551 \u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
1083
+ \u2588\u2588\u2551\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
1084
+ \u255A\u2588\u2588\u2588\u2554\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551
1085
+ \u255A\u2550\u2550\u255D\u255A\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D
1086
+ Wilt CLI \u2014 Cloudflare Workers Framework
1087
+ `.trim();
1088
+ var HELP = `
1089
+ ${BANNER}
1090
+
1091
+ Usage:
1092
+ pnpm wilt <command> [subcommand] [args...]
1093
+
1094
+ Commands:
1095
+ new Create a new Wilt project
1096
+ generate (g) Scaffold module, service, or controller files
1097
+ add Add a Cloudflare service binding to wrangler.jsonc
1098
+ help Show this help message
1099
+
1100
+ Generate subcommands:
1101
+ module (m) Scaffold a full module (module + controller + service + dto)
1102
+ service (s) Scaffold a service file
1103
+ controller (c) Scaffold a controller file
1104
+
1105
+ Add subcommands:
1106
+ d1 D1 database binding
1107
+ r2 R2 bucket binding
1108
+ kv KV namespace binding
1109
+ queue Queue producer + consumer
1110
+ ai Workers AI binding
1111
+ durable-object Durable Object binding + migration
1112
+ vectorize Vectorize index binding
1113
+ browser Browser rendering binding
1114
+ hyperdrive Hyperdrive binding
1115
+
1116
+ Examples:
1117
+ pnpm wilt new my-app
1118
+ pnpm wilt generate module payment
1119
+ pnpm wilt g m payment
1120
+ pnpm wilt add d1 PAYMENTS_DB payments-db
1121
+ pnpm wilt add r2 ASSETS_BUCKET my-assets
1122
+ pnpm wilt add kv SESSION_KV
1123
+ pnpm wilt add queue MAIL_QUEUE mailer
1124
+ pnpm wilt add ai AI
1125
+ pnpm wilt add durable-object CHAT_ROOM ChatRoom
1126
+ `.trim();
1127
+ async function main() {
1128
+ const args = process.argv.slice(2);
1129
+ const [command, ...rest] = args;
1130
+ if (!command || command === "help" || command === "--help" || command === "-h") {
1131
+ console.log(`
1132
+ ${HELP}
1133
+ `);
1134
+ process.exit(0);
1135
+ }
1136
+ const cmd = command.toLowerCase();
1137
+ if (cmd === "new" || cmd === "n") {
1138
+ runNew(rest);
1139
+ } else if (cmd === "generate" || cmd === "g") {
1140
+ await runGenerate(rest);
1141
+ } else if (cmd === "add") {
1142
+ runAdd(rest);
1143
+ } else {
1144
+ console.error(` \u2717 Unknown command: "${command}"
1145
+ `);
1146
+ console.log(HELP);
1147
+ process.exit(1);
1148
+ }
1149
+ }
1150
+ main();
1151
+ //# sourceMappingURL=wilt.js.map