@gravito/scaffold 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  BaseGenerator: () => BaseGenerator,
34
34
  CleanArchitectureGenerator: () => CleanArchitectureGenerator,
35
35
  DddGenerator: () => DddGenerator,
36
+ DependencyValidator: () => DependencyValidator,
36
37
  EnterpriseMvcGenerator: () => EnterpriseMvcGenerator,
37
38
  EnvironmentDetector: () => EnvironmentDetector,
38
39
  FileMerger: () => FileMerger,
@@ -44,6 +45,147 @@ __export(index_exports, {
44
45
  });
45
46
  module.exports = __toCommonJS(index_exports);
46
47
 
48
+ // src/DependencyValidator.ts
49
+ var DependencyValidator = class _DependencyValidator {
50
+ /**
51
+ * Driver 到 Package 的映射規則
52
+ */
53
+ static DRIVER_DEPENDENCIES = [
54
+ {
55
+ driver: "redis",
56
+ requiredPackages: ["@gravito/ion"],
57
+ description: "Redis cache/queue driver requires @gravito/ion"
58
+ },
59
+ {
60
+ driver: "postgresql",
61
+ requiredPackages: ["@gravito/atlas", "pg"],
62
+ description: "PostgreSQL driver requires @gravito/atlas and pg"
63
+ },
64
+ {
65
+ driver: "mysql",
66
+ requiredPackages: ["@gravito/atlas", "mysql2"],
67
+ description: "MySQL driver requires @gravito/atlas and mysql2"
68
+ },
69
+ {
70
+ driver: "sqlite",
71
+ requiredPackages: ["@gravito/atlas", "better-sqlite3"],
72
+ description: "SQLite driver requires @gravito/atlas and better-sqlite3"
73
+ },
74
+ {
75
+ driver: "s3",
76
+ requiredPackages: ["@gravito/stasis", "@aws-sdk/client-s3"],
77
+ description: "S3 storage driver requires @gravito/stasis and AWS SDK"
78
+ },
79
+ {
80
+ driver: "r2",
81
+ requiredPackages: ["@gravito/stasis", "@aws-sdk/client-s3"],
82
+ description: "R2 storage driver requires @gravito/stasis and AWS SDK"
83
+ }
84
+ ];
85
+ /**
86
+ * Feature 衝突規則
87
+ */
88
+ static CONFLICTS = [
89
+ {
90
+ features: ["postgres", "mysql", "sqlite"],
91
+ reason: "\u4E0D\u80FD\u540C\u6642\u4F7F\u7528\u591A\u500B\u8CC7\u6599\u5EAB driver (PostgreSQL, MySQL, SQLite)"
92
+ }
93
+ ];
94
+ /**
95
+ * Feature 依賴映射
96
+ */
97
+ static FEATURE_DEPENDENCIES = {
98
+ stream: ["@gravito/beam"],
99
+ monitor: ["@gravito/spectrum"],
100
+ graphql: ["@gravito/constellation"]
101
+ };
102
+ /**
103
+ * 驗證 Profile 配置
104
+ *
105
+ * @param config - Profile 配置
106
+ * @param packageJson - 專案的 package.json 內容
107
+ * @returns 驗證結果
108
+ */
109
+ validate(config, packageJson) {
110
+ const errors = [];
111
+ const warnings = [];
112
+ this.validateDriverDependencies(config, packageJson, errors);
113
+ this.validateFeatureConflicts(config, errors);
114
+ this.validateFeatureDependencies(config, packageJson, warnings);
115
+ return {
116
+ valid: errors.length === 0,
117
+ errors,
118
+ warnings
119
+ };
120
+ }
121
+ /**
122
+ * 驗證 driver 依賴
123
+ */
124
+ validateDriverDependencies(config, packageJson, errors) {
125
+ for (const [service, driver] of Object.entries(config.drivers)) {
126
+ const rule = _DependencyValidator.DRIVER_DEPENDENCIES.find((r) => r.driver === driver);
127
+ if (rule) {
128
+ for (const pkg of rule.requiredPackages) {
129
+ if (!this.hasPackage(packageJson, pkg)) {
130
+ errors.push(`\u7F3A\u5C11\u4F9D\u8CF4: ${pkg} (${service} driver '${driver}' \u6240\u9700)`);
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * 驗證 feature 衝突
138
+ */
139
+ validateFeatureConflicts(config, errors) {
140
+ for (const conflict of _DependencyValidator.CONFLICTS) {
141
+ const conflictingFeatures = conflict.features.filter((f) => config.features.includes(f));
142
+ if (conflictingFeatures.length > 1) {
143
+ errors.push(`Feature \u885D\u7A81: ${conflictingFeatures.join(", ")} - ${conflict.reason}`);
144
+ }
145
+ }
146
+ }
147
+ /**
148
+ * 驗證 feature 依賴
149
+ */
150
+ validateFeatureDependencies(config, packageJson, warnings) {
151
+ for (const feature of config.features) {
152
+ const requiredPackages = _DependencyValidator.FEATURE_DEPENDENCIES[feature];
153
+ if (requiredPackages) {
154
+ for (const pkg of requiredPackages) {
155
+ if (!this.hasPackage(packageJson, pkg)) {
156
+ warnings.push(`Feature "${feature}" \u9700\u8981 ${pkg}`);
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ /**
163
+ * 檢查 package.json 是否包含指定 package
164
+ */
165
+ hasPackage(packageJson, packageName) {
166
+ return packageJson.dependencies?.[packageName] !== void 0 || packageJson.devDependencies?.[packageName] !== void 0;
167
+ }
168
+ /**
169
+ * 建議安裝缺失的依賴
170
+ *
171
+ * @param result - 驗證結果
172
+ * @returns 安裝命令建議
173
+ */
174
+ static suggestInstallCommand(result) {
175
+ if (result.errors.length === 0) {
176
+ return null;
177
+ }
178
+ const missingPackages = result.errors.map((err) => {
179
+ const match = err.match(/缺少依賴: ([@\w/-]+)/);
180
+ return match ? match[1] : null;
181
+ }).filter((pkg) => pkg !== null);
182
+ if (missingPackages.length === 0) {
183
+ return null;
184
+ }
185
+ return `bun add ${missingPackages.join(" ")}`;
186
+ }
187
+ };
188
+
47
189
  // src/EnvironmentDetector.ts
48
190
  var EnvironmentDetector = class {
49
191
  detect() {
@@ -144,12 +286,52 @@ ${overlay}`;
144
286
  };
145
287
 
146
288
  // src/generators/BaseGenerator.ts
147
- var import_promises2 = __toESM(require("fs/promises"), 1);
148
- var import_node_path2 = __toESM(require("path"), 1);
289
+ var import_promises4 = __toESM(require("fs/promises"), 1);
290
+ var import_node_path4 = __toESM(require("path"), 1);
149
291
 
150
- // src/generators/StubGenerator.ts
292
+ // src/utils/FileUtilities.ts
151
293
  var import_promises = __toESM(require("fs/promises"), 1);
152
294
  var import_node_path = __toESM(require("path"), 1);
295
+ var FileUtilities = class _FileUtilities {
296
+ static async walk(dir) {
297
+ const files = await import_promises.default.readdir(dir);
298
+ const paths = [];
299
+ for (const file of files) {
300
+ const filePath = import_node_path.default.join(dir, file);
301
+ const stat = await import_promises.default.stat(filePath);
302
+ if (stat.isDirectory()) {
303
+ paths.push(...await _FileUtilities.walk(filePath));
304
+ } else {
305
+ paths.push(filePath);
306
+ }
307
+ }
308
+ return paths;
309
+ }
310
+ static async writeFile(basePath, relativePath, content, fileMerger, log) {
311
+ const fullPath = import_node_path.default.resolve(basePath, relativePath);
312
+ await import_promises.default.mkdir(import_node_path.default.dirname(fullPath), { recursive: true });
313
+ let finalContent = content;
314
+ try {
315
+ const existingContent = await import_promises.default.readFile(fullPath, "utf-8");
316
+ finalContent = fileMerger.merge(relativePath, existingContent, content);
317
+ if (finalContent !== content) {
318
+ log?.(`\u{1F504} Merged file: ${relativePath}`);
319
+ }
320
+ } catch {
321
+ }
322
+ await import_promises.default.writeFile(fullPath, finalContent, "utf-8");
323
+ log?.(`\u{1F4C4} Created file: ${relativePath}`);
324
+ return fullPath;
325
+ }
326
+ };
327
+
328
+ // src/utils/TemplateManager.ts
329
+ var import_promises3 = __toESM(require("fs/promises"), 1);
330
+ var import_node_path3 = __toESM(require("path"), 1);
331
+
332
+ // src/generators/StubGenerator.ts
333
+ var import_promises2 = __toESM(require("fs/promises"), 1);
334
+ var import_node_path2 = __toESM(require("path"), 1);
153
335
  var import_handlebars = __toESM(require("handlebars"), 1);
154
336
  var StubGenerator = class {
155
337
  config;
@@ -238,16 +420,16 @@ var StubGenerator = class {
238
420
  * @returns Path to the generated file
239
421
  */
240
422
  async generate(stubName, outputPath, variables = {}) {
241
- const stubPath = import_node_path.default.resolve(this.config.stubsDir, stubName);
242
- const template = await import_promises.default.readFile(stubPath, "utf-8");
423
+ const stubPath = import_node_path2.default.resolve(this.config.stubsDir, stubName);
424
+ const template = await import_promises2.default.readFile(stubPath, "utf-8");
243
425
  const compiled = this.handlebars.compile(template);
244
426
  const content = compiled({
245
427
  ...this.config.defaultVariables,
246
428
  ...variables
247
429
  });
248
- const fullOutputPath = import_node_path.default.resolve(this.config.outputDir, outputPath);
249
- await import_promises.default.mkdir(import_node_path.default.dirname(fullOutputPath), { recursive: true });
250
- await import_promises.default.writeFile(fullOutputPath, content, "utf-8");
430
+ const fullOutputPath = import_node_path2.default.resolve(this.config.outputDir, outputPath);
431
+ await import_promises2.default.mkdir(import_node_path2.default.dirname(fullOutputPath), { recursive: true });
432
+ await import_promises2.default.writeFile(fullOutputPath, content, "utf-8");
251
433
  return fullOutputPath;
252
434
  }
253
435
  /**
@@ -299,41 +481,57 @@ var StubGenerator = class {
299
481
  }
300
482
  };
301
483
 
302
- // src/generators/BaseGenerator.ts
303
- async function walk(dir) {
304
- const files = await import_promises2.default.readdir(dir);
305
- const paths = [];
306
- for (const file of files) {
307
- const filePath = import_node_path2.default.join(dir, file);
308
- const stat = await import_promises2.default.stat(filePath);
309
- if (stat.isDirectory()) {
310
- paths.push(...await walk(filePath));
311
- } else {
312
- paths.push(filePath);
484
+ // src/utils/TemplateManager.ts
485
+ var TemplateManager = class {
486
+ stubGenerator;
487
+ constructor(templatesDir) {
488
+ this.stubGenerator = new StubGenerator({
489
+ stubsDir: templatesDir,
490
+ outputDir: ""
491
+ });
492
+ }
493
+ render(template, context) {
494
+ return this.stubGenerator.render(template, context);
495
+ }
496
+ async applyOverlay(sourceDir, targetDir, context, fileMerger, log) {
497
+ const createdFiles = [];
498
+ try {
499
+ await import_promises3.default.access(sourceDir);
500
+ } catch {
501
+ return [];
502
+ }
503
+ const files = await FileUtilities.walk(sourceDir);
504
+ for (const filePath of files) {
505
+ const relativePath = import_node_path3.default.relative(sourceDir, filePath);
506
+ let content = await import_promises3.default.readFile(filePath, "utf-8");
507
+ try {
508
+ content = this.render(content, context);
509
+ } catch {
510
+ }
511
+ const fullPath = await FileUtilities.writeFile(
512
+ targetDir,
513
+ relativePath,
514
+ content,
515
+ fileMerger,
516
+ log
517
+ );
518
+ createdFiles.push(fullPath);
313
519
  }
520
+ return createdFiles;
314
521
  }
315
- return paths;
316
- }
522
+ };
523
+
524
+ // src/generators/BaseGenerator.ts
317
525
  var BaseGenerator = class {
318
526
  config;
319
- stubGenerator;
527
+ templateManager;
320
528
  fileMerger;
321
529
  filesCreated = [];
322
530
  constructor(config) {
323
531
  this.config = config;
324
- this.stubGenerator = new StubGenerator({
325
- stubsDir: config.templatesDir,
326
- outputDir: ""
327
- // Set per-generation
328
- });
532
+ this.templateManager = new TemplateManager(config.templatesDir);
329
533
  this.fileMerger = new FileMerger();
330
534
  }
331
- /**
332
- * Generate the project scaffold.
333
- *
334
- * @param context - Generator context
335
- * @returns Array of created file paths
336
- */
337
535
  async generate(context) {
338
536
  this.filesCreated = [];
339
537
  const structure = this.getDirectoryStructure(context);
@@ -343,50 +541,73 @@ var BaseGenerator = class {
343
541
  await this.applyFeatureOverlays(context);
344
542
  return this.filesCreated;
345
543
  }
346
- /**
347
- * Create directory structure recursively.
348
- */
349
544
  async createStructure(basePath, nodes, context) {
350
545
  for (const node of nodes) {
351
- const fullPath = import_node_path2.default.resolve(basePath, node.name);
546
+ const fullPath = import_node_path4.default.resolve(basePath, node.name);
352
547
  if (node.type === "directory") {
353
- await import_promises2.default.mkdir(fullPath, { recursive: true });
548
+ await import_promises4.default.mkdir(fullPath, { recursive: true });
354
549
  this.log(`\u{1F4C1} Created directory: ${node.name}`);
355
550
  if (node.children) {
356
551
  await this.createStructure(fullPath, node.children, context);
357
552
  }
358
553
  } else {
359
- await import_promises2.default.mkdir(import_node_path2.default.dirname(fullPath), { recursive: true });
554
+ await import_promises4.default.mkdir(import_node_path4.default.dirname(fullPath), { recursive: true });
555
+ let content = "";
360
556
  if (node.template) {
361
- const templatePath = import_node_path2.default.resolve(this.config.templatesDir, node.template);
362
557
  try {
363
- const template = await import_promises2.default.readFile(templatePath, "utf-8");
364
- const content = this.stubGenerator.render(template, context);
365
- await import_promises2.default.writeFile(fullPath, content, "utf-8");
558
+ const templatePath = import_node_path4.default.resolve(this.config.templatesDir, node.template);
559
+ const template = await import_promises4.default.readFile(templatePath, "utf-8");
560
+ content = this.templateManager.render(template, context);
366
561
  } catch {
367
- await import_promises2.default.writeFile(fullPath, node.content ?? "", "utf-8");
562
+ content = node.content ?? "";
368
563
  }
369
- } else if (node.content) {
370
- await import_promises2.default.writeFile(fullPath, node.content, "utf-8");
371
564
  } else {
372
- await import_promises2.default.writeFile(fullPath, "", "utf-8");
565
+ content = node.content ?? "";
373
566
  }
374
- this.filesCreated.push(fullPath);
375
- this.log(`\u{1F4C4} Created file: ${node.name}`);
567
+ const relativePath = import_node_path4.default.relative(context.targetDir, fullPath);
568
+ const writtenPath = await FileUtilities.writeFile(
569
+ context.targetDir,
570
+ relativePath,
571
+ content,
572
+ this.fileMerger,
573
+ (msg) => this.log(msg)
574
+ );
575
+ this.filesCreated.push(writtenPath);
376
576
  }
377
577
  }
378
578
  }
379
- /**
380
- * Generate common files (package.json, .env, etc.)
381
- */
382
579
  async generateCommonFiles(context) {
580
+ const commonDir = import_node_path4.default.resolve(this.config.templatesDir, "common");
581
+ const extendedContext = {
582
+ ...context,
583
+ entrypoint: context.architecture === "ddd" ? "dist/main.js" : "dist/bootstrap.js",
584
+ dbConnection: context.profile === "core" ? "sqlite" : "postgres"
585
+ };
586
+ await this.generateFileFromTemplate(
587
+ commonDir,
588
+ "env.example.hbs",
589
+ ".env.example",
590
+ extendedContext
591
+ );
592
+ await this.generateFileFromTemplate(commonDir, "env.example.hbs", ".env", extendedContext);
593
+ await this.generateFileFromTemplate(commonDir, "gitignore.hbs", ".gitignore", extendedContext);
594
+ await this.generateFileFromTemplate(
595
+ commonDir,
596
+ "tsconfig.json.hbs",
597
+ "tsconfig.json",
598
+ extendedContext
599
+ );
600
+ await this.generateFileFromTemplate(commonDir, "Dockerfile.hbs", "Dockerfile", extendedContext);
383
601
  await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
384
- await this.writeFile(context.targetDir, ".env.example", this.generateEnvExample(context));
385
- await this.writeFile(context.targetDir, ".env", this.generateEnvExample(context));
386
- await this.writeFile(context.targetDir, ".gitignore", this.generateGitignore());
387
- await this.writeFile(context.targetDir, "tsconfig.json", this.generateTsConfig());
388
- await this.writeFile(context.targetDir, "Dockerfile", this.generateDockerfile(context));
389
- await this.writeFile(context.targetDir, ".dockerignore", this.generateDockerIgnore());
602
+ await this.writeFile(
603
+ context.targetDir,
604
+ ".dockerignore",
605
+ `node_modules
606
+ dist
607
+ .git
608
+ .env
609
+ `
610
+ );
390
611
  await this.writeFile(
391
612
  context.targetDir,
392
613
  "ARCHITECTURE.md",
@@ -395,104 +616,74 @@ var BaseGenerator = class {
395
616
  await this.generateCheckScripts(context);
396
617
  await this.generateSkills(context);
397
618
  }
398
- /**
399
- * Copy AI Skills to the project
400
- */
401
- async generateSkills(context) {
402
- const skillsDir = import_node_path2.default.resolve(this.config.templatesDir, "skills");
403
- const targetSkillsDir = import_node_path2.default.join(".skills");
619
+ async generateFileFromTemplate(tplDir, tplName, targetName, context) {
404
620
  try {
405
- await import_promises2.default.access(skillsDir);
406
- } catch {
407
- return;
408
- }
409
- const files = await walk(skillsDir);
410
- for (const filePath of files) {
411
- const relativePath = import_node_path2.default.relative(skillsDir, filePath);
412
- const targetPath = import_node_path2.default.join(targetSkillsDir, relativePath);
413
- let content = await import_promises2.default.readFile(filePath, "utf-8");
414
- try {
415
- content = this.stubGenerator.render(content, context);
416
- } catch {
417
- }
418
- await this.writeFile(context.targetDir, targetPath, content);
621
+ const template = await import_promises4.default.readFile(import_node_path4.default.join(tplDir, tplName), "utf-8");
622
+ const content = this.templateManager.render(template, context);
623
+ await this.writeFile(context.targetDir, targetName, content);
624
+ } catch (e) {
625
+ this.log(`\u26A0\uFE0F Failed to generate ${targetName}: ${e}`);
419
626
  }
420
627
  }
421
- /**
422
- * Apply profile-specific overlays
423
- */
628
+ async generateSkills(context) {
629
+ const skillsDir = import_node_path4.default.resolve(this.config.templatesDir, "skills");
630
+ const created = await this.templateManager.applyOverlay(
631
+ skillsDir,
632
+ import_node_path4.default.join(context.targetDir, ".skills"),
633
+ context,
634
+ this.fileMerger,
635
+ (msg) => this.log(msg)
636
+ );
637
+ this.filesCreated.push(...created);
638
+ }
424
639
  async applyOverlays(context) {
425
640
  const profile = context.profile;
426
- if (!profile) return;
427
- const overlayDir = import_node_path2.default.resolve(this.config.templatesDir, "overlays", profile);
428
- await this.copyOverlayDirectory(overlayDir, context);
641
+ if (profile) {
642
+ const overlayDir = import_node_path4.default.resolve(this.config.templatesDir, "overlays", profile);
643
+ await this.copyOverlayDirectory(overlayDir, context);
644
+ }
429
645
  }
430
- /**
431
- * Apply feature-specific overlays
432
- */
433
646
  async applyFeatureOverlays(context) {
434
647
  const features = context.features || [];
435
648
  for (const feature of features) {
436
- const overlayDir = import_node_path2.default.resolve(this.config.templatesDir, "features", feature);
649
+ const overlayDir = import_node_path4.default.resolve(this.config.templatesDir, "features", feature);
437
650
  await this.copyOverlayDirectory(overlayDir, context);
438
651
  }
439
652
  }
440
- /**
441
- * Helper to copy/merge an overlay directory into the target
442
- */
443
653
  async copyOverlayDirectory(sourceDir, context) {
444
- try {
445
- await import_promises2.default.access(sourceDir);
446
- } catch {
447
- return;
448
- }
449
- const files = await walk(sourceDir);
450
- for (const filePath of files) {
451
- const relativePath = import_node_path2.default.relative(sourceDir, filePath);
452
- let content = await import_promises2.default.readFile(filePath, "utf-8");
453
- try {
454
- content = this.stubGenerator.render(content, context);
455
- } catch {
456
- }
457
- await this.writeFile(context.targetDir, relativePath, content);
458
- }
654
+ const created = await this.templateManager.applyOverlay(
655
+ sourceDir,
656
+ context.targetDir,
657
+ context,
658
+ this.fileMerger,
659
+ (msg) => this.log(msg)
660
+ );
661
+ this.filesCreated.push(...created);
459
662
  }
460
- /**
461
- * Write a file and track it.
462
- */
463
663
  async writeFile(basePath, relativePath, content) {
464
- const fullPath = import_node_path2.default.resolve(basePath, relativePath);
465
- await import_promises2.default.mkdir(import_node_path2.default.dirname(fullPath), { recursive: true });
466
- let finalContent = content;
467
- try {
468
- const existingContent = await import_promises2.default.readFile(fullPath, "utf-8");
469
- finalContent = this.fileMerger.merge(relativePath, existingContent, content);
470
- if (finalContent !== content) {
471
- this.log(`\u{1F504} Merged file: ${relativePath}`);
472
- }
473
- } catch {
474
- }
475
- await import_promises2.default.writeFile(fullPath, finalContent, "utf-8");
476
- this.filesCreated.push(fullPath);
477
- this.log(`\u{1F4C4} Created file: ${relativePath}`);
664
+ const writtenPath = await FileUtilities.writeFile(
665
+ basePath,
666
+ relativePath,
667
+ content,
668
+ this.fileMerger,
669
+ (msg) => this.log(msg)
670
+ );
671
+ this.filesCreated.push(writtenPath);
478
672
  }
479
- /**
480
- * Generate package.json content.
481
- */
482
673
  generatePackageJson(context) {
483
674
  const profile = context.profile || "core";
484
- const baseDependencies = {
675
+ const deps = {
485
676
  "@gravito/core": "^1.0.0-beta.5",
486
677
  "@gravito/atlas": "^1.0.0-beta.5",
487
678
  "@gravito/plasma": "^1.0.0-beta.5",
488
679
  "@gravito/stream": "^1.0.0-beta.5"
489
680
  };
490
681
  if (profile === "enterprise" || profile === "scale") {
491
- baseDependencies["@gravito/quasar"] = "^1.0.0-beta.5";
492
- baseDependencies["@gravito/horizon"] = "^1.0.0-beta.5";
682
+ deps["@gravito/quasar"] = "^1.0.0-beta.5";
683
+ deps["@gravito/horizon"] = "^1.0.0-beta.5";
493
684
  }
494
685
  if (context.withSpectrum) {
495
- baseDependencies["@gravito/spectrum"] = "^1.0.0-beta.1";
686
+ deps["@gravito/spectrum"] = "^1.0.0-beta.5";
496
687
  }
497
688
  const pkg = {
498
689
  name: context.nameKebabCase,
@@ -503,717 +694,426 @@ var BaseGenerator = class {
503
694
  build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
504
695
  start: "bun run dist/bootstrap.js",
505
696
  test: "bun test",
506
- typecheck: "tsc --noEmit",
507
- check: "bun run typecheck && bun run test",
508
- "check:deps": "bun run scripts/check-dependencies.ts",
509
- validate: "bun run check && bun run check:deps",
510
- precommit: "bun run validate",
511
- "docker:build": `docker build -t ${context.nameKebabCase} .`,
512
- "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
697
+ typecheck: "bun tsc --noEmit",
698
+ validate: "bun run typecheck && bun run test"
513
699
  },
514
- dependencies: baseDependencies,
515
- devDependencies: {
516
- "bun-types": "latest",
517
- typescript: "^5.0.0"
518
- }
700
+ dependencies: deps,
701
+ devDependencies: { "bun-types": "latest", typescript: "^5.9.3" }
519
702
  };
520
703
  return JSON.stringify(pkg, null, 2);
521
704
  }
705
+ async generateCheckScripts(context) {
706
+ const scriptsDir = import_node_path4.default.resolve(context.targetDir, "scripts");
707
+ await import_promises4.default.mkdir(scriptsDir, { recursive: true });
708
+ const templatesDir = import_node_path4.default.resolve(this.config.templatesDir, "scripts");
709
+ await this.generateFileFromTemplate(
710
+ templatesDir,
711
+ "check-dependencies.ts.hbs",
712
+ "scripts/check-dependencies.ts",
713
+ context
714
+ );
715
+ await this.generateFileFromTemplate(templatesDir, "check.sh.hbs", "scripts/check.sh", context);
716
+ await this.generateFileFromTemplate(
717
+ templatesDir,
718
+ "pre-commit.sh.hbs",
719
+ "scripts/pre-commit.sh",
720
+ context
721
+ );
722
+ await this.writeFile(
723
+ context.targetDir,
724
+ "CHECK_SYSTEM.md",
725
+ "# Project Check System\n\nRun `bun run validate` to check everything.\n"
726
+ );
727
+ }
728
+ log(message) {
729
+ if (this.config.verbose) {
730
+ console.log(message);
731
+ }
732
+ }
733
+ static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
734
+ const toPascalCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_ ]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
735
+ const toCamelCase = (str) => {
736
+ const pascal = toPascalCase(str);
737
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
738
+ };
739
+ const toSnakeCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1_$2").split(/[-_ ]+/).map((word) => word.toLowerCase()).join("_");
740
+ const toKebabCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").split(/[-_ ]+/).map((word) => word.toLowerCase()).join("-");
741
+ return {
742
+ name,
743
+ namePascalCase: toPascalCase(name),
744
+ nameCamelCase: toCamelCase(name),
745
+ nameSnakeCase: toSnakeCase(name),
746
+ nameKebabCase: toKebabCase(name),
747
+ targetDir,
748
+ architecture,
749
+ packageManager,
750
+ year: (/* @__PURE__ */ new Date()).getFullYear().toString(),
751
+ date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
752
+ ...extra
753
+ };
754
+ }
755
+ };
756
+
757
+ // src/utils/ConfigGenerator.ts
758
+ var ConfigGenerator = class {
522
759
  /**
523
- * Generate Dockerfile content.
760
+ * Generate app configuration (simple version for Clean Architecture)
524
761
  */
525
- generateDockerfile(context) {
526
- const entrypoint = context.architecture === "ddd" ? "dist/main.js" : "dist/bootstrap.js";
527
- return `FROM oven/bun:1.0 AS base
528
- WORKDIR /usr/src/app
529
-
530
- # Install dependencies
531
- FROM base AS install
532
- RUN mkdir -p /temp/dev
533
- COPY package.json bun.lockb /temp/dev/
534
- RUN cd /temp/dev && bun install --frozen-lockfile
535
-
536
- # Build application
537
- FROM base AS build
538
- COPY --from=install /temp/dev/node_modules node_modules
539
- COPY . .
540
- ENV NODE_ENV=production
541
- RUN bun run build
542
-
543
- # Final production image
544
- FROM base AS release
545
- COPY --from=build /usr/src/app/${entrypoint} index.js
546
- COPY --from=build /usr/src/app/package.json .
547
-
548
- # Create a non-root user for security
549
- USER bun
550
- EXPOSE 3000/tcp
551
- ENTRYPOINT [ "bun", "run", "index.js" ]
762
+ static generateSimpleAppConfig(context) {
763
+ return `export default {
764
+ name: process.env.APP_NAME ?? '${context.name}',
765
+ env: process.env.APP_ENV ?? 'development',
766
+ debug: process.env.APP_DEBUG === 'true',
767
+ url: process.env.APP_URL ?? 'http://localhost:3000',
768
+ key: process.env.APP_KEY,
769
+ }
552
770
  `;
553
771
  }
554
772
  /**
555
- * Generate .dockerignore content.
773
+ * Generate app configuration (detailed version for Enterprise MVC)
556
774
  */
557
- generateDockerIgnore() {
558
- return `node_modules
559
- dist
560
- .git
561
- .env
562
- *.log
563
- .vscode
564
- .idea
565
- tests
566
- `;
567
- }
775
+ static generateDetailedAppConfig(context) {
776
+ return `/**
777
+ * Application Configuration
778
+ */
779
+ export default {
568
780
  /**
569
- * Generate .env.example content.
781
+ * Application name
570
782
  */
571
- generateEnvExample(context) {
572
- const profile = context.profile || "core";
573
- let envContent = `# ============================================================================
574
- # Application Configuration
575
- # ============================================================================
576
-
577
- APP_NAME=${context.name}
578
- APP_ENV=development
579
- APP_DEBUG=true
580
- APP_URL=http://localhost:3000
581
- APP_KEY=
582
-
583
- # ============================================================================
584
- # Database Configuration
585
- # ============================================================================
586
-
587
- # Database Connection (sqlite, postgres, mysql)
588
- DB_CONNECTION=${profile === "core" ? "sqlite" : "postgres"}
589
-
590
- # SQLite Configuration (when DB_CONNECTION=sqlite)
591
- DB_DATABASE=database/database.sqlite
592
-
593
- # PostgreSQL Configuration (when DB_CONNECTION=postgres)
594
- ${profile !== "core" ? `DB_HOST=127.0.0.1
595
- DB_PORT=5432
596
- DB_DATABASE=${context.name}
597
- DB_USERNAME=postgres
598
- DB_PASSWORD=
599
- DB_SSLMODE=prefer` : `# DB_HOST=127.0.0.1
600
- # DB_PORT=5432
601
- # DB_DATABASE=${context.name}
602
- # DB_USERNAME=postgres
603
- # DB_PASSWORD=
604
- # DB_SSLMODE=prefer`}
605
-
606
- # MySQL Configuration (when DB_CONNECTION=mysql)
607
- # DB_HOST=127.0.0.1
608
- # DB_PORT=3306
609
- # DB_DATABASE=${context.name}
610
- # DB_USERNAME=root
611
- # DB_PASSWORD=
612
-
613
- # ============================================================================
614
- # Redis Configuration (@gravito/plasma)
615
- # ============================================================================
616
-
617
- # Default Redis Connection
618
- REDIS_CONNECTION=default
619
- REDIS_HOST=127.0.0.1
620
- REDIS_PORT=6379
621
- REDIS_PASSWORD=
622
- REDIS_DB=0
623
-
624
- # Redis Connection Options
625
- REDIS_CONNECT_TIMEOUT=10000
626
- REDIS_COMMAND_TIMEOUT=5000
627
- REDIS_KEY_PREFIX=
628
- REDIS_MAX_RETRIES=3
629
- REDIS_RETRY_DELAY=1000
630
-
631
- # Cache-specific Redis Connection (optional, falls back to default)
632
- # REDIS_CACHE_HOST=127.0.0.1
633
- # REDIS_CACHE_PORT=6379
634
- # REDIS_CACHE_PASSWORD=
635
- REDIS_CACHE_DB=1
636
-
637
- # Queue-specific Redis Connection (optional, falls back to default)
638
- # REDIS_QUEUE_HOST=127.0.0.1
639
- # REDIS_QUEUE_PORT=6379
640
- # REDIS_QUEUE_PASSWORD=
641
- REDIS_QUEUE_DB=2
642
-
643
- # ============================================================================
644
- # Cache Configuration (@gravito/stasis)
645
- # ============================================================================
646
-
647
- # Cache Driver (memory, file, redis)
648
- CACHE_DRIVER=${profile === "core" ? "memory" : "redis"}
649
-
650
- # File Cache Path (when CACHE_DRIVER=file)
651
- CACHE_PATH=storage/framework/cache
652
-
653
- # Redis Cache Configuration (when CACHE_DRIVER=redis)
654
- REDIS_CACHE_CONNECTION=cache
655
- REDIS_CACHE_PREFIX=cache:
656
-
657
- # ============================================================================
658
- # Queue Configuration (@gravito/stream)
659
- # ============================================================================
660
-
661
- # Queue Connection (sync, memory, database, redis, kafka, sqs, rabbitmq)
662
- QUEUE_CONNECTION=${profile === "core" ? "sync" : "redis"}
663
-
664
- # Database Queue Configuration (when QUEUE_CONNECTION=database)
665
- QUEUE_TABLE=jobs
666
-
667
- # Redis Queue Configuration (when QUEUE_CONNECTION=redis)
668
- REDIS_PREFIX=queue:
669
-
670
- `;
671
- if (profile === "enterprise" || profile === "scale") {
672
- envContent += `# Kafka Queue Configuration (when QUEUE_CONNECTION=kafka)
673
- # KAFKA_BROKERS=localhost:9092
674
- # KAFKA_CONSUMER_GROUP_ID=gravito-workers
675
- # KAFKA_CLIENT_ID=${context.name}
676
-
677
- # AWS SQS Queue Configuration (when QUEUE_CONNECTION=sqs)
678
- # AWS_REGION=us-east-1
679
- # SQS_QUEUE_URL_PREFIX=
680
- # SQS_VISIBILITY_TIMEOUT=30
681
- # SQS_WAIT_TIME_SECONDS=20
682
-
683
- # RabbitMQ Queue Configuration (when QUEUE_CONNECTION=rabbitmq)
684
- # RABBITMQ_URL=amqp://localhost
685
- # RABBITMQ_EXCHANGE=gravito.events
686
- # RABBITMQ_EXCHANGE_TYPE=fanout
687
-
688
- `;
689
- }
690
- envContent += `# ============================================================================
691
- # Logging Configuration
692
- # ============================================================================
783
+ name: process.env.APP_NAME ?? '${context.name}',
693
784
 
694
- LOG_LEVEL=debug
695
- `;
696
- return envContent;
697
- }
698
785
  /**
699
- * Generate .gitignore content.
786
+ * Application environment
700
787
  */
701
- generateGitignore() {
702
- return `# Dependencies
703
- node_modules/
704
-
705
- # Build output
706
- dist/
707
-
708
- # Environment
709
- .env
710
- .env.local
711
- .env.*.local
712
-
713
- # IDE
714
- .idea/
715
- .vscode/
716
- *.swp
717
- *.swo
718
-
719
- # System
720
- .DS_Store
721
- Thumbs.db
788
+ env: process.env.APP_ENV ?? 'development',
722
789
 
723
- # Logs
724
- *.log
725
- logs/
790
+ /**
791
+ * Application port
792
+ */
793
+ port: Number.parseInt(process.env.PORT ?? '3000', 10),
726
794
 
727
- # Database
728
- *.sqlite
729
- *.sqlite-journal
795
+ /**
796
+ * View directory
797
+ */
798
+ VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
730
799
 
731
- # Coverage
732
- coverage/
733
- `;
734
- }
735
800
  /**
736
- * Generate tsconfig.json content.
801
+ * Debug mode
737
802
  */
738
- generateTsConfig() {
739
- const config = {
740
- compilerOptions: {
741
- target: "ESNext",
742
- module: "ESNext",
743
- moduleResolution: "bundler",
744
- esModuleInterop: true,
745
- strict: true,
746
- skipLibCheck: true,
747
- declaration: true,
748
- experimentalDecorators: true,
749
- emitDecoratorMetadata: true,
750
- types: ["bun-types"],
751
- outDir: "./dist",
752
- rootDir: "./src",
753
- baseUrl: ".",
754
- paths: {
755
- "@/*": ["./src/*"]
756
- }
757
- },
758
- include: ["src/**/*"],
759
- exclude: ["node_modules", "dist"]
760
- };
761
- return JSON.stringify(config, null, 2);
762
- }
803
+ debug: process.env.APP_DEBUG === 'true',
804
+
763
805
  /**
764
- * Generate check scripts for project validation.
806
+ * Application URL
765
807
  */
766
- async generateCheckScripts(context) {
767
- const scriptsDir = import_node_path2.default.resolve(context.targetDir, "scripts");
768
- await import_promises2.default.mkdir(scriptsDir, { recursive: true });
769
- await this.writeFile(
770
- scriptsDir,
771
- "check-dependencies.ts",
772
- this.generateCheckDependenciesScript()
773
- );
774
- await this.writeFile(scriptsDir, "check.sh", this.generateCheckShellScript());
775
- await this.writeFile(scriptsDir, "pre-commit.sh", this.generatePreCommitScript());
776
- await this.writeFile(context.targetDir, "CHECK_SYSTEM.md", this.generateCheckSystemDoc(context));
777
- }
808
+ url: process.env.APP_URL ?? 'http://localhost:3000',
809
+
778
810
  /**
779
- * Generate check-dependencies.ts script content.
811
+ * Timezone
780
812
  */
781
- generateCheckDependenciesScript() {
782
- return `/**
783
- * \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5\u8173\u672C
784
- *
785
- * \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
786
- * \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
787
- */
813
+ timezone: 'UTC',
788
814
 
789
- import { readFileSync } from 'fs'
790
- import { join } from 'path'
815
+ /**
816
+ * Locale
817
+ */
818
+ locale: 'en',
791
819
 
792
- interface PackageJson {
793
- dependencies?: Record<string, string>
794
- devDependencies?: Record<string, string>
795
- }
796
-
797
- interface PackageInfo {
798
- name: string
799
- current: string
800
- latest: string
801
- outdated: boolean
802
- }
803
-
804
- const colors = {
805
- reset: '\\x1b[0m',
806
- green: '\\x1b[32m',
807
- yellow: '\\x1b[33m',
808
- red: '\\x1b[31m',
809
- blue: '\\x1b[36m',
810
- }
820
+ /**
821
+ * Fallback locale
822
+ */
823
+ fallbackLocale: 'en',
811
824
 
812
- function log(message: string, color: keyof typeof colors = 'reset') {
813
- console.log(\`\${colors[color]}\${message}\${colors.reset}\`)
814
- }
825
+ /**
826
+ * Encryption key
827
+ */
828
+ key: process.env.APP_KEY,
815
829
 
816
- async function getLatestVersion(packageName: string): Promise<string | null> {
817
- try {
818
- const response = await fetch(\`https://registry.npmjs.org/\${packageName}/latest\`)
819
- if (!response.ok) return null
820
- const data = await response.json()
821
- return data.version
822
- } catch {
823
- return null
824
- }
825
- }
830
+ /**
831
+ * Service providers to register
832
+ */
833
+ providers: [
834
+ // Framework providers
835
+ // 'RouteServiceProvider',
826
836
 
827
- function parseVersion(version: string): string {
828
- // \u79FB\u9664 ^, ~, >= \u7B49\u524D\u7DB4
829
- return version.replace(/^[\\^~>=<]/, '')
837
+ // Application providers
838
+ // 'AppServiceProvider',
839
+ ],
830
840
  }
831
-
832
- async function checkPackage(
833
- name: string,
834
- currentVersion: string
835
- ): Promise<PackageInfo | null> {
836
- // \u8DF3\u904E\u672C\u5730\u9023\u7D50\u7684\u5957\u4EF6
837
- if (currentVersion.startsWith('link:') || currentVersion.startsWith('workspace:')) {
838
- return null
839
- }
840
-
841
- const current = parseVersion(currentVersion)
842
- const latest = await getLatestVersion(name)
843
-
844
- if (!latest) {
845
- return null
846
- }
847
-
848
- return {
849
- name,
850
- current,
851
- latest,
852
- outdated: current !== latest,
841
+ `;
853
842
  }
843
+ /**
844
+ * Generate database configuration (simple version)
845
+ */
846
+ static generateSimpleDatabaseConfig() {
847
+ return `export default {
848
+ default: process.env.DB_CONNECTION ?? 'sqlite',
849
+ connections: {
850
+ sqlite: {
851
+ driver: 'sqlite',
852
+ database: process.env.DB_DATABASE ?? 'database/database.sqlite',
853
+ },
854
+ },
854
855
  }
855
-
856
- async function main() {
857
- log('\\n=== \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5 ===\\n', 'blue')
858
-
859
- const packageJsonPath = join(process.cwd(), 'package.json')
860
- const packageJson: PackageJson = JSON.parse(
861
- readFileSync(packageJsonPath, 'utf-8')
862
- )
863
-
864
- const allDependencies = {
865
- ...packageJson.dependencies,
866
- ...packageJson.devDependencies,
867
- }
868
-
869
- log(\`\u6AA2\u67E5 \${Object.keys(allDependencies).length} \u500B\u5957\u4EF6...\\n\`, 'yellow')
870
-
871
- const results: PackageInfo[] = []
872
- const outdated: PackageInfo[] = []
873
- const upToDate: PackageInfo[] = []
874
-
875
- // \u6AA2\u67E5\u6240\u6709\u5957\u4EF6
876
- for (const [name, version] of Object.entries(allDependencies)) {
877
- const info = await checkPackage(name, version)
878
- if (info) {
879
- results.push(info)
880
- if (info.outdated) {
881
- outdated.push(info)
882
- } else {
883
- upToDate.push(info)
884
- }
885
- }
856
+ `;
886
857
  }
858
+ /**
859
+ * Generate database configuration (detailed version)
860
+ */
861
+ static generateDetailedDatabaseConfig() {
862
+ return `/**
863
+ * Database Configuration
864
+ */
865
+ export default {
866
+ /**
867
+ * Default connection
868
+ */
869
+ default: process.env.DB_CONNECTION ?? 'sqlite',
887
870
 
888
- // \u986F\u793A\u7D50\u679C
889
- if (upToDate.length > 0) {
890
- log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
891
- upToDate.forEach((pkg) => {
892
- log(\` \${pkg.name}: \${pkg.current}\`, 'green')
893
- })
894
- }
871
+ /**
872
+ * Database connections
873
+ */
874
+ connections: {
875
+ sqlite: {
876
+ driver: 'sqlite',
877
+ database: process.env.DB_DATABASE ?? 'database/database.sqlite',
878
+ },
895
879
 
896
- if (outdated.length > 0) {
897
- log(\`\\n\u26A0 \u9700\u8981\u66F4\u65B0 (\${outdated.length}):\`, 'yellow')
898
- outdated.forEach((pkg) => {
899
- log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
900
- })
901
- }
880
+ mysql: {
881
+ driver: 'mysql',
882
+ host: process.env.DB_HOST ?? 'localhost',
883
+ port: Number(process.env.DB_PORT ?? 3306),
884
+ database: process.env.DB_DATABASE ?? 'forge',
885
+ username: process.env.DB_USERNAME ?? 'forge',
886
+ password: process.env.DB_PASSWORD ?? '',
887
+ },
902
888
 
903
- // \u7E3D\u7D50
904
- log('\\n=== \u6AA2\u67E5\u7D50\u679C ===', 'blue')
905
- log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
906
- log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
907
- log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
889
+ postgres: {
890
+ driver: 'postgres',
891
+ host: process.env.DB_HOST ?? 'localhost',
892
+ port: Number(process.env.DB_PORT ?? 5432),
893
+ database: process.env.DB_DATABASE ?? 'forge',
894
+ username: process.env.DB_USERNAME ?? 'forge',
895
+ password: process.env.DB_PASSWORD ?? '',
896
+ },
897
+ },
908
898
 
909
- // \u5982\u679C\u6709\u9700\u8981\u66F4\u65B0\u7684\u5957\u4EF6\uFF0C\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u78BC
910
- if (outdated.length > 0) {
911
- log('\\n\u5EFA\u8B70\u57F7\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0\u5957\u4EF6\uFF1A', 'yellow')
912
- log(' bun update', 'yellow')
913
- process.exit(1)
914
- } else {
915
- log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
916
- process.exit(0)
899
+ /**
900
+ * Migration settings
901
+ */
902
+ migrations: {
903
+ table: 'migrations',
904
+ path: 'database/migrations',
905
+ },
906
+ }
907
+ `;
917
908
  }
909
+ /**
910
+ * Generate auth configuration
911
+ */
912
+ static generateAuthConfig() {
913
+ return `export default {
914
+ defaults: { guard: 'web' },
915
+ guards: {
916
+ web: { driver: 'session', provider: 'users' },
917
+ api: { driver: 'token', provider: 'users' },
918
+ },
918
919
  }
919
-
920
- main().catch((error) => {
921
- log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
922
- process.exit(1)
923
- })
924
920
  `;
925
921
  }
926
922
  /**
927
- * Generate check.sh script content.
923
+ * Generate cache configuration
928
924
  */
929
- generateCheckShellScript() {
930
- return `#!/bin/bash
931
-
932
- # \u5C08\u6848\u6AA2\u67E5\u8173\u672C
933
- # \u57F7\u884C\u6240\u6709\u5FC5\u8981\u7684\u6AA2\u67E5\uFF1A\u985E\u578B\u6AA2\u67E5\u3001\u6E2C\u8A66\u3001\u4F9D\u8CF4\u6AA2\u67E5\u7B49
934
-
935
- set -e
936
-
937
- # \u984F\u8272\u5B9A\u7FA9
938
- GREEN='\\033[0;32m'
939
- YELLOW='\\033[1;33m'
940
- RED='\\033[0;31m'
941
- BLUE='\\033[0;34m'
942
- NC='\\033[0m' # No Color
943
-
944
- echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
945
-
946
- # \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
947
- if [ ! -f "package.json" ]; then
948
- echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
949
- exit 1
950
- fi
951
-
952
- # \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
953
- if ! command -v bun &> /dev/null; then
954
- echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
955
- exit 1
956
- fi
957
-
958
- # 1. \u985E\u578B\u6AA2\u67E5
959
- echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
960
- if bun run typecheck; then
961
- echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
962
- else
963
- echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
964
- exit 1
965
- fi
966
-
967
- # 2. \u57F7\u884C\u6E2C\u8A66
968
- echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
969
- if bun test; then
970
- echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
971
- else
972
- echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
973
- exit 1
974
- fi
975
-
976
- # 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
977
- echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
978
- if bun run check:deps; then
979
- echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
980
- else
981
- echo -e "\${YELLOW}\u26A0 \u4F9D\u8CF4\u6AA2\u67E5\u6709\u8B66\u544A\uFF08\u67D0\u4E9B\u5957\u4EF6\u53EF\u80FD\u9700\u8981\u66F4\u65B0\uFF09\${NC}\\n"
982
- fi
983
-
984
- echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
925
+ static generateCacheConfig() {
926
+ return `export default {
927
+ default: process.env.CACHE_DRIVER ?? 'memory',
928
+ stores: {
929
+ memory: { driver: 'memory' },
930
+ },
931
+ }
985
932
  `;
986
933
  }
987
934
  /**
988
- * Generate pre-commit.sh script content.
935
+ * Generate logging configuration
989
936
  */
990
- generatePreCommitScript() {
991
- return `#!/bin/bash
992
-
993
- # Pre-commit Hook
994
- # \u5728 git commit \u524D\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5
995
- #
996
- # \u5B89\u88DD\u65B9\u5F0F\uFF1A
997
- # ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
998
- # \u6216
999
- # cp scripts/pre-commit.sh .git/hooks/pre-commit
1000
- # chmod +x .git/hooks/pre-commit
1001
-
1002
- set -e
1003
-
1004
- # \u984F\u8272\u5B9A\u7FA9
1005
- GREEN='\\033[0;32m'
1006
- YELLOW='\\033[1;33m'
1007
- RED='\\033[0;31m'
1008
- BLUE='\\033[0;34m'
1009
- NC='\\033[0m' # No Color
1010
-
1011
- echo -e "\${BLUE}=== Pre-commit \u6AA2\u67E5 ===\${NC}\\n"
1012
-
1013
- # \u5207\u63DB\u5230\u5C08\u6848\u6839\u76EE\u9304
1014
- cd "$(git rev-parse --show-toplevel)"
1015
-
1016
- # \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
1017
- if [ ! -f "package.json" ]; then
1018
- echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
1019
- exit 1
1020
- fi
1021
-
1022
- # \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
1023
- if ! command -v bun &> /dev/null; then
1024
- echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
1025
- exit 1
1026
- fi
1027
-
1028
- # 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
1029
- echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
1030
- if bun run typecheck; then
1031
- echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
1032
- else
1033
- echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
1034
- echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
1035
- exit 1
1036
- fi
1037
-
1038
- # 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
1039
- echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
1040
- if bun test; then
1041
- echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
1042
- else
1043
- echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
1044
- echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
1045
- exit 1
1046
- fi
1047
-
1048
- echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
937
+ static generateLoggingConfig() {
938
+ return `export default {
939
+ default: process.env.LOG_CHANNEL ?? 'console',
940
+ channels: {
941
+ console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
942
+ },
943
+ }
1049
944
  `;
1050
945
  }
1051
946
  /**
1052
- * Generate CHECK_SYSTEM.md documentation.
947
+ * Generate view configuration
1053
948
  */
1054
- generateCheckSystemDoc(context) {
1055
- return `# \u5C08\u6848\u6AA2\u67E5\u7CFB\u7D71
1056
-
1057
- \u672C\u5C08\u6848\u5DF2\u5EFA\u7ACB\u5B8C\u6574\u7684\u672C\u5730\u6AA2\u67E5\u6A5F\u5236\uFF0C\u7121\u9700\u4F9D\u8CF4 GitHub CI\u3002
1058
-
1059
- ## \u5FEB\u901F\u958B\u59CB
1060
-
1061
- ### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
1062
- \`\`\`bash
1063
- bun run validate
1064
- \`\`\`
1065
-
1066
- ### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
1067
- \`\`\`bash
1068
- # \u985E\u578B\u6AA2\u67E5
1069
- bun run typecheck
1070
-
1071
- # \u6E2C\u8A66
1072
- bun run test
1073
-
1074
- # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
1075
- bun run check:deps
1076
- \`\`\`
1077
-
1078
- ## \u53EF\u7528\u547D\u4EE4
1079
-
1080
- ### Package.json \u8173\u672C
1081
-
1082
- | \u547D\u4EE4 | \u8AAA\u660E |
1083
- |------|------|
1084
- | \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
1085
- | \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
1086
- | \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
1087
- | \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
1088
- | \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
1089
- | \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
1090
-
1091
- ### Shell \u8173\u672C
1092
-
1093
- | \u8173\u672C | \u8AAA\u660E |
1094
- |------|------|
1095
- | \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
1096
- | \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
1097
-
1098
- ## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
1099
-
1100
- \u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
1101
-
1102
- \`\`\`bash
1103
- # \u5B89\u88DD pre-commit hook
1104
- ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
1105
-
1106
- # \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
1107
- cp scripts/pre-commit.sh .git/hooks/pre-commit
1108
- chmod +x .git/hooks/pre-commit
1109
- \`\`\`
1110
-
1111
- **\u529F\u80FD\uFF1A**
1112
- - \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
1113
- - \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
1114
- - \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
949
+ static generateViewConfig() {
950
+ return `/**
951
+ * View Configuration
952
+ */
953
+ export default {
954
+ /**
955
+ * View engine
956
+ */
957
+ engine: 'html',
1115
958
 
1116
- **\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
1117
- \`\`\`bash
1118
- git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
1119
- \`\`\`
959
+ /**
960
+ * View directory
961
+ */
962
+ path: 'src/views',
1120
963
 
1121
- ## \u6AA2\u67E5\u9805\u76EE
964
+ /**
965
+ * Cache views in production
966
+ */
967
+ cache: process.env.NODE_ENV === 'production',
968
+ }
969
+ `;
970
+ }
971
+ };
1122
972
 
1123
- ### 1. \u985E\u578B\u6AA2\u67E5
1124
- - \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
1125
- - \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
973
+ // src/utils/ServiceProviderGenerator.ts
974
+ var ServiceProviderGenerator = class {
975
+ /**
976
+ * Generate App Service Provider
977
+ */
978
+ static generateAppServiceProvider(context, architectureName) {
979
+ return `/**
980
+ * App Service Provider
981
+ */
1126
982
 
1127
- ### 2. \u6E2C\u8A66
1128
- - \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
1129
- - \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
983
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1130
984
 
1131
- ### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
1132
- - \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
1133
- - \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
1134
- - \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
985
+ export class AppServiceProvider extends ServiceProvider {
986
+ register(_container: Container): void {
987
+ // Register application services
988
+ }
1135
989
 
1136
- ## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
990
+ boot(_core: PlanetCore): void {
991
+ console.log('${context.name} (${architectureName}) booted!')
992
+ }
993
+ }
994
+ `;
995
+ }
996
+ /**
997
+ * Generate Middleware Provider
998
+ */
999
+ static generateMiddlewareProvider() {
1000
+ return `/**
1001
+ * Middleware Service Provider
1002
+ */
1137
1003
 
1138
- ### \u958B\u767C\u6642
1139
- 1. \u958B\u767C\u529F\u80FD
1140
- 2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
1141
- 3. \u4FEE\u6B63\u554F\u984C
1142
- 4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
1004
+ import {
1005
+ ServiceProvider,
1006
+ type Container,
1007
+ type PlanetCore,
1008
+ bodySizeLimit,
1009
+ securityHeaders,
1010
+ } from '@gravito/core'
1143
1011
 
1144
- ### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
1145
- 1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
1146
- 2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
1147
- 3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
1148
- 4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
1012
+ export class MiddlewareProvider extends ServiceProvider {
1013
+ register(_container: Container): void {}
1149
1014
 
1150
- ## \u6A94\u6848\u7D50\u69CB
1015
+ boot(core: PlanetCore): void {
1016
+ const isDev = process.env.NODE_ENV !== 'production'
1151
1017
 
1152
- \`\`\`
1153
- ${context.nameKebabCase}/
1154
- \u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
1155
- \u251C\u2500\u2500 scripts/
1156
- \u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
1157
- \u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
1158
- \u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
1159
- \u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
1160
- \`\`\`
1018
+ core.adapter.use('*', securityHeaders({
1019
+ contentSecurityPolicy: isDev ? false : undefined,
1020
+ }))
1161
1021
 
1162
- ## \u6CE8\u610F\u4E8B\u9805
1022
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
1163
1023
 
1164
- 1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
1165
- 2. **\u6E2C\u8A66\u6642\u9593**\uFF1A\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\uFF0C\u53EF\u4EE5\u7DE8\u8F2F \`pre-commit.sh\` \u8A3B\u89E3\u6389\u6E2C\u8A66\u90E8\u5206
1166
- 3. **\u985E\u578B\u932F\u8AA4**\uFF1A\u5C08\u6848\u4E2D\u53EF\u80FD\u9084\u6709\u4E00\u4E9B\u65E2\u6709\u7684\u985E\u578B\u932F\u8AA4\uFF0C\u5EFA\u8B70\u9010\u6B65\u4FEE\u6B63
1024
+ core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
1025
+ }
1026
+ }
1027
+ `;
1028
+ }
1029
+ /**
1030
+ * Generate Route Provider
1031
+ */
1032
+ static generateRouteProvider(routePath = "../../routes/api", importType = "default") {
1033
+ const importStatement = importType === "default" ? `import routes from '${routePath}'` : `import { registerApiRoutes } from '${routePath}'`;
1034
+ const routeCall = importType === "default" ? "routes(core.router)" : "registerApiRoutes(core.router)";
1035
+ return `/**
1036
+ * Route Service Provider
1037
+ */
1167
1038
 
1168
- ## \u6545\u969C\u6392\u9664
1039
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1040
+ ${importStatement}
1169
1041
 
1170
- ### \u6AA2\u67E5\u5931\u6557
1171
- 1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
1172
- 2. \u4FEE\u6B63\u554F\u984C
1173
- 3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
1042
+ export class RouteProvider extends ServiceProvider {
1043
+ register(_container: Container): void {}
1174
1044
 
1175
- ### \u8DF3\u904E\u6AA2\u67E5
1176
- \u53EA\u6709\u5728\u7DCA\u6025\u60C5\u6CC1\u4E0B\u624D\u4F7F\u7528\uFF1A
1177
- \`\`\`bash
1178
- git commit --no-verify
1179
- \`\`\`
1045
+ boot(core: PlanetCore): void {
1046
+ ${routeCall}
1047
+ core.logger.info('\u{1F6E4}\uFE0F Routes registered')
1048
+ }
1049
+ }
1050
+ `;
1051
+ }
1052
+ /**
1053
+ * Generate Providers Index
1054
+ */
1055
+ static generateProvidersIndex(providers = ["AppServiceProvider", "MiddlewareProvider", "RouteProvider"]) {
1056
+ const exports2 = providers.map((p) => `export { ${p} } from './${p}'`).join("\n");
1057
+ return `/**
1058
+ * Application Service Providers
1059
+ */
1180
1060
 
1181
- ### \u79FB\u9664 Pre-commit Hook
1182
- \`\`\`bash
1183
- rm .git/hooks/pre-commit
1184
- \`\`\`
1061
+ ${exports2}
1185
1062
  `;
1186
1063
  }
1187
1064
  /**
1188
- * Log a message if verbose mode is enabled.
1065
+ * Generate Repository Service Provider
1189
1066
  */
1190
- log(message) {
1191
- if (this.config.verbose) {
1192
- console.log(message);
1193
- }
1067
+ static generateRepositoryServiceProvider(repositories = [], additionalServices = []) {
1068
+ const repositoryRegistrations = repositories.map((repo) => ` container.singleton('${repo}', () => new ${repo}())`).join("\n");
1069
+ const serviceRegistrations = additionalServices.map((service) => ` container.singleton('${service}', () => new ${service}())`).join("\n");
1070
+ const imports = [
1071
+ ...repositories.map(
1072
+ (repo) => `import { ${repo} } from '../Persistence/Repositories/${repo}'`
1073
+ ),
1074
+ ...additionalServices.map(
1075
+ (service) => `import { ${service} } from '../ExternalServices/${service}'`
1076
+ )
1077
+ ].join("\n");
1078
+ return `/**
1079
+ * Repository Service Provider
1080
+ *
1081
+ * Binds repository interfaces to implementations.
1082
+ */
1083
+
1084
+ import { ServiceProvider, type Container } from '@gravito/core'
1085
+ ${imports}
1086
+
1087
+ export class RepositoryServiceProvider extends ServiceProvider {
1088
+ register(container: Container): void {
1089
+ ${repositoryRegistrations || " // Bind repositories here"}
1090
+ ${serviceRegistrations || " // Bind external services here"}
1091
+ }
1092
+ }
1093
+ `;
1194
1094
  }
1195
1095
  /**
1196
- * Create generator context from options.
1096
+ * Generate Database Provider
1197
1097
  */
1198
- static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
1199
- const now = /* @__PURE__ */ new Date();
1200
- const pascalCase = name.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toUpperCase());
1201
- const camelCase = pascalCase.replace(/^./, (c) => c.toLowerCase());
1202
- const snakeCase = name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[-\s]+/g, "_");
1203
- const kebabCase = name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "").replace(/[_\s]+/g, "-");
1204
- return {
1205
- name,
1206
- namePascalCase: pascalCase,
1207
- nameCamelCase: camelCase,
1208
- nameSnakeCase: snakeCase,
1209
- nameKebabCase: kebabCase,
1210
- targetDir,
1211
- architecture,
1212
- packageManager,
1213
- year: now.getFullYear().toString(),
1214
- date: now.toISOString().split("T")[0] ?? now.toISOString().slice(0, 10),
1215
- ...extra
1216
- };
1098
+ static generateDatabaseProvider() {
1099
+ return `/**
1100
+ * Database Service Provider
1101
+ */
1102
+
1103
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1104
+ import { OrbitAtlas } from '@gravito/atlas'
1105
+
1106
+ export class DatabaseProvider extends ServiceProvider {
1107
+ register(_container: Container): void {
1108
+ // Register database connections
1109
+ }
1110
+
1111
+ boot(core: PlanetCore): void {
1112
+ // Initialize database
1113
+ core.logger.info('\u{1F5C4}\uFE0F Database initialized')
1114
+ }
1115
+ }
1116
+ `;
1217
1117
  }
1218
1118
  };
1219
1119
 
@@ -1467,57 +1367,22 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
1467
1367
  ];
1468
1368
  }
1469
1369
  // ─────────────────────────────────────────────────────────────
1470
- // Config Generators (similar to MVC but simplified)
1370
+ // Config Generators (using shared ConfigGenerator)
1471
1371
  // ─────────────────────────────────────────────────────────────
1472
1372
  generateAppConfig(context) {
1473
- return `export default {
1474
- name: process.env.APP_NAME ?? '${context.name}',
1475
- env: process.env.APP_ENV ?? 'development',
1476
- debug: process.env.APP_DEBUG === 'true',
1477
- url: process.env.APP_URL ?? 'http://localhost:3000',
1478
- key: process.env.APP_KEY,
1479
- }
1480
- `;
1373
+ return ConfigGenerator.generateSimpleAppConfig(context);
1481
1374
  }
1482
1375
  generateDatabaseConfig() {
1483
- return `export default {
1484
- default: process.env.DB_CONNECTION ?? 'sqlite',
1485
- connections: {
1486
- sqlite: {
1487
- driver: 'sqlite',
1488
- database: process.env.DB_DATABASE ?? 'database/database.sqlite',
1489
- },
1490
- },
1491
- }
1492
- `;
1376
+ return ConfigGenerator.generateSimpleDatabaseConfig();
1493
1377
  }
1494
1378
  generateAuthConfig() {
1495
- return `export default {
1496
- defaults: { guard: 'web' },
1497
- guards: {
1498
- web: { driver: 'session', provider: 'users' },
1499
- api: { driver: 'token', provider: 'users' },
1500
- },
1501
- }
1502
- `;
1379
+ return ConfigGenerator.generateAuthConfig();
1503
1380
  }
1504
1381
  generateCacheConfig() {
1505
- return `export default {
1506
- default: process.env.CACHE_DRIVER ?? 'memory',
1507
- stores: {
1508
- memory: { driver: 'memory' },
1509
- },
1510
- }
1511
- `;
1382
+ return ConfigGenerator.generateCacheConfig();
1512
1383
  }
1513
1384
  generateLoggingConfig() {
1514
- return `export default {
1515
- default: process.env.LOG_CHANNEL ?? 'console',
1516
- channels: {
1517
- console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
1518
- },
1519
- }
1520
- `;
1385
+ return ConfigGenerator.generateLoggingConfig();
1521
1386
  }
1522
1387
  generateUserEntity() {
1523
1388
  return `/**
@@ -1816,132 +1681,59 @@ export class UserRepository implements IUserRepository {
1816
1681
 
1817
1682
  async delete(id: string): Promise<void> {
1818
1683
  users.delete(id)
1819
- }
1820
-
1821
- async findAll(): Promise<User[]> {
1822
- return Array.from(users.values())
1823
- }
1824
-
1825
- async exists(id: string): Promise<boolean> {
1826
- return users.has(id)
1827
- }
1828
- }
1829
- `;
1830
- }
1831
- generateMailService() {
1832
- return `/**
1833
- * Mail Service Implementation
1834
- */
1835
-
1836
- import type { IMailService, MailMessage } from '../../Application/Interfaces/IMailService'
1837
-
1838
- export class MailService implements IMailService {
1839
- async send(message: MailMessage): Promise<void> {
1840
- // TODO: Implement actual email sending
1841
- console.log(\`[Mail] Sending to \${message.to}: \${message.subject}\`)
1842
- }
1843
- }
1844
- `;
1845
- }
1846
- generateAppServiceProvider(context) {
1847
- return `/**
1848
- * App Service Provider
1849
- */
1850
-
1851
- import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1852
-
1853
- export class AppServiceProvider extends ServiceProvider {
1854
- register(_container: Container): void {
1855
- // Register application services
1856
- }
1857
-
1858
- boot(_core: PlanetCore): void {
1859
- console.log('${context.name} (Clean Architecture) booted!')
1860
- }
1861
- }
1862
- `;
1863
- }
1864
- generateRepositoryServiceProvider() {
1865
- return `/**
1866
- * Repository Service Provider
1867
- *
1868
- * Binds repository interfaces to implementations.
1869
- */
1870
-
1871
- import { ServiceProvider, type Container } from '@gravito/core'
1872
- import { UserRepository } from '../Persistence/Repositories/UserRepository'
1873
- import { MailService } from '../ExternalServices/MailService'
1874
-
1875
- export class RepositoryServiceProvider extends ServiceProvider {
1876
- register(container: Container): void {
1877
- // Bind repositories
1878
- container.singleton('userRepository', () => new UserRepository())
1879
-
1880
- // Bind external services
1881
- container.singleton('mailService', () => new MailService())
1882
- }
1883
- }
1884
- `;
1885
- }
1886
- generateProvidersIndex() {
1887
- return `/**
1888
- * Application Service Providers
1889
- */
1890
-
1891
- export { AppServiceProvider } from './AppServiceProvider'
1892
- export { RepositoryServiceProvider } from './RepositoryServiceProvider'
1893
- export { MiddlewareProvider } from './MiddlewareProvider'
1894
- export { RouteProvider } from './RouteProvider'
1895
- `;
1896
- }
1897
- generateMiddlewareProvider() {
1898
- return `/**
1899
- * Middleware Service Provider
1900
- */
1901
-
1902
- import {
1903
- ServiceProvider,
1904
- type Container,
1905
- type PlanetCore,
1906
- bodySizeLimit,
1907
- securityHeaders,
1908
- } from '@gravito/core'
1909
-
1910
- export class MiddlewareProvider extends ServiceProvider {
1911
- register(_container: Container): void {}
1912
-
1913
- boot(core: PlanetCore): void {
1914
- const isDev = process.env.NODE_ENV !== 'production'
1915
-
1916
- core.adapter.use('*', securityHeaders({
1917
- contentSecurityPolicy: isDev ? false : undefined,
1918
- }))
1684
+ }
1919
1685
 
1920
- core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
1686
+ async findAll(): Promise<User[]> {
1687
+ return Array.from(users.values())
1688
+ }
1921
1689
 
1922
- core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
1690
+ async exists(id: string): Promise<boolean> {
1691
+ return users.has(id)
1923
1692
  }
1924
1693
  }
1925
1694
  `;
1926
1695
  }
1927
- generateRouteProvider() {
1696
+ generateMailService() {
1928
1697
  return `/**
1929
- * Route Service Provider
1698
+ * Mail Service Implementation
1930
1699
  */
1931
1700
 
1932
- import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1933
- import { registerApiRoutes } from '../../Interface/Http/Routes/api'
1934
-
1935
- export class RouteProvider extends ServiceProvider {
1936
- register(_container: Container): void {}
1701
+ import type { IMailService, MailMessage } from '../../Application/Interfaces/IMailService'
1937
1702
 
1938
- boot(core: PlanetCore): void {
1939
- registerApiRoutes(core.router)
1940
- core.logger.info('\u{1F6E4}\uFE0F Routes registered')
1703
+ export class MailService implements IMailService {
1704
+ async send(message: MailMessage): Promise<void> {
1705
+ // TODO: Implement actual email sending
1706
+ console.log(\`[Mail] Sending to \${message.to}: \${message.subject}\`)
1941
1707
  }
1942
1708
  }
1943
1709
  `;
1944
1710
  }
1711
+ generateAppServiceProvider(context) {
1712
+ return ServiceProviderGenerator.generateAppServiceProvider(context, "Clean Architecture");
1713
+ }
1714
+ generateRepositoryServiceProvider() {
1715
+ return ServiceProviderGenerator.generateRepositoryServiceProvider(
1716
+ ["UserRepository"],
1717
+ ["MailService"]
1718
+ );
1719
+ }
1720
+ generateProvidersIndex() {
1721
+ return ServiceProviderGenerator.generateProvidersIndex([
1722
+ "AppServiceProvider",
1723
+ "RepositoryServiceProvider",
1724
+ "MiddlewareProvider",
1725
+ "RouteProvider"
1726
+ ]);
1727
+ }
1728
+ generateMiddlewareProvider() {
1729
+ return ServiceProviderGenerator.generateMiddlewareProvider();
1730
+ }
1731
+ generateRouteProvider() {
1732
+ return ServiceProviderGenerator.generateRouteProvider(
1733
+ "../../Interface/Http/Routes/api",
1734
+ "named"
1735
+ );
1736
+ }
1945
1737
  // ─────────────────────────────────────────────────────────────
1946
1738
  // Interface Layer
1947
1739
  // ─────────────────────────────────────────────────────────────
@@ -2163,7 +1955,7 @@ Created with \u2764\uFE0F using Gravito Framework
2163
1955
  build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
2164
1956
  start: "bun run dist/bootstrap.js",
2165
1957
  test: "bun test",
2166
- typecheck: "tsc --noEmit",
1958
+ typecheck: "bun tsc --noEmit",
2167
1959
  check: "bun run typecheck && bun run test",
2168
1960
  "check:deps": "bun run scripts/check-dependencies.ts",
2169
1961
  validate: "bun run check && bun run check:deps",
@@ -2178,107 +1970,16 @@ Created with \u2764\uFE0F using Gravito Framework
2178
1970
  },
2179
1971
  devDependencies: {
2180
1972
  "bun-types": "latest",
2181
- typescript: "^5.0.0"
1973
+ typescript: "^5.9.3"
2182
1974
  }
2183
1975
  };
2184
1976
  return JSON.stringify(pkg, null, 2);
2185
1977
  }
2186
1978
  };
2187
1979
 
2188
- // src/generators/DddGenerator.ts
2189
- var DddGenerator = class extends BaseGenerator {
2190
- get architectureType() {
2191
- return "ddd";
2192
- }
2193
- get displayName() {
2194
- return "Domain-Driven Design (DDD)";
2195
- }
2196
- get description() {
2197
- return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
2198
- }
2199
- getDirectoryStructure(context) {
2200
- return [
2201
- {
2202
- type: "directory",
2203
- name: "config",
2204
- children: [
2205
- { type: "file", name: "app.ts", content: this.generateAppConfig(context) },
2206
- { type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
2207
- { type: "file", name: "modules.ts", content: this.generateModulesConfig() },
2208
- { type: "file", name: "cache.ts", content: this.generateCacheConfig() },
2209
- { type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
2210
- ]
2211
- },
2212
- {
2213
- type: "directory",
2214
- name: "src",
2215
- children: [
2216
- // Bootstrap - Application startup and configuration
2217
- this.generateBootstrapDirectory(context),
2218
- // Shared - Cross-module shared components
2219
- this.generateShared(),
2220
- // Modules - Bounded Contexts
2221
- {
2222
- type: "directory",
2223
- name: "Modules",
2224
- children: [
2225
- this.generateModule("Ordering", context),
2226
- this.generateModule("Catalog", context)
2227
- ]
2228
- },
2229
- { type: "file", name: "main.ts", content: this.generateMainEntry(context) }
2230
- ]
2231
- },
2232
- {
2233
- type: "directory",
2234
- name: "tests",
2235
- children: [
2236
- {
2237
- type: "directory",
2238
- name: "Modules",
2239
- children: [
2240
- {
2241
- type: "directory",
2242
- name: "Ordering",
2243
- children: [
2244
- {
2245
- type: "directory",
2246
- name: "Unit",
2247
- children: [{ type: "file", name: ".gitkeep", content: "" }]
2248
- },
2249
- {
2250
- type: "directory",
2251
- name: "Integration",
2252
- children: [{ type: "file", name: ".gitkeep", content: "" }]
2253
- }
2254
- ]
2255
- },
2256
- {
2257
- type: "directory",
2258
- name: "Catalog",
2259
- children: [
2260
- {
2261
- type: "directory",
2262
- name: "Unit",
2263
- children: [{ type: "file", name: ".gitkeep", content: "" }]
2264
- }
2265
- ]
2266
- }
2267
- ]
2268
- },
2269
- {
2270
- type: "directory",
2271
- name: "Shared",
2272
- children: [{ type: "file", name: ".gitkeep", content: "" }]
2273
- }
2274
- ]
2275
- }
2276
- ];
2277
- }
2278
- // ─────────────────────────────────────────────────────────────
2279
- // Bootstrap Directory
2280
- // ─────────────────────────────────────────────────────────────
2281
- generateBootstrapDirectory(context) {
1980
+ // src/generators/ddd/BootstrapGenerator.ts
1981
+ var BootstrapGenerator = class {
1982
+ generate(context) {
2282
1983
  return {
2283
1984
  type: "directory",
2284
1985
  name: "Bootstrap",
@@ -2290,60 +1991,227 @@ var DddGenerator = class extends BaseGenerator {
2290
1991
  ]
2291
1992
  };
2292
1993
  }
2293
- // ─────────────────────────────────────────────────────────────
2294
- // Shared Directory (replaces SharedKernel with user's structure)
2295
- // ─────────────────────────────────────────────────────────────
2296
- generateShared() {
2297
- return {
2298
- type: "directory",
2299
- name: "Shared",
2300
- children: [
2301
- {
2302
- type: "directory",
2303
- name: "Domain",
2304
- children: [
2305
- {
2306
- type: "directory",
2307
- name: "ValueObjects",
2308
- children: [
2309
- { type: "file", name: "Id.ts", content: this.generateIdValueObject() },
2310
- { type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
2311
- { type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
2312
- ]
2313
- }
2314
- ]
1994
+ generateConfigDirectory(context) {
1995
+ return {
1996
+ type: "directory",
1997
+ name: "config",
1998
+ children: [
1999
+ { type: "file", name: "app.ts", content: this.generateAppConfig(context) },
2000
+ { type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
2001
+ { type: "file", name: "modules.ts", content: this.generateModulesConfig() },
2002
+ { type: "file", name: "cache.ts", content: this.generateCacheConfig() },
2003
+ { type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
2004
+ ]
2005
+ };
2006
+ }
2007
+ generateMainEntry(_context) {
2008
+ return `/**
2009
+ * Application Entry Point
2010
+ *
2011
+ * Start the HTTP server.
2012
+ */
2013
+
2014
+ import { createApp } from './Bootstrap/app'
2015
+
2016
+ const app = await createApp()
2017
+
2018
+ export default app.liftoff()
2019
+ `;
2020
+ }
2021
+ generateBootstrapApp(_context) {
2022
+ return `/**
2023
+ * Application Bootstrap
2024
+ *
2025
+ * Central configuration and initialization using the ServiceProvider pattern.
2026
+ *
2027
+ * Lifecycle:
2028
+ * 1. Configure: Load app config and orbits
2029
+ * 2. Boot: Initialize PlanetCore
2030
+ * 3. Register Providers: Bind services to container
2031
+ * 4. Bootstrap: Boot all providers
2032
+ */
2033
+
2034
+ import { defineConfig, PlanetCore } from '@gravito/core'
2035
+ import { OrbitAtlas } from '@gravito/atlas'
2036
+ import appConfig from '../../config/app'
2037
+ import { registerProviders } from './providers'
2038
+ import { registerRoutes } from './routes'
2039
+
2040
+ export async function createApp(): Promise<PlanetCore> {
2041
+ // 1. Configure
2042
+ const config = defineConfig({
2043
+ config: appConfig,
2044
+ orbits: [new OrbitAtlas()],
2045
+ })
2046
+
2047
+ // 2. Boot Core
2048
+ const core = await PlanetCore.boot(config)
2049
+ core.registerGlobalErrorHandlers()
2050
+
2051
+ // 3. Register Providers
2052
+ await registerProviders(core)
2053
+
2054
+ // 4. Bootstrap All Providers
2055
+ await core.bootstrap()
2056
+
2057
+ // Register routes after bootstrap
2058
+ registerRoutes(core.router)
2059
+
2060
+ return core
2061
+ }
2062
+ `;
2063
+ }
2064
+ generateProvidersRegistry(_context) {
2065
+ return `/**
2066
+ * Service Providers Registry
2067
+ *
2068
+ * Register all service providers here.
2069
+ * Include both global and module-specific providers.
2070
+ */
2071
+
2072
+ import {
2073
+ ServiceProvider,
2074
+ type Container,
2075
+ type PlanetCore,
2076
+ bodySizeLimit,
2077
+ securityHeaders,
2078
+ } from '@gravito/core'
2079
+ import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
2080
+ import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
2081
+
2082
+ /**
2083
+ * Middleware Provider - Global middleware registration
2084
+ */
2085
+ export class MiddlewareProvider extends ServiceProvider {
2086
+ register(_container: Container): void {}
2087
+
2088
+ boot(core: PlanetCore): void {
2089
+ const isDev = process.env.NODE_ENV !== 'production'
2090
+
2091
+ core.adapter.use('*', securityHeaders({
2092
+ contentSecurityPolicy: isDev ? false : undefined,
2093
+ }))
2094
+
2095
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
2096
+
2097
+ core.logger.info('\u{1F6E1}\uFE0F Global middleware registered')
2098
+ }
2099
+ }
2100
+
2101
+ export async function registerProviders(core: PlanetCore): Promise<void> {
2102
+ // Global Providers
2103
+ core.register(new MiddlewareProvider())
2104
+
2105
+ // Module Providers
2106
+ core.register(new OrderingServiceProvider())
2107
+ core.register(new CatalogServiceProvider())
2108
+
2109
+ // Add more providers as needed
2110
+ }
2111
+ `;
2112
+ }
2113
+ generateEventsRegistry() {
2114
+ return `/**
2115
+ * Domain Events Registry
2116
+ *
2117
+ * Register all domain event handlers here.
2118
+ */
2119
+
2120
+ import { EventDispatcher } from '../Shared/Infrastructure/EventBus/EventDispatcher'
2121
+
2122
+ export function registerEvents(dispatcher: EventDispatcher): void {
2123
+ // Register event handlers
2124
+ // dispatcher.subscribe('ordering.created', async (event) => { ... })
2125
+ }
2126
+ `;
2127
+ }
2128
+ generateRoutesRegistry(_context) {
2129
+ return `/**
2130
+ * Routes Registry
2131
+ *
2132
+ * Register all module routes here.
2133
+ */
2134
+
2135
+ export function registerRoutes(router: any): void {
2136
+ // Health check
2137
+ router.get('/health', (c: any) => c.json({ status: 'healthy' }))
2138
+
2139
+ // Ordering module
2140
+ router.get('/api/orders', (c: any) => c.json({ message: 'Order list' }))
2141
+ router.post('/api/orders', (c: any) => c.json({ message: 'Order created' }, 201))
2142
+
2143
+ // Catalog module
2144
+ router.get('/api/products', (c: any) => c.json({ message: 'Product list' }))
2145
+ }
2146
+ `;
2147
+ }
2148
+ generateModulesConfig() {
2149
+ return `/**
2150
+ * Modules Configuration
2151
+ *
2152
+ * Define module boundaries and their dependencies.
2153
+ */
2154
+
2155
+ export default {
2156
+ modules: {
2157
+ ordering: {
2158
+ name: 'Ordering',
2159
+ description: 'Order management module',
2160
+ prefix: '/api/orders',
2315
2161
  },
2316
- {
2317
- type: "directory",
2318
- name: "Infrastructure",
2319
- children: [
2320
- {
2321
- type: "directory",
2322
- name: "EventBus",
2323
- children: [
2324
- {
2325
- type: "file",
2326
- name: "EventDispatcher.ts",
2327
- content: this.generateEventDispatcher()
2328
- }
2329
- ]
2330
- }
2331
- ]
2162
+ catalog: {
2163
+ name: 'Catalog',
2164
+ description: 'Product catalog module',
2165
+ prefix: '/api/products',
2332
2166
  },
2333
- {
2334
- type: "directory",
2335
- name: "Exceptions",
2336
- children: [
2337
- { type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
2338
- ]
2339
- }
2340
- ]
2341
- };
2167
+ },
2168
+
2169
+ // Module dependencies
2170
+ dependencies: {
2171
+ ordering: ['catalog'], // Ordering depends on Catalog
2172
+ },
2173
+ }
2174
+ `;
2342
2175
  }
2343
- // ─────────────────────────────────────────────────────────────
2344
- // Module Generator (replaces Bounded Context)
2345
- // ─────────────────────────────────────────────────────────────
2346
- generateModule(name, context) {
2176
+ generateAppConfig(context) {
2177
+ return `export default {
2178
+ name: process.env.APP_NAME ?? '${context.name}',
2179
+ env: process.env.APP_ENV ?? 'development',
2180
+ port: Number.parseInt(process.env.PORT ?? '3000', 10),
2181
+ VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
2182
+ debug: process.env.APP_DEBUG === 'true',
2183
+ url: process.env.APP_URL ?? 'http://localhost:3000',
2184
+ }
2185
+ `;
2186
+ }
2187
+ generateDatabaseConfig() {
2188
+ return `export default {
2189
+ default: process.env.DB_CONNECTION ?? 'sqlite',
2190
+ connections: {
2191
+ sqlite: { driver: 'sqlite', database: 'database/database.sqlite' },
2192
+ },
2193
+ }
2194
+ `;
2195
+ }
2196
+ generateCacheConfig() {
2197
+ return `export default {
2198
+ default: process.env.CACHE_DRIVER ?? 'memory',
2199
+ stores: { memory: { driver: 'memory' } },
2200
+ }
2201
+ `;
2202
+ }
2203
+ generateLoggingConfig() {
2204
+ return `export default {
2205
+ default: 'console',
2206
+ channels: { console: { driver: 'console', level: 'debug' } },
2207
+ }
2208
+ `;
2209
+ }
2210
+ };
2211
+
2212
+ // src/generators/ddd/ModuleGenerator.ts
2213
+ var ModuleGenerator = class {
2214
+ generate(name, context) {
2347
2215
  return {
2348
2216
  type: "directory",
2349
2217
  name,
@@ -2488,175 +2356,224 @@ var DddGenerator = class extends BaseGenerator {
2488
2356
  ]
2489
2357
  };
2490
2358
  }
2491
- // ─────────────────────────────────────────────────────────────
2492
- // Bootstrap File Generators
2493
- // ─────────────────────────────────────────────────────────────
2494
- generateBootstrapApp(_context) {
2359
+ generateAggregate(name) {
2495
2360
  return `/**
2496
- * Application Bootstrap
2497
- *
2498
- * Central configuration and initialization using the ServiceProvider pattern.
2499
- *
2500
- * Lifecycle:
2501
- * 1. Configure: Load app config and orbits
2502
- * 2. Boot: Initialize PlanetCore
2503
- * 3. Register Providers: Bind services to container
2504
- * 4. Bootstrap: Boot all providers
2361
+ * ${name} Aggregate Root
2505
2362
  */
2506
2363
 
2507
- import { defineConfig, PlanetCore } from '@gravito/core'
2508
- import { OrbitAtlas } from '@gravito/atlas'
2509
- import appConfig from '../../config/app'
2510
- import { registerProviders } from './providers'
2511
- import { registerRoutes } from './routes'
2364
+ import { AggregateRoot } from '@gravito/enterprise'
2365
+ import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2366
+ import { ${name}Created } from '../../Events/${name}Created'
2367
+ import { ${name}Status } from './${name}Status'
2512
2368
 
2513
- export async function createApp(): Promise<PlanetCore> {
2514
- // 1. Configure
2515
- const config = defineConfig({
2516
- config: appConfig,
2517
- orbits: [new OrbitAtlas()],
2518
- })
2369
+ export interface ${name}Props {
2370
+ // Add properties here
2371
+ status: ${name}Status
2372
+ createdAt: Date
2373
+ }
2519
2374
 
2520
- // 2. Boot Core
2521
- const core = await PlanetCore.boot(config)
2522
- core.registerGlobalErrorHandlers()
2375
+ export class ${name} extends AggregateRoot<Id> {
2376
+ private props: ${name}Props
2523
2377
 
2524
- // 3. Register Providers
2525
- await registerProviders(core)
2378
+ private constructor(id: Id, props: ${name}Props) {
2379
+ super(id)
2380
+ this.props = props
2381
+ }
2526
2382
 
2527
- // 4. Bootstrap All Providers
2528
- await core.bootstrap()
2383
+ static create(id: Id): ${name} {
2384
+ const aggregate = new ${name}(id, {
2385
+ status: ${name}Status.PENDING,
2386
+ createdAt: new Date(),
2387
+ })
2529
2388
 
2530
- // Register routes after bootstrap
2531
- registerRoutes(core.router)
2389
+ aggregate.addDomainEvent(new ${name}Created(id.value))
2532
2390
 
2533
- return core
2391
+ return aggregate
2392
+ }
2393
+
2394
+ get status(): ${name}Status {
2395
+ return this.props.status
2396
+ }
2397
+
2398
+ // Add domain methods here
2534
2399
  }
2535
2400
  `;
2536
2401
  }
2537
- generateProvidersRegistry(_context) {
2402
+ generateAggregateStatus(name) {
2538
2403
  return `/**
2539
- * Service Providers Registry
2540
- *
2541
- * Register all service providers here.
2542
- * Include both global and module-specific providers.
2404
+ * ${name} Status
2543
2405
  */
2544
2406
 
2545
- import {
2546
- ServiceProvider,
2547
- type Container,
2548
- type PlanetCore,
2549
- bodySizeLimit,
2550
- securityHeaders,
2551
- } from '@gravito/core'
2552
- import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
2553
- import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
2407
+ export enum ${name}Status {
2408
+ PENDING = 'pending',
2409
+ ACTIVE = 'active',
2410
+ COMPLETED = 'completed',
2411
+ CANCELLED = 'cancelled',
2412
+ }
2413
+ `;
2414
+ }
2415
+ generateCreatedEvent(name) {
2416
+ return `/**
2417
+ * ${name} Created Event
2418
+ */
2554
2419
 
2555
- /**
2556
- * Middleware Provider - Global middleware registration
2420
+ import { DomainEvent } from '@gravito/enterprise'
2421
+
2422
+ export class ${name}Created extends DomainEvent {
2423
+ constructor(public readonly ${name.toLowerCase()}Id: string) {
2424
+ super()
2425
+ }
2426
+
2427
+ override get eventName(): string {
2428
+ return '${name.toLowerCase()}.created'
2429
+ }
2430
+
2431
+ get aggregateId(): string {
2432
+ return this.${name.toLowerCase()}Id
2433
+ }
2434
+ }
2435
+ `;
2436
+ }
2437
+ generateRepositoryInterface(name) {
2438
+ return `/**
2439
+ * ${name} Repository Interface
2557
2440
  */
2558
- export class MiddlewareProvider extends ServiceProvider {
2559
- register(_container: Container): void {}
2560
2441
 
2561
- boot(core: PlanetCore): void {
2562
- const isDev = process.env.NODE_ENV !== 'production'
2442
+ import { Repository } from '@gravito/enterprise'
2443
+ import type { ${name} } from '../Aggregates/${name}/${name}'
2444
+ import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2563
2445
 
2564
- core.adapter.use('*', securityHeaders({
2565
- contentSecurityPolicy: isDev ? false : undefined,
2566
- }))
2446
+ export interface I${name}Repository extends Repository<${name}, Id> {
2447
+ // Add specific methods for this repository if needed
2448
+ }
2449
+ `;
2450
+ }
2451
+ generateCommand(name) {
2452
+ return `/**
2453
+ * Create ${name} Command
2454
+ */
2567
2455
 
2568
- core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
2456
+ import { Command } from '@gravito/enterprise'
2569
2457
 
2570
- core.logger.info('\u{1F6E1}\uFE0F Global middleware registered')
2458
+ export class Create${name}Command extends Command {
2459
+ constructor(
2460
+ // Add command properties
2461
+ public readonly id?: string
2462
+ ) {
2463
+ super()
2571
2464
  }
2572
2465
  }
2466
+ `;
2467
+ }
2468
+ generateCommandHandler(name) {
2469
+ return `/**
2470
+ * Create ${name} Handler
2471
+ */
2573
2472
 
2574
- export async function registerProviders(core: PlanetCore): Promise<void> {
2575
- // Global Providers
2576
- core.register(new MiddlewareProvider())
2473
+ import { CommandHandler } from '@gravito/enterprise'
2474
+ import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
2475
+ import { ${name} } from '../../../Domain/Aggregates/${name}/${name}'
2476
+ import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2477
+ import type { Create${name}Command } from './Create${name}Command'
2577
2478
 
2578
- // Module Providers
2579
- core.register(new OrderingServiceProvider())
2580
- core.register(new CatalogServiceProvider())
2479
+ export class Create${name}Handler implements CommandHandler<Create${name}Command, string> {
2480
+ constructor(private repository: I${name}Repository) {}
2581
2481
 
2582
- // Add more providers as needed
2482
+ async handle(command: Create${name}Command): Promise<string> {
2483
+ const id = command.id ? Id.from(command.id) : Id.create()
2484
+ const aggregate = ${name}.create(id)
2485
+
2486
+ await this.repository.save(aggregate)
2487
+
2488
+ return id.value
2489
+ }
2583
2490
  }
2584
2491
  `;
2585
2492
  }
2586
- generateEventsRegistry() {
2493
+ generateQuery(name) {
2587
2494
  return `/**
2588
- * Domain Events Registry
2589
- *
2590
- * Register all domain event handlers here.
2495
+ * Get ${name} By Id Query
2591
2496
  */
2592
2497
 
2593
- import { EventDispatcher } from '../Shared/Infrastructure/EventBus/EventDispatcher'
2498
+ import { Query } from '@gravito/enterprise'
2594
2499
 
2595
- export function registerEvents(dispatcher: EventDispatcher): void {
2596
- // Register event handlers
2597
- // dispatcher.subscribe('ordering.created', async (event) => { ... })
2500
+ export class Get${name}ByIdQuery extends Query {
2501
+ constructor(public readonly id: string) {
2502
+ super()
2503
+ }
2598
2504
  }
2599
2505
  `;
2600
2506
  }
2601
- generateRoutesRegistry(_context) {
2507
+ generateQueryHandler(name) {
2602
2508
  return `/**
2603
- * Routes Registry
2604
- *
2605
- * Register all module routes here.
2509
+ * Get ${name} By Id Handler
2606
2510
  */
2607
2511
 
2608
- export function registerRoutes(router: any): void {
2609
- // Health check
2610
- router.get('/health', (c: any) => c.json({ status: 'healthy' }))
2512
+ import { QueryHandler } from '@gravito/enterprise'
2513
+ import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
2514
+ import type { ${name}DTO } from '../../DTOs/${name}DTO'
2515
+ import type { Get${name}ByIdQuery } from './Get${name}ByIdQuery'
2611
2516
 
2612
- // Ordering module
2613
- router.get('/api/orders', (c: any) => c.json({ message: 'Order list' }))
2614
- router.post('/api/orders', (c: any) => c.json({ message: 'Order created' }, 201))
2517
+ export class Get${name}ByIdHandler implements QueryHandler<Get${name}ByIdQuery, ${name}DTO | null> {
2518
+ constructor(private repository: I${name}Repository) {}
2615
2519
 
2616
- // Catalog module
2617
- router.get('/api/products', (c: any) => c.json({ message: 'Product list' }))
2520
+ async handle(query: Get${name}ByIdQuery): Promise<${name}DTO | null> {
2521
+ const aggregate = await this.repository.findById(query.id as any) // Simplified for demo
2522
+ if (!aggregate) return null
2523
+
2524
+ return {
2525
+ id: aggregate.id.value,
2526
+ status: aggregate.status,
2527
+ }
2528
+ }
2618
2529
  }
2619
2530
  `;
2620
2531
  }
2621
- generateMainEntry(_context) {
2532
+ generateDTO(name) {
2622
2533
  return `/**
2623
- * Application Entry Point
2624
- *
2625
- * Start the HTTP server.
2534
+ * ${name} DTO
2626
2535
  */
2627
2536
 
2628
- import { createApp } from './Bootstrap/app'
2629
-
2630
- const app = await createApp()
2537
+ import type { ${name}Status } from '../../Domain/Aggregates/${name}/${name}Status'
2631
2538
 
2632
- export default app.liftoff()
2539
+ export interface ${name}DTO {
2540
+ id: string
2541
+ status: ${name}Status
2542
+ // Add more fields
2543
+ }
2633
2544
  `;
2634
2545
  }
2635
- generateModulesConfig() {
2546
+ generateRepository(name) {
2636
2547
  return `/**
2637
- * Modules Configuration
2638
- *
2639
- * Define module boundaries and their dependencies.
2548
+ * ${name} Repository Implementation
2640
2549
  */
2641
2550
 
2642
- export default {
2643
- modules: {
2644
- ordering: {
2645
- name: 'Ordering',
2646
- description: 'Order management module',
2647
- prefix: '/api/orders',
2648
- },
2649
- catalog: {
2650
- name: 'Catalog',
2651
- description: 'Product catalog module',
2652
- prefix: '/api/products',
2653
- },
2654
- },
2551
+ import type { ${name} } from '../../Domain/Aggregates/${name}/${name}'
2552
+ import type { I${name}Repository } from '../../Domain/Repositories/I${name}Repository'
2553
+ import type { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2655
2554
 
2656
- // Module dependencies
2657
- dependencies: {
2658
- ordering: ['catalog'], // Ordering depends on Catalog
2659
- },
2555
+ const store = new Map<string, ${name}>()
2556
+
2557
+ export class ${name}Repository implements I${name}Repository {
2558
+ async findById(id: Id): Promise<${name} | null> {
2559
+ return store.get(id.value) ?? null
2560
+ }
2561
+
2562
+ async save(aggregate: ${name}): Promise<void> {
2563
+ store.set(aggregate.id.value, aggregate)
2564
+ }
2565
+
2566
+ async delete(id: Id): Promise<void> {
2567
+ store.delete(id.value)
2568
+ }
2569
+
2570
+ async findAll(): Promise<${name}[]> {
2571
+ return Array.from(store.values())
2572
+ }
2573
+
2574
+ async exists(id: Id): Promise<boolean> {
2575
+ return store.has(id.value)
2576
+ }
2660
2577
  }
2661
2578
  `;
2662
2579
  }
@@ -2679,78 +2596,57 @@ export class ${name}ServiceProvider extends ServiceProvider {
2679
2596
  }
2680
2597
  `;
2681
2598
  }
2682
- /**
2683
- * Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
2684
- */
2685
- generatePackageJson(context) {
2686
- const pkg = {
2687
- name: context.nameKebabCase,
2688
- version: "0.1.0",
2689
- type: "module",
2690
- scripts: {
2691
- dev: "bun run --watch src/main.ts",
2692
- build: "bun build ./src/main.ts --outdir ./dist --target bun",
2693
- start: "bun run dist/main.js",
2694
- test: "bun test",
2695
- typecheck: "tsc --noEmit",
2696
- check: "bun run typecheck && bun run test",
2697
- "check:deps": "bun run scripts/check-dependencies.ts",
2698
- validate: "bun run check && bun run check:deps",
2699
- precommit: "bun run validate",
2700
- "docker:build": `docker build -t ${context.nameKebabCase} .`,
2701
- "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
2702
- },
2703
- dependencies: {
2704
- "@gravito/core": "^1.0.0-beta.5",
2705
- "@gravito/enterprise": "workspace:*"
2706
- },
2707
- devDependencies: {
2708
- "bun-types": "latest",
2709
- typescript: "^5.0.0"
2710
- }
2599
+ };
2600
+
2601
+ // src/generators/ddd/SharedKernelGenerator.ts
2602
+ var SharedKernelGenerator = class {
2603
+ generate() {
2604
+ return {
2605
+ type: "directory",
2606
+ name: "Shared",
2607
+ children: [
2608
+ {
2609
+ type: "directory",
2610
+ name: "Domain",
2611
+ children: [
2612
+ {
2613
+ type: "directory",
2614
+ name: "ValueObjects",
2615
+ children: [
2616
+ { type: "file", name: "Id.ts", content: this.generateIdValueObject() },
2617
+ { type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
2618
+ { type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
2619
+ ]
2620
+ }
2621
+ ]
2622
+ },
2623
+ {
2624
+ type: "directory",
2625
+ name: "Infrastructure",
2626
+ children: [
2627
+ {
2628
+ type: "directory",
2629
+ name: "EventBus",
2630
+ children: [
2631
+ {
2632
+ type: "file",
2633
+ name: "EventDispatcher.ts",
2634
+ content: this.generateEventDispatcher()
2635
+ }
2636
+ ]
2637
+ }
2638
+ ]
2639
+ },
2640
+ {
2641
+ type: "directory",
2642
+ name: "Exceptions",
2643
+ children: [
2644
+ { type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
2645
+ ]
2646
+ }
2647
+ ]
2711
2648
  };
2712
- return JSON.stringify(pkg, null, 2);
2713
- }
2714
- // ─────────────────────────────────────────────────────────────
2715
- // Config Generators
2716
- // ─────────────────────────────────────────────────────────────
2717
- generateAppConfig(context) {
2718
- return `export default {
2719
- name: process.env.APP_NAME ?? '${context.name}',
2720
- env: process.env.APP_ENV ?? 'development',
2721
- port: Number.parseInt(process.env.PORT ?? '3000', 10),
2722
- VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
2723
- debug: process.env.APP_DEBUG === 'true',
2724
- url: process.env.APP_URL ?? 'http://localhost:3000',
2725
- }
2726
- `;
2727
- }
2728
- generateDatabaseConfig() {
2729
- return `export default {
2730
- default: process.env.DB_CONNECTION ?? 'sqlite',
2731
- connections: {
2732
- sqlite: { driver: 'sqlite', database: 'database/database.sqlite' },
2733
- },
2734
- }
2735
- `;
2736
- }
2737
- generateCacheConfig() {
2738
- return `export default {
2739
- default: process.env.CACHE_DRIVER ?? 'memory',
2740
- stores: { memory: { driver: 'memory' } },
2741
- }
2742
- `;
2743
- }
2744
- generateLoggingConfig() {
2745
- return `export default {
2746
- default: 'console',
2747
- channels: { console: { driver: 'console', level: 'debug' } },
2748
- }
2749
- `;
2750
2649
  }
2751
- // ─────────────────────────────────────────────────────────────
2752
- // Shared Kernel Files
2753
- // ─────────────────────────────────────────────────────────────
2754
2650
  generateIdValueObject() {
2755
2651
  return `/**
2756
2652
  * ID Value Object
@@ -2898,239 +2794,142 @@ export class EventDispatcher {
2898
2794
  }
2899
2795
  `;
2900
2796
  }
2901
- // ─────────────────────────────────────────────────────────────
2902
- // Bounded Context Templates
2903
- // ─────────────────────────────────────────────────────────────
2904
- generateAggregate(name) {
2905
- return `/**
2906
- * ${name} Aggregate Root
2907
- */
2908
-
2909
- import { AggregateRoot } from '@gravito/enterprise'
2910
- import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2911
- import { ${name}Created } from '../../Events/${name}Created'
2912
- import { ${name}Status } from './${name}Status'
2913
-
2914
- export interface ${name}Props {
2915
- // Add properties here
2916
- status: ${name}Status
2917
- createdAt: Date
2918
- }
2919
-
2920
- export class ${name} extends AggregateRoot<Id> {
2921
- private props: ${name}Props
2922
-
2923
- private constructor(id: Id, props: ${name}Props) {
2924
- super(id)
2925
- this.props = props
2926
- }
2927
-
2928
- static create(id: Id): ${name} {
2929
- const aggregate = new ${name}(id, {
2930
- status: ${name}Status.PENDING,
2931
- createdAt: new Date(),
2932
- })
2933
-
2934
- aggregate.addDomainEvent(new ${name}Created(id.value))
2935
-
2936
- return aggregate
2937
- }
2938
-
2939
- get status(): ${name}Status {
2940
- return this.props.status
2941
- }
2942
-
2943
- // Add domain methods here
2944
- }
2945
- `;
2946
- }
2947
- generateAggregateStatus(name) {
2948
- return `/**
2949
- * ${name} Status
2950
- */
2951
-
2952
- export enum ${name}Status {
2953
- PENDING = 'pending',
2954
- ACTIVE = 'active',
2955
- COMPLETED = 'completed',
2956
- CANCELLED = 'cancelled',
2957
- }
2958
- `;
2959
- }
2960
- generateCreatedEvent(name) {
2961
- return `/**
2962
- * ${name} Created Event
2963
- */
2964
-
2965
- import { DomainEvent } from '@gravito/enterprise'
2966
-
2967
- export class ${name}Created extends DomainEvent {
2968
- constructor(public readonly ${name.toLowerCase()}Id: string) {
2969
- super()
2970
- }
2971
-
2972
- override get eventName(): string {
2973
- return '${name.toLowerCase()}.created'
2974
- }
2975
-
2976
- get aggregateId(): string {
2977
- return this.${name.toLowerCase()}Id
2978
- }
2979
- }
2980
- `;
2981
- }
2982
- generateRepositoryInterface(name) {
2983
- return `/**
2984
- * ${name} Repository Interface
2985
- */
2986
-
2987
- import { Repository } from '@gravito/enterprise'
2988
- import type { ${name} } from '../Aggregates/${name}/${name}'
2989
- import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2990
-
2991
- export interface I${name}Repository extends Repository<${name}, Id> {
2992
- // Add specific methods for this repository if needed
2993
- }
2994
- `;
2995
- }
2996
- generateCommand(name) {
2997
- return `/**
2998
- * Create ${name} Command
2999
- */
3000
-
3001
- import { Command } from '@gravito/enterprise'
3002
-
3003
- export class Create${name}Command extends Command {
3004
- constructor(
3005
- // Add command properties
3006
- public readonly id?: string
3007
- ) {
3008
- super()
3009
- }
3010
- }
3011
- `;
3012
- }
3013
- generateCommandHandler(name) {
3014
- return `/**
3015
- * Create ${name} Handler
3016
- */
3017
-
3018
- import { CommandHandler } from '@gravito/enterprise'
3019
- import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
3020
- import { ${name} } from '../../../Domain/Aggregates/${name}/${name}'
3021
- import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
3022
- import type { Create${name}Command } from './Create${name}Command'
3023
-
3024
- export class Create${name}Handler implements CommandHandler<Create${name}Command, string> {
3025
- constructor(private repository: I${name}Repository) {}
3026
-
3027
- async handle(command: Create${name}Command): Promise<string> {
3028
- const id = command.id ? Id.from(command.id) : Id.create()
3029
- const aggregate = ${name}.create(id)
3030
-
3031
- await this.repository.save(aggregate)
3032
-
3033
- return id.value
3034
- }
3035
- }
3036
- `;
3037
- }
3038
- generateQuery(name) {
3039
- return `/**
3040
- * Get ${name} By Id Query
3041
- */
3042
-
3043
- import { Query } from '@gravito/enterprise'
3044
-
3045
- export class Get${name}ByIdQuery extends Query {
3046
- constructor(public readonly id: string) {
3047
- super()
3048
- }
3049
- }
3050
- `;
3051
- }
3052
- generateQueryHandler(name) {
3053
- return `/**
3054
- * Get ${name} By Id Handler
3055
- */
3056
-
3057
- import { QueryHandler } from '@gravito/enterprise'
3058
- import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
3059
- import type { ${name}DTO } from '../../DTOs/${name}DTO'
3060
- import type { Get${name}ByIdQuery } from './Get${name}ByIdQuery'
3061
-
3062
- export class Get${name}ByIdHandler implements QueryHandler<Get${name}ByIdQuery, ${name}DTO | null> {
3063
- constructor(private repository: I${name}Repository) {}
3064
-
3065
- async handle(query: Get${name}ByIdQuery): Promise<${name}DTO | null> {
3066
- const aggregate = await this.repository.findById(query.id as any) // Simplified for demo
3067
- if (!aggregate) return null
3068
-
3069
- return {
3070
- id: aggregate.id.value,
3071
- status: aggregate.status,
3072
- }
3073
- }
3074
- }
3075
- `;
3076
- }
3077
- generateDTO(name) {
2797
+ generateExceptionHandler() {
3078
2798
  return `/**
3079
- * ${name} DTO
2799
+ * Exception Handler
3080
2800
  */
3081
2801
 
3082
- import type { ${name}Status } from '../../Domain/Aggregates/${name}/${name}Status'
3083
-
3084
- export interface ${name}DTO {
3085
- id: string
3086
- status: ${name}Status
3087
- // Add more fields
2802
+ export function report(error: unknown): void {
2803
+ console.error('[Exception]', error)
3088
2804
  }
3089
2805
  `;
3090
2806
  }
3091
- generateRepository(name) {
3092
- return `/**
3093
- * ${name} Repository Implementation
3094
- */
3095
-
3096
- import type { ${name} } from '../../Domain/Aggregates/${name}/${name}'
3097
- import type { I${name}Repository } from '../../Domain/Repositories/I${name}Repository'
3098
- import type { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
3099
-
3100
- const store = new Map<string, ${name}>()
3101
-
3102
- export class ${name}Repository implements I${name}Repository {
3103
- async findById(id: Id): Promise<${name} | null> {
3104
- return store.get(id.value) ?? null
3105
- }
2807
+ };
3106
2808
 
3107
- async save(aggregate: ${name}): Promise<void> {
3108
- store.set(aggregate.id.value, aggregate)
2809
+ // src/generators/DddGenerator.ts
2810
+ var DddGenerator = class extends BaseGenerator {
2811
+ moduleGenerator;
2812
+ sharedKernelGenerator;
2813
+ bootstrapGenerator;
2814
+ constructor(config) {
2815
+ super(config);
2816
+ this.moduleGenerator = new ModuleGenerator();
2817
+ this.sharedKernelGenerator = new SharedKernelGenerator();
2818
+ this.bootstrapGenerator = new BootstrapGenerator();
3109
2819
  }
3110
-
3111
- async delete(id: Id): Promise<void> {
3112
- store.delete(id.value)
2820
+ get architectureType() {
2821
+ return "ddd";
3113
2822
  }
3114
-
3115
- async findAll(): Promise<${name}[]> {
3116
- return Array.from(store.values())
2823
+ get displayName() {
2824
+ return "Domain-Driven Design (DDD)";
3117
2825
  }
3118
-
3119
- async exists(id: Id): Promise<boolean> {
3120
- return store.has(id.value)
2826
+ get description() {
2827
+ return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
3121
2828
  }
3122
- }
3123
- `;
2829
+ getDirectoryStructure(context) {
2830
+ return [
2831
+ this.bootstrapGenerator.generateConfigDirectory(context),
2832
+ {
2833
+ type: "directory",
2834
+ name: "src",
2835
+ children: [
2836
+ // Bootstrap - Application startup and configuration
2837
+ this.bootstrapGenerator.generate(context),
2838
+ // Shared - Cross-module shared components
2839
+ this.sharedKernelGenerator.generate(),
2840
+ // Modules - Bounded Contexts
2841
+ {
2842
+ type: "directory",
2843
+ name: "Modules",
2844
+ children: [
2845
+ this.moduleGenerator.generate("Ordering", context),
2846
+ this.moduleGenerator.generate("Catalog", context)
2847
+ ]
2848
+ },
2849
+ {
2850
+ type: "file",
2851
+ name: "main.ts",
2852
+ content: this.bootstrapGenerator.generateMainEntry(context)
2853
+ }
2854
+ ]
2855
+ },
2856
+ {
2857
+ type: "directory",
2858
+ name: "tests",
2859
+ children: [
2860
+ {
2861
+ type: "directory",
2862
+ name: "Modules",
2863
+ children: [
2864
+ {
2865
+ type: "directory",
2866
+ name: "Ordering",
2867
+ children: [
2868
+ {
2869
+ type: "directory",
2870
+ name: "Unit",
2871
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2872
+ },
2873
+ {
2874
+ type: "directory",
2875
+ name: "Integration",
2876
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2877
+ }
2878
+ ]
2879
+ },
2880
+ {
2881
+ type: "directory",
2882
+ name: "Catalog",
2883
+ children: [
2884
+ {
2885
+ type: "directory",
2886
+ name: "Unit",
2887
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2888
+ }
2889
+ ]
2890
+ }
2891
+ ]
2892
+ },
2893
+ {
2894
+ type: "directory",
2895
+ name: "Shared",
2896
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2897
+ }
2898
+ ]
2899
+ }
2900
+ ];
3124
2901
  }
3125
- generateExceptionHandler() {
3126
- return `/**
3127
- * Exception Handler
3128
- */
3129
-
3130
- export function report(error: unknown): void {
3131
- console.error('[Exception]', error)
3132
- }
3133
- `;
2902
+ /**
2903
+ * Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
2904
+ */
2905
+ generatePackageJson(context) {
2906
+ const pkg = {
2907
+ name: context.nameKebabCase,
2908
+ version: "0.1.0",
2909
+ type: "module",
2910
+ scripts: {
2911
+ dev: "bun run --watch src/main.ts",
2912
+ build: "bun build ./src/main.ts --outdir ./dist --target bun",
2913
+ start: "bun run dist/main.js",
2914
+ test: "bun test",
2915
+ typecheck: "bun tsc --noEmit",
2916
+ check: "bun run typecheck && bun run test",
2917
+ "check:deps": "bun run scripts/check-dependencies.ts",
2918
+ validate: "bun run check && bun run check:deps",
2919
+ precommit: "bun run validate",
2920
+ "docker:build": `docker build -t ${context.nameKebabCase} .`,
2921
+ "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
2922
+ },
2923
+ dependencies: {
2924
+ "@gravito/core": "^1.0.0-beta.5",
2925
+ "@gravito/enterprise": "workspace:*"
2926
+ },
2927
+ devDependencies: {
2928
+ "bun-types": "latest",
2929
+ typescript: "^5.9.3"
2930
+ }
2931
+ };
2932
+ return JSON.stringify(pkg, null, 2);
3134
2933
  }
3135
2934
  generateArchitectureDoc(context) {
3136
2935
  return `# ${context.name} - DDD Architecture Guide
@@ -3359,123 +3158,13 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
3359
3158
  ];
3360
3159
  }
3361
3160
  // ─────────────────────────────────────────────────────────────
3362
- // Config Generators
3161
+ // Config Generators (using shared ConfigGenerator)
3363
3162
  // ─────────────────────────────────────────────────────────────
3364
3163
  generateAppConfig(context) {
3365
- return `/**
3366
- * Application Configuration
3367
- */
3368
- export default {
3369
- /**
3370
- * Application name
3371
- */
3372
- name: process.env.APP_NAME ?? '${context.name}',
3373
-
3374
- /**
3375
- * Application environment
3376
- */
3377
- env: process.env.APP_ENV ?? 'development',
3378
-
3379
- /**
3380
- * Application port
3381
- */
3382
- port: Number.parseInt(process.env.PORT ?? '3000', 10),
3383
-
3384
- /**
3385
- * View directory
3386
- */
3387
- VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
3388
-
3389
- /**
3390
- * Debug mode
3391
- */
3392
- debug: process.env.APP_DEBUG === 'true',
3393
-
3394
- /**
3395
- * Application URL
3396
- */
3397
- url: process.env.APP_URL ?? 'http://localhost:3000',
3398
-
3399
- /**
3400
- * Timezone
3401
- */
3402
- timezone: 'UTC',
3403
-
3404
- /**
3405
- * Locale
3406
- */
3407
- locale: 'en',
3408
-
3409
- /**
3410
- * Fallback locale
3411
- */
3412
- fallbackLocale: 'en',
3413
-
3414
- /**
3415
- * Encryption key
3416
- */
3417
- key: process.env.APP_KEY,
3418
-
3419
- /**
3420
- * Service providers to register
3421
- */
3422
- providers: [
3423
- // Framework providers
3424
- // 'RouteServiceProvider',
3425
-
3426
- // Application providers
3427
- // 'AppServiceProvider',
3428
- ],
3429
- }
3430
- `;
3164
+ return ConfigGenerator.generateDetailedAppConfig(context);
3431
3165
  }
3432
3166
  generateDatabaseConfig() {
3433
- return `/**
3434
- * Database Configuration
3435
- */
3436
- export default {
3437
- /**
3438
- * Default connection
3439
- */
3440
- default: process.env.DB_CONNECTION ?? 'sqlite',
3441
-
3442
- /**
3443
- * Database connections
3444
- */
3445
- connections: {
3446
- sqlite: {
3447
- driver: 'sqlite',
3448
- database: process.env.DB_DATABASE ?? 'database/database.sqlite',
3449
- },
3450
-
3451
- mysql: {
3452
- driver: 'mysql',
3453
- host: process.env.DB_HOST ?? 'localhost',
3454
- port: Number(process.env.DB_PORT ?? 3306),
3455
- database: process.env.DB_DATABASE ?? 'forge',
3456
- username: process.env.DB_USERNAME ?? 'forge',
3457
- password: process.env.DB_PASSWORD ?? '',
3458
- },
3459
-
3460
- postgres: {
3461
- driver: 'postgres',
3462
- host: process.env.DB_HOST ?? 'localhost',
3463
- port: Number(process.env.DB_PORT ?? 5432),
3464
- database: process.env.DB_DATABASE ?? 'forge',
3465
- username: process.env.DB_USERNAME ?? 'forge',
3466
- password: process.env.DB_PASSWORD ?? '',
3467
- },
3468
- },
3469
-
3470
- /**
3471
- * Migration settings
3472
- */
3473
- migrations: {
3474
- table: 'migrations',
3475
- path: 'database/migrations',
3476
- },
3477
- }
3478
- `;
3167
+ return ConfigGenerator.generateDetailedDatabaseConfig();
3479
3168
  }
3480
3169
  generateAuthConfig() {
3481
3170
  return `/**
@@ -3758,33 +3447,6 @@ export class AppServiceProvider extends ServiceProvider {
3758
3447
  console.log('${context.name} application booted!')
3759
3448
  }
3760
3449
  }
3761
- `;
3762
- }
3763
- generateRouteServiceProvider(_context) {
3764
- return `/**
3765
- * Route Service Provider
3766
- *
3767
- * Configures and registers application routes.
3768
- */
3769
-
3770
- import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
3771
- import { registerRoutes } from '../routes'
3772
-
3773
- export class RouteServiceProvider extends ServiceProvider {
3774
- /**
3775
- * Register any application services.
3776
- */
3777
- register(_container: Container): void {
3778
- // Routes are registered in boot
3779
- }
3780
-
3781
- /**
3782
- * Bootstrap any application services.
3783
- */
3784
- boot(core: PlanetCore): void {
3785
- registerRoutes(core.router)
3786
- }
3787
- }
3788
3450
  `;
3789
3451
  }
3790
3452
  // ─────────────────────────────────────────────────────────────
@@ -4455,11 +4117,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
4455
4117
  module: "dist/index.mjs",
4456
4118
  types: "dist/index.d.ts",
4457
4119
  scripts: {
4458
- build: "tsup src/index.ts --format cjs,esm --dts",
4120
+ build: "tsup src/index.ts --format esm --dts",
4459
4121
  test: "bun test",
4460
- typecheck: "tsc --noEmit",
4461
- check: "bun run typecheck && bun run test",
4462
- validate: "bun run check"
4122
+ typecheck: "bun tsc --noEmit"
4463
4123
  },
4464
4124
  dependencies: {
4465
4125
  "@gravito/core": depVersion,
@@ -4468,8 +4128,12 @@ export class ${name}ServiceProvider extends ServiceProvider {
4468
4128
  "@gravito/stasis": depVersion
4469
4129
  },
4470
4130
  devDependencies: {
4471
- tsup: "^8.0.0",
4472
- typescript: "^5.0.0"
4131
+ "bun-types": "latest",
4132
+ typescript: "^5.9.3",
4133
+ tsup: "^8.0.0"
4134
+ },
4135
+ peerDependencies: {
4136
+ "@gravito/core": ">=1.0.0"
4473
4137
  }
4474
4138
  };
4475
4139
  return JSON.stringify(pkg, null, 2);
@@ -4619,7 +4283,7 @@ var ProfileResolver = class _ProfileResolver {
4619
4283
  };
4620
4284
 
4621
4285
  // src/Scaffold.ts
4622
- var import_node_path3 = __toESM(require("path"), 1);
4286
+ var import_node_path6 = __toESM(require("path"), 1);
4623
4287
 
4624
4288
  // src/generators/ActionDomainGenerator.ts
4625
4289
  var ActionDomainGenerator = class extends BaseGenerator {
@@ -4826,6 +4490,9 @@ var ActionDomainGenerator = class extends BaseGenerator {
4826
4490
 
4827
4491
  import { Model, column } from '@gravito/atlas'
4828
4492
 
4493
+ /**
4494
+ * Represents a user in the system.
4495
+ */
4829
4496
  export class User extends Model {
4830
4497
  static table = 'users'
4831
4498
 
@@ -4849,6 +4516,11 @@ export class User extends Model {
4849
4516
  // ─────────────────────────────────────────────────────────────
4850
4517
  // Action Generators
4851
4518
  // ─────────────────────────────────────────────────────────────
4519
+ /**
4520
+ * Generates the base Action class source code.
4521
+ *
4522
+ * @returns The complete source code for the abstract Action class.
4523
+ */
4852
4524
  generateActionBase() {
4853
4525
  return `/**
4854
4526
  * Action Base Class
@@ -4865,6 +4537,11 @@ export abstract class Action<TInput = unknown, TOutput = unknown> {
4865
4537
  }
4866
4538
  `;
4867
4539
  }
4540
+ /**
4541
+ * Generates the GetServerStatusAction source code.
4542
+ *
4543
+ * @returns The complete source code for the example action.
4544
+ */
4868
4545
  generateGetServerStatusAction() {
4869
4546
  return `/**
4870
4547
  * Get Server Status Action
@@ -4887,6 +4564,11 @@ export class GetServerStatusAction extends Action<void, ServerStatusResponse> {
4887
4564
  // ─────────────────────────────────────────────────────────────
4888
4565
  // Controller Generators
4889
4566
  // ─────────────────────────────────────────────────────────────
4567
+ /**
4568
+ * Generates the Server Controller source code.
4569
+ *
4570
+ * @returns The complete source code for the ServerController class.
4571
+ */
4890
4572
  generateServerController() {
4891
4573
  return `/**
4892
4574
  * Server Controller
@@ -4914,6 +4596,11 @@ export class ServerController {
4914
4596
  // ─────────────────────────────────────────────────────────────
4915
4597
  // Type Generators
4916
4598
  // ─────────────────────────────────────────────────────────────
4599
+ /**
4600
+ * Generates the ServerStatusResponse type definition.
4601
+ *
4602
+ * @returns The complete source code for the response interface.
4603
+ */
4917
4604
  generateServerStatusResponse() {
4918
4605
  return `/**
4919
4606
  * Server Status Response Type
@@ -4929,6 +4616,11 @@ export interface ServerStatusResponse {
4929
4616
  // ─────────────────────────────────────────────────────────────
4930
4617
  // Routes & Bootstrap
4931
4618
  // ─────────────────────────────────────────────────────────────
4619
+ /**
4620
+ * Generates the API routes registration function.
4621
+ *
4622
+ * @returns The complete source code for the api.ts routes file.
4623
+ */
4932
4624
  generateApiRoutes() {
4933
4625
  return `/**
4934
4626
  * API Routes Registration
@@ -4947,6 +4639,12 @@ export function registerApiRoutes(router: Router) {
4947
4639
  }
4948
4640
  `;
4949
4641
  }
4642
+ /**
4643
+ * Generates the App Service Provider source code.
4644
+ *
4645
+ * @param context - The generator context containing project details.
4646
+ * @returns The complete source code for AppServiceProvider.
4647
+ */
4950
4648
  generateAppServiceProvider(context) {
4951
4649
  return `/**
4952
4650
  * App Service Provider
@@ -4975,6 +4673,11 @@ export { MiddlewareProvider } from './MiddlewareProvider'
4975
4673
  export { RouteProvider } from './RouteProvider'
4976
4674
  `;
4977
4675
  }
4676
+ /**
4677
+ * Generates the Middleware Service Provider source code.
4678
+ *
4679
+ * @returns The complete source code for MiddlewareProvider.
4680
+ */
4978
4681
  generateMiddlewareProvider() {
4979
4682
  return `/**
4980
4683
  * Middleware Service Provider
@@ -4999,6 +4702,11 @@ export class MiddlewareProvider extends ServiceProvider {
4999
4702
  }
5000
4703
  `;
5001
4704
  }
4705
+ /**
4706
+ * Generates the Route Service Provider source code.
4707
+ *
4708
+ * @returns The complete source code for RouteProvider.
4709
+ */
5002
4710
  generateRouteProvider() {
5003
4711
  return `/**
5004
4712
  * Route Service Provider
@@ -5110,7 +4818,7 @@ Created with \u2764\uFE0F using Gravito Framework
5110
4818
  build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
5111
4819
  start: "bun run dist/bootstrap.js",
5112
4820
  test: "bun test",
5113
- typecheck: "tsc --noEmit",
4821
+ typecheck: "bun tsc --noEmit",
5114
4822
  check: "bun run typecheck && bun run test",
5115
4823
  "check:deps": "bun run scripts/check-dependencies.ts",
5116
4824
  validate: "bun run check && bun run check:deps",
@@ -5124,7 +4832,7 @@ Created with \u2764\uFE0F using Gravito Framework
5124
4832
  },
5125
4833
  devDependencies: {
5126
4834
  "bun-types": "latest",
5127
- typescript: "^5.0.0"
4835
+ typescript: "^5.9.3"
5128
4836
  }
5129
4837
  };
5130
4838
  return JSON.stringify(pkg, null, 2);
@@ -5132,6 +4840,7 @@ Created with \u2764\uFE0F using Gravito Framework
5132
4840
  };
5133
4841
 
5134
4842
  // src/generators/StandaloneEngineGenerator.ts
4843
+ var import_node_path5 = __toESM(require("path"), 1);
5135
4844
  var StandaloneEngineGenerator = class extends BaseGenerator {
5136
4845
  get architectureType() {
5137
4846
  return "standalone-engine";
@@ -5163,9 +4872,16 @@ var StandaloneEngineGenerator = class extends BaseGenerator {
5163
4872
  ];
5164
4873
  }
5165
4874
  async generateCommonFiles(context) {
4875
+ const commonDir = import_node_path5.default.resolve(this.config.templatesDir, "common");
4876
+ const extendedContext = { ...context };
5166
4877
  await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
5167
- await this.writeFile(context.targetDir, "tsconfig.json", this.generateTsConfig());
5168
- await this.writeFile(context.targetDir, ".gitignore", this.generateGitignore());
4878
+ await this.generateFileFromTemplate(
4879
+ commonDir,
4880
+ "tsconfig.json.hbs",
4881
+ "tsconfig.json",
4882
+ extendedContext
4883
+ );
4884
+ await this.generateFileFromTemplate(commonDir, "gitignore.hbs", ".gitignore", extendedContext);
5169
4885
  }
5170
4886
  generatePackageJson(context) {
5171
4887
  const pkg = {
@@ -5220,23 +4936,17 @@ A high-performance web application powered by Gravito Engine.
5220
4936
 
5221
4937
  ### Install Dependencies
5222
4938
 
5223
- \`\`\`bash
5224
- bun install
5225
- \`\`\`
5226
-
4939
+ bun install
4940
+
5227
4941
  ### Run Development Server
5228
4942
 
5229
- \`\`\`bash
5230
- bun run dev
5231
- \`\`\`
5232
-
4943
+ bun run dev
4944
+
5233
4945
  ### Production Build
5234
4946
 
5235
- \`\`\`bash
5236
- bun run build
4947
+ bun run build
5237
4948
  bun start
5238
- \`\`\`
5239
- `;
4949
+ `;
5240
4950
  }
5241
4951
  };
5242
4952
 
@@ -5245,11 +4955,14 @@ var Scaffold = class {
5245
4955
  templatesDir;
5246
4956
  verbose;
5247
4957
  constructor(options = {}) {
5248
- this.templatesDir = options.templatesDir ?? import_node_path3.default.resolve(__dirname, "../templates");
4958
+ this.templatesDir = options.templatesDir ?? import_node_path6.default.resolve(__dirname, "../templates");
5249
4959
  this.verbose = options.verbose ?? false;
5250
4960
  }
5251
4961
  /**
5252
- * Get all available architecture types.
4962
+ * Returns a list of all architectural patterns supported by the engine,
4963
+ * along with human-readable names and descriptions.
4964
+ *
4965
+ * @returns {Array<{type: ArchitectureType, name: string, description: string}>}
5253
4966
  */
5254
4967
  getArchitectureTypes() {
5255
4968
  return [
@@ -5286,11 +4999,16 @@ var Scaffold = class {
5286
4999
  ];
5287
5000
  }
5288
5001
  /**
5289
- * Create a new project scaffold.
5002
+ * Orchestrates the complete project generation lifecycle.
5003
+ * This includes directory creation, file layout, profile resolution,
5004
+ * dependency mapping, and optional post-install hooks.
5005
+ *
5006
+ * @param {ScaffoldOptions} options - Detailed configuration for the new project.
5007
+ * @returns {Promise<ScaffoldResult>}
5290
5008
  */
5291
5009
  async create(options) {
5292
5010
  const generator = this.createGenerator(options.architecture);
5293
- const fs3 = await import("fs/promises");
5011
+ const fs5 = await import("fs/promises");
5294
5012
  const profileResolver = new ProfileResolver();
5295
5013
  const profileConfig = profileResolver.resolve(options.profile, options.features);
5296
5014
  const context = BaseGenerator.createContext(
@@ -5317,8 +5035,8 @@ var Scaffold = class {
5317
5035
  // Default template for now, should come from options if applicable
5318
5036
  "1.0.0"
5319
5037
  );
5320
- const lockPath = import_node_path3.default.resolve(options.targetDir, "gravito.lock.json");
5321
- await fs3.writeFile(lockPath, lockContent, "utf-8");
5038
+ const lockPath = import_node_path6.default.resolve(options.targetDir, "gravito.lock.json");
5039
+ await fs5.writeFile(lockPath, lockContent, "utf-8");
5322
5040
  filesCreated.push(lockPath);
5323
5041
  return {
5324
5042
  success: true,
@@ -5377,6 +5095,7 @@ var Scaffold = class {
5377
5095
  BaseGenerator,
5378
5096
  CleanArchitectureGenerator,
5379
5097
  DddGenerator,
5098
+ DependencyValidator,
5380
5099
  EnterpriseMvcGenerator,
5381
5100
  EnvironmentDetector,
5382
5101
  FileMerger,