@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,457 @@
1
+ import { mkdirSync, writeFileSync, existsSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { execSync } from "node:child_process";
4
+
5
+ function pascal(name: string) {
6
+ return name
7
+ .replace(/[-_](.)/g, (_, c) => c.toUpperCase())
8
+ .replace(/^(.)/, (_, c) => c.toUpperCase());
9
+ }
10
+
11
+ function kebab(name: string) {
12
+ return name
13
+ .replace(/([A-Z])/g, "-$1")
14
+ .replace(/^-/, "")
15
+ .replace(/_/g, "-")
16
+ .toLowerCase();
17
+ }
18
+
19
+ // ─── File templates ───────────────────────────────────────────────────────────
20
+
21
+ function packageJsonTemplate(name: string) {
22
+ return JSON.stringify(
23
+ {
24
+ name: kebab(name),
25
+ version: "0.0.1",
26
+ private: true,
27
+ type: "module",
28
+ scripts: {
29
+ dev: "vite dev --host --port 4001",
30
+ "dev:cf": "wrangler dev --port 4001",
31
+ build: "vite build",
32
+ deploy: "vite build && wrangler deploy",
33
+ "cf-typegen": "wrangler types --env-interface CloudflareBindings",
34
+ test: "vitest --run",
35
+ "test:watch": "vitest --watch",
36
+ "db:gen": "drizzle-kit generate",
37
+ "db:migrate": "wrangler d1 migrations apply DB --local",
38
+ "db:migrate:remote": "wrangler d1 migrations apply DB --remote",
39
+ check: "tsc --noEmit",
40
+ wilt: "wilt",
41
+ },
42
+ dependencies: {
43
+ hono: "^4.12.0",
44
+ wilt: "^0.1.0",
45
+ "reflect-metadata": "^0.2.2",
46
+ tsyringe: "^4.10.0",
47
+ zod: "^4.0.0",
48
+ "drizzle-orm": "^0.45.0",
49
+ ulid: "^3.0.0",
50
+ },
51
+ devDependencies: {
52
+ "@cloudflare/vite-plugin": "^1.36.0",
53
+ "@cloudflare/workers-types": "^4.0.0",
54
+ "@cloudflare/vitest-pool-workers": "^0.16.0",
55
+ "@sohan_fahad/wilt": "^0.1.0",
56
+ "@types/node": "^22.0.0",
57
+ "drizzle-kit": "^0.31.0",
58
+ typescript: "^5.0.0",
59
+ vite: "^6.0.0",
60
+ vitest: "^4.1.0",
61
+ tsx: "^4.0.0",
62
+ wrangler: "^4.0.0",
63
+ },
64
+ },
65
+ null,
66
+ 2
67
+ );
68
+ }
69
+
70
+ function wranglerTemplate(name: string) {
71
+ return `{
72
+ "$schema": "node_modules/wrangler/config-schema.json",
73
+ "name": "${kebab(name)}",
74
+ "main": "./src/index.ts",
75
+ "compatibility_date": "2025-06-17",
76
+ "compatibility_flags": ["nodejs_compat"],
77
+ "vars": {
78
+ "MODE": "development"
79
+ },
80
+ "observability": { "enabled": true },
81
+ "d1_databases": [
82
+ {
83
+ "binding": "DB",
84
+ "database_name": "REPLACE_WITH_YOUR_D1_NAME",
85
+ "database_id": "00000000-0000-0000-0000-000000000001",
86
+ "migrations_dir": "migrations",
87
+ "preview_database_id": "00000000-0000-0000-0000-000000000002"
88
+ }
89
+ ],
90
+ "dev": { "port": 4001 }
91
+ }
92
+ `;
93
+ }
94
+
95
+ function tsconfigTemplate() {
96
+ return `{
97
+ "compilerOptions": {
98
+ "target": "ESNext",
99
+ "module": "ESNext",
100
+ "moduleResolution": "Bundler",
101
+ "strict": true,
102
+ "skipLibCheck": true,
103
+ "experimentalDecorators": true,
104
+ "emitDecoratorMetadata": true,
105
+ "lib": ["ESNext", "DOM"],
106
+ "types": ["vite/client", "@cloudflare/workers-types"],
107
+ "baseUrl": ".",
108
+ "paths": {
109
+ "@app/*": ["src/*"]
110
+ }
111
+ },
112
+ "include": ["src/**/*", "worker-configuration.d.ts"],
113
+ "exclude": ["node_modules", "dist"]
114
+ }
115
+ `;
116
+ }
117
+
118
+ function viteConfigTemplate() {
119
+ return `import { defineConfig } from "vite";
120
+ import { cloudflare } from "@cloudflare/vite-plugin";
121
+ import { fileURLToPath, URL } from "node:url";
122
+
123
+ export default defineConfig({
124
+ plugins: [cloudflare()],
125
+ resolve: {
126
+ alias: {
127
+ "@app": fileURLToPath(new URL("./src", import.meta.url)),
128
+ },
129
+ },
130
+ });
131
+ `;
132
+ }
133
+
134
+ function workerTypesTemplate() {
135
+ return `// Generated by \`pnpm cf-typegen\`. Do not edit manually.
136
+ /// <reference types="@cloudflare/vitest-pool-workers/types" />
137
+ interface CloudflareBindings {
138
+ DB: D1Database;
139
+ MODE: string;
140
+ }
141
+
142
+ declare namespace Cloudflare {
143
+ interface Env extends CloudflareBindings {
144
+ TEST_MIGRATIONS: string;
145
+ }
146
+ }
147
+ `;
148
+ }
149
+
150
+ function vitestConfigTemplate() {
151
+ return `import path from "path";
152
+ import { defineConfig } from "vitest/config";
153
+ import { cloudflarePool, cloudflareTest, readD1Migrations } from "@cloudflare/vitest-pool-workers";
154
+
155
+ export default defineConfig(async () => {
156
+ const migrations = await readD1Migrations("./migrations");
157
+ const poolOptions = {
158
+ wrangler: { configPath: "./wrangler.jsonc" },
159
+ miniflare: {
160
+ bindings: { TEST_MIGRATIONS: JSON.stringify(migrations) },
161
+ },
162
+ };
163
+ return {
164
+ plugins: [cloudflareTest(poolOptions)],
165
+ resolve: {
166
+ alias: { "@app": path.resolve("./src") },
167
+ },
168
+ test: {
169
+ pool: cloudflarePool(poolOptions),
170
+ },
171
+ };
172
+ });
173
+ `;
174
+ }
175
+
176
+ function indexTemplate() {
177
+ return `import "reflect-metadata";
178
+ import { app } from "./app.module";
179
+
180
+ export default {
181
+ async fetch(request: Request, env: CloudflareBindings, ctx: ExecutionContext): Promise<Response> {
182
+ return app.fetch(request, env, ctx);
183
+ },
184
+ };
185
+ `;
186
+ }
187
+
188
+ function appModuleTemplate(name: string) {
189
+ const p = pascal(name);
190
+ return `import { cors } from "hono/cors";
191
+ import { secureHeaders } from "hono/secure-headers";
192
+
193
+ import { Module, createModule } from "wilt";
194
+ import { requestLogger } from "wilt/middleware";
195
+ import { HelloModule } from "./modules/app/hello/hello.module";
196
+
197
+ @Module({
198
+ imports: [HelloModule],
199
+ })
200
+ export class AppModule {}
201
+
202
+ const app = createModule(AppModule, {
203
+ middlewares: [cors(), secureHeaders(), requestLogger],
204
+ });
205
+
206
+ app.get("/", (c) => c.json({ success: true, status: "healthy", timestamp: new Date().toISOString() }));
207
+ app.get("/health", (c) => c.json({ success: true, status: "healthy", timestamp: new Date().toISOString() }));
208
+
209
+ export { app };
210
+ `;
211
+ }
212
+
213
+ function helloModuleTemplate() {
214
+ return `import { Module } from "wilt";
215
+ import { HelloController } from "./hello.controller";
216
+ import { HelloService } from "./hello.service";
217
+
218
+ @Module({
219
+ controllers: [HelloController],
220
+ providers: [HelloService],
221
+ })
222
+ export class HelloModule {}
223
+ `;
224
+ }
225
+
226
+ function helloControllerTemplate() {
227
+ return `import type { Context } from "hono";
228
+ import { Controller, Get, Inject } from "wilt";
229
+ import { ResponseUtil } from "wilt";
230
+ import { HelloService } from "./hello.service";
231
+
232
+ @Controller("/hello")
233
+ export class HelloController {
234
+ constructor(@Inject(HelloService) private helloService: HelloService) {}
235
+
236
+ @Get()
237
+ async greet(c: Context) {
238
+ const message = this.helloService.greet();
239
+ return ResponseUtil.success(c, { message });
240
+ }
241
+ }
242
+ `;
243
+ }
244
+
245
+ function helloServiceTemplate() {
246
+ return `import { Injectable } from "wilt";
247
+
248
+ @Injectable()
249
+ export class HelloService {
250
+ greet() {
251
+ return "Hello from Wilt!";
252
+ }
253
+ }
254
+ `;
255
+ }
256
+
257
+ function dbSchemaTemplate() {
258
+ return `// Register all your entity tables here so Drizzle can build relations and
259
+ // drizzle-kit can generate migrations from a single source of truth.
260
+ export const schema = {};
261
+ `;
262
+ }
263
+
264
+ function dbConnectionTemplate() {
265
+ return `import { drizzle } from "drizzle-orm/d1";
266
+ import type { Context } from "hono";
267
+ import { schema } from "./schema";
268
+ export function getDb(c: Context) {
269
+ return drizzle(c.env.DB, { schema: schema });
270
+ }
271
+ `;
272
+ }
273
+
274
+ function gitignoreTemplate() {
275
+ return `dist/
276
+ node_modules/
277
+ .wrangler
278
+ .env
279
+ .env.production
280
+ .dev.vars
281
+ .DS_Store
282
+ coverage/
283
+ *.log
284
+ `;
285
+ }
286
+
287
+
288
+ function wiltConfigTemplate() {
289
+ return `import { defineConfig } from "wilt/config";
290
+
291
+ export default defineConfig({
292
+ // Directory where generated modules are placed (relative to project root)
293
+ modulesDir: "src/modules/app",
294
+
295
+ // Default files scaffolded by \`wilt generate module <name>\`
296
+ // Remove "test" to skip test files, or pass --no-test at the CLI
297
+ generate: {
298
+ files: ["module", "controller", "service", "dto", "entity", "test"],
299
+ },
300
+ });
301
+ `;
302
+ }
303
+
304
+ function envExampleTemplate() {
305
+ return `CLOUDFLARE_ACCOUNT_ID=
306
+ CLOUDFLARE_TOKEN=
307
+
308
+ CLOUDFLARE_DATABASE_ID=
309
+ `;
310
+ }
311
+
312
+ function drizzleConfigTemplate() {
313
+ return `import { defineConfig } from "drizzle-kit";
314
+
315
+ export default defineConfig({
316
+ schema: "./src/database/schema.ts",
317
+ out: "./migrations",
318
+ dialect: "sqlite",
319
+ driver: "d1-http",
320
+ dbCredentials: {
321
+ accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
322
+ databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
323
+ token: process.env.CLOUDFLARE_TOKEN!,
324
+ },
325
+ verbose: true,
326
+ strict: true,
327
+ });
328
+ `;
329
+ }
330
+
331
+ function migrationTemplate() {
332
+ return `-- Initial migration
333
+ -- Add your tables here
334
+ `;
335
+ }
336
+
337
+ function migrationMetaTemplate(name: string) {
338
+ return JSON.stringify(
339
+ {
340
+ version: "5",
341
+ dialect: "sqlite",
342
+ entries: [
343
+ {
344
+ idx: 0,
345
+ version: "5",
346
+ when: Date.now(),
347
+ tag: "0000_initial",
348
+ breakpoints: true,
349
+ },
350
+ ],
351
+ },
352
+ null,
353
+ 2
354
+ );
355
+ }
356
+
357
+ // ─── Scaffold ─────────────────────────────────────────────────────────────────
358
+
359
+ function write(path: string, content: string) {
360
+ writeFileSync(path, content, "utf-8");
361
+ console.log(` ✔ ${path.replace(process.cwd() + "/", "")}`);
362
+ }
363
+
364
+ function dir(path: string) {
365
+ mkdirSync(path, { recursive: true });
366
+ }
367
+
368
+ export function runNew(args: string[]): void {
369
+ const [name] = args;
370
+
371
+ if (!name) {
372
+ console.error(" ✗ Usage: wilt new <project-name>");
373
+ process.exit(1);
374
+ }
375
+
376
+ const projectDir = resolve(process.cwd(), name);
377
+
378
+ if (existsSync(projectDir)) {
379
+ console.error(` ✗ Directory already exists: ${projectDir}`);
380
+ process.exit(1);
381
+ }
382
+
383
+ console.log(`\n Creating Wilt app "${name}"...\n`);
384
+
385
+ // Directories
386
+ dir(join(projectDir, "src/modules/app/hello"));
387
+ dir(join(projectDir, "src/database"));
388
+ dir(join(projectDir, "migrations/meta"));
389
+
390
+ // Config files
391
+ write(join(projectDir, "package.json"), packageJsonTemplate(name));
392
+ write(join(projectDir, "wrangler.jsonc"), wranglerTemplate(name));
393
+ write(join(projectDir, "tsconfig.json"), tsconfigTemplate());
394
+ write(join(projectDir, "vite.config.ts"), viteConfigTemplate());
395
+ write(join(projectDir, "vitest.config.ts"), vitestConfigTemplate());
396
+ write(join(projectDir, "worker-configuration.d.ts"), workerTypesTemplate());
397
+ write(join(projectDir, "wilt.config.ts"), wiltConfigTemplate());
398
+ write(join(projectDir, ".gitignore"), gitignoreTemplate());
399
+ write(join(projectDir, ".env.example"), envExampleTemplate());
400
+ write(join(projectDir, "drizzle.config.ts"), drizzleConfigTemplate());
401
+
402
+ // App entry
403
+ write(join(projectDir, "src/index.ts"), indexTemplate());
404
+ write(join(projectDir, "src/app.module.ts"), appModuleTemplate(name));
405
+
406
+ // Hello module
407
+ write(join(projectDir, "src/modules/app/hello/hello.module.ts"), helloModuleTemplate());
408
+ write(join(projectDir, "src/modules/app/hello/hello.controller.ts"), helloControllerTemplate());
409
+ write(join(projectDir, "src/modules/app/hello/hello.service.ts"), helloServiceTemplate());
410
+
411
+ // Database
412
+ write(join(projectDir, "src/database/schema.ts"), dbSchemaTemplate());
413
+ write(join(projectDir, "src/database/connection.ts"), dbConnectionTemplate());
414
+
415
+ // Migrations
416
+ write(join(projectDir, "migrations/0000_initial.sql"), migrationTemplate());
417
+ write(join(projectDir, "migrations/meta/_journal.json"), migrationMetaTemplate(name));
418
+
419
+ // Try to install dependencies
420
+ console.log("\n Installing dependencies...\n");
421
+ try {
422
+ const pm = detectPackageManager();
423
+ execSync(`${pm} install`, { cwd: projectDir, stdio: "inherit" });
424
+ } catch {
425
+ console.log(" ⚠ Could not auto-install. Run `pnpm install` manually.\n");
426
+ }
427
+
428
+ console.log(`
429
+ ✔ Project created!
430
+
431
+ Next steps:
432
+ cd ${name}
433
+ cp .env.example .env # fill in Cloudflare credentials
434
+ # Edit wrangler.jsonc: set database_name + database_id
435
+ pnpm db:migrate # apply local D1 migrations
436
+ pnpm dev # http://localhost:4001
437
+
438
+ Try it:
439
+ curl http://localhost:4001/health
440
+ curl http://localhost:4001/hello
441
+
442
+ Generate a new module:
443
+ pnpm wilt generate module posts
444
+ `);
445
+ }
446
+
447
+ function detectPackageManager(): string {
448
+ try {
449
+ execSync("pnpm --version", { stdio: "ignore" });
450
+ return "pnpm";
451
+ } catch { }
452
+ try {
453
+ execSync("bun --version", { stdio: "ignore" });
454
+ return "bun";
455
+ } catch { }
456
+ return "npm";
457
+ }
@@ -0,0 +1,3 @@
1
+ import { runNew } from "./commands/new.js";
2
+
3
+ runNew(process.argv.slice(2));