@gravito/scaffold 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,3269 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BaseGenerator: () => BaseGenerator,
34
+ CleanArchitectureGenerator: () => CleanArchitectureGenerator,
35
+ DddGenerator: () => DddGenerator,
36
+ EnterpriseMvcGenerator: () => EnterpriseMvcGenerator,
37
+ Scaffold: () => Scaffold,
38
+ StubGenerator: () => StubGenerator
39
+ });
40
+ module.exports = __toCommonJS(index_exports);
41
+
42
+ // src/generators/BaseGenerator.ts
43
+ var import_promises2 = __toESM(require("fs/promises"), 1);
44
+ var import_node_path2 = __toESM(require("path"), 1);
45
+
46
+ // src/generators/StubGenerator.ts
47
+ var import_promises = __toESM(require("fs/promises"), 1);
48
+ var import_node_path = __toESM(require("path"), 1);
49
+ var import_handlebars = __toESM(require("handlebars"), 1);
50
+ var StubGenerator = class {
51
+ config;
52
+ handlebars;
53
+ constructor(config) {
54
+ this.config = config;
55
+ this.handlebars = import_handlebars.default.create();
56
+ this.registerBuiltinHelpers();
57
+ if (config.helpers) {
58
+ for (const [name, helper] of Object.entries(config.helpers)) {
59
+ this.handlebars.registerHelper(name, helper);
60
+ }
61
+ }
62
+ }
63
+ /**
64
+ * Register built-in Handlebars helpers.
65
+ */
66
+ registerBuiltinHelpers() {
67
+ this.handlebars.registerHelper("capitalize", (str) => {
68
+ if (!str) {
69
+ return "";
70
+ }
71
+ return str.charAt(0).toUpperCase() + str.slice(1);
72
+ });
73
+ this.handlebars.registerHelper("lowercase", (str) => {
74
+ return str?.toLowerCase() ?? "";
75
+ });
76
+ this.handlebars.registerHelper("uppercase", (str) => {
77
+ return str?.toUpperCase() ?? "";
78
+ });
79
+ this.handlebars.registerHelper("camelCase", (str) => {
80
+ if (!str) {
81
+ return "";
82
+ }
83
+ return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toLowerCase());
84
+ });
85
+ this.handlebars.registerHelper("pascalCase", (str) => {
86
+ if (!str) {
87
+ return "";
88
+ }
89
+ return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toUpperCase());
90
+ });
91
+ this.handlebars.registerHelper("snakeCase", (str) => {
92
+ if (!str) {
93
+ return "";
94
+ }
95
+ return str.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[-\s]+/g, "_");
96
+ });
97
+ this.handlebars.registerHelper("kebabCase", (str) => {
98
+ if (!str) {
99
+ return "";
100
+ }
101
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "").replace(/[_\s]+/g, "-");
102
+ });
103
+ this.handlebars.registerHelper("pluralize", (str) => {
104
+ if (!str) {
105
+ return "";
106
+ }
107
+ if (str.endsWith("y")) {
108
+ return `${str.slice(0, -1)}ies`;
109
+ }
110
+ if (str.endsWith("s") || str.endsWith("x") || str.endsWith("ch") || str.endsWith("sh")) {
111
+ return `${str}es`;
112
+ }
113
+ return `${str}s`;
114
+ });
115
+ this.handlebars.registerHelper("date", (format) => {
116
+ const now = /* @__PURE__ */ new Date();
117
+ if (format === "iso") {
118
+ return now.toISOString();
119
+ }
120
+ if (format === "year") {
121
+ return now.getFullYear().toString();
122
+ }
123
+ return now.toISOString().split("T")[0];
124
+ });
125
+ this.handlebars.registerHelper("eq", (a, b) => a === b);
126
+ this.handlebars.registerHelper("neq", (a, b) => a !== b);
127
+ }
128
+ /**
129
+ * Generate a file from a stub template.
130
+ *
131
+ * @param stubName - Name of the stub file (relative to stubsDir)
132
+ * @param outputPath - Output path (relative to outputDir)
133
+ * @param variables - Template variables
134
+ * @returns Path to the generated file
135
+ */
136
+ async generate(stubName, outputPath, variables = {}) {
137
+ const stubPath = import_node_path.default.resolve(this.config.stubsDir, stubName);
138
+ const template = await import_promises.default.readFile(stubPath, "utf-8");
139
+ const compiled = this.handlebars.compile(template);
140
+ const content = compiled({
141
+ ...this.config.defaultVariables,
142
+ ...variables
143
+ });
144
+ const fullOutputPath = import_node_path.default.resolve(this.config.outputDir, outputPath);
145
+ await import_promises.default.mkdir(import_node_path.default.dirname(fullOutputPath), { recursive: true });
146
+ await import_promises.default.writeFile(fullOutputPath, content, "utf-8");
147
+ return fullOutputPath;
148
+ }
149
+ /**
150
+ * Generate multiple files from a stub template.
151
+ *
152
+ * @param stubName - Name of the stub file
153
+ * @param outputs - Array of [outputPath, variables] tuples
154
+ * @returns Array of generated file paths
155
+ */
156
+ async generateMany(stubName, outputs) {
157
+ const results = [];
158
+ for (const [outputPath, variables] of outputs) {
159
+ const result = await this.generate(stubName, outputPath, variables);
160
+ results.push(result);
161
+ }
162
+ return results;
163
+ }
164
+ /**
165
+ * Render a template string directly.
166
+ *
167
+ * @param template - Template string
168
+ * @param variables - Template variables
169
+ * @returns Rendered content
170
+ */
171
+ render(template, variables = {}) {
172
+ const compiled = this.handlebars.compile(template);
173
+ return compiled({
174
+ ...this.config.defaultVariables,
175
+ ...variables
176
+ });
177
+ }
178
+ /**
179
+ * Register a custom Handlebars helper.
180
+ *
181
+ * @param name - Helper name
182
+ * @param helper - Helper function
183
+ */
184
+ registerHelper(name, helper) {
185
+ this.handlebars.registerHelper(name, helper);
186
+ }
187
+ /**
188
+ * Register a Handlebars partial.
189
+ *
190
+ * @param name - Partial name
191
+ * @param partial - Partial template string
192
+ */
193
+ registerPartial(name, partial) {
194
+ this.handlebars.registerPartial(name, partial);
195
+ }
196
+ };
197
+
198
+ // src/generators/BaseGenerator.ts
199
+ var BaseGenerator = class {
200
+ config;
201
+ stubGenerator;
202
+ filesCreated = [];
203
+ constructor(config) {
204
+ this.config = config;
205
+ this.stubGenerator = new StubGenerator({
206
+ stubsDir: config.templatesDir,
207
+ outputDir: ""
208
+ // Set per-generation
209
+ });
210
+ }
211
+ /**
212
+ * Generate the project scaffold.
213
+ *
214
+ * @param context - Generator context
215
+ * @returns Array of created file paths
216
+ */
217
+ async generate(context) {
218
+ this.filesCreated = [];
219
+ const structure = this.getDirectoryStructure(context);
220
+ await this.createStructure(context.targetDir, structure, context);
221
+ await this.generateCommonFiles(context);
222
+ return this.filesCreated;
223
+ }
224
+ /**
225
+ * Create directory structure recursively.
226
+ */
227
+ async createStructure(basePath, nodes, context) {
228
+ for (const node of nodes) {
229
+ const fullPath = import_node_path2.default.resolve(basePath, node.name);
230
+ if (node.type === "directory") {
231
+ await import_promises2.default.mkdir(fullPath, { recursive: true });
232
+ this.log(`\u{1F4C1} Created directory: ${node.name}`);
233
+ if (node.children) {
234
+ await this.createStructure(fullPath, node.children, context);
235
+ }
236
+ } else {
237
+ await import_promises2.default.mkdir(import_node_path2.default.dirname(fullPath), { recursive: true });
238
+ if (node.template) {
239
+ const templatePath = import_node_path2.default.resolve(this.config.templatesDir, node.template);
240
+ try {
241
+ const template = await import_promises2.default.readFile(templatePath, "utf-8");
242
+ const content = this.stubGenerator.render(template, context);
243
+ await import_promises2.default.writeFile(fullPath, content, "utf-8");
244
+ } catch {
245
+ await import_promises2.default.writeFile(fullPath, node.content ?? "", "utf-8");
246
+ }
247
+ } else if (node.content) {
248
+ await import_promises2.default.writeFile(fullPath, node.content, "utf-8");
249
+ } else {
250
+ await import_promises2.default.writeFile(fullPath, "", "utf-8");
251
+ }
252
+ this.filesCreated.push(fullPath);
253
+ this.log(`\u{1F4C4} Created file: ${node.name}`);
254
+ }
255
+ }
256
+ }
257
+ /**
258
+ * Generate common files (package.json, .env, etc.)
259
+ */
260
+ async generateCommonFiles(context) {
261
+ await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
262
+ await this.writeFile(context.targetDir, ".env.example", this.generateEnvExample(context));
263
+ await this.writeFile(context.targetDir, ".env", this.generateEnvExample(context));
264
+ await this.writeFile(context.targetDir, ".gitignore", this.generateGitignore());
265
+ await this.writeFile(context.targetDir, "tsconfig.json", this.generateTsConfig());
266
+ await this.writeFile(
267
+ context.targetDir,
268
+ "ARCHITECTURE.md",
269
+ this.generateArchitectureDoc(context)
270
+ );
271
+ }
272
+ /**
273
+ * Write a file and track it.
274
+ */
275
+ async writeFile(basePath, relativePath, content) {
276
+ const fullPath = import_node_path2.default.resolve(basePath, relativePath);
277
+ await import_promises2.default.mkdir(import_node_path2.default.dirname(fullPath), { recursive: true });
278
+ await import_promises2.default.writeFile(fullPath, content, "utf-8");
279
+ this.filesCreated.push(fullPath);
280
+ this.log(`\u{1F4C4} Created file: ${relativePath}`);
281
+ }
282
+ /**
283
+ * Generate package.json content.
284
+ */
285
+ generatePackageJson(context) {
286
+ const pkg = {
287
+ name: context.nameKebabCase,
288
+ version: "0.1.0",
289
+ type: "module",
290
+ scripts: {
291
+ dev: "bun run --watch src/bootstrap.ts",
292
+ build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
293
+ start: "bun run dist/bootstrap.js",
294
+ test: "bun test",
295
+ typecheck: "tsc --noEmit"
296
+ },
297
+ dependencies: {
298
+ "gravito-core": "^1.0.0-beta.5"
299
+ },
300
+ devDependencies: {
301
+ "@types/bun": "latest",
302
+ typescript: "^5.0.0"
303
+ }
304
+ };
305
+ return JSON.stringify(pkg, null, 2);
306
+ }
307
+ /**
308
+ * Generate .env.example content.
309
+ */
310
+ generateEnvExample(context) {
311
+ return `# Application
312
+ APP_NAME="${context.name}"
313
+ APP_ENV=development
314
+ APP_KEY=
315
+ APP_DEBUG=true
316
+ APP_URL=http://localhost:3000
317
+
318
+ # Server
319
+ PORT=3000
320
+
321
+ # Database
322
+ DB_CONNECTION=sqlite
323
+ DB_DATABASE=database/database.sqlite
324
+
325
+ # Cache
326
+ CACHE_DRIVER=memory
327
+
328
+ # Logging
329
+ LOG_LEVEL=debug
330
+ `;
331
+ }
332
+ /**
333
+ * Generate .gitignore content.
334
+ */
335
+ generateGitignore() {
336
+ return `# Dependencies
337
+ node_modules/
338
+
339
+ # Build output
340
+ dist/
341
+
342
+ # Environment
343
+ .env
344
+ .env.local
345
+ .env.*.local
346
+
347
+ # IDE
348
+ .idea/
349
+ .vscode/
350
+ *.swp
351
+ *.swo
352
+
353
+ # System
354
+ .DS_Store
355
+ Thumbs.db
356
+
357
+ # Logs
358
+ *.log
359
+ logs/
360
+
361
+ # Database
362
+ *.sqlite
363
+ *.sqlite-journal
364
+
365
+ # Coverage
366
+ coverage/
367
+ `;
368
+ }
369
+ /**
370
+ * Generate tsconfig.json content.
371
+ */
372
+ generateTsConfig() {
373
+ const config = {
374
+ compilerOptions: {
375
+ target: "ESNext",
376
+ module: "ESNext",
377
+ moduleResolution: "bundler",
378
+ esModuleInterop: true,
379
+ strict: true,
380
+ skipLibCheck: true,
381
+ declaration: true,
382
+ outDir: "./dist",
383
+ rootDir: "./src",
384
+ baseUrl: ".",
385
+ paths: {
386
+ "@/*": ["./src/*"]
387
+ }
388
+ },
389
+ include: ["src/**/*"],
390
+ exclude: ["node_modules", "dist"]
391
+ };
392
+ return JSON.stringify(config, null, 2);
393
+ }
394
+ /**
395
+ * Log a message if verbose mode is enabled.
396
+ */
397
+ log(message) {
398
+ if (this.config.verbose) {
399
+ console.log(message);
400
+ }
401
+ }
402
+ /**
403
+ * Create generator context from options.
404
+ */
405
+ static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
406
+ const now = /* @__PURE__ */ new Date();
407
+ const pascalCase = name.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toUpperCase());
408
+ const camelCase = pascalCase.replace(/^./, (c) => c.toLowerCase());
409
+ const snakeCase = name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[-\s]+/g, "_");
410
+ const kebabCase = name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "").replace(/[_\s]+/g, "-");
411
+ return {
412
+ name,
413
+ namePascalCase: pascalCase,
414
+ nameCamelCase: camelCase,
415
+ nameSnakeCase: snakeCase,
416
+ nameKebabCase: kebabCase,
417
+ targetDir,
418
+ architecture,
419
+ packageManager,
420
+ year: now.getFullYear().toString(),
421
+ date: now.toISOString().split("T")[0] ?? now.toISOString().slice(0, 10),
422
+ ...extra
423
+ };
424
+ }
425
+ };
426
+
427
+ // src/generators/CleanArchitectureGenerator.ts
428
+ var CleanArchitectureGenerator = class extends BaseGenerator {
429
+ get architectureType() {
430
+ return "clean";
431
+ }
432
+ get displayName() {
433
+ return "Clean Architecture";
434
+ }
435
+ get description() {
436
+ return "Uncle Bob's Clean Architecture with strict dependency rules and pure domain layer";
437
+ }
438
+ getDirectoryStructure(context) {
439
+ return [
440
+ {
441
+ type: "directory",
442
+ name: "config",
443
+ children: [
444
+ { type: "file", name: "app.ts", content: this.generateAppConfig(context) },
445
+ { type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
446
+ { type: "file", name: "auth.ts", content: this.generateAuthConfig() },
447
+ { type: "file", name: "cache.ts", content: this.generateCacheConfig() },
448
+ { type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
449
+ ]
450
+ },
451
+ {
452
+ type: "directory",
453
+ name: "src",
454
+ children: [
455
+ // Domain Layer (innermost - no dependencies)
456
+ {
457
+ type: "directory",
458
+ name: "Domain",
459
+ children: [
460
+ {
461
+ type: "directory",
462
+ name: "Entities",
463
+ children: [
464
+ { type: "file", name: "Entity.ts", content: this.generateBaseEntity() },
465
+ { type: "file", name: "User.ts", content: this.generateUserEntity() }
466
+ ]
467
+ },
468
+ {
469
+ type: "directory",
470
+ name: "ValueObjects",
471
+ children: [
472
+ { type: "file", name: "ValueObject.ts", content: this.generateBaseValueObject() },
473
+ { type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
474
+ ]
475
+ },
476
+ {
477
+ type: "directory",
478
+ name: "Interfaces",
479
+ children: [
480
+ {
481
+ type: "file",
482
+ name: "IUserRepository.ts",
483
+ content: this.generateUserRepositoryInterface()
484
+ }
485
+ ]
486
+ },
487
+ {
488
+ type: "directory",
489
+ name: "Exceptions",
490
+ children: [
491
+ {
492
+ type: "file",
493
+ name: "DomainException.ts",
494
+ content: this.generateDomainException()
495
+ }
496
+ ]
497
+ }
498
+ ]
499
+ },
500
+ // Application Layer (use cases)
501
+ {
502
+ type: "directory",
503
+ name: "Application",
504
+ children: [
505
+ {
506
+ type: "directory",
507
+ name: "UseCases",
508
+ children: [
509
+ {
510
+ type: "directory",
511
+ name: "User",
512
+ children: [
513
+ {
514
+ type: "file",
515
+ name: "CreateUser.ts",
516
+ content: this.generateCreateUserUseCase()
517
+ },
518
+ { type: "file", name: "GetUser.ts", content: this.generateGetUserUseCase() }
519
+ ]
520
+ }
521
+ ]
522
+ },
523
+ {
524
+ type: "directory",
525
+ name: "DTOs",
526
+ children: [{ type: "file", name: "UserDTO.ts", content: this.generateUserDTO() }]
527
+ },
528
+ {
529
+ type: "directory",
530
+ name: "Interfaces",
531
+ children: [
532
+ {
533
+ type: "file",
534
+ name: "IMailService.ts",
535
+ content: this.generateMailServiceInterface()
536
+ }
537
+ ]
538
+ }
539
+ ]
540
+ },
541
+ // Infrastructure Layer (external implementations)
542
+ {
543
+ type: "directory",
544
+ name: "Infrastructure",
545
+ children: [
546
+ {
547
+ type: "directory",
548
+ name: "Persistence",
549
+ children: [
550
+ {
551
+ type: "directory",
552
+ name: "Repositories",
553
+ children: [
554
+ {
555
+ type: "file",
556
+ name: "UserRepository.ts",
557
+ content: this.generateUserRepository()
558
+ }
559
+ ]
560
+ },
561
+ {
562
+ type: "directory",
563
+ name: "Migrations",
564
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
565
+ }
566
+ ]
567
+ },
568
+ {
569
+ type: "directory",
570
+ name: "ExternalServices",
571
+ children: [
572
+ { type: "file", name: "MailService.ts", content: this.generateMailService() }
573
+ ]
574
+ },
575
+ {
576
+ type: "directory",
577
+ name: "Providers",
578
+ children: [
579
+ {
580
+ type: "file",
581
+ name: "AppServiceProvider.ts",
582
+ content: this.generateAppServiceProvider(context)
583
+ },
584
+ {
585
+ type: "file",
586
+ name: "RepositoryServiceProvider.ts",
587
+ content: this.generateRepositoryServiceProvider()
588
+ }
589
+ ]
590
+ }
591
+ ]
592
+ },
593
+ // Interface Layer (controllers, presenters)
594
+ {
595
+ type: "directory",
596
+ name: "Interface",
597
+ children: [
598
+ {
599
+ type: "directory",
600
+ name: "Http",
601
+ children: [
602
+ {
603
+ type: "directory",
604
+ name: "Controllers",
605
+ children: [
606
+ {
607
+ type: "file",
608
+ name: "UserController.ts",
609
+ content: this.generateUserController()
610
+ }
611
+ ]
612
+ },
613
+ {
614
+ type: "directory",
615
+ name: "Middleware",
616
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
617
+ },
618
+ {
619
+ type: "directory",
620
+ name: "Routes",
621
+ children: [{ type: "file", name: "api.ts", content: this.generateApiRoutes() }]
622
+ }
623
+ ]
624
+ },
625
+ {
626
+ type: "directory",
627
+ name: "Presenters",
628
+ children: [
629
+ { type: "file", name: "UserPresenter.ts", content: this.generateUserPresenter() }
630
+ ]
631
+ }
632
+ ]
633
+ },
634
+ { type: "file", name: "bootstrap.ts", content: this.generateBootstrap(context) }
635
+ ]
636
+ },
637
+ {
638
+ type: "directory",
639
+ name: "tests",
640
+ children: [
641
+ {
642
+ type: "directory",
643
+ name: "Unit",
644
+ children: [
645
+ {
646
+ type: "directory",
647
+ name: "Domain",
648
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
649
+ },
650
+ {
651
+ type: "directory",
652
+ name: "Application",
653
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
654
+ }
655
+ ]
656
+ },
657
+ {
658
+ type: "directory",
659
+ name: "Integration",
660
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
661
+ }
662
+ ]
663
+ }
664
+ ];
665
+ }
666
+ // ─────────────────────────────────────────────────────────────
667
+ // Config Generators (similar to MVC but simplified)
668
+ // ─────────────────────────────────────────────────────────────
669
+ generateAppConfig(context) {
670
+ return `export default {
671
+ name: process.env.APP_NAME ?? '${context.name}',
672
+ env: process.env.APP_ENV ?? 'development',
673
+ debug: process.env.APP_DEBUG === 'true',
674
+ url: process.env.APP_URL ?? 'http://localhost:3000',
675
+ key: process.env.APP_KEY,
676
+ }
677
+ `;
678
+ }
679
+ generateDatabaseConfig() {
680
+ return `export default {
681
+ default: process.env.DB_CONNECTION ?? 'sqlite',
682
+ connections: {
683
+ sqlite: {
684
+ driver: 'sqlite',
685
+ database: process.env.DB_DATABASE ?? 'database/database.sqlite',
686
+ },
687
+ },
688
+ }
689
+ `;
690
+ }
691
+ generateAuthConfig() {
692
+ return `export default {
693
+ defaults: { guard: 'web' },
694
+ guards: {
695
+ web: { driver: 'session', provider: 'users' },
696
+ api: { driver: 'token', provider: 'users' },
697
+ },
698
+ }
699
+ `;
700
+ }
701
+ generateCacheConfig() {
702
+ return `export default {
703
+ default: process.env.CACHE_DRIVER ?? 'memory',
704
+ stores: {
705
+ memory: { driver: 'memory' },
706
+ },
707
+ }
708
+ `;
709
+ }
710
+ generateLoggingConfig() {
711
+ return `export default {
712
+ default: process.env.LOG_CHANNEL ?? 'console',
713
+ channels: {
714
+ console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
715
+ },
716
+ }
717
+ `;
718
+ }
719
+ // ─────────────────────────────────────────────────────────────
720
+ // Domain Layer
721
+ // ─────────────────────────────────────────────────────────────
722
+ generateBaseEntity() {
723
+ return `/**
724
+ * Base Entity
725
+ *
726
+ * All domain entities extend this base class.
727
+ * Entities have identity and lifecycle.
728
+ *
729
+ * IMPORTANT: This layer must have NO external dependencies.
730
+ */
731
+
732
+ export abstract class Entity<T> {
733
+ protected readonly _id: T
734
+
735
+ constructor(id: T) {
736
+ this._id = id
737
+ }
738
+
739
+ get id(): T {
740
+ return this._id
741
+ }
742
+
743
+ equals(other: Entity<T>): boolean {
744
+ if (other === null || other === undefined) {
745
+ return false
746
+ }
747
+ if (!(other instanceof Entity)) {
748
+ return false
749
+ }
750
+ return this._id === other._id
751
+ }
752
+ }
753
+ `;
754
+ }
755
+ generateUserEntity() {
756
+ return `/**
757
+ * User Entity
758
+ *
759
+ * Core domain entity representing a user.
760
+ * Contains business logic related to users.
761
+ */
762
+
763
+ import { Entity } from './Entity'
764
+ import { Email } from '../ValueObjects/Email'
765
+
766
+ export interface UserProps {
767
+ name: string
768
+ email: Email
769
+ createdAt: Date
770
+ updatedAt?: Date
771
+ }
772
+
773
+ export class User extends Entity<string> {
774
+ private props: UserProps
775
+
776
+ private constructor(id: string, props: UserProps) {
777
+ super(id)
778
+ this.props = props
779
+ }
780
+
781
+ static create(id: string, name: string, email: string): User {
782
+ return new User(id, {
783
+ name,
784
+ email: Email.create(email),
785
+ createdAt: new Date(),
786
+ })
787
+ }
788
+
789
+ static reconstitute(id: string, props: UserProps): User {
790
+ return new User(id, props)
791
+ }
792
+
793
+ get name(): string {
794
+ return this.props.name
795
+ }
796
+
797
+ get email(): Email {
798
+ return this.props.email
799
+ }
800
+
801
+ get createdAt(): Date {
802
+ return this.props.createdAt
803
+ }
804
+
805
+ updateName(name: string): void {
806
+ if (!name || name.trim().length === 0) {
807
+ throw new Error('Name cannot be empty')
808
+ }
809
+ this.props.name = name.trim()
810
+ this.props.updatedAt = new Date()
811
+ }
812
+ }
813
+ `;
814
+ }
815
+ generateBaseValueObject() {
816
+ return `/**
817
+ * Base Value Object
818
+ *
819
+ * Value objects are immutable and compared by value.
820
+ * They have no identity of their own.
821
+ */
822
+
823
+ export abstract class ValueObject<T> {
824
+ protected readonly props: T
825
+
826
+ constructor(props: T) {
827
+ this.props = Object.freeze(props)
828
+ }
829
+
830
+ equals(other: ValueObject<T>): boolean {
831
+ if (other === null || other === undefined) {
832
+ return false
833
+ }
834
+ return JSON.stringify(this.props) === JSON.stringify(other.props)
835
+ }
836
+ }
837
+ `;
838
+ }
839
+ generateEmailValueObject() {
840
+ return `/**
841
+ * Email Value Object
842
+ *
843
+ * Encapsulates email validation and comparison.
844
+ */
845
+
846
+ import { ValueObject } from './ValueObject'
847
+
848
+ interface EmailProps {
849
+ value: string
850
+ }
851
+
852
+ export class Email extends ValueObject<EmailProps> {
853
+ private constructor(props: EmailProps) {
854
+ super(props)
855
+ }
856
+
857
+ static create(email: string): Email {
858
+ if (!Email.isValid(email)) {
859
+ throw new Error(\`Invalid email: \${email}\`)
860
+ }
861
+ return new Email({ value: email.toLowerCase().trim() })
862
+ }
863
+
864
+ static isValid(email: string): boolean {
865
+ const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/
866
+ return emailRegex.test(email)
867
+ }
868
+
869
+ get value(): string {
870
+ return this.props.value
871
+ }
872
+
873
+ toString(): string {
874
+ return this.value
875
+ }
876
+ }
877
+ `;
878
+ }
879
+ generateUserRepositoryInterface() {
880
+ return `/**
881
+ * User Repository Interface
882
+ *
883
+ * Defines the contract for user persistence.
884
+ * Implementations are in Infrastructure layer.
885
+ */
886
+
887
+ import type { User } from '../Entities/User'
888
+
889
+ export interface IUserRepository {
890
+ findById(id: string): Promise<User | null>
891
+ findByEmail(email: string): Promise<User | null>
892
+ save(user: User): Promise<void>
893
+ delete(id: string): Promise<void>
894
+ findAll(): Promise<User[]>
895
+ }
896
+ `;
897
+ }
898
+ generateDomainException() {
899
+ return `/**
900
+ * Domain Exception
901
+ *
902
+ * Base exception for domain-level errors.
903
+ */
904
+
905
+ export class DomainException extends Error {
906
+ constructor(message: string) {
907
+ super(message)
908
+ this.name = 'DomainException'
909
+ }
910
+ }
911
+
912
+ export class EntityNotFoundException extends DomainException {
913
+ constructor(entity: string, id: string) {
914
+ super(\`\${entity} with id \${id} not found\`)
915
+ this.name = 'EntityNotFoundException'
916
+ }
917
+ }
918
+
919
+ export class InvalidValueException extends DomainException {
920
+ constructor(message: string) {
921
+ super(message)
922
+ this.name = 'InvalidValueException'
923
+ }
924
+ }
925
+ `;
926
+ }
927
+ // ─────────────────────────────────────────────────────────────
928
+ // Application Layer
929
+ // ─────────────────────────────────────────────────────────────
930
+ generateCreateUserUseCase() {
931
+ return `/**
932
+ * Create User Use Case
933
+ *
934
+ * Application service for creating new users.
935
+ */
936
+
937
+ import { User } from '../../../Domain/Entities/User'
938
+ import type { IUserRepository } from '../../../Domain/Interfaces/IUserRepository'
939
+ import type { UserDTO } from '../../DTOs/UserDTO'
940
+
941
+ export interface CreateUserInput {
942
+ name: string
943
+ email: string
944
+ }
945
+
946
+ export interface CreateUserOutput {
947
+ user: UserDTO
948
+ }
949
+
950
+ export class CreateUserUseCase {
951
+ constructor(private userRepository: IUserRepository) {}
952
+
953
+ async execute(input: CreateUserInput): Promise<CreateUserOutput> {
954
+ // Check if email already exists
955
+ const existing = await this.userRepository.findByEmail(input.email)
956
+ if (existing) {
957
+ throw new Error('User with this email already exists')
958
+ }
959
+
960
+ // Create user entity
961
+ const user = User.create(
962
+ crypto.randomUUID(),
963
+ input.name,
964
+ input.email
965
+ )
966
+
967
+ // Persist
968
+ await this.userRepository.save(user)
969
+
970
+ // Return DTO
971
+ return {
972
+ user: {
973
+ id: user.id,
974
+ name: user.name,
975
+ email: user.email.value,
976
+ createdAt: user.createdAt.toISOString(),
977
+ },
978
+ }
979
+ }
980
+ }
981
+ `;
982
+ }
983
+ generateGetUserUseCase() {
984
+ return `/**
985
+ * Get User Use Case
986
+ */
987
+
988
+ import type { IUserRepository } from '../../../Domain/Interfaces/IUserRepository'
989
+ import { EntityNotFoundException } from '../../../Domain/Exceptions/DomainException'
990
+ import type { UserDTO } from '../../DTOs/UserDTO'
991
+
992
+ export interface GetUserInput {
993
+ id: string
994
+ }
995
+
996
+ export interface GetUserOutput {
997
+ user: UserDTO
998
+ }
999
+
1000
+ export class GetUserUseCase {
1001
+ constructor(private userRepository: IUserRepository) {}
1002
+
1003
+ async execute(input: GetUserInput): Promise<GetUserOutput> {
1004
+ const user = await this.userRepository.findById(input.id)
1005
+
1006
+ if (!user) {
1007
+ throw new EntityNotFoundException('User', input.id)
1008
+ }
1009
+
1010
+ return {
1011
+ user: {
1012
+ id: user.id,
1013
+ name: user.name,
1014
+ email: user.email.value,
1015
+ createdAt: user.createdAt.toISOString(),
1016
+ },
1017
+ }
1018
+ }
1019
+ }
1020
+ `;
1021
+ }
1022
+ generateUserDTO() {
1023
+ return `/**
1024
+ * User Data Transfer Object
1025
+ *
1026
+ * Used for transferring user data across boundaries.
1027
+ */
1028
+
1029
+ export interface UserDTO {
1030
+ id: string
1031
+ name: string
1032
+ email: string
1033
+ createdAt: string
1034
+ updatedAt?: string
1035
+ }
1036
+
1037
+ export interface CreateUserDTO {
1038
+ name: string
1039
+ email: string
1040
+ }
1041
+
1042
+ export interface UpdateUserDTO {
1043
+ name?: string
1044
+ email?: string
1045
+ }
1046
+ `;
1047
+ }
1048
+ generateMailServiceInterface() {
1049
+ return `/**
1050
+ * Mail Service Interface
1051
+ *
1052
+ * Contract for email sending functionality.
1053
+ */
1054
+
1055
+ export interface MailMessage {
1056
+ to: string
1057
+ subject: string
1058
+ body: string
1059
+ html?: string
1060
+ }
1061
+
1062
+ export interface IMailService {
1063
+ send(message: MailMessage): Promise<void>
1064
+ }
1065
+ `;
1066
+ }
1067
+ // ─────────────────────────────────────────────────────────────
1068
+ // Infrastructure Layer
1069
+ // ─────────────────────────────────────────────────────────────
1070
+ generateUserRepository() {
1071
+ return `/**
1072
+ * User Repository Implementation
1073
+ *
1074
+ * Concrete implementation of IUserRepository.
1075
+ */
1076
+
1077
+ import type { User } from '../../../Domain/Entities/User'
1078
+ import type { IUserRepository } from '../../../Domain/Interfaces/IUserRepository'
1079
+
1080
+ // In-memory store for demo purposes
1081
+ const users = new Map<string, User>()
1082
+
1083
+ export class UserRepository implements IUserRepository {
1084
+ async findById(id: string): Promise<User | null> {
1085
+ return users.get(id) ?? null
1086
+ }
1087
+
1088
+ async findByEmail(email: string): Promise<User | null> {
1089
+ for (const user of users.values()) {
1090
+ if (user.email.value === email) {
1091
+ return user
1092
+ }
1093
+ }
1094
+ return null
1095
+ }
1096
+
1097
+ async save(user: User): Promise<void> {
1098
+ users.set(user.id, user)
1099
+ }
1100
+
1101
+ async delete(id: string): Promise<void> {
1102
+ users.delete(id)
1103
+ }
1104
+
1105
+ async findAll(): Promise<User[]> {
1106
+ return Array.from(users.values())
1107
+ }
1108
+ }
1109
+ `;
1110
+ }
1111
+ generateMailService() {
1112
+ return `/**
1113
+ * Mail Service Implementation
1114
+ */
1115
+
1116
+ import type { IMailService, MailMessage } from '../../Application/Interfaces/IMailService'
1117
+
1118
+ export class MailService implements IMailService {
1119
+ async send(message: MailMessage): Promise<void> {
1120
+ // TODO: Implement actual email sending
1121
+ console.log(\`[Mail] Sending to \${message.to}: \${message.subject}\`)
1122
+ }
1123
+ }
1124
+ `;
1125
+ }
1126
+ generateAppServiceProvider(context) {
1127
+ return `/**
1128
+ * App Service Provider
1129
+ */
1130
+
1131
+ import { ServiceProvider, type Container, type PlanetCore } from 'gravito-core'
1132
+
1133
+ export class AppServiceProvider extends ServiceProvider {
1134
+ register(_container: Container): void {
1135
+ // Register application services
1136
+ }
1137
+
1138
+ boot(_core: PlanetCore): void {
1139
+ console.log('${context.name} (Clean Architecture) booted!')
1140
+ }
1141
+ }
1142
+ `;
1143
+ }
1144
+ generateRepositoryServiceProvider() {
1145
+ return `/**
1146
+ * Repository Service Provider
1147
+ *
1148
+ * Binds repository interfaces to implementations.
1149
+ */
1150
+
1151
+ import { ServiceProvider, type Container } from 'gravito-core'
1152
+ import { UserRepository } from '../Persistence/Repositories/UserRepository'
1153
+ import { MailService } from '../ExternalServices/MailService'
1154
+
1155
+ export class RepositoryServiceProvider extends ServiceProvider {
1156
+ register(container: Container): void {
1157
+ // Bind repositories
1158
+ container.singleton('userRepository', () => new UserRepository())
1159
+
1160
+ // Bind external services
1161
+ container.singleton('mailService', () => new MailService())
1162
+ }
1163
+ }
1164
+ `;
1165
+ }
1166
+ // ─────────────────────────────────────────────────────────────
1167
+ // Interface Layer
1168
+ // ─────────────────────────────────────────────────────────────
1169
+ generateUserController() {
1170
+ return `/**
1171
+ * User Controller
1172
+ */
1173
+
1174
+ import type { GravitoContext } from 'gravito-core'
1175
+ import { CreateUserUseCase } from '../../../Application/UseCases/User/CreateUser'
1176
+ import { GetUserUseCase } from '../../../Application/UseCases/User/GetUser'
1177
+ import { UserRepository } from '../../../Infrastructure/Persistence/Repositories/UserRepository'
1178
+
1179
+ const userRepository = new UserRepository()
1180
+
1181
+ export class UserController {
1182
+ async create(c: GravitoContext) {
1183
+ const body = await c.req.json() as { name: string; email: string }
1184
+ const useCase = new CreateUserUseCase(userRepository)
1185
+
1186
+ const result = await useCase.execute({
1187
+ name: body.name,
1188
+ email: body.email,
1189
+ })
1190
+
1191
+ return c.json({ success: true, data: result.user }, 201)
1192
+ }
1193
+
1194
+ async show(c: GravitoContext) {
1195
+ const id = c.req.param('id') ?? ''
1196
+ const useCase = new GetUserUseCase(userRepository)
1197
+
1198
+ const result = await useCase.execute({ id })
1199
+
1200
+ return c.json({ success: true, data: result.user })
1201
+ }
1202
+ }
1203
+ `;
1204
+ }
1205
+ generateApiRoutes() {
1206
+ return `/**
1207
+ * API Routes
1208
+ */
1209
+
1210
+ import { UserController } from '../Controllers/UserController'
1211
+
1212
+ export function registerApiRoutes(router: any): void {
1213
+ const users = new UserController()
1214
+
1215
+ router.post('/api/users', (c: any) => users.create(c))
1216
+ router.get('/api/users/:id', (c: any) => users.show(c))
1217
+ }
1218
+ `;
1219
+ }
1220
+ generateUserPresenter() {
1221
+ return `/**
1222
+ * User Presenter
1223
+ *
1224
+ * Formats user data for different output formats.
1225
+ */
1226
+
1227
+ import type { UserDTO } from '../../Application/DTOs/UserDTO'
1228
+
1229
+ export class UserPresenter {
1230
+ static toJson(user: UserDTO): object {
1231
+ return {
1232
+ id: user.id,
1233
+ name: user.name,
1234
+ email: user.email,
1235
+ created_at: user.createdAt,
1236
+ }
1237
+ }
1238
+
1239
+ static toList(users: UserDTO[]): object {
1240
+ return {
1241
+ data: users.map((u) => this.toJson(u)),
1242
+ total: users.length,
1243
+ }
1244
+ }
1245
+ }
1246
+ `;
1247
+ }
1248
+ generateBootstrap(context) {
1249
+ return `/**
1250
+ * Application Bootstrap
1251
+ */
1252
+
1253
+ import { PlanetCore } from 'gravito-core'
1254
+ import { AppServiceProvider } from './Infrastructure/Providers/AppServiceProvider'
1255
+ import { RepositoryServiceProvider } from './Infrastructure/Providers/RepositoryServiceProvider'
1256
+ import { registerApiRoutes } from './Interface/Http/Routes/api'
1257
+
1258
+ const core = new PlanetCore({
1259
+ config: { APP_NAME: '${context.name}' },
1260
+ })
1261
+
1262
+ core.register(new RepositoryServiceProvider())
1263
+ core.register(new AppServiceProvider())
1264
+
1265
+ await core.bootstrap()
1266
+
1267
+ // Register routes
1268
+ registerApiRoutes(core.router)
1269
+
1270
+ export default core.liftoff()
1271
+ `;
1272
+ }
1273
+ generateArchitectureDoc(context) {
1274
+ return `# ${context.name} - Clean Architecture Guide
1275
+
1276
+ ## Overview
1277
+
1278
+ This project follows **Clean Architecture** (by Robert C. Martin).
1279
+ The key principle is the **Dependency Rule**: dependencies point inward.
1280
+
1281
+ ## Layer Structure
1282
+
1283
+ \`\`\`
1284
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
1285
+ \u2502 Interface Layer \u2502
1286
+ \u2502 (Controllers, Presenters) \u2502
1287
+ \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
1288
+ \u2502 Infrastructure Layer \u2502
1289
+ \u2502 (Repositories, External Services) \u2502
1290
+ \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
1291
+ \u2502 Application Layer \u2502
1292
+ \u2502 (Use Cases, DTOs) \u2502
1293
+ \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
1294
+ \u2502 Domain Layer \u2502
1295
+ \u2502 (Entities, Value Objects, Interfaces) \u2502
1296
+ \u2502 \u2B50 PURE \u2B50 \u2502
1297
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
1298
+ \`\`\`
1299
+
1300
+ ## Dependency Rule
1301
+
1302
+ > Inner layers know nothing about outer layers.
1303
+
1304
+ - **Domain**: No dependencies (pure TypeScript only)
1305
+ - **Application**: Depends only on Domain
1306
+ - **Infrastructure**: Implements Domain interfaces
1307
+ - **Interface**: Calls Application use cases
1308
+
1309
+ ## Directory Structure
1310
+
1311
+ \`\`\`
1312
+ src/
1313
+ \u251C\u2500\u2500 Domain/ # Enterprise Business Rules
1314
+ \u2502 \u251C\u2500\u2500 Entities/ # Business objects with identity
1315
+ \u2502 \u251C\u2500\u2500 ValueObjects/ # Immutable value types
1316
+ \u2502 \u251C\u2500\u2500 Interfaces/ # Repository contracts
1317
+ \u2502 \u2514\u2500\u2500 Exceptions/ # Domain errors
1318
+ \u251C\u2500\u2500 Application/ # Application Business Rules
1319
+ \u2502 \u251C\u2500\u2500 UseCases/ # Business operations
1320
+ \u2502 \u251C\u2500\u2500 DTOs/ # Data transfer objects
1321
+ \u2502 \u2514\u2500\u2500 Interfaces/ # Service contracts
1322
+ \u251C\u2500\u2500 Infrastructure/ # Frameworks & Drivers
1323
+ \u2502 \u251C\u2500\u2500 Persistence/ # Database implementations
1324
+ \u2502 \u251C\u2500\u2500 ExternalServices/ # Third-party integrations
1325
+ \u2502 \u2514\u2500\u2500 Providers/ # Service providers
1326
+ \u2514\u2500\u2500 Interface/ # Interface Adapters
1327
+ \u251C\u2500\u2500 Http/ # HTTP controllers & routes
1328
+ \u2514\u2500\u2500 Presenters/ # Output formatters
1329
+ \`\`\`
1330
+
1331
+ ## Key Benefits
1332
+
1333
+ 1. **Testable**: Domain and Use Cases can be tested without frameworks
1334
+ 2. **Independent**: Business logic is framework-agnostic
1335
+ 3. **Maintainable**: Changes in outer layers don't affect inner layers
1336
+ 4. **Flexible**: Easy to swap databases or frameworks
1337
+
1338
+ Created with \u2764\uFE0F using Gravito Framework
1339
+ `;
1340
+ }
1341
+ };
1342
+
1343
+ // src/generators/DddGenerator.ts
1344
+ var DddGenerator = class extends BaseGenerator {
1345
+ get architectureType() {
1346
+ return "ddd";
1347
+ }
1348
+ get displayName() {
1349
+ return "Domain-Driven Design (DDD)";
1350
+ }
1351
+ get description() {
1352
+ return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
1353
+ }
1354
+ getDirectoryStructure(context) {
1355
+ return [
1356
+ {
1357
+ type: "directory",
1358
+ name: "config",
1359
+ children: [
1360
+ { type: "file", name: "app.ts", content: this.generateAppConfig(context) },
1361
+ { type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
1362
+ { type: "file", name: "modules.ts", content: this.generateModulesConfig() },
1363
+ { type: "file", name: "cache.ts", content: this.generateCacheConfig() },
1364
+ { type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
1365
+ ]
1366
+ },
1367
+ {
1368
+ type: "directory",
1369
+ name: "src",
1370
+ children: [
1371
+ // Bootstrap - Application startup and configuration
1372
+ this.generateBootstrapDirectory(context),
1373
+ // Shared - Cross-module shared components
1374
+ this.generateShared(),
1375
+ // Modules - Bounded Contexts
1376
+ {
1377
+ type: "directory",
1378
+ name: "Modules",
1379
+ children: [
1380
+ this.generateModule("Ordering", context),
1381
+ this.generateModule("Catalog", context)
1382
+ ]
1383
+ },
1384
+ { type: "file", name: "main.ts", content: this.generateMainEntry(context) }
1385
+ ]
1386
+ },
1387
+ {
1388
+ type: "directory",
1389
+ name: "tests",
1390
+ children: [
1391
+ {
1392
+ type: "directory",
1393
+ name: "Modules",
1394
+ children: [
1395
+ {
1396
+ type: "directory",
1397
+ name: "Ordering",
1398
+ children: [
1399
+ {
1400
+ type: "directory",
1401
+ name: "Unit",
1402
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
1403
+ },
1404
+ {
1405
+ type: "directory",
1406
+ name: "Integration",
1407
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
1408
+ }
1409
+ ]
1410
+ },
1411
+ {
1412
+ type: "directory",
1413
+ name: "Catalog",
1414
+ children: [
1415
+ {
1416
+ type: "directory",
1417
+ name: "Unit",
1418
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
1419
+ }
1420
+ ]
1421
+ }
1422
+ ]
1423
+ },
1424
+ {
1425
+ type: "directory",
1426
+ name: "Shared",
1427
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
1428
+ }
1429
+ ]
1430
+ }
1431
+ ];
1432
+ }
1433
+ // ─────────────────────────────────────────────────────────────
1434
+ // Bootstrap Directory
1435
+ // ─────────────────────────────────────────────────────────────
1436
+ generateBootstrapDirectory(context) {
1437
+ return {
1438
+ type: "directory",
1439
+ name: "Bootstrap",
1440
+ children: [
1441
+ { type: "file", name: "app.ts", content: this.generateBootstrapApp(context) },
1442
+ { type: "file", name: "providers.ts", content: this.generateProvidersRegistry(context) },
1443
+ { type: "file", name: "events.ts", content: this.generateEventsRegistry() },
1444
+ { type: "file", name: "routes.ts", content: this.generateRoutesRegistry(context) }
1445
+ ]
1446
+ };
1447
+ }
1448
+ // ─────────────────────────────────────────────────────────────
1449
+ // Shared Directory (replaces SharedKernel with user's structure)
1450
+ // ─────────────────────────────────────────────────────────────
1451
+ generateShared() {
1452
+ return {
1453
+ type: "directory",
1454
+ name: "Shared",
1455
+ children: [
1456
+ {
1457
+ type: "directory",
1458
+ name: "Domain",
1459
+ children: [
1460
+ {
1461
+ type: "directory",
1462
+ name: "ValueObjects",
1463
+ children: [
1464
+ { type: "file", name: "Id.ts", content: this.generateIdValueObject() },
1465
+ { type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
1466
+ { type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
1467
+ ]
1468
+ },
1469
+ {
1470
+ type: "directory",
1471
+ name: "Events",
1472
+ children: [
1473
+ { type: "file", name: "DomainEvent.ts", content: this.generateDomainEvent() }
1474
+ ]
1475
+ },
1476
+ {
1477
+ type: "directory",
1478
+ name: "Primitives",
1479
+ children: [
1480
+ { type: "file", name: "AggregateRoot.ts", content: this.generateAggregateRoot() },
1481
+ { type: "file", name: "Entity.ts", content: this.generateEntity() },
1482
+ { type: "file", name: "ValueObject.ts", content: this.generateValueObject() }
1483
+ ]
1484
+ }
1485
+ ]
1486
+ },
1487
+ {
1488
+ type: "directory",
1489
+ name: "Infrastructure",
1490
+ children: [
1491
+ {
1492
+ type: "directory",
1493
+ name: "EventBus",
1494
+ children: [
1495
+ {
1496
+ type: "file",
1497
+ name: "EventDispatcher.ts",
1498
+ content: this.generateEventDispatcher()
1499
+ }
1500
+ ]
1501
+ }
1502
+ ]
1503
+ },
1504
+ {
1505
+ type: "directory",
1506
+ name: "Exceptions",
1507
+ children: [
1508
+ { type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
1509
+ ]
1510
+ }
1511
+ ]
1512
+ };
1513
+ }
1514
+ // ─────────────────────────────────────────────────────────────
1515
+ // Module Generator (replaces Bounded Context)
1516
+ // ─────────────────────────────────────────────────────────────
1517
+ generateModule(name, context) {
1518
+ return {
1519
+ type: "directory",
1520
+ name,
1521
+ children: [
1522
+ // Domain Layer
1523
+ {
1524
+ type: "directory",
1525
+ name: "Domain",
1526
+ children: [
1527
+ {
1528
+ type: "directory",
1529
+ name: "Aggregates",
1530
+ children: [
1531
+ {
1532
+ type: "directory",
1533
+ name,
1534
+ children: [
1535
+ { type: "file", name: `${name}.ts`, content: this.generateAggregate(name) },
1536
+ {
1537
+ type: "file",
1538
+ name: `${name}Status.ts`,
1539
+ content: this.generateAggregateStatus(name)
1540
+ }
1541
+ ]
1542
+ }
1543
+ ]
1544
+ },
1545
+ {
1546
+ type: "directory",
1547
+ name: "Events",
1548
+ children: [
1549
+ {
1550
+ type: "file",
1551
+ name: `${name}Created.ts`,
1552
+ content: this.generateCreatedEvent(name)
1553
+ }
1554
+ ]
1555
+ },
1556
+ {
1557
+ type: "directory",
1558
+ name: "Repositories",
1559
+ children: [
1560
+ {
1561
+ type: "file",
1562
+ name: `I${name}Repository.ts`,
1563
+ content: this.generateRepositoryInterface(name)
1564
+ }
1565
+ ]
1566
+ },
1567
+ {
1568
+ type: "directory",
1569
+ name: "Services",
1570
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
1571
+ }
1572
+ ]
1573
+ },
1574
+ // Application Layer
1575
+ {
1576
+ type: "directory",
1577
+ name: "Application",
1578
+ children: [
1579
+ {
1580
+ type: "directory",
1581
+ name: "Commands",
1582
+ children: [
1583
+ {
1584
+ type: "directory",
1585
+ name: `Create${name}`,
1586
+ children: [
1587
+ {
1588
+ type: "file",
1589
+ name: `Create${name}Command.ts`,
1590
+ content: this.generateCommand(name)
1591
+ },
1592
+ {
1593
+ type: "file",
1594
+ name: `Create${name}Handler.ts`,
1595
+ content: this.generateCommandHandler(name)
1596
+ }
1597
+ ]
1598
+ }
1599
+ ]
1600
+ },
1601
+ {
1602
+ type: "directory",
1603
+ name: "Queries",
1604
+ children: [
1605
+ {
1606
+ type: "directory",
1607
+ name: `Get${name}ById`,
1608
+ children: [
1609
+ {
1610
+ type: "file",
1611
+ name: `Get${name}ByIdQuery.ts`,
1612
+ content: this.generateQuery(name)
1613
+ },
1614
+ {
1615
+ type: "file",
1616
+ name: `Get${name}ByIdHandler.ts`,
1617
+ content: this.generateQueryHandler(name)
1618
+ }
1619
+ ]
1620
+ }
1621
+ ]
1622
+ },
1623
+ {
1624
+ type: "directory",
1625
+ name: "DTOs",
1626
+ children: [{ type: "file", name: `${name}DTO.ts`, content: this.generateDTO(name) }]
1627
+ }
1628
+ ]
1629
+ },
1630
+ // Infrastructure Layer
1631
+ {
1632
+ type: "directory",
1633
+ name: "Infrastructure",
1634
+ children: [
1635
+ {
1636
+ type: "directory",
1637
+ name: "Persistence",
1638
+ children: [
1639
+ {
1640
+ type: "file",
1641
+ name: `${name}Repository.ts`,
1642
+ content: this.generateRepository(name)
1643
+ }
1644
+ ]
1645
+ },
1646
+ {
1647
+ type: "directory",
1648
+ name: "Providers",
1649
+ children: [
1650
+ {
1651
+ type: "file",
1652
+ name: `${name}ServiceProvider.ts`,
1653
+ content: this.generateModuleServiceProvider(name, context)
1654
+ }
1655
+ ]
1656
+ }
1657
+ ]
1658
+ }
1659
+ ]
1660
+ };
1661
+ }
1662
+ // ─────────────────────────────────────────────────────────────
1663
+ // Bootstrap File Generators
1664
+ // ─────────────────────────────────────────────────────────────
1665
+ generateBootstrapApp(context) {
1666
+ return `/**
1667
+ * Application Bootstrap
1668
+ *
1669
+ * Central configuration and initialization of the application.
1670
+ */
1671
+
1672
+ import { PlanetCore } from 'gravito-core'
1673
+ import { registerProviders } from './providers'
1674
+ import { registerRoutes } from './routes'
1675
+
1676
+ export async function createApp(): Promise<PlanetCore> {
1677
+ const core = new PlanetCore({
1678
+ config: {
1679
+ APP_NAME: '${context.name}',
1680
+ },
1681
+ })
1682
+
1683
+ // Register all service providers
1684
+ await registerProviders(core)
1685
+
1686
+ // Bootstrap the application
1687
+ await core.bootstrap()
1688
+
1689
+ // Register routes
1690
+ registerRoutes(core.router)
1691
+
1692
+ return core
1693
+ }
1694
+ `;
1695
+ }
1696
+ generateProvidersRegistry(_context) {
1697
+ return `/**
1698
+ * Service Providers Registry
1699
+ *
1700
+ * Register all module service providers here.
1701
+ */
1702
+
1703
+ import type { PlanetCore } from 'gravito-core'
1704
+ import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
1705
+ import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
1706
+
1707
+ export async function registerProviders(core: PlanetCore): Promise<void> {
1708
+ // Register module providers
1709
+ core.register(new OrderingServiceProvider())
1710
+ core.register(new CatalogServiceProvider())
1711
+
1712
+ // Add more providers as needed
1713
+ }
1714
+ `;
1715
+ }
1716
+ generateEventsRegistry() {
1717
+ return `/**
1718
+ * Domain Events Registry
1719
+ *
1720
+ * Register all domain event handlers here.
1721
+ */
1722
+
1723
+ import { EventDispatcher } from '../Shared/Infrastructure/EventBus/EventDispatcher'
1724
+
1725
+ export function registerEvents(dispatcher: EventDispatcher): void {
1726
+ // Register event handlers
1727
+ // dispatcher.subscribe('ordering.created', async (event) => { ... })
1728
+ }
1729
+ `;
1730
+ }
1731
+ generateRoutesRegistry(_context) {
1732
+ return `/**
1733
+ * Routes Registry
1734
+ *
1735
+ * Register all module routes here.
1736
+ */
1737
+
1738
+ export function registerRoutes(router: any): void {
1739
+ // Health check
1740
+ router.get('/health', (c: any) => c.json({ status: 'healthy' }))
1741
+
1742
+ // Ordering module
1743
+ router.get('/api/orders', (c: any) => c.json({ message: 'Order list' }))
1744
+ router.post('/api/orders', (c: any) => c.json({ message: 'Order created' }, 201))
1745
+
1746
+ // Catalog module
1747
+ router.get('/api/products', (c: any) => c.json({ message: 'Product list' }))
1748
+ }
1749
+ `;
1750
+ }
1751
+ generateMainEntry(_context) {
1752
+ return `/**
1753
+ * Application Entry Point
1754
+ *
1755
+ * Start the HTTP server.
1756
+ */
1757
+
1758
+ import { createApp } from './Bootstrap/app'
1759
+
1760
+ const app = await createApp()
1761
+
1762
+ export default app.liftoff()
1763
+ `;
1764
+ }
1765
+ generateModulesConfig() {
1766
+ return `/**
1767
+ * Modules Configuration
1768
+ *
1769
+ * Define module boundaries and their dependencies.
1770
+ */
1771
+
1772
+ export default {
1773
+ modules: {
1774
+ ordering: {
1775
+ name: 'Ordering',
1776
+ description: 'Order management module',
1777
+ prefix: '/api/orders',
1778
+ },
1779
+ catalog: {
1780
+ name: 'Catalog',
1781
+ description: 'Product catalog module',
1782
+ prefix: '/api/products',
1783
+ },
1784
+ },
1785
+
1786
+ // Module dependencies
1787
+ dependencies: {
1788
+ ordering: ['catalog'], // Ordering depends on Catalog
1789
+ },
1790
+ }
1791
+ `;
1792
+ }
1793
+ generateModuleServiceProvider(name, _context) {
1794
+ return `/**
1795
+ * ${name} Service Provider
1796
+ */
1797
+
1798
+ import { ServiceProvider, type Container, type PlanetCore } from 'gravito-core'
1799
+ import { ${name}Repository } from '../Persistence/${name}Repository'
1800
+
1801
+ export class ${name}ServiceProvider extends ServiceProvider {
1802
+ register(container: Container): void {
1803
+ container.singleton('${name.toLowerCase()}Repository', () => new ${name}Repository())
1804
+ }
1805
+
1806
+ boot(_core: PlanetCore): void {
1807
+ console.log('[${name}] Module loaded')
1808
+ }
1809
+ }
1810
+ `;
1811
+ }
1812
+ /**
1813
+ * Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
1814
+ */
1815
+ generatePackageJson(context) {
1816
+ const pkg = {
1817
+ name: context.nameKebabCase,
1818
+ version: "0.1.0",
1819
+ type: "module",
1820
+ scripts: {
1821
+ dev: "bun run --watch src/main.ts",
1822
+ build: "bun build ./src/main.ts --outdir ./dist --target bun",
1823
+ start: "bun run dist/main.js",
1824
+ test: "bun test",
1825
+ typecheck: "tsc --noEmit"
1826
+ },
1827
+ dependencies: {
1828
+ "gravito-core": "^1.0.0-beta.5"
1829
+ },
1830
+ devDependencies: {
1831
+ "@types/bun": "latest",
1832
+ typescript: "^5.0.0"
1833
+ }
1834
+ };
1835
+ return JSON.stringify(pkg, null, 2);
1836
+ }
1837
+ // ─────────────────────────────────────────────────────────────
1838
+ // Config Generators
1839
+ // ─────────────────────────────────────────────────────────────
1840
+ generateAppConfig(context) {
1841
+ return `export default {
1842
+ name: process.env.APP_NAME ?? '${context.name}',
1843
+ env: process.env.APP_ENV ?? 'development',
1844
+ debug: process.env.APP_DEBUG === 'true',
1845
+ url: process.env.APP_URL ?? 'http://localhost:3000',
1846
+ }
1847
+ `;
1848
+ }
1849
+ generateDatabaseConfig() {
1850
+ return `export default {
1851
+ default: process.env.DB_CONNECTION ?? 'sqlite',
1852
+ connections: {
1853
+ sqlite: { driver: 'sqlite', database: 'database/database.sqlite' },
1854
+ },
1855
+ }
1856
+ `;
1857
+ }
1858
+ generateCacheConfig() {
1859
+ return `export default {
1860
+ default: process.env.CACHE_DRIVER ?? 'memory',
1861
+ stores: { memory: { driver: 'memory' } },
1862
+ }
1863
+ `;
1864
+ }
1865
+ generateLoggingConfig() {
1866
+ return `export default {
1867
+ default: 'console',
1868
+ channels: { console: { driver: 'console', level: 'debug' } },
1869
+ }
1870
+ `;
1871
+ }
1872
+ // ─────────────────────────────────────────────────────────────
1873
+ // Shared Kernel Files
1874
+ // ─────────────────────────────────────────────────────────────
1875
+ generateIdValueObject() {
1876
+ return `/**
1877
+ * ID Value Object
1878
+ *
1879
+ * Shared identifier across all contexts.
1880
+ */
1881
+
1882
+ export class Id {
1883
+ private readonly _value: string
1884
+
1885
+ private constructor(value: string) {
1886
+ this._value = value
1887
+ }
1888
+
1889
+ static create(): Id {
1890
+ return new Id(crypto.randomUUID())
1891
+ }
1892
+
1893
+ static from(value: string): Id {
1894
+ if (!value) throw new Error('Id cannot be empty')
1895
+ return new Id(value)
1896
+ }
1897
+
1898
+ get value(): string {
1899
+ return this._value
1900
+ }
1901
+
1902
+ equals(other: Id): boolean {
1903
+ return this._value === other._value
1904
+ }
1905
+
1906
+ toString(): string {
1907
+ return this._value
1908
+ }
1909
+ }
1910
+ `;
1911
+ }
1912
+ generateMoneyValueObject() {
1913
+ return `/**
1914
+ * Money Value Object
1915
+ */
1916
+
1917
+ export class Money {
1918
+ constructor(
1919
+ public readonly amount: number,
1920
+ public readonly currency: string = 'USD'
1921
+ ) {
1922
+ if (amount < 0) throw new Error('Amount cannot be negative')
1923
+ }
1924
+
1925
+ add(other: Money): Money {
1926
+ this.assertSameCurrency(other)
1927
+ return new Money(this.amount + other.amount, this.currency)
1928
+ }
1929
+
1930
+ subtract(other: Money): Money {
1931
+ this.assertSameCurrency(other)
1932
+ return new Money(this.amount - other.amount, this.currency)
1933
+ }
1934
+
1935
+ private assertSameCurrency(other: Money): void {
1936
+ if (this.currency !== other.currency) {
1937
+ throw new Error('Cannot operate on different currencies')
1938
+ }
1939
+ }
1940
+
1941
+ equals(other: Money): boolean {
1942
+ return this.amount === other.amount && this.currency === other.currency
1943
+ }
1944
+ }
1945
+ `;
1946
+ }
1947
+ generateEmailValueObject() {
1948
+ return `/**
1949
+ * Email Value Object
1950
+ */
1951
+
1952
+ export class Email {
1953
+ private readonly _value: string
1954
+
1955
+ private constructor(value: string) {
1956
+ this._value = value.toLowerCase().trim()
1957
+ }
1958
+
1959
+ static create(email: string): Email {
1960
+ if (!Email.isValid(email)) {
1961
+ throw new Error(\`Invalid email: \${email}\`)
1962
+ }
1963
+ return new Email(email)
1964
+ }
1965
+
1966
+ static isValid(email: string): boolean {
1967
+ return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)
1968
+ }
1969
+
1970
+ get value(): string {
1971
+ return this._value
1972
+ }
1973
+ }
1974
+ `;
1975
+ }
1976
+ generateDomainEvent() {
1977
+ return `/**
1978
+ * Domain Event Base
1979
+ */
1980
+
1981
+ export abstract class DomainEvent {
1982
+ readonly occurredOn: Date
1983
+ readonly eventId: string
1984
+
1985
+ constructor() {
1986
+ this.occurredOn = new Date()
1987
+ this.eventId = crypto.randomUUID()
1988
+ }
1989
+
1990
+ abstract get eventName(): string
1991
+ abstract get aggregateId(): string
1992
+ }
1993
+ `;
1994
+ }
1995
+ generateAggregateRoot() {
1996
+ return `/**
1997
+ * Aggregate Root Base
1998
+ */
1999
+
2000
+ import type { DomainEvent } from '../Events/DomainEvent'
2001
+ import type { Id } from '../ValueObjects/Id'
2002
+
2003
+ export abstract class AggregateRoot<T extends Id = Id> {
2004
+ private _domainEvents: DomainEvent[] = []
2005
+
2006
+ protected constructor(protected readonly _id: T) {}
2007
+
2008
+ get id(): T {
2009
+ return this._id
2010
+ }
2011
+
2012
+ get domainEvents(): DomainEvent[] {
2013
+ return [...this._domainEvents]
2014
+ }
2015
+
2016
+ protected addDomainEvent(event: DomainEvent): void {
2017
+ this._domainEvents.push(event)
2018
+ }
2019
+
2020
+ clearDomainEvents(): DomainEvent[] {
2021
+ const events = [...this._domainEvents]
2022
+ this._domainEvents = []
2023
+ return events
2024
+ }
2025
+ }
2026
+ `;
2027
+ }
2028
+ generateEntity() {
2029
+ return `/**
2030
+ * Entity Base
2031
+ */
2032
+
2033
+ import type { Id } from '../ValueObjects/Id'
2034
+
2035
+ export abstract class Entity<T extends Id = Id> {
2036
+ protected constructor(protected readonly _id: T) {}
2037
+
2038
+ get id(): T {
2039
+ return this._id
2040
+ }
2041
+
2042
+ equals(other: Entity<T>): boolean {
2043
+ return this._id.equals(other._id)
2044
+ }
2045
+ }
2046
+ `;
2047
+ }
2048
+ generateValueObject() {
2049
+ return `/**
2050
+ * Value Object Base
2051
+ */
2052
+
2053
+ export abstract class ValueObject<T> {
2054
+ protected readonly props: T
2055
+
2056
+ constructor(props: T) {
2057
+ this.props = Object.freeze(props)
2058
+ }
2059
+
2060
+ equals(other: ValueObject<T>): boolean {
2061
+ return JSON.stringify(this.props) === JSON.stringify(other.props)
2062
+ }
2063
+ }
2064
+ `;
2065
+ }
2066
+ generateEventDispatcher() {
2067
+ return `/**
2068
+ * Event Dispatcher
2069
+ */
2070
+
2071
+ import type { DomainEvent } from '../../Domain/Events/DomainEvent'
2072
+
2073
+ type EventHandler = (event: DomainEvent) => void | Promise<void>
2074
+
2075
+ export class EventDispatcher {
2076
+ private handlers: Map<string, EventHandler[]> = new Map()
2077
+
2078
+ subscribe(eventName: string, handler: EventHandler): void {
2079
+ const handlers = this.handlers.get(eventName) ?? []
2080
+ handlers.push(handler)
2081
+ this.handlers.set(eventName, handlers)
2082
+ }
2083
+
2084
+ async dispatch(event: DomainEvent): Promise<void> {
2085
+ const handlers = this.handlers.get(event.eventName) ?? []
2086
+ for (const handler of handlers) {
2087
+ await handler(event)
2088
+ }
2089
+ }
2090
+
2091
+ async dispatchAll(events: DomainEvent[]): Promise<void> {
2092
+ for (const event of events) {
2093
+ await this.dispatch(event)
2094
+ }
2095
+ }
2096
+ }
2097
+ `;
2098
+ }
2099
+ // ─────────────────────────────────────────────────────────────
2100
+ // Bounded Context Templates
2101
+ // ─────────────────────────────────────────────────────────────
2102
+ generateAggregate(name) {
2103
+ return `/**
2104
+ * ${name} Aggregate Root
2105
+ */
2106
+
2107
+ import { AggregateRoot } from '../../../../../Shared/Domain/Primitives/AggregateRoot'
2108
+ import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2109
+ import { ${name}Created } from '../../Events/${name}Created'
2110
+ import { ${name}Status } from './${name}Status'
2111
+
2112
+ export interface ${name}Props {
2113
+ // Add properties here
2114
+ status: ${name}Status
2115
+ createdAt: Date
2116
+ }
2117
+
2118
+ export class ${name} extends AggregateRoot {
2119
+ private props: ${name}Props
2120
+
2121
+ private constructor(id: Id, props: ${name}Props) {
2122
+ super(id)
2123
+ this.props = props
2124
+ }
2125
+
2126
+ static create(id: Id): ${name} {
2127
+ const aggregate = new ${name}(id, {
2128
+ status: ${name}Status.PENDING,
2129
+ createdAt: new Date(),
2130
+ })
2131
+
2132
+ aggregate.addDomainEvent(new ${name}Created(id.value))
2133
+
2134
+ return aggregate
2135
+ }
2136
+
2137
+ get status(): ${name}Status {
2138
+ return this.props.status
2139
+ }
2140
+
2141
+ // Add domain methods here
2142
+ }
2143
+ `;
2144
+ }
2145
+ generateAggregateStatus(name) {
2146
+ return `/**
2147
+ * ${name} Status
2148
+ */
2149
+
2150
+ export enum ${name}Status {
2151
+ PENDING = 'pending',
2152
+ ACTIVE = 'active',
2153
+ COMPLETED = 'completed',
2154
+ CANCELLED = 'cancelled',
2155
+ }
2156
+ `;
2157
+ }
2158
+ generateCreatedEvent(name) {
2159
+ return `/**
2160
+ * ${name} Created Event
2161
+ */
2162
+
2163
+ import { DomainEvent } from '../../../../Shared/Domain/Events/DomainEvent'
2164
+
2165
+ export class ${name}Created extends DomainEvent {
2166
+ constructor(public readonly ${name.toLowerCase()}Id: string) {
2167
+ super()
2168
+ }
2169
+
2170
+ get eventName(): string {
2171
+ return '${name.toLowerCase()}.created'
2172
+ }
2173
+
2174
+ get aggregateId(): string {
2175
+ return this.${name.toLowerCase()}Id
2176
+ }
2177
+ }
2178
+ `;
2179
+ }
2180
+ generateRepositoryInterface(name) {
2181
+ return `/**
2182
+ * ${name} Repository Interface
2183
+ */
2184
+
2185
+ import type { ${name} } from '../Aggregates/${name}/${name}'
2186
+
2187
+ export interface I${name}Repository {
2188
+ findById(id: string): Promise<${name} | null>
2189
+ save(aggregate: ${name}): Promise<void>
2190
+ delete(id: string): Promise<void>
2191
+ }
2192
+ `;
2193
+ }
2194
+ generateCommand(name) {
2195
+ return `/**
2196
+ * Create ${name} Command
2197
+ */
2198
+
2199
+ export class Create${name}Command {
2200
+ constructor(
2201
+ // Add command properties
2202
+ public readonly id?: string
2203
+ ) {}
2204
+ }
2205
+ `;
2206
+ }
2207
+ generateCommandHandler(name) {
2208
+ return `/**
2209
+ * Create ${name} Handler
2210
+ */
2211
+
2212
+ import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
2213
+ import { ${name} } from '../../../Domain/Aggregates/${name}/${name}'
2214
+ import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2215
+ import type { Create${name}Command } from './Create${name}Command'
2216
+
2217
+ export class Create${name}Handler {
2218
+ constructor(private repository: I${name}Repository) {}
2219
+
2220
+ async handle(command: Create${name}Command): Promise<string> {
2221
+ const id = command.id ? Id.from(command.id) : Id.create()
2222
+ const aggregate = ${name}.create(id)
2223
+
2224
+ await this.repository.save(aggregate)
2225
+
2226
+ return id.value
2227
+ }
2228
+ }
2229
+ `;
2230
+ }
2231
+ generateQuery(name) {
2232
+ return `/**
2233
+ * Get ${name} By Id Query
2234
+ */
2235
+
2236
+ export class Get${name}ByIdQuery {
2237
+ constructor(public readonly id: string) {}
2238
+ }
2239
+ `;
2240
+ }
2241
+ generateQueryHandler(name) {
2242
+ return `/**
2243
+ * Get ${name} By Id Handler
2244
+ */
2245
+
2246
+ import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
2247
+ import type { ${name}DTO } from '../../DTOs/${name}DTO'
2248
+ import type { Get${name}ByIdQuery } from './Get${name}ByIdQuery'
2249
+
2250
+ export class Get${name}ByIdHandler {
2251
+ constructor(private repository: I${name}Repository) {}
2252
+
2253
+ async handle(query: Get${name}ByIdQuery): Promise<${name}DTO | null> {
2254
+ const aggregate = await this.repository.findById(query.id)
2255
+ if (!aggregate) return null
2256
+
2257
+ return {
2258
+ id: aggregate.id.value,
2259
+ status: aggregate.status,
2260
+ }
2261
+ }
2262
+ }
2263
+ `;
2264
+ }
2265
+ generateDTO(name) {
2266
+ return `/**
2267
+ * ${name} DTO
2268
+ */
2269
+
2270
+ import type { ${name}Status } from '../../Domain/Aggregates/${name}/${name}Status'
2271
+
2272
+ export interface ${name}DTO {
2273
+ id: string
2274
+ status: ${name}Status
2275
+ // Add more fields
2276
+ }
2277
+ `;
2278
+ }
2279
+ generateRepository(name) {
2280
+ return `/**
2281
+ * ${name} Repository Implementation
2282
+ */
2283
+
2284
+ import type { ${name} } from '../../Domain/Aggregates/${name}/${name}'
2285
+ import type { I${name}Repository } from '../../Domain/Repositories/I${name}Repository'
2286
+
2287
+ const store = new Map<string, ${name}>()
2288
+
2289
+ export class ${name}Repository implements I${name}Repository {
2290
+ async findById(id: string): Promise<${name} | null> {
2291
+ return store.get(id) ?? null
2292
+ }
2293
+
2294
+ async save(aggregate: ${name}): Promise<void> {
2295
+ store.set(aggregate.id.value, aggregate)
2296
+ }
2297
+
2298
+ async delete(id: string): Promise<void> {
2299
+ store.delete(id)
2300
+ }
2301
+ }
2302
+ `;
2303
+ }
2304
+ generateExceptionHandler() {
2305
+ return `/**
2306
+ * Exception Handler
2307
+ */
2308
+
2309
+ export function report(error: unknown): void {
2310
+ console.error('[Exception]', error)
2311
+ }
2312
+ `;
2313
+ }
2314
+ generateArchitectureDoc(context) {
2315
+ return `# ${context.name} - DDD Architecture Guide
2316
+
2317
+ ## Overview
2318
+
2319
+ This project follows **Domain-Driven Design (DDD)** with strategic and tactical patterns.
2320
+
2321
+ ## Bounded Contexts
2322
+
2323
+ \`\`\`
2324
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
2325
+ \u2502 Ordering \u2502\u2500\u2500\u2500\u2500\u25B6\u2502 Catalog \u2502
2326
+ \u2502 (Core Domain) \u2502 \u2502 (Supporting) \u2502
2327
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
2328
+ \u2502
2329
+ \u25BC
2330
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
2331
+ \u2502 SharedKernel \u2502
2332
+ \u2502 (Shared Types) \u2502
2333
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
2334
+ \`\`\`
2335
+
2336
+ ## Context Structure
2337
+
2338
+ Each bounded context follows this structure:
2339
+
2340
+ \`\`\`
2341
+ Context/
2342
+ \u251C\u2500\u2500 Domain/ # Core business logic
2343
+ \u2502 \u251C\u2500\u2500 Aggregates/ # Aggregate roots + entities
2344
+ \u2502 \u251C\u2500\u2500 Events/ # Domain events
2345
+ \u2502 \u251C\u2500\u2500 Repositories/ # Repository interfaces
2346
+ \u2502 \u2514\u2500\u2500 Services/ # Domain services
2347
+ \u251C\u2500\u2500 Application/ # Use cases
2348
+ \u2502 \u251C\u2500\u2500 Commands/ # Write operations
2349
+ \u2502 \u251C\u2500\u2500 Queries/ # Read operations
2350
+ \u2502 \u251C\u2500\u2500 EventHandlers/ # Event reactions
2351
+ \u2502 \u2514\u2500\u2500 DTOs/ # Data transfer objects
2352
+ \u251C\u2500\u2500 Infrastructure/ # External concerns
2353
+ \u2502 \u251C\u2500\u2500 Persistence/ # Repository implementations
2354
+ \u2502 \u2514\u2500\u2500 Providers/ # DI configuration
2355
+ \u2514\u2500\u2500 UserInterface/ # Entry points
2356
+ \u251C\u2500\u2500 Http/ # REST controllers
2357
+ \u2514\u2500\u2500 Cli/ # CLI commands
2358
+ \`\`\`
2359
+
2360
+ ## SharedKernel
2361
+
2362
+ Contains types shared across contexts:
2363
+ - **ValueObjects**: Id, Money, Email
2364
+ - **Primitives**: AggregateRoot, Entity, ValueObject
2365
+ - **Events**: DomainEvent base class
2366
+ - **EventBus**: Event dispatcher
2367
+
2368
+ ## Key Patterns
2369
+
2370
+ 1. **Aggregates**: Consistency boundaries
2371
+ 2. **Domain Events**: Inter-context communication
2372
+ 3. **CQRS**: Separate read/write models
2373
+ 4. **Repository Pattern**: Persistence abstraction
2374
+
2375
+ Created with \u2764\uFE0F using Gravito Framework
2376
+ `;
2377
+ }
2378
+ };
2379
+
2380
+ // src/generators/EnterpriseMvcGenerator.ts
2381
+ var EnterpriseMvcGenerator = class extends BaseGenerator {
2382
+ get architectureType() {
2383
+ return "enterprise-mvc";
2384
+ }
2385
+ get displayName() {
2386
+ return "Enterprise MVC";
2387
+ }
2388
+ get description() {
2389
+ return "Laravel-inspired MVC with Services and Repositories for enterprise applications";
2390
+ }
2391
+ getDirectoryStructure(context) {
2392
+ return [
2393
+ {
2394
+ type: "directory",
2395
+ name: "config",
2396
+ children: [
2397
+ { type: "file", name: "app.ts", content: this.generateAppConfig(context) },
2398
+ { type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
2399
+ { type: "file", name: "auth.ts", content: this.generateAuthConfig() },
2400
+ { type: "file", name: "cache.ts", content: this.generateCacheConfig() },
2401
+ { type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
2402
+ ]
2403
+ },
2404
+ {
2405
+ type: "directory",
2406
+ name: "src",
2407
+ children: [
2408
+ {
2409
+ type: "directory",
2410
+ name: "Http",
2411
+ children: [
2412
+ { type: "file", name: "Kernel.ts", content: this.generateHttpKernel() },
2413
+ {
2414
+ type: "directory",
2415
+ name: "Controllers",
2416
+ children: [
2417
+ { type: "file", name: "Controller.ts", content: this.generateBaseController() },
2418
+ {
2419
+ type: "file",
2420
+ name: "HomeController.ts",
2421
+ content: this.generateHomeController(context)
2422
+ }
2423
+ ]
2424
+ },
2425
+ {
2426
+ type: "directory",
2427
+ name: "Middleware",
2428
+ children: [
2429
+ { type: "file", name: "Authenticate.ts", content: this.generateAuthMiddleware() }
2430
+ ]
2431
+ }
2432
+ ]
2433
+ },
2434
+ {
2435
+ type: "directory",
2436
+ name: "Services",
2437
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2438
+ },
2439
+ {
2440
+ type: "directory",
2441
+ name: "Repositories",
2442
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2443
+ },
2444
+ {
2445
+ type: "directory",
2446
+ name: "Models",
2447
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2448
+ },
2449
+ {
2450
+ type: "directory",
2451
+ name: "Resources",
2452
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2453
+ },
2454
+ {
2455
+ type: "directory",
2456
+ name: "Providers",
2457
+ children: [
2458
+ {
2459
+ type: "file",
2460
+ name: "AppServiceProvider.ts",
2461
+ content: this.generateAppServiceProvider(context)
2462
+ },
2463
+ {
2464
+ type: "file",
2465
+ name: "RouteServiceProvider.ts",
2466
+ content: this.generateRouteServiceProvider(context)
2467
+ }
2468
+ ]
2469
+ },
2470
+ {
2471
+ type: "directory",
2472
+ name: "Exceptions",
2473
+ children: [
2474
+ { type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
2475
+ ]
2476
+ },
2477
+ { type: "file", name: "bootstrap.ts", content: this.generateBootstrap(context) },
2478
+ { type: "file", name: "routes.ts", content: this.generateRoutes(context) }
2479
+ ]
2480
+ },
2481
+ {
2482
+ type: "directory",
2483
+ name: "tests",
2484
+ children: [
2485
+ {
2486
+ type: "directory",
2487
+ name: "Unit",
2488
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2489
+ },
2490
+ {
2491
+ type: "directory",
2492
+ name: "Feature",
2493
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2494
+ }
2495
+ ]
2496
+ },
2497
+ {
2498
+ type: "directory",
2499
+ name: "database",
2500
+ children: [
2501
+ {
2502
+ type: "directory",
2503
+ name: "migrations",
2504
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2505
+ },
2506
+ {
2507
+ type: "directory",
2508
+ name: "seeders",
2509
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2510
+ }
2511
+ ]
2512
+ }
2513
+ ];
2514
+ }
2515
+ // ─────────────────────────────────────────────────────────────
2516
+ // Config Generators
2517
+ // ─────────────────────────────────────────────────────────────
2518
+ generateAppConfig(context) {
2519
+ return `/**
2520
+ * Application Configuration
2521
+ */
2522
+ export default {
2523
+ /**
2524
+ * Application name
2525
+ */
2526
+ name: process.env.APP_NAME ?? '${context.name}',
2527
+
2528
+ /**
2529
+ * Application environment
2530
+ */
2531
+ env: process.env.APP_ENV ?? 'development',
2532
+
2533
+ /**
2534
+ * Debug mode
2535
+ */
2536
+ debug: process.env.APP_DEBUG === 'true',
2537
+
2538
+ /**
2539
+ * Application URL
2540
+ */
2541
+ url: process.env.APP_URL ?? 'http://localhost:3000',
2542
+
2543
+ /**
2544
+ * Timezone
2545
+ */
2546
+ timezone: 'UTC',
2547
+
2548
+ /**
2549
+ * Locale
2550
+ */
2551
+ locale: 'en',
2552
+
2553
+ /**
2554
+ * Fallback locale
2555
+ */
2556
+ fallbackLocale: 'en',
2557
+
2558
+ /**
2559
+ * Encryption key
2560
+ */
2561
+ key: process.env.APP_KEY,
2562
+
2563
+ /**
2564
+ * Service providers to register
2565
+ */
2566
+ providers: [
2567
+ // Framework providers
2568
+ // 'RouteServiceProvider',
2569
+
2570
+ // Application providers
2571
+ // 'AppServiceProvider',
2572
+ ],
2573
+ }
2574
+ `;
2575
+ }
2576
+ generateDatabaseConfig() {
2577
+ return `/**
2578
+ * Database Configuration
2579
+ */
2580
+ export default {
2581
+ /**
2582
+ * Default connection
2583
+ */
2584
+ default: process.env.DB_CONNECTION ?? 'sqlite',
2585
+
2586
+ /**
2587
+ * Database connections
2588
+ */
2589
+ connections: {
2590
+ sqlite: {
2591
+ driver: 'sqlite',
2592
+ database: process.env.DB_DATABASE ?? 'database/database.sqlite',
2593
+ },
2594
+
2595
+ mysql: {
2596
+ driver: 'mysql',
2597
+ host: process.env.DB_HOST ?? 'localhost',
2598
+ port: Number(process.env.DB_PORT ?? 3306),
2599
+ database: process.env.DB_DATABASE ?? 'forge',
2600
+ username: process.env.DB_USERNAME ?? 'forge',
2601
+ password: process.env.DB_PASSWORD ?? '',
2602
+ },
2603
+
2604
+ postgres: {
2605
+ driver: 'postgres',
2606
+ host: process.env.DB_HOST ?? 'localhost',
2607
+ port: Number(process.env.DB_PORT ?? 5432),
2608
+ database: process.env.DB_DATABASE ?? 'forge',
2609
+ username: process.env.DB_USERNAME ?? 'forge',
2610
+ password: process.env.DB_PASSWORD ?? '',
2611
+ },
2612
+ },
2613
+
2614
+ /**
2615
+ * Migration settings
2616
+ */
2617
+ migrations: {
2618
+ table: 'migrations',
2619
+ path: 'database/migrations',
2620
+ },
2621
+ }
2622
+ `;
2623
+ }
2624
+ generateAuthConfig() {
2625
+ return `/**
2626
+ * Authentication Configuration
2627
+ */
2628
+ export default {
2629
+ /**
2630
+ * Default guard
2631
+ */
2632
+ defaults: {
2633
+ guard: 'web',
2634
+ },
2635
+
2636
+ /**
2637
+ * Authentication guards
2638
+ */
2639
+ guards: {
2640
+ web: {
2641
+ driver: 'session',
2642
+ provider: 'users',
2643
+ },
2644
+
2645
+ api: {
2646
+ driver: 'token',
2647
+ provider: 'users',
2648
+ },
2649
+ },
2650
+
2651
+ /**
2652
+ * User providers
2653
+ */
2654
+ providers: {
2655
+ users: {
2656
+ driver: 'database',
2657
+ table: 'users',
2658
+ },
2659
+ },
2660
+ }
2661
+ `;
2662
+ }
2663
+ generateCacheConfig() {
2664
+ return `/**
2665
+ * Cache Configuration
2666
+ */
2667
+ export default {
2668
+ /**
2669
+ * Default cache driver
2670
+ */
2671
+ default: process.env.CACHE_DRIVER ?? 'memory',
2672
+
2673
+ /**
2674
+ * Cache stores
2675
+ */
2676
+ stores: {
2677
+ memory: {
2678
+ driver: 'memory',
2679
+ },
2680
+
2681
+ file: {
2682
+ driver: 'file',
2683
+ path: 'storage/cache',
2684
+ },
2685
+
2686
+ redis: {
2687
+ driver: 'redis',
2688
+ host: process.env.REDIS_HOST ?? 'localhost',
2689
+ port: Number(process.env.REDIS_PORT ?? 6379),
2690
+ password: process.env.REDIS_PASSWORD ?? null,
2691
+ database: Number(process.env.REDIS_CACHE_DB ?? 0),
2692
+ },
2693
+ },
2694
+
2695
+ /**
2696
+ * Cache key prefix
2697
+ */
2698
+ prefix: 'app_cache_',
2699
+ }
2700
+ `;
2701
+ }
2702
+ generateLoggingConfig() {
2703
+ return `/**
2704
+ * Logging Configuration
2705
+ */
2706
+ export default {
2707
+ /**
2708
+ * Default log channel
2709
+ */
2710
+ default: process.env.LOG_CHANNEL ?? 'console',
2711
+
2712
+ /**
2713
+ * Log channels
2714
+ */
2715
+ channels: {
2716
+ console: {
2717
+ driver: 'console',
2718
+ level: process.env.LOG_LEVEL ?? 'debug',
2719
+ },
2720
+
2721
+ file: {
2722
+ driver: 'file',
2723
+ path: 'storage/logs/app.log',
2724
+ level: process.env.LOG_LEVEL ?? 'debug',
2725
+ },
2726
+ },
2727
+ }
2728
+ `;
2729
+ }
2730
+ // ─────────────────────────────────────────────────────────────
2731
+ // Core File Generators
2732
+ // ─────────────────────────────────────────────────────────────
2733
+ generateHttpKernel() {
2734
+ return `/**
2735
+ * HTTP Kernel
2736
+ *
2737
+ * The HTTP kernel is the central point for managing HTTP middleware.
2738
+ * Global middleware runs on every request, while route middleware
2739
+ * can be assigned to specific routes.
2740
+ */
2741
+
2742
+ import type { GravitoMiddleware } from 'gravito-core'
2743
+
2744
+ /**
2745
+ * Global middleware stack.
2746
+ * These middleware are run during every request.
2747
+ */
2748
+ export const globalMiddleware: GravitoMiddleware[] = [
2749
+ // Add global middleware here
2750
+ ]
2751
+
2752
+ /**
2753
+ * Route middleware groups.
2754
+ * These can be assigned to routes using the middleware name.
2755
+ */
2756
+ export const middlewareGroups: Record<string, GravitoMiddleware[]> = {
2757
+ web: [
2758
+ // Session middleware
2759
+ // CSRF middleware
2760
+ ],
2761
+ api: [
2762
+ // Rate limiting
2763
+ // API authentication
2764
+ ],
2765
+ }
2766
+
2767
+ /**
2768
+ * Route middleware aliases.
2769
+ * Convenient names for middleware that can be assigned to routes.
2770
+ */
2771
+ export const routeMiddleware: Record<string, GravitoMiddleware> = {
2772
+ // auth: AuthenticateMiddleware,
2773
+ // guest: RedirectIfAuthenticated,
2774
+ // throttle: ThrottleRequests,
2775
+ }
2776
+
2777
+ /**
2778
+ * Middleware priority.
2779
+ * Determines the order in which middleware are executed.
2780
+ */
2781
+ export const middlewarePriority: string[] = [
2782
+ // StartSession
2783
+ // ShareErrorsFromSession
2784
+ // AuthenticateSession
2785
+ // SubstituteBindings
2786
+ // Authorize
2787
+ ]
2788
+ `;
2789
+ }
2790
+ generateBaseController() {
2791
+ return `/**
2792
+ * Base Controller
2793
+ *
2794
+ * All controllers should extend this base class.
2795
+ * Provides common functionality and helper methods.
2796
+ */
2797
+
2798
+ export abstract class Controller {
2799
+ /**
2800
+ * Create a success response.
2801
+ */
2802
+ protected success<T>(data: T, message = 'Success') {
2803
+ return {
2804
+ success: true,
2805
+ message,
2806
+ data,
2807
+ }
2808
+ }
2809
+
2810
+ /**
2811
+ * Create an error response.
2812
+ */
2813
+ protected error(message: string, code = 'ERROR', details?: unknown) {
2814
+ return {
2815
+ success: false,
2816
+ error: {
2817
+ code,
2818
+ message,
2819
+ details,
2820
+ },
2821
+ }
2822
+ }
2823
+ }
2824
+ `;
2825
+ }
2826
+ generateHomeController(context) {
2827
+ return `/**
2828
+ * Home Controller
2829
+ */
2830
+
2831
+ import type { GravitoContext } from 'gravito-core'
2832
+ import { Controller } from './Controller'
2833
+
2834
+ export class HomeController extends Controller {
2835
+ /**
2836
+ * Display the home page.
2837
+ */
2838
+ async index(c: GravitoContext) {
2839
+ return c.json(this.success({
2840
+ name: '${context.name}',
2841
+ message: 'Welcome to your new Gravito application!',
2842
+ architecture: 'Enterprise MVC',
2843
+ }))
2844
+ }
2845
+
2846
+ /**
2847
+ * Health check endpoint.
2848
+ */
2849
+ async health(c: GravitoContext) {
2850
+ return c.json({
2851
+ status: 'healthy',
2852
+ timestamp: new Date().toISOString(),
2853
+ })
2854
+ }
2855
+ }
2856
+ `;
2857
+ }
2858
+ generateAuthMiddleware() {
2859
+ return `/**
2860
+ * Authenticate Middleware
2861
+ *
2862
+ * Protects routes that require authentication.
2863
+ */
2864
+
2865
+ import type { GravitoContext, GravitoNext } from 'gravito-core'
2866
+
2867
+ export async function Authenticate(c: GravitoContext, next: GravitoNext) {
2868
+ // TODO: Implement authentication check
2869
+ // const session = c.get('session')
2870
+ // if (!session?.user) {
2871
+ // return c.json({ error: 'Authentication required' }, 401)
2872
+ // }
2873
+
2874
+ await next()
2875
+ }
2876
+ `;
2877
+ }
2878
+ generateAppServiceProvider(context) {
2879
+ return `/**
2880
+ * App Service Provider
2881
+ *
2882
+ * This is the main application service provider.
2883
+ * Register and bootstrap application services here.
2884
+ */
2885
+
2886
+ import { ServiceProvider, type Container, type PlanetCore } from 'gravito-core'
2887
+
2888
+ export class AppServiceProvider extends ServiceProvider {
2889
+ /**
2890
+ * Register any application services.
2891
+ */
2892
+ register(container: Container): void {
2893
+ // Register services
2894
+ // container.singleton('myService', () => new MyService())
2895
+ }
2896
+
2897
+ /**
2898
+ * Bootstrap any application services.
2899
+ */
2900
+ boot(core: PlanetCore): void {
2901
+ // Bootstrap services
2902
+ console.log('${context.name} application booted!')
2903
+ }
2904
+ }
2905
+ `;
2906
+ }
2907
+ generateRouteServiceProvider(_context) {
2908
+ return `/**
2909
+ * Route Service Provider
2910
+ *
2911
+ * Configures and registers application routes.
2912
+ */
2913
+
2914
+ import { ServiceProvider, type Container, type PlanetCore } from 'gravito-core'
2915
+ import { registerRoutes } from '../routes'
2916
+
2917
+ export class RouteServiceProvider extends ServiceProvider {
2918
+ /**
2919
+ * Register any application services.
2920
+ */
2921
+ register(_container: Container): void {
2922
+ // Routes are registered in boot
2923
+ }
2924
+
2925
+ /**
2926
+ * Bootstrap any application services.
2927
+ */
2928
+ boot(core: PlanetCore): void {
2929
+ registerRoutes(core.router)
2930
+ }
2931
+ }
2932
+ `;
2933
+ }
2934
+ generateExceptionHandler() {
2935
+ return `/**
2936
+ * Exception Handler
2937
+ *
2938
+ * Handles all exceptions thrown by the application.
2939
+ * Customize error responses and logging here.
2940
+ */
2941
+
2942
+ import type { ErrorHandlerContext } from 'gravito-core'
2943
+
2944
+ /**
2945
+ * Report an exception (logging, monitoring, etc.)
2946
+ */
2947
+ export function report(error: unknown, context: ErrorHandlerContext): void {
2948
+ // Log to external service (Sentry, etc.)
2949
+ if (!context.isProduction) {
2950
+ console.error('[Exception Handler]', error)
2951
+ }
2952
+ }
2953
+
2954
+ /**
2955
+ * Determine if the exception should be reported.
2956
+ */
2957
+ export function shouldReport(error: unknown): boolean {
2958
+ // Don't report 4xx errors
2959
+ if (error instanceof Error && 'status' in error) {
2960
+ const status = (error as any).status
2961
+ if (status >= 400 && status < 500) {
2962
+ return false
2963
+ }
2964
+ }
2965
+
2966
+ return true
2967
+ }
2968
+
2969
+ /**
2970
+ * A list of exception types that should not be reported.
2971
+ */
2972
+ export const dontReport: string[] = [
2973
+ 'ValidationException',
2974
+ 'NotFoundException',
2975
+ ]
2976
+ `;
2977
+ }
2978
+ generateBootstrap(context) {
2979
+ return `/**
2980
+ * Application Bootstrap
2981
+ *
2982
+ * This is the entry point for your application.
2983
+ * It initializes the core and registers all providers.
2984
+ */
2985
+
2986
+ import { PlanetCore } from 'gravito-core'
2987
+ import { AppServiceProvider } from './Providers/AppServiceProvider'
2988
+ import { RouteServiceProvider } from './Providers/RouteServiceProvider'
2989
+
2990
+ // Load environment variables
2991
+ // Bun automatically loads .env
2992
+
2993
+ // Create application core
2994
+ const core = new PlanetCore({
2995
+ config: {
2996
+ APP_NAME: '${context.name}',
2997
+ },
2998
+ })
2999
+
3000
+ // Register service providers
3001
+ core.register(new AppServiceProvider())
3002
+ core.register(new RouteServiceProvider())
3003
+
3004
+ // Bootstrap the application
3005
+ await core.bootstrap()
3006
+
3007
+ // Export for Bun.serve()
3008
+ export default core.liftoff()
3009
+ `;
3010
+ }
3011
+ generateRoutes(_context) {
3012
+ return `/**
3013
+ * Application Routes
3014
+ *
3015
+ * Define your application routes here.
3016
+ */
3017
+
3018
+ import { HomeController } from './Http/Controllers/HomeController'
3019
+
3020
+ export function registerRoutes(router: any): void {
3021
+ const home = new HomeController()
3022
+
3023
+ // API Routes
3024
+ router.get('/api', (c: any) => home.index(c))
3025
+ router.get('/api/health', (c: any) => home.health(c))
3026
+
3027
+ // Web Routes
3028
+ router.get('/', (c: any) => home.index(c))
3029
+ }
3030
+ `;
3031
+ }
3032
+ // ─────────────────────────────────────────────────────────────
3033
+ // Architecture Documentation
3034
+ // ─────────────────────────────────────────────────────────────
3035
+ generateArchitectureDoc(context) {
3036
+ return `# ${context.name} - Architecture Guide
3037
+
3038
+ ## Overview
3039
+
3040
+ This project uses **Enterprise MVC Architecture**, inspired by Laravel's conventions.
3041
+ It provides a clear separation of concerns while remaining pragmatic and easy to understand.
3042
+
3043
+ ## Directory Structure
3044
+
3045
+ \`\`\`
3046
+ ${context.name}/
3047
+ \u251C\u2500\u2500 config/ # Configuration files
3048
+ \u2502 \u251C\u2500\u2500 app.ts # Application settings
3049
+ \u2502 \u251C\u2500\u2500 database.ts # Database connections
3050
+ \u2502 \u251C\u2500\u2500 auth.ts # Authentication settings
3051
+ \u2502 \u251C\u2500\u2500 cache.ts # Cache configuration
3052
+ \u2502 \u2514\u2500\u2500 logging.ts # Logging channels
3053
+ \u251C\u2500\u2500 src/
3054
+ \u2502 \u251C\u2500\u2500 Http/
3055
+ \u2502 \u2502 \u251C\u2500\u2500 Kernel.ts # HTTP middleware management
3056
+ \u2502 \u2502 \u251C\u2500\u2500 Controllers/ # HTTP request handlers
3057
+ \u2502 \u2502 \u2514\u2500\u2500 Middleware/ # Request/Response interceptors
3058
+ \u2502 \u251C\u2500\u2500 Services/ # Business logic layer
3059
+ \u2502 \u251C\u2500\u2500 Repositories/ # Data access layer
3060
+ \u2502 \u251C\u2500\u2500 Models/ # Data models/entities
3061
+ \u2502 \u251C\u2500\u2500 Resources/ # API response transformers
3062
+ \u2502 \u251C\u2500\u2500 Providers/ # Service providers
3063
+ \u2502 \u251C\u2500\u2500 Exceptions/ # Custom exceptions
3064
+ \u2502 \u251C\u2500\u2500 bootstrap.ts # Application entry point
3065
+ \u2502 \u2514\u2500\u2500 routes.ts # Route definitions
3066
+ \u251C\u2500\u2500 tests/
3067
+ \u2502 \u251C\u2500\u2500 Unit/ # Unit tests
3068
+ \u2502 \u2514\u2500\u2500 Feature/ # Integration tests
3069
+ \u2514\u2500\u2500 database/
3070
+ \u251C\u2500\u2500 migrations/ # Database migrations
3071
+ \u2514\u2500\u2500 seeders/ # Database seeders
3072
+ \`\`\`
3073
+
3074
+ ## Request Flow
3075
+
3076
+ \`\`\`
3077
+ Request
3078
+ \u2502
3079
+ \u25BC
3080
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
3081
+ \u2502 Http/Kernel \u2502 \u2500\u2500\u25B6 Global Middleware
3082
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
3083
+ \u2502
3084
+ \u25BC
3085
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
3086
+ \u2502 Router \u2502 \u2500\u2500\u25B6 Route Matching
3087
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
3088
+ \u2502
3089
+ \u25BC
3090
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
3091
+ \u2502 Controller \u2502 \u2500\u2500\u25B6 Request Handling
3092
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
3093
+ \u2502
3094
+ \u25BC
3095
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
3096
+ \u2502 Service \u2502 \u2500\u2500\u25B6 Business Logic
3097
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
3098
+ \u2502
3099
+ \u25BC
3100
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
3101
+ \u2502 Repository \u2502 \u2500\u2500\u25B6 Data Access
3102
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
3103
+ \u2502
3104
+ \u25BC
3105
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
3106
+ \u2502 Resource \u2502 \u2500\u2500\u25B6 Response Transform
3107
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
3108
+ \u2502
3109
+ \u25BC
3110
+ Response
3111
+ \`\`\`
3112
+
3113
+ ## Layer Responsibilities
3114
+
3115
+ ### Controllers
3116
+ - Handle HTTP requests and responses
3117
+ - Validate input (or delegate to Form Requests)
3118
+ - Call services for business logic
3119
+ - Return transformed responses
3120
+
3121
+ ### Services
3122
+ - Contain business logic
3123
+ - Orchestrate multiple repositories
3124
+ - Enforce business rules
3125
+ - Should be framework-agnostic
3126
+
3127
+ ### Repositories
3128
+ - Abstract data access
3129
+ - Handle database queries
3130
+ - Return domain objects
3131
+
3132
+ ### Resources
3133
+ - Transform models for API responses
3134
+ - Control what data is exposed
3135
+ - Handle nested relationships
3136
+
3137
+ ## Getting Started
3138
+
3139
+ \`\`\`bash
3140
+ # Install dependencies
3141
+ bun install
3142
+
3143
+ # Run development server
3144
+ bun run dev
3145
+
3146
+ # Run tests
3147
+ bun test
3148
+ \`\`\`
3149
+
3150
+ ## Configuration
3151
+
3152
+ All configuration is in the \`config/\` directory.
3153
+ Environment-specific values should use environment variables.
3154
+
3155
+ ## Service Providers
3156
+
3157
+ Service providers are the central place to configure your application.
3158
+ They are registered in \`src/bootstrap.ts\`:
3159
+
3160
+ \`\`\`typescript
3161
+ core.register(new AppServiceProvider())
3162
+ core.register(new RouteServiceProvider())
3163
+ \`\`\`
3164
+
3165
+ Created with \u2764\uFE0F using Gravito Framework
3166
+ `;
3167
+ }
3168
+ };
3169
+
3170
+ // src/Scaffold.ts
3171
+ var import_node_path3 = __toESM(require("path"), 1);
3172
+ var Scaffold = class {
3173
+ templatesDir;
3174
+ verbose;
3175
+ constructor(options = {}) {
3176
+ this.templatesDir = options.templatesDir ?? import_node_path3.default.resolve(__dirname, "../templates");
3177
+ this.verbose = options.verbose ?? false;
3178
+ }
3179
+ /**
3180
+ * Get all available architecture types.
3181
+ */
3182
+ getArchitectureTypes() {
3183
+ return [
3184
+ {
3185
+ type: "enterprise-mvc",
3186
+ name: "Enterprise MVC",
3187
+ description: "Laravel-inspired MVC with Services and Repositories"
3188
+ },
3189
+ {
3190
+ type: "clean",
3191
+ name: "Clean Architecture",
3192
+ description: "Uncle Bob's Clean Architecture with strict dependency rules"
3193
+ },
3194
+ {
3195
+ type: "ddd",
3196
+ name: "Domain-Driven Design",
3197
+ description: "Full DDD with Bounded Contexts and CQRS"
3198
+ }
3199
+ ];
3200
+ }
3201
+ /**
3202
+ * Create a new project scaffold.
3203
+ */
3204
+ async create(options) {
3205
+ const generator = this.createGenerator(options.architecture);
3206
+ const context = BaseGenerator.createContext(
3207
+ options.name,
3208
+ options.targetDir,
3209
+ options.architecture,
3210
+ options.packageManager ?? "bun",
3211
+ options.context ?? {}
3212
+ );
3213
+ try {
3214
+ const filesCreated = await generator.generate(context);
3215
+ return {
3216
+ success: true,
3217
+ targetDir: options.targetDir,
3218
+ filesCreated
3219
+ };
3220
+ } catch (error) {
3221
+ return {
3222
+ success: false,
3223
+ targetDir: options.targetDir,
3224
+ filesCreated: [],
3225
+ errors: [error instanceof Error ? error.message : String(error)]
3226
+ };
3227
+ }
3228
+ }
3229
+ /**
3230
+ * Create a generator for the specified architecture.
3231
+ */
3232
+ createGenerator(type) {
3233
+ const config = {
3234
+ templatesDir: this.templatesDir,
3235
+ verbose: this.verbose
3236
+ };
3237
+ switch (type) {
3238
+ case "enterprise-mvc":
3239
+ return new EnterpriseMvcGenerator(config);
3240
+ case "clean":
3241
+ return new CleanArchitectureGenerator(config);
3242
+ case "ddd":
3243
+ return new DddGenerator(config);
3244
+ default:
3245
+ throw new Error(`Unknown architecture type: ${type}`);
3246
+ }
3247
+ }
3248
+ /**
3249
+ * Generate a single module (for DDD bounded context).
3250
+ */
3251
+ async generateModule(_targetDir, _moduleName, _options = {}) {
3252
+ throw new Error("Module generation not yet implemented");
3253
+ }
3254
+ /**
3255
+ * Generate a service provider.
3256
+ */
3257
+ async generateProvider(_targetDir, _providerName) {
3258
+ throw new Error("Provider generation not yet implemented");
3259
+ }
3260
+ };
3261
+ // Annotate the CommonJS export names for ESM import in node:
3262
+ 0 && (module.exports = {
3263
+ BaseGenerator,
3264
+ CleanArchitectureGenerator,
3265
+ DddGenerator,
3266
+ EnterpriseMvcGenerator,
3267
+ Scaffold,
3268
+ StubGenerator
3269
+ });