@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.js CHANGED
@@ -1,3 +1,144 @@
1
+ // src/DependencyValidator.ts
2
+ var DependencyValidator = class _DependencyValidator {
3
+ /**
4
+ * Driver 到 Package 的映射規則
5
+ */
6
+ static DRIVER_DEPENDENCIES = [
7
+ {
8
+ driver: "redis",
9
+ requiredPackages: ["@gravito/ion"],
10
+ description: "Redis cache/queue driver requires @gravito/ion"
11
+ },
12
+ {
13
+ driver: "postgresql",
14
+ requiredPackages: ["@gravito/atlas", "pg"],
15
+ description: "PostgreSQL driver requires @gravito/atlas and pg"
16
+ },
17
+ {
18
+ driver: "mysql",
19
+ requiredPackages: ["@gravito/atlas", "mysql2"],
20
+ description: "MySQL driver requires @gravito/atlas and mysql2"
21
+ },
22
+ {
23
+ driver: "sqlite",
24
+ requiredPackages: ["@gravito/atlas", "better-sqlite3"],
25
+ description: "SQLite driver requires @gravito/atlas and better-sqlite3"
26
+ },
27
+ {
28
+ driver: "s3",
29
+ requiredPackages: ["@gravito/stasis", "@aws-sdk/client-s3"],
30
+ description: "S3 storage driver requires @gravito/stasis and AWS SDK"
31
+ },
32
+ {
33
+ driver: "r2",
34
+ requiredPackages: ["@gravito/stasis", "@aws-sdk/client-s3"],
35
+ description: "R2 storage driver requires @gravito/stasis and AWS SDK"
36
+ }
37
+ ];
38
+ /**
39
+ * Feature 衝突規則
40
+ */
41
+ static CONFLICTS = [
42
+ {
43
+ features: ["postgres", "mysql", "sqlite"],
44
+ reason: "\u4E0D\u80FD\u540C\u6642\u4F7F\u7528\u591A\u500B\u8CC7\u6599\u5EAB driver (PostgreSQL, MySQL, SQLite)"
45
+ }
46
+ ];
47
+ /**
48
+ * Feature 依賴映射
49
+ */
50
+ static FEATURE_DEPENDENCIES = {
51
+ stream: ["@gravito/beam"],
52
+ monitor: ["@gravito/spectrum"],
53
+ graphql: ["@gravito/constellation"]
54
+ };
55
+ /**
56
+ * 驗證 Profile 配置
57
+ *
58
+ * @param config - Profile 配置
59
+ * @param packageJson - 專案的 package.json 內容
60
+ * @returns 驗證結果
61
+ */
62
+ validate(config, packageJson) {
63
+ const errors = [];
64
+ const warnings = [];
65
+ this.validateDriverDependencies(config, packageJson, errors);
66
+ this.validateFeatureConflicts(config, errors);
67
+ this.validateFeatureDependencies(config, packageJson, warnings);
68
+ return {
69
+ valid: errors.length === 0,
70
+ errors,
71
+ warnings
72
+ };
73
+ }
74
+ /**
75
+ * 驗證 driver 依賴
76
+ */
77
+ validateDriverDependencies(config, packageJson, errors) {
78
+ for (const [service, driver] of Object.entries(config.drivers)) {
79
+ const rule = _DependencyValidator.DRIVER_DEPENDENCIES.find((r) => r.driver === driver);
80
+ if (rule) {
81
+ for (const pkg of rule.requiredPackages) {
82
+ if (!this.hasPackage(packageJson, pkg)) {
83
+ errors.push(`\u7F3A\u5C11\u4F9D\u8CF4: ${pkg} (${service} driver '${driver}' \u6240\u9700)`);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ }
89
+ /**
90
+ * 驗證 feature 衝突
91
+ */
92
+ validateFeatureConflicts(config, errors) {
93
+ for (const conflict of _DependencyValidator.CONFLICTS) {
94
+ const conflictingFeatures = conflict.features.filter((f) => config.features.includes(f));
95
+ if (conflictingFeatures.length > 1) {
96
+ errors.push(`Feature \u885D\u7A81: ${conflictingFeatures.join(", ")} - ${conflict.reason}`);
97
+ }
98
+ }
99
+ }
100
+ /**
101
+ * 驗證 feature 依賴
102
+ */
103
+ validateFeatureDependencies(config, packageJson, warnings) {
104
+ for (const feature of config.features) {
105
+ const requiredPackages = _DependencyValidator.FEATURE_DEPENDENCIES[feature];
106
+ if (requiredPackages) {
107
+ for (const pkg of requiredPackages) {
108
+ if (!this.hasPackage(packageJson, pkg)) {
109
+ warnings.push(`Feature "${feature}" \u9700\u8981 ${pkg}`);
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ /**
116
+ * 檢查 package.json 是否包含指定 package
117
+ */
118
+ hasPackage(packageJson, packageName) {
119
+ return packageJson.dependencies?.[packageName] !== void 0 || packageJson.devDependencies?.[packageName] !== void 0;
120
+ }
121
+ /**
122
+ * 建議安裝缺失的依賴
123
+ *
124
+ * @param result - 驗證結果
125
+ * @returns 安裝命令建議
126
+ */
127
+ static suggestInstallCommand(result) {
128
+ if (result.errors.length === 0) {
129
+ return null;
130
+ }
131
+ const missingPackages = result.errors.map((err) => {
132
+ const match = err.match(/缺少依賴: ([@\w/-]+)/);
133
+ return match ? match[1] : null;
134
+ }).filter((pkg) => pkg !== null);
135
+ if (missingPackages.length === 0) {
136
+ return null;
137
+ }
138
+ return `bun add ${missingPackages.join(" ")}`;
139
+ }
140
+ };
141
+
1
142
  // src/EnvironmentDetector.ts
2
143
  var EnvironmentDetector = class {
3
144
  detect() {
@@ -98,12 +239,52 @@ ${overlay}`;
98
239
  };
99
240
 
100
241
  // src/generators/BaseGenerator.ts
101
- import fs2 from "fs/promises";
102
- import path2 from "path";
242
+ import fs4 from "fs/promises";
243
+ import path4 from "path";
103
244
 
104
- // src/generators/StubGenerator.ts
245
+ // src/utils/FileUtilities.ts
105
246
  import fs from "fs/promises";
106
247
  import path from "path";
248
+ var FileUtilities = class _FileUtilities {
249
+ static async walk(dir) {
250
+ const files = await fs.readdir(dir);
251
+ const paths = [];
252
+ for (const file of files) {
253
+ const filePath = path.join(dir, file);
254
+ const stat = await fs.stat(filePath);
255
+ if (stat.isDirectory()) {
256
+ paths.push(...await _FileUtilities.walk(filePath));
257
+ } else {
258
+ paths.push(filePath);
259
+ }
260
+ }
261
+ return paths;
262
+ }
263
+ static async writeFile(basePath, relativePath, content, fileMerger, log) {
264
+ const fullPath = path.resolve(basePath, relativePath);
265
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
266
+ let finalContent = content;
267
+ try {
268
+ const existingContent = await fs.readFile(fullPath, "utf-8");
269
+ finalContent = fileMerger.merge(relativePath, existingContent, content);
270
+ if (finalContent !== content) {
271
+ log?.(`\u{1F504} Merged file: ${relativePath}`);
272
+ }
273
+ } catch {
274
+ }
275
+ await fs.writeFile(fullPath, finalContent, "utf-8");
276
+ log?.(`\u{1F4C4} Created file: ${relativePath}`);
277
+ return fullPath;
278
+ }
279
+ };
280
+
281
+ // src/utils/TemplateManager.ts
282
+ import fs3 from "fs/promises";
283
+ import path3 from "path";
284
+
285
+ // src/generators/StubGenerator.ts
286
+ import fs2 from "fs/promises";
287
+ import path2 from "path";
107
288
  import Handlebars from "handlebars";
108
289
  var StubGenerator = class {
109
290
  config;
@@ -192,16 +373,16 @@ var StubGenerator = class {
192
373
  * @returns Path to the generated file
193
374
  */
194
375
  async generate(stubName, outputPath, variables = {}) {
195
- const stubPath = path.resolve(this.config.stubsDir, stubName);
196
- const template = await fs.readFile(stubPath, "utf-8");
376
+ const stubPath = path2.resolve(this.config.stubsDir, stubName);
377
+ const template = await fs2.readFile(stubPath, "utf-8");
197
378
  const compiled = this.handlebars.compile(template);
198
379
  const content = compiled({
199
380
  ...this.config.defaultVariables,
200
381
  ...variables
201
382
  });
202
- const fullOutputPath = path.resolve(this.config.outputDir, outputPath);
203
- await fs.mkdir(path.dirname(fullOutputPath), { recursive: true });
204
- await fs.writeFile(fullOutputPath, content, "utf-8");
383
+ const fullOutputPath = path2.resolve(this.config.outputDir, outputPath);
384
+ await fs2.mkdir(path2.dirname(fullOutputPath), { recursive: true });
385
+ await fs2.writeFile(fullOutputPath, content, "utf-8");
205
386
  return fullOutputPath;
206
387
  }
207
388
  /**
@@ -253,41 +434,57 @@ var StubGenerator = class {
253
434
  }
254
435
  };
255
436
 
256
- // src/generators/BaseGenerator.ts
257
- async function walk(dir) {
258
- const files = await fs2.readdir(dir);
259
- const paths = [];
260
- for (const file of files) {
261
- const filePath = path2.join(dir, file);
262
- const stat = await fs2.stat(filePath);
263
- if (stat.isDirectory()) {
264
- paths.push(...await walk(filePath));
265
- } else {
266
- paths.push(filePath);
437
+ // src/utils/TemplateManager.ts
438
+ var TemplateManager = class {
439
+ stubGenerator;
440
+ constructor(templatesDir) {
441
+ this.stubGenerator = new StubGenerator({
442
+ stubsDir: templatesDir,
443
+ outputDir: ""
444
+ });
445
+ }
446
+ render(template, context) {
447
+ return this.stubGenerator.render(template, context);
448
+ }
449
+ async applyOverlay(sourceDir, targetDir, context, fileMerger, log) {
450
+ const createdFiles = [];
451
+ try {
452
+ await fs3.access(sourceDir);
453
+ } catch {
454
+ return [];
455
+ }
456
+ const files = await FileUtilities.walk(sourceDir);
457
+ for (const filePath of files) {
458
+ const relativePath = path3.relative(sourceDir, filePath);
459
+ let content = await fs3.readFile(filePath, "utf-8");
460
+ try {
461
+ content = this.render(content, context);
462
+ } catch {
463
+ }
464
+ const fullPath = await FileUtilities.writeFile(
465
+ targetDir,
466
+ relativePath,
467
+ content,
468
+ fileMerger,
469
+ log
470
+ );
471
+ createdFiles.push(fullPath);
267
472
  }
473
+ return createdFiles;
268
474
  }
269
- return paths;
270
- }
475
+ };
476
+
477
+ // src/generators/BaseGenerator.ts
271
478
  var BaseGenerator = class {
272
479
  config;
273
- stubGenerator;
480
+ templateManager;
274
481
  fileMerger;
275
482
  filesCreated = [];
276
483
  constructor(config) {
277
484
  this.config = config;
278
- this.stubGenerator = new StubGenerator({
279
- stubsDir: config.templatesDir,
280
- outputDir: ""
281
- // Set per-generation
282
- });
485
+ this.templateManager = new TemplateManager(config.templatesDir);
283
486
  this.fileMerger = new FileMerger();
284
487
  }
285
- /**
286
- * Generate the project scaffold.
287
- *
288
- * @param context - Generator context
289
- * @returns Array of created file paths
290
- */
291
488
  async generate(context) {
292
489
  this.filesCreated = [];
293
490
  const structure = this.getDirectoryStructure(context);
@@ -297,50 +494,73 @@ var BaseGenerator = class {
297
494
  await this.applyFeatureOverlays(context);
298
495
  return this.filesCreated;
299
496
  }
300
- /**
301
- * Create directory structure recursively.
302
- */
303
497
  async createStructure(basePath, nodes, context) {
304
498
  for (const node of nodes) {
305
- const fullPath = path2.resolve(basePath, node.name);
499
+ const fullPath = path4.resolve(basePath, node.name);
306
500
  if (node.type === "directory") {
307
- await fs2.mkdir(fullPath, { recursive: true });
501
+ await fs4.mkdir(fullPath, { recursive: true });
308
502
  this.log(`\u{1F4C1} Created directory: ${node.name}`);
309
503
  if (node.children) {
310
504
  await this.createStructure(fullPath, node.children, context);
311
505
  }
312
506
  } else {
313
- await fs2.mkdir(path2.dirname(fullPath), { recursive: true });
507
+ await fs4.mkdir(path4.dirname(fullPath), { recursive: true });
508
+ let content = "";
314
509
  if (node.template) {
315
- const templatePath = path2.resolve(this.config.templatesDir, node.template);
316
510
  try {
317
- const template = await fs2.readFile(templatePath, "utf-8");
318
- const content = this.stubGenerator.render(template, context);
319
- await fs2.writeFile(fullPath, content, "utf-8");
511
+ const templatePath = path4.resolve(this.config.templatesDir, node.template);
512
+ const template = await fs4.readFile(templatePath, "utf-8");
513
+ content = this.templateManager.render(template, context);
320
514
  } catch {
321
- await fs2.writeFile(fullPath, node.content ?? "", "utf-8");
515
+ content = node.content ?? "";
322
516
  }
323
- } else if (node.content) {
324
- await fs2.writeFile(fullPath, node.content, "utf-8");
325
517
  } else {
326
- await fs2.writeFile(fullPath, "", "utf-8");
518
+ content = node.content ?? "";
327
519
  }
328
- this.filesCreated.push(fullPath);
329
- this.log(`\u{1F4C4} Created file: ${node.name}`);
520
+ const relativePath = path4.relative(context.targetDir, fullPath);
521
+ const writtenPath = await FileUtilities.writeFile(
522
+ context.targetDir,
523
+ relativePath,
524
+ content,
525
+ this.fileMerger,
526
+ (msg) => this.log(msg)
527
+ );
528
+ this.filesCreated.push(writtenPath);
330
529
  }
331
530
  }
332
531
  }
333
- /**
334
- * Generate common files (package.json, .env, etc.)
335
- */
336
532
  async generateCommonFiles(context) {
533
+ const commonDir = path4.resolve(this.config.templatesDir, "common");
534
+ const extendedContext = {
535
+ ...context,
536
+ entrypoint: context.architecture === "ddd" ? "dist/main.js" : "dist/bootstrap.js",
537
+ dbConnection: context.profile === "core" ? "sqlite" : "postgres"
538
+ };
539
+ await this.generateFileFromTemplate(
540
+ commonDir,
541
+ "env.example.hbs",
542
+ ".env.example",
543
+ extendedContext
544
+ );
545
+ await this.generateFileFromTemplate(commonDir, "env.example.hbs", ".env", extendedContext);
546
+ await this.generateFileFromTemplate(commonDir, "gitignore.hbs", ".gitignore", extendedContext);
547
+ await this.generateFileFromTemplate(
548
+ commonDir,
549
+ "tsconfig.json.hbs",
550
+ "tsconfig.json",
551
+ extendedContext
552
+ );
553
+ await this.generateFileFromTemplate(commonDir, "Dockerfile.hbs", "Dockerfile", extendedContext);
337
554
  await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
338
- await this.writeFile(context.targetDir, ".env.example", this.generateEnvExample(context));
339
- await this.writeFile(context.targetDir, ".env", this.generateEnvExample(context));
340
- await this.writeFile(context.targetDir, ".gitignore", this.generateGitignore());
341
- await this.writeFile(context.targetDir, "tsconfig.json", this.generateTsConfig());
342
- await this.writeFile(context.targetDir, "Dockerfile", this.generateDockerfile(context));
343
- await this.writeFile(context.targetDir, ".dockerignore", this.generateDockerIgnore());
555
+ await this.writeFile(
556
+ context.targetDir,
557
+ ".dockerignore",
558
+ `node_modules
559
+ dist
560
+ .git
561
+ .env
562
+ `
563
+ );
344
564
  await this.writeFile(
345
565
  context.targetDir,
346
566
  "ARCHITECTURE.md",
@@ -349,104 +569,74 @@ var BaseGenerator = class {
349
569
  await this.generateCheckScripts(context);
350
570
  await this.generateSkills(context);
351
571
  }
352
- /**
353
- * Copy AI Skills to the project
354
- */
355
- async generateSkills(context) {
356
- const skillsDir = path2.resolve(this.config.templatesDir, "skills");
357
- const targetSkillsDir = path2.join(".skills");
572
+ async generateFileFromTemplate(tplDir, tplName, targetName, context) {
358
573
  try {
359
- await fs2.access(skillsDir);
360
- } catch {
361
- return;
362
- }
363
- const files = await walk(skillsDir);
364
- for (const filePath of files) {
365
- const relativePath = path2.relative(skillsDir, filePath);
366
- const targetPath = path2.join(targetSkillsDir, relativePath);
367
- let content = await fs2.readFile(filePath, "utf-8");
368
- try {
369
- content = this.stubGenerator.render(content, context);
370
- } catch {
371
- }
372
- await this.writeFile(context.targetDir, targetPath, content);
574
+ const template = await fs4.readFile(path4.join(tplDir, tplName), "utf-8");
575
+ const content = this.templateManager.render(template, context);
576
+ await this.writeFile(context.targetDir, targetName, content);
577
+ } catch (e) {
578
+ this.log(`\u26A0\uFE0F Failed to generate ${targetName}: ${e}`);
373
579
  }
374
580
  }
375
- /**
376
- * Apply profile-specific overlays
377
- */
581
+ async generateSkills(context) {
582
+ const skillsDir = path4.resolve(this.config.templatesDir, "skills");
583
+ const created = await this.templateManager.applyOverlay(
584
+ skillsDir,
585
+ path4.join(context.targetDir, ".skills"),
586
+ context,
587
+ this.fileMerger,
588
+ (msg) => this.log(msg)
589
+ );
590
+ this.filesCreated.push(...created);
591
+ }
378
592
  async applyOverlays(context) {
379
593
  const profile = context.profile;
380
- if (!profile) return;
381
- const overlayDir = path2.resolve(this.config.templatesDir, "overlays", profile);
382
- await this.copyOverlayDirectory(overlayDir, context);
594
+ if (profile) {
595
+ const overlayDir = path4.resolve(this.config.templatesDir, "overlays", profile);
596
+ await this.copyOverlayDirectory(overlayDir, context);
597
+ }
383
598
  }
384
- /**
385
- * Apply feature-specific overlays
386
- */
387
599
  async applyFeatureOverlays(context) {
388
600
  const features = context.features || [];
389
601
  for (const feature of features) {
390
- const overlayDir = path2.resolve(this.config.templatesDir, "features", feature);
602
+ const overlayDir = path4.resolve(this.config.templatesDir, "features", feature);
391
603
  await this.copyOverlayDirectory(overlayDir, context);
392
604
  }
393
605
  }
394
- /**
395
- * Helper to copy/merge an overlay directory into the target
396
- */
397
606
  async copyOverlayDirectory(sourceDir, context) {
398
- try {
399
- await fs2.access(sourceDir);
400
- } catch {
401
- return;
402
- }
403
- const files = await walk(sourceDir);
404
- for (const filePath of files) {
405
- const relativePath = path2.relative(sourceDir, filePath);
406
- let content = await fs2.readFile(filePath, "utf-8");
407
- try {
408
- content = this.stubGenerator.render(content, context);
409
- } catch {
410
- }
411
- await this.writeFile(context.targetDir, relativePath, content);
412
- }
607
+ const created = await this.templateManager.applyOverlay(
608
+ sourceDir,
609
+ context.targetDir,
610
+ context,
611
+ this.fileMerger,
612
+ (msg) => this.log(msg)
613
+ );
614
+ this.filesCreated.push(...created);
413
615
  }
414
- /**
415
- * Write a file and track it.
416
- */
417
616
  async writeFile(basePath, relativePath, content) {
418
- const fullPath = path2.resolve(basePath, relativePath);
419
- await fs2.mkdir(path2.dirname(fullPath), { recursive: true });
420
- let finalContent = content;
421
- try {
422
- const existingContent = await fs2.readFile(fullPath, "utf-8");
423
- finalContent = this.fileMerger.merge(relativePath, existingContent, content);
424
- if (finalContent !== content) {
425
- this.log(`\u{1F504} Merged file: ${relativePath}`);
426
- }
427
- } catch {
428
- }
429
- await fs2.writeFile(fullPath, finalContent, "utf-8");
430
- this.filesCreated.push(fullPath);
431
- this.log(`\u{1F4C4} Created file: ${relativePath}`);
617
+ const writtenPath = await FileUtilities.writeFile(
618
+ basePath,
619
+ relativePath,
620
+ content,
621
+ this.fileMerger,
622
+ (msg) => this.log(msg)
623
+ );
624
+ this.filesCreated.push(writtenPath);
432
625
  }
433
- /**
434
- * Generate package.json content.
435
- */
436
626
  generatePackageJson(context) {
437
627
  const profile = context.profile || "core";
438
- const baseDependencies = {
628
+ const deps = {
439
629
  "@gravito/core": "^1.0.0-beta.5",
440
630
  "@gravito/atlas": "^1.0.0-beta.5",
441
631
  "@gravito/plasma": "^1.0.0-beta.5",
442
632
  "@gravito/stream": "^1.0.0-beta.5"
443
633
  };
444
634
  if (profile === "enterprise" || profile === "scale") {
445
- baseDependencies["@gravito/quasar"] = "^1.0.0-beta.5";
446
- baseDependencies["@gravito/horizon"] = "^1.0.0-beta.5";
635
+ deps["@gravito/quasar"] = "^1.0.0-beta.5";
636
+ deps["@gravito/horizon"] = "^1.0.0-beta.5";
447
637
  }
448
638
  if (context.withSpectrum) {
449
- baseDependencies["@gravito/spectrum"] = "^1.0.0-beta.1";
639
+ deps["@gravito/spectrum"] = "^1.0.0-beta.5";
450
640
  }
451
641
  const pkg = {
452
642
  name: context.nameKebabCase,
@@ -457,717 +647,426 @@ var BaseGenerator = class {
457
647
  build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
458
648
  start: "bun run dist/bootstrap.js",
459
649
  test: "bun test",
460
- typecheck: "tsc --noEmit",
461
- check: "bun run typecheck && bun run test",
462
- "check:deps": "bun run scripts/check-dependencies.ts",
463
- validate: "bun run check && bun run check:deps",
464
- precommit: "bun run validate",
465
- "docker:build": `docker build -t ${context.nameKebabCase} .`,
466
- "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
650
+ typecheck: "bun tsc --noEmit",
651
+ validate: "bun run typecheck && bun run test"
467
652
  },
468
- dependencies: baseDependencies,
469
- devDependencies: {
470
- "bun-types": "latest",
471
- typescript: "^5.0.0"
472
- }
653
+ dependencies: deps,
654
+ devDependencies: { "bun-types": "latest", typescript: "^5.9.3" }
473
655
  };
474
656
  return JSON.stringify(pkg, null, 2);
475
657
  }
658
+ async generateCheckScripts(context) {
659
+ const scriptsDir = path4.resolve(context.targetDir, "scripts");
660
+ await fs4.mkdir(scriptsDir, { recursive: true });
661
+ const templatesDir = path4.resolve(this.config.templatesDir, "scripts");
662
+ await this.generateFileFromTemplate(
663
+ templatesDir,
664
+ "check-dependencies.ts.hbs",
665
+ "scripts/check-dependencies.ts",
666
+ context
667
+ );
668
+ await this.generateFileFromTemplate(templatesDir, "check.sh.hbs", "scripts/check.sh", context);
669
+ await this.generateFileFromTemplate(
670
+ templatesDir,
671
+ "pre-commit.sh.hbs",
672
+ "scripts/pre-commit.sh",
673
+ context
674
+ );
675
+ await this.writeFile(
676
+ context.targetDir,
677
+ "CHECK_SYSTEM.md",
678
+ "# Project Check System\n\nRun `bun run validate` to check everything.\n"
679
+ );
680
+ }
681
+ log(message) {
682
+ if (this.config.verbose) {
683
+ console.log(message);
684
+ }
685
+ }
686
+ static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
687
+ const toPascalCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_ ]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
688
+ const toCamelCase = (str) => {
689
+ const pascal = toPascalCase(str);
690
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
691
+ };
692
+ const toSnakeCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1_$2").split(/[-_ ]+/).map((word) => word.toLowerCase()).join("_");
693
+ const toKebabCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").split(/[-_ ]+/).map((word) => word.toLowerCase()).join("-");
694
+ return {
695
+ name,
696
+ namePascalCase: toPascalCase(name),
697
+ nameCamelCase: toCamelCase(name),
698
+ nameSnakeCase: toSnakeCase(name),
699
+ nameKebabCase: toKebabCase(name),
700
+ targetDir,
701
+ architecture,
702
+ packageManager,
703
+ year: (/* @__PURE__ */ new Date()).getFullYear().toString(),
704
+ date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
705
+ ...extra
706
+ };
707
+ }
708
+ };
709
+
710
+ // src/utils/ConfigGenerator.ts
711
+ var ConfigGenerator = class {
476
712
  /**
477
- * Generate Dockerfile content.
713
+ * Generate app configuration (simple version for Clean Architecture)
478
714
  */
479
- generateDockerfile(context) {
480
- const entrypoint = context.architecture === "ddd" ? "dist/main.js" : "dist/bootstrap.js";
481
- return `FROM oven/bun:1.0 AS base
482
- WORKDIR /usr/src/app
483
-
484
- # Install dependencies
485
- FROM base AS install
486
- RUN mkdir -p /temp/dev
487
- COPY package.json bun.lockb /temp/dev/
488
- RUN cd /temp/dev && bun install --frozen-lockfile
489
-
490
- # Build application
491
- FROM base AS build
492
- COPY --from=install /temp/dev/node_modules node_modules
493
- COPY . .
494
- ENV NODE_ENV=production
495
- RUN bun run build
496
-
497
- # Final production image
498
- FROM base AS release
499
- COPY --from=build /usr/src/app/${entrypoint} index.js
500
- COPY --from=build /usr/src/app/package.json .
501
-
502
- # Create a non-root user for security
503
- USER bun
504
- EXPOSE 3000/tcp
505
- ENTRYPOINT [ "bun", "run", "index.js" ]
715
+ static generateSimpleAppConfig(context) {
716
+ return `export default {
717
+ name: process.env.APP_NAME ?? '${context.name}',
718
+ env: process.env.APP_ENV ?? 'development',
719
+ debug: process.env.APP_DEBUG === 'true',
720
+ url: process.env.APP_URL ?? 'http://localhost:3000',
721
+ key: process.env.APP_KEY,
722
+ }
506
723
  `;
507
724
  }
508
725
  /**
509
- * Generate .dockerignore content.
726
+ * Generate app configuration (detailed version for Enterprise MVC)
510
727
  */
511
- generateDockerIgnore() {
512
- return `node_modules
513
- dist
514
- .git
515
- .env
516
- *.log
517
- .vscode
518
- .idea
519
- tests
520
- `;
521
- }
728
+ static generateDetailedAppConfig(context) {
729
+ return `/**
730
+ * Application Configuration
731
+ */
732
+ export default {
522
733
  /**
523
- * Generate .env.example content.
734
+ * Application name
524
735
  */
525
- generateEnvExample(context) {
526
- const profile = context.profile || "core";
527
- let envContent = `# ============================================================================
528
- # Application Configuration
529
- # ============================================================================
530
-
531
- APP_NAME=${context.name}
532
- APP_ENV=development
533
- APP_DEBUG=true
534
- APP_URL=http://localhost:3000
535
- APP_KEY=
536
-
537
- # ============================================================================
538
- # Database Configuration
539
- # ============================================================================
540
-
541
- # Database Connection (sqlite, postgres, mysql)
542
- DB_CONNECTION=${profile === "core" ? "sqlite" : "postgres"}
543
-
544
- # SQLite Configuration (when DB_CONNECTION=sqlite)
545
- DB_DATABASE=database/database.sqlite
546
-
547
- # PostgreSQL Configuration (when DB_CONNECTION=postgres)
548
- ${profile !== "core" ? `DB_HOST=127.0.0.1
549
- DB_PORT=5432
550
- DB_DATABASE=${context.name}
551
- DB_USERNAME=postgres
552
- DB_PASSWORD=
553
- DB_SSLMODE=prefer` : `# DB_HOST=127.0.0.1
554
- # DB_PORT=5432
555
- # DB_DATABASE=${context.name}
556
- # DB_USERNAME=postgres
557
- # DB_PASSWORD=
558
- # DB_SSLMODE=prefer`}
559
-
560
- # MySQL Configuration (when DB_CONNECTION=mysql)
561
- # DB_HOST=127.0.0.1
562
- # DB_PORT=3306
563
- # DB_DATABASE=${context.name}
564
- # DB_USERNAME=root
565
- # DB_PASSWORD=
566
-
567
- # ============================================================================
568
- # Redis Configuration (@gravito/plasma)
569
- # ============================================================================
570
-
571
- # Default Redis Connection
572
- REDIS_CONNECTION=default
573
- REDIS_HOST=127.0.0.1
574
- REDIS_PORT=6379
575
- REDIS_PASSWORD=
576
- REDIS_DB=0
577
-
578
- # Redis Connection Options
579
- REDIS_CONNECT_TIMEOUT=10000
580
- REDIS_COMMAND_TIMEOUT=5000
581
- REDIS_KEY_PREFIX=
582
- REDIS_MAX_RETRIES=3
583
- REDIS_RETRY_DELAY=1000
584
-
585
- # Cache-specific Redis Connection (optional, falls back to default)
586
- # REDIS_CACHE_HOST=127.0.0.1
587
- # REDIS_CACHE_PORT=6379
588
- # REDIS_CACHE_PASSWORD=
589
- REDIS_CACHE_DB=1
590
-
591
- # Queue-specific Redis Connection (optional, falls back to default)
592
- # REDIS_QUEUE_HOST=127.0.0.1
593
- # REDIS_QUEUE_PORT=6379
594
- # REDIS_QUEUE_PASSWORD=
595
- REDIS_QUEUE_DB=2
596
-
597
- # ============================================================================
598
- # Cache Configuration (@gravito/stasis)
599
- # ============================================================================
600
-
601
- # Cache Driver (memory, file, redis)
602
- CACHE_DRIVER=${profile === "core" ? "memory" : "redis"}
603
-
604
- # File Cache Path (when CACHE_DRIVER=file)
605
- CACHE_PATH=storage/framework/cache
606
-
607
- # Redis Cache Configuration (when CACHE_DRIVER=redis)
608
- REDIS_CACHE_CONNECTION=cache
609
- REDIS_CACHE_PREFIX=cache:
610
-
611
- # ============================================================================
612
- # Queue Configuration (@gravito/stream)
613
- # ============================================================================
614
-
615
- # Queue Connection (sync, memory, database, redis, kafka, sqs, rabbitmq)
616
- QUEUE_CONNECTION=${profile === "core" ? "sync" : "redis"}
617
-
618
- # Database Queue Configuration (when QUEUE_CONNECTION=database)
619
- QUEUE_TABLE=jobs
620
-
621
- # Redis Queue Configuration (when QUEUE_CONNECTION=redis)
622
- REDIS_PREFIX=queue:
623
-
624
- `;
625
- if (profile === "enterprise" || profile === "scale") {
626
- envContent += `# Kafka Queue Configuration (when QUEUE_CONNECTION=kafka)
627
- # KAFKA_BROKERS=localhost:9092
628
- # KAFKA_CONSUMER_GROUP_ID=gravito-workers
629
- # KAFKA_CLIENT_ID=${context.name}
630
-
631
- # AWS SQS Queue Configuration (when QUEUE_CONNECTION=sqs)
632
- # AWS_REGION=us-east-1
633
- # SQS_QUEUE_URL_PREFIX=
634
- # SQS_VISIBILITY_TIMEOUT=30
635
- # SQS_WAIT_TIME_SECONDS=20
636
-
637
- # RabbitMQ Queue Configuration (when QUEUE_CONNECTION=rabbitmq)
638
- # RABBITMQ_URL=amqp://localhost
639
- # RABBITMQ_EXCHANGE=gravito.events
640
- # RABBITMQ_EXCHANGE_TYPE=fanout
641
-
642
- `;
643
- }
644
- envContent += `# ============================================================================
645
- # Logging Configuration
646
- # ============================================================================
736
+ name: process.env.APP_NAME ?? '${context.name}',
647
737
 
648
- LOG_LEVEL=debug
649
- `;
650
- return envContent;
651
- }
652
738
  /**
653
- * Generate .gitignore content.
739
+ * Application environment
654
740
  */
655
- generateGitignore() {
656
- return `# Dependencies
657
- node_modules/
658
-
659
- # Build output
660
- dist/
661
-
662
- # Environment
663
- .env
664
- .env.local
665
- .env.*.local
666
-
667
- # IDE
668
- .idea/
669
- .vscode/
670
- *.swp
671
- *.swo
672
-
673
- # System
674
- .DS_Store
675
- Thumbs.db
741
+ env: process.env.APP_ENV ?? 'development',
676
742
 
677
- # Logs
678
- *.log
679
- logs/
743
+ /**
744
+ * Application port
745
+ */
746
+ port: Number.parseInt(process.env.PORT ?? '3000', 10),
680
747
 
681
- # Database
682
- *.sqlite
683
- *.sqlite-journal
748
+ /**
749
+ * View directory
750
+ */
751
+ VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
684
752
 
685
- # Coverage
686
- coverage/
687
- `;
688
- }
689
753
  /**
690
- * Generate tsconfig.json content.
754
+ * Debug mode
691
755
  */
692
- generateTsConfig() {
693
- const config = {
694
- compilerOptions: {
695
- target: "ESNext",
696
- module: "ESNext",
697
- moduleResolution: "bundler",
698
- esModuleInterop: true,
699
- strict: true,
700
- skipLibCheck: true,
701
- declaration: true,
702
- experimentalDecorators: true,
703
- emitDecoratorMetadata: true,
704
- types: ["bun-types"],
705
- outDir: "./dist",
706
- rootDir: "./src",
707
- baseUrl: ".",
708
- paths: {
709
- "@/*": ["./src/*"]
710
- }
711
- },
712
- include: ["src/**/*"],
713
- exclude: ["node_modules", "dist"]
714
- };
715
- return JSON.stringify(config, null, 2);
716
- }
756
+ debug: process.env.APP_DEBUG === 'true',
757
+
717
758
  /**
718
- * Generate check scripts for project validation.
759
+ * Application URL
719
760
  */
720
- async generateCheckScripts(context) {
721
- const scriptsDir = path2.resolve(context.targetDir, "scripts");
722
- await fs2.mkdir(scriptsDir, { recursive: true });
723
- await this.writeFile(
724
- scriptsDir,
725
- "check-dependencies.ts",
726
- this.generateCheckDependenciesScript()
727
- );
728
- await this.writeFile(scriptsDir, "check.sh", this.generateCheckShellScript());
729
- await this.writeFile(scriptsDir, "pre-commit.sh", this.generatePreCommitScript());
730
- await this.writeFile(context.targetDir, "CHECK_SYSTEM.md", this.generateCheckSystemDoc(context));
731
- }
761
+ url: process.env.APP_URL ?? 'http://localhost:3000',
762
+
732
763
  /**
733
- * Generate check-dependencies.ts script content.
764
+ * Timezone
734
765
  */
735
- generateCheckDependenciesScript() {
736
- return `/**
737
- * \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5\u8173\u672C
738
- *
739
- * \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
740
- * \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
741
- */
766
+ timezone: 'UTC',
742
767
 
743
- import { readFileSync } from 'fs'
744
- import { join } from 'path'
768
+ /**
769
+ * Locale
770
+ */
771
+ locale: 'en',
745
772
 
746
- interface PackageJson {
747
- dependencies?: Record<string, string>
748
- devDependencies?: Record<string, string>
749
- }
750
-
751
- interface PackageInfo {
752
- name: string
753
- current: string
754
- latest: string
755
- outdated: boolean
756
- }
757
-
758
- const colors = {
759
- reset: '\\x1b[0m',
760
- green: '\\x1b[32m',
761
- yellow: '\\x1b[33m',
762
- red: '\\x1b[31m',
763
- blue: '\\x1b[36m',
764
- }
773
+ /**
774
+ * Fallback locale
775
+ */
776
+ fallbackLocale: 'en',
765
777
 
766
- function log(message: string, color: keyof typeof colors = 'reset') {
767
- console.log(\`\${colors[color]}\${message}\${colors.reset}\`)
768
- }
778
+ /**
779
+ * Encryption key
780
+ */
781
+ key: process.env.APP_KEY,
769
782
 
770
- async function getLatestVersion(packageName: string): Promise<string | null> {
771
- try {
772
- const response = await fetch(\`https://registry.npmjs.org/\${packageName}/latest\`)
773
- if (!response.ok) return null
774
- const data = await response.json()
775
- return data.version
776
- } catch {
777
- return null
778
- }
779
- }
783
+ /**
784
+ * Service providers to register
785
+ */
786
+ providers: [
787
+ // Framework providers
788
+ // 'RouteServiceProvider',
780
789
 
781
- function parseVersion(version: string): string {
782
- // \u79FB\u9664 ^, ~, >= \u7B49\u524D\u7DB4
783
- return version.replace(/^[\\^~>=<]/, '')
790
+ // Application providers
791
+ // 'AppServiceProvider',
792
+ ],
784
793
  }
785
-
786
- async function checkPackage(
787
- name: string,
788
- currentVersion: string
789
- ): Promise<PackageInfo | null> {
790
- // \u8DF3\u904E\u672C\u5730\u9023\u7D50\u7684\u5957\u4EF6
791
- if (currentVersion.startsWith('link:') || currentVersion.startsWith('workspace:')) {
792
- return null
793
- }
794
-
795
- const current = parseVersion(currentVersion)
796
- const latest = await getLatestVersion(name)
797
-
798
- if (!latest) {
799
- return null
800
- }
801
-
802
- return {
803
- name,
804
- current,
805
- latest,
806
- outdated: current !== latest,
794
+ `;
807
795
  }
796
+ /**
797
+ * Generate database configuration (simple version)
798
+ */
799
+ static generateSimpleDatabaseConfig() {
800
+ return `export default {
801
+ default: process.env.DB_CONNECTION ?? 'sqlite',
802
+ connections: {
803
+ sqlite: {
804
+ driver: 'sqlite',
805
+ database: process.env.DB_DATABASE ?? 'database/database.sqlite',
806
+ },
807
+ },
808
808
  }
809
-
810
- async function main() {
811
- log('\\n=== \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5 ===\\n', 'blue')
812
-
813
- const packageJsonPath = join(process.cwd(), 'package.json')
814
- const packageJson: PackageJson = JSON.parse(
815
- readFileSync(packageJsonPath, 'utf-8')
816
- )
817
-
818
- const allDependencies = {
819
- ...packageJson.dependencies,
820
- ...packageJson.devDependencies,
821
- }
822
-
823
- log(\`\u6AA2\u67E5 \${Object.keys(allDependencies).length} \u500B\u5957\u4EF6...\\n\`, 'yellow')
824
-
825
- const results: PackageInfo[] = []
826
- const outdated: PackageInfo[] = []
827
- const upToDate: PackageInfo[] = []
828
-
829
- // \u6AA2\u67E5\u6240\u6709\u5957\u4EF6
830
- for (const [name, version] of Object.entries(allDependencies)) {
831
- const info = await checkPackage(name, version)
832
- if (info) {
833
- results.push(info)
834
- if (info.outdated) {
835
- outdated.push(info)
836
- } else {
837
- upToDate.push(info)
838
- }
839
- }
809
+ `;
840
810
  }
811
+ /**
812
+ * Generate database configuration (detailed version)
813
+ */
814
+ static generateDetailedDatabaseConfig() {
815
+ return `/**
816
+ * Database Configuration
817
+ */
818
+ export default {
819
+ /**
820
+ * Default connection
821
+ */
822
+ default: process.env.DB_CONNECTION ?? 'sqlite',
841
823
 
842
- // \u986F\u793A\u7D50\u679C
843
- if (upToDate.length > 0) {
844
- log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
845
- upToDate.forEach((pkg) => {
846
- log(\` \${pkg.name}: \${pkg.current}\`, 'green')
847
- })
848
- }
824
+ /**
825
+ * Database connections
826
+ */
827
+ connections: {
828
+ sqlite: {
829
+ driver: 'sqlite',
830
+ database: process.env.DB_DATABASE ?? 'database/database.sqlite',
831
+ },
849
832
 
850
- if (outdated.length > 0) {
851
- log(\`\\n\u26A0 \u9700\u8981\u66F4\u65B0 (\${outdated.length}):\`, 'yellow')
852
- outdated.forEach((pkg) => {
853
- log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
854
- })
855
- }
833
+ mysql: {
834
+ driver: 'mysql',
835
+ host: process.env.DB_HOST ?? 'localhost',
836
+ port: Number(process.env.DB_PORT ?? 3306),
837
+ database: process.env.DB_DATABASE ?? 'forge',
838
+ username: process.env.DB_USERNAME ?? 'forge',
839
+ password: process.env.DB_PASSWORD ?? '',
840
+ },
856
841
 
857
- // \u7E3D\u7D50
858
- log('\\n=== \u6AA2\u67E5\u7D50\u679C ===', 'blue')
859
- log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
860
- log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
861
- log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
842
+ postgres: {
843
+ driver: 'postgres',
844
+ host: process.env.DB_HOST ?? 'localhost',
845
+ port: Number(process.env.DB_PORT ?? 5432),
846
+ database: process.env.DB_DATABASE ?? 'forge',
847
+ username: process.env.DB_USERNAME ?? 'forge',
848
+ password: process.env.DB_PASSWORD ?? '',
849
+ },
850
+ },
862
851
 
863
- // \u5982\u679C\u6709\u9700\u8981\u66F4\u65B0\u7684\u5957\u4EF6\uFF0C\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u78BC
864
- if (outdated.length > 0) {
865
- log('\\n\u5EFA\u8B70\u57F7\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0\u5957\u4EF6\uFF1A', 'yellow')
866
- log(' bun update', 'yellow')
867
- process.exit(1)
868
- } else {
869
- log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
870
- process.exit(0)
852
+ /**
853
+ * Migration settings
854
+ */
855
+ migrations: {
856
+ table: 'migrations',
857
+ path: 'database/migrations',
858
+ },
859
+ }
860
+ `;
871
861
  }
862
+ /**
863
+ * Generate auth configuration
864
+ */
865
+ static generateAuthConfig() {
866
+ return `export default {
867
+ defaults: { guard: 'web' },
868
+ guards: {
869
+ web: { driver: 'session', provider: 'users' },
870
+ api: { driver: 'token', provider: 'users' },
871
+ },
872
872
  }
873
-
874
- main().catch((error) => {
875
- log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
876
- process.exit(1)
877
- })
878
873
  `;
879
874
  }
880
875
  /**
881
- * Generate check.sh script content.
876
+ * Generate cache configuration
882
877
  */
883
- generateCheckShellScript() {
884
- return `#!/bin/bash
885
-
886
- # \u5C08\u6848\u6AA2\u67E5\u8173\u672C
887
- # \u57F7\u884C\u6240\u6709\u5FC5\u8981\u7684\u6AA2\u67E5\uFF1A\u985E\u578B\u6AA2\u67E5\u3001\u6E2C\u8A66\u3001\u4F9D\u8CF4\u6AA2\u67E5\u7B49
888
-
889
- set -e
890
-
891
- # \u984F\u8272\u5B9A\u7FA9
892
- GREEN='\\033[0;32m'
893
- YELLOW='\\033[1;33m'
894
- RED='\\033[0;31m'
895
- BLUE='\\033[0;34m'
896
- NC='\\033[0m' # No Color
897
-
898
- echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
899
-
900
- # \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
901
- if [ ! -f "package.json" ]; then
902
- echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
903
- exit 1
904
- fi
905
-
906
- # \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
907
- if ! command -v bun &> /dev/null; then
908
- echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
909
- exit 1
910
- fi
911
-
912
- # 1. \u985E\u578B\u6AA2\u67E5
913
- echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
914
- if bun run typecheck; then
915
- echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
916
- else
917
- echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
918
- exit 1
919
- fi
920
-
921
- # 2. \u57F7\u884C\u6E2C\u8A66
922
- echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
923
- if bun test; then
924
- echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
925
- else
926
- echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
927
- exit 1
928
- fi
929
-
930
- # 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
931
- echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
932
- if bun run check:deps; then
933
- echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
934
- else
935
- 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"
936
- fi
937
-
938
- echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
878
+ static generateCacheConfig() {
879
+ return `export default {
880
+ default: process.env.CACHE_DRIVER ?? 'memory',
881
+ stores: {
882
+ memory: { driver: 'memory' },
883
+ },
884
+ }
939
885
  `;
940
886
  }
941
887
  /**
942
- * Generate pre-commit.sh script content.
888
+ * Generate logging configuration
943
889
  */
944
- generatePreCommitScript() {
945
- return `#!/bin/bash
946
-
947
- # Pre-commit Hook
948
- # \u5728 git commit \u524D\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5
949
- #
950
- # \u5B89\u88DD\u65B9\u5F0F\uFF1A
951
- # ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
952
- # \u6216
953
- # cp scripts/pre-commit.sh .git/hooks/pre-commit
954
- # chmod +x .git/hooks/pre-commit
955
-
956
- set -e
957
-
958
- # \u984F\u8272\u5B9A\u7FA9
959
- GREEN='\\033[0;32m'
960
- YELLOW='\\033[1;33m'
961
- RED='\\033[0;31m'
962
- BLUE='\\033[0;34m'
963
- NC='\\033[0m' # No Color
964
-
965
- echo -e "\${BLUE}=== Pre-commit \u6AA2\u67E5 ===\${NC}\\n"
966
-
967
- # \u5207\u63DB\u5230\u5C08\u6848\u6839\u76EE\u9304
968
- cd "$(git rev-parse --show-toplevel)"
969
-
970
- # \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
971
- if [ ! -f "package.json" ]; then
972
- echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
973
- exit 1
974
- fi
975
-
976
- # \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
977
- if ! command -v bun &> /dev/null; then
978
- echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
979
- exit 1
980
- fi
981
-
982
- # 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
983
- echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
984
- if bun run typecheck; then
985
- echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
986
- else
987
- echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
988
- echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
989
- exit 1
990
- fi
991
-
992
- # 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
993
- echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
994
- if bun test; then
995
- echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
996
- else
997
- echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
998
- echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
999
- exit 1
1000
- fi
1001
-
1002
- echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
890
+ static generateLoggingConfig() {
891
+ return `export default {
892
+ default: process.env.LOG_CHANNEL ?? 'console',
893
+ channels: {
894
+ console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
895
+ },
896
+ }
1003
897
  `;
1004
898
  }
1005
899
  /**
1006
- * Generate CHECK_SYSTEM.md documentation.
900
+ * Generate view configuration
1007
901
  */
1008
- generateCheckSystemDoc(context) {
1009
- return `# \u5C08\u6848\u6AA2\u67E5\u7CFB\u7D71
1010
-
1011
- \u672C\u5C08\u6848\u5DF2\u5EFA\u7ACB\u5B8C\u6574\u7684\u672C\u5730\u6AA2\u67E5\u6A5F\u5236\uFF0C\u7121\u9700\u4F9D\u8CF4 GitHub CI\u3002
1012
-
1013
- ## \u5FEB\u901F\u958B\u59CB
1014
-
1015
- ### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
1016
- \`\`\`bash
1017
- bun run validate
1018
- \`\`\`
1019
-
1020
- ### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
1021
- \`\`\`bash
1022
- # \u985E\u578B\u6AA2\u67E5
1023
- bun run typecheck
1024
-
1025
- # \u6E2C\u8A66
1026
- bun run test
1027
-
1028
- # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
1029
- bun run check:deps
1030
- \`\`\`
1031
-
1032
- ## \u53EF\u7528\u547D\u4EE4
1033
-
1034
- ### Package.json \u8173\u672C
1035
-
1036
- | \u547D\u4EE4 | \u8AAA\u660E |
1037
- |------|------|
1038
- | \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
1039
- | \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
1040
- | \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
1041
- | \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
1042
- | \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
1043
- | \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
1044
-
1045
- ### Shell \u8173\u672C
1046
-
1047
- | \u8173\u672C | \u8AAA\u660E |
1048
- |------|------|
1049
- | \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
1050
- | \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
1051
-
1052
- ## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
1053
-
1054
- \u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
1055
-
1056
- \`\`\`bash
1057
- # \u5B89\u88DD pre-commit hook
1058
- ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
1059
-
1060
- # \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
1061
- cp scripts/pre-commit.sh .git/hooks/pre-commit
1062
- chmod +x .git/hooks/pre-commit
1063
- \`\`\`
1064
-
1065
- **\u529F\u80FD\uFF1A**
1066
- - \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
1067
- - \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
1068
- - \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
902
+ static generateViewConfig() {
903
+ return `/**
904
+ * View Configuration
905
+ */
906
+ export default {
907
+ /**
908
+ * View engine
909
+ */
910
+ engine: 'html',
1069
911
 
1070
- **\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
1071
- \`\`\`bash
1072
- git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
1073
- \`\`\`
912
+ /**
913
+ * View directory
914
+ */
915
+ path: 'src/views',
1074
916
 
1075
- ## \u6AA2\u67E5\u9805\u76EE
917
+ /**
918
+ * Cache views in production
919
+ */
920
+ cache: process.env.NODE_ENV === 'production',
921
+ }
922
+ `;
923
+ }
924
+ };
1076
925
 
1077
- ### 1. \u985E\u578B\u6AA2\u67E5
1078
- - \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
1079
- - \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
926
+ // src/utils/ServiceProviderGenerator.ts
927
+ var ServiceProviderGenerator = class {
928
+ /**
929
+ * Generate App Service Provider
930
+ */
931
+ static generateAppServiceProvider(context, architectureName) {
932
+ return `/**
933
+ * App Service Provider
934
+ */
1080
935
 
1081
- ### 2. \u6E2C\u8A66
1082
- - \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
1083
- - \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
936
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1084
937
 
1085
- ### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
1086
- - \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
1087
- - \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
1088
- - \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
938
+ export class AppServiceProvider extends ServiceProvider {
939
+ register(_container: Container): void {
940
+ // Register application services
941
+ }
1089
942
 
1090
- ## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
943
+ boot(_core: PlanetCore): void {
944
+ console.log('${context.name} (${architectureName}) booted!')
945
+ }
946
+ }
947
+ `;
948
+ }
949
+ /**
950
+ * Generate Middleware Provider
951
+ */
952
+ static generateMiddlewareProvider() {
953
+ return `/**
954
+ * Middleware Service Provider
955
+ */
1091
956
 
1092
- ### \u958B\u767C\u6642
1093
- 1. \u958B\u767C\u529F\u80FD
1094
- 2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
1095
- 3. \u4FEE\u6B63\u554F\u984C
1096
- 4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
957
+ import {
958
+ ServiceProvider,
959
+ type Container,
960
+ type PlanetCore,
961
+ bodySizeLimit,
962
+ securityHeaders,
963
+ } from '@gravito/core'
1097
964
 
1098
- ### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
1099
- 1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
1100
- 2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
1101
- 3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
1102
- 4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
965
+ export class MiddlewareProvider extends ServiceProvider {
966
+ register(_container: Container): void {}
1103
967
 
1104
- ## \u6A94\u6848\u7D50\u69CB
968
+ boot(core: PlanetCore): void {
969
+ const isDev = process.env.NODE_ENV !== 'production'
1105
970
 
1106
- \`\`\`
1107
- ${context.nameKebabCase}/
1108
- \u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
1109
- \u251C\u2500\u2500 scripts/
1110
- \u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
1111
- \u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
1112
- \u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
1113
- \u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
1114
- \`\`\`
971
+ core.adapter.use('*', securityHeaders({
972
+ contentSecurityPolicy: isDev ? false : undefined,
973
+ }))
1115
974
 
1116
- ## \u6CE8\u610F\u4E8B\u9805
975
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
1117
976
 
1118
- 1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
1119
- 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
1120
- 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
977
+ core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
978
+ }
979
+ }
980
+ `;
981
+ }
982
+ /**
983
+ * Generate Route Provider
984
+ */
985
+ static generateRouteProvider(routePath = "../../routes/api", importType = "default") {
986
+ const importStatement = importType === "default" ? `import routes from '${routePath}'` : `import { registerApiRoutes } from '${routePath}'`;
987
+ const routeCall = importType === "default" ? "routes(core.router)" : "registerApiRoutes(core.router)";
988
+ return `/**
989
+ * Route Service Provider
990
+ */
1121
991
 
1122
- ## \u6545\u969C\u6392\u9664
992
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
993
+ ${importStatement}
1123
994
 
1124
- ### \u6AA2\u67E5\u5931\u6557
1125
- 1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
1126
- 2. \u4FEE\u6B63\u554F\u984C
1127
- 3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
995
+ export class RouteProvider extends ServiceProvider {
996
+ register(_container: Container): void {}
1128
997
 
1129
- ### \u8DF3\u904E\u6AA2\u67E5
1130
- \u53EA\u6709\u5728\u7DCA\u6025\u60C5\u6CC1\u4E0B\u624D\u4F7F\u7528\uFF1A
1131
- \`\`\`bash
1132
- git commit --no-verify
1133
- \`\`\`
998
+ boot(core: PlanetCore): void {
999
+ ${routeCall}
1000
+ core.logger.info('\u{1F6E4}\uFE0F Routes registered')
1001
+ }
1002
+ }
1003
+ `;
1004
+ }
1005
+ /**
1006
+ * Generate Providers Index
1007
+ */
1008
+ static generateProvidersIndex(providers = ["AppServiceProvider", "MiddlewareProvider", "RouteProvider"]) {
1009
+ const exports = providers.map((p) => `export { ${p} } from './${p}'`).join("\n");
1010
+ return `/**
1011
+ * Application Service Providers
1012
+ */
1134
1013
 
1135
- ### \u79FB\u9664 Pre-commit Hook
1136
- \`\`\`bash
1137
- rm .git/hooks/pre-commit
1138
- \`\`\`
1014
+ ${exports}
1139
1015
  `;
1140
1016
  }
1141
1017
  /**
1142
- * Log a message if verbose mode is enabled.
1018
+ * Generate Repository Service Provider
1143
1019
  */
1144
- log(message) {
1145
- if (this.config.verbose) {
1146
- console.log(message);
1147
- }
1020
+ static generateRepositoryServiceProvider(repositories = [], additionalServices = []) {
1021
+ const repositoryRegistrations = repositories.map((repo) => ` container.singleton('${repo}', () => new ${repo}())`).join("\n");
1022
+ const serviceRegistrations = additionalServices.map((service) => ` container.singleton('${service}', () => new ${service}())`).join("\n");
1023
+ const imports = [
1024
+ ...repositories.map(
1025
+ (repo) => `import { ${repo} } from '../Persistence/Repositories/${repo}'`
1026
+ ),
1027
+ ...additionalServices.map(
1028
+ (service) => `import { ${service} } from '../ExternalServices/${service}'`
1029
+ )
1030
+ ].join("\n");
1031
+ return `/**
1032
+ * Repository Service Provider
1033
+ *
1034
+ * Binds repository interfaces to implementations.
1035
+ */
1036
+
1037
+ import { ServiceProvider, type Container } from '@gravito/core'
1038
+ ${imports}
1039
+
1040
+ export class RepositoryServiceProvider extends ServiceProvider {
1041
+ register(container: Container): void {
1042
+ ${repositoryRegistrations || " // Bind repositories here"}
1043
+ ${serviceRegistrations || " // Bind external services here"}
1044
+ }
1045
+ }
1046
+ `;
1148
1047
  }
1149
1048
  /**
1150
- * Create generator context from options.
1049
+ * Generate Database Provider
1151
1050
  */
1152
- static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
1153
- const now = /* @__PURE__ */ new Date();
1154
- const pascalCase = name.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toUpperCase());
1155
- const camelCase = pascalCase.replace(/^./, (c) => c.toLowerCase());
1156
- const snakeCase = name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[-\s]+/g, "_");
1157
- const kebabCase = name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "").replace(/[_\s]+/g, "-");
1158
- return {
1159
- name,
1160
- namePascalCase: pascalCase,
1161
- nameCamelCase: camelCase,
1162
- nameSnakeCase: snakeCase,
1163
- nameKebabCase: kebabCase,
1164
- targetDir,
1165
- architecture,
1166
- packageManager,
1167
- year: now.getFullYear().toString(),
1168
- date: now.toISOString().split("T")[0] ?? now.toISOString().slice(0, 10),
1169
- ...extra
1170
- };
1051
+ static generateDatabaseProvider() {
1052
+ return `/**
1053
+ * Database Service Provider
1054
+ */
1055
+
1056
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1057
+ import { OrbitAtlas } from '@gravito/atlas'
1058
+
1059
+ export class DatabaseProvider extends ServiceProvider {
1060
+ register(_container: Container): void {
1061
+ // Register database connections
1062
+ }
1063
+
1064
+ boot(core: PlanetCore): void {
1065
+ // Initialize database
1066
+ core.logger.info('\u{1F5C4}\uFE0F Database initialized')
1067
+ }
1068
+ }
1069
+ `;
1171
1070
  }
1172
1071
  };
1173
1072
 
@@ -1421,57 +1320,22 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
1421
1320
  ];
1422
1321
  }
1423
1322
  // ─────────────────────────────────────────────────────────────
1424
- // Config Generators (similar to MVC but simplified)
1323
+ // Config Generators (using shared ConfigGenerator)
1425
1324
  // ─────────────────────────────────────────────────────────────
1426
1325
  generateAppConfig(context) {
1427
- return `export default {
1428
- name: process.env.APP_NAME ?? '${context.name}',
1429
- env: process.env.APP_ENV ?? 'development',
1430
- debug: process.env.APP_DEBUG === 'true',
1431
- url: process.env.APP_URL ?? 'http://localhost:3000',
1432
- key: process.env.APP_KEY,
1433
- }
1434
- `;
1326
+ return ConfigGenerator.generateSimpleAppConfig(context);
1435
1327
  }
1436
1328
  generateDatabaseConfig() {
1437
- return `export default {
1438
- default: process.env.DB_CONNECTION ?? 'sqlite',
1439
- connections: {
1440
- sqlite: {
1441
- driver: 'sqlite',
1442
- database: process.env.DB_DATABASE ?? 'database/database.sqlite',
1443
- },
1444
- },
1445
- }
1446
- `;
1329
+ return ConfigGenerator.generateSimpleDatabaseConfig();
1447
1330
  }
1448
1331
  generateAuthConfig() {
1449
- return `export default {
1450
- defaults: { guard: 'web' },
1451
- guards: {
1452
- web: { driver: 'session', provider: 'users' },
1453
- api: { driver: 'token', provider: 'users' },
1454
- },
1455
- }
1456
- `;
1332
+ return ConfigGenerator.generateAuthConfig();
1457
1333
  }
1458
1334
  generateCacheConfig() {
1459
- return `export default {
1460
- default: process.env.CACHE_DRIVER ?? 'memory',
1461
- stores: {
1462
- memory: { driver: 'memory' },
1463
- },
1464
- }
1465
- `;
1335
+ return ConfigGenerator.generateCacheConfig();
1466
1336
  }
1467
1337
  generateLoggingConfig() {
1468
- return `export default {
1469
- default: process.env.LOG_CHANNEL ?? 'console',
1470
- channels: {
1471
- console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
1472
- },
1473
- }
1474
- `;
1338
+ return ConfigGenerator.generateLoggingConfig();
1475
1339
  }
1476
1340
  generateUserEntity() {
1477
1341
  return `/**
@@ -1770,132 +1634,59 @@ export class UserRepository implements IUserRepository {
1770
1634
 
1771
1635
  async delete(id: string): Promise<void> {
1772
1636
  users.delete(id)
1773
- }
1774
-
1775
- async findAll(): Promise<User[]> {
1776
- return Array.from(users.values())
1777
- }
1778
-
1779
- async exists(id: string): Promise<boolean> {
1780
- return users.has(id)
1781
- }
1782
- }
1783
- `;
1784
- }
1785
- generateMailService() {
1786
- return `/**
1787
- * Mail Service Implementation
1788
- */
1789
-
1790
- import type { IMailService, MailMessage } from '../../Application/Interfaces/IMailService'
1791
-
1792
- export class MailService implements IMailService {
1793
- async send(message: MailMessage): Promise<void> {
1794
- // TODO: Implement actual email sending
1795
- console.log(\`[Mail] Sending to \${message.to}: \${message.subject}\`)
1796
- }
1797
- }
1798
- `;
1799
- }
1800
- generateAppServiceProvider(context) {
1801
- return `/**
1802
- * App Service Provider
1803
- */
1804
-
1805
- import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1806
-
1807
- export class AppServiceProvider extends ServiceProvider {
1808
- register(_container: Container): void {
1809
- // Register application services
1810
- }
1811
-
1812
- boot(_core: PlanetCore): void {
1813
- console.log('${context.name} (Clean Architecture) booted!')
1814
- }
1815
- }
1816
- `;
1817
- }
1818
- generateRepositoryServiceProvider() {
1819
- return `/**
1820
- * Repository Service Provider
1821
- *
1822
- * Binds repository interfaces to implementations.
1823
- */
1824
-
1825
- import { ServiceProvider, type Container } from '@gravito/core'
1826
- import { UserRepository } from '../Persistence/Repositories/UserRepository'
1827
- import { MailService } from '../ExternalServices/MailService'
1828
-
1829
- export class RepositoryServiceProvider extends ServiceProvider {
1830
- register(container: Container): void {
1831
- // Bind repositories
1832
- container.singleton('userRepository', () => new UserRepository())
1833
-
1834
- // Bind external services
1835
- container.singleton('mailService', () => new MailService())
1836
- }
1837
- }
1838
- `;
1839
- }
1840
- generateProvidersIndex() {
1841
- return `/**
1842
- * Application Service Providers
1843
- */
1844
-
1845
- export { AppServiceProvider } from './AppServiceProvider'
1846
- export { RepositoryServiceProvider } from './RepositoryServiceProvider'
1847
- export { MiddlewareProvider } from './MiddlewareProvider'
1848
- export { RouteProvider } from './RouteProvider'
1849
- `;
1850
- }
1851
- generateMiddlewareProvider() {
1852
- return `/**
1853
- * Middleware Service Provider
1854
- */
1855
-
1856
- import {
1857
- ServiceProvider,
1858
- type Container,
1859
- type PlanetCore,
1860
- bodySizeLimit,
1861
- securityHeaders,
1862
- } from '@gravito/core'
1863
-
1864
- export class MiddlewareProvider extends ServiceProvider {
1865
- register(_container: Container): void {}
1866
-
1867
- boot(core: PlanetCore): void {
1868
- const isDev = process.env.NODE_ENV !== 'production'
1869
-
1870
- core.adapter.use('*', securityHeaders({
1871
- contentSecurityPolicy: isDev ? false : undefined,
1872
- }))
1637
+ }
1873
1638
 
1874
- core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
1639
+ async findAll(): Promise<User[]> {
1640
+ return Array.from(users.values())
1641
+ }
1875
1642
 
1876
- core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
1643
+ async exists(id: string): Promise<boolean> {
1644
+ return users.has(id)
1877
1645
  }
1878
1646
  }
1879
1647
  `;
1880
1648
  }
1881
- generateRouteProvider() {
1649
+ generateMailService() {
1882
1650
  return `/**
1883
- * Route Service Provider
1651
+ * Mail Service Implementation
1884
1652
  */
1885
1653
 
1886
- import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1887
- import { registerApiRoutes } from '../../Interface/Http/Routes/api'
1888
-
1889
- export class RouteProvider extends ServiceProvider {
1890
- register(_container: Container): void {}
1654
+ import type { IMailService, MailMessage } from '../../Application/Interfaces/IMailService'
1891
1655
 
1892
- boot(core: PlanetCore): void {
1893
- registerApiRoutes(core.router)
1894
- core.logger.info('\u{1F6E4}\uFE0F Routes registered')
1656
+ export class MailService implements IMailService {
1657
+ async send(message: MailMessage): Promise<void> {
1658
+ // TODO: Implement actual email sending
1659
+ console.log(\`[Mail] Sending to \${message.to}: \${message.subject}\`)
1895
1660
  }
1896
1661
  }
1897
1662
  `;
1898
1663
  }
1664
+ generateAppServiceProvider(context) {
1665
+ return ServiceProviderGenerator.generateAppServiceProvider(context, "Clean Architecture");
1666
+ }
1667
+ generateRepositoryServiceProvider() {
1668
+ return ServiceProviderGenerator.generateRepositoryServiceProvider(
1669
+ ["UserRepository"],
1670
+ ["MailService"]
1671
+ );
1672
+ }
1673
+ generateProvidersIndex() {
1674
+ return ServiceProviderGenerator.generateProvidersIndex([
1675
+ "AppServiceProvider",
1676
+ "RepositoryServiceProvider",
1677
+ "MiddlewareProvider",
1678
+ "RouteProvider"
1679
+ ]);
1680
+ }
1681
+ generateMiddlewareProvider() {
1682
+ return ServiceProviderGenerator.generateMiddlewareProvider();
1683
+ }
1684
+ generateRouteProvider() {
1685
+ return ServiceProviderGenerator.generateRouteProvider(
1686
+ "../../Interface/Http/Routes/api",
1687
+ "named"
1688
+ );
1689
+ }
1899
1690
  // ─────────────────────────────────────────────────────────────
1900
1691
  // Interface Layer
1901
1692
  // ─────────────────────────────────────────────────────────────
@@ -2117,7 +1908,7 @@ Created with \u2764\uFE0F using Gravito Framework
2117
1908
  build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
2118
1909
  start: "bun run dist/bootstrap.js",
2119
1910
  test: "bun test",
2120
- typecheck: "tsc --noEmit",
1911
+ typecheck: "bun tsc --noEmit",
2121
1912
  check: "bun run typecheck && bun run test",
2122
1913
  "check:deps": "bun run scripts/check-dependencies.ts",
2123
1914
  validate: "bun run check && bun run check:deps",
@@ -2132,107 +1923,16 @@ Created with \u2764\uFE0F using Gravito Framework
2132
1923
  },
2133
1924
  devDependencies: {
2134
1925
  "bun-types": "latest",
2135
- typescript: "^5.0.0"
1926
+ typescript: "^5.9.3"
2136
1927
  }
2137
1928
  };
2138
1929
  return JSON.stringify(pkg, null, 2);
2139
1930
  }
2140
1931
  };
2141
1932
 
2142
- // src/generators/DddGenerator.ts
2143
- var DddGenerator = class extends BaseGenerator {
2144
- get architectureType() {
2145
- return "ddd";
2146
- }
2147
- get displayName() {
2148
- return "Domain-Driven Design (DDD)";
2149
- }
2150
- get description() {
2151
- return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
2152
- }
2153
- getDirectoryStructure(context) {
2154
- return [
2155
- {
2156
- type: "directory",
2157
- name: "config",
2158
- children: [
2159
- { type: "file", name: "app.ts", content: this.generateAppConfig(context) },
2160
- { type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
2161
- { type: "file", name: "modules.ts", content: this.generateModulesConfig() },
2162
- { type: "file", name: "cache.ts", content: this.generateCacheConfig() },
2163
- { type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
2164
- ]
2165
- },
2166
- {
2167
- type: "directory",
2168
- name: "src",
2169
- children: [
2170
- // Bootstrap - Application startup and configuration
2171
- this.generateBootstrapDirectory(context),
2172
- // Shared - Cross-module shared components
2173
- this.generateShared(),
2174
- // Modules - Bounded Contexts
2175
- {
2176
- type: "directory",
2177
- name: "Modules",
2178
- children: [
2179
- this.generateModule("Ordering", context),
2180
- this.generateModule("Catalog", context)
2181
- ]
2182
- },
2183
- { type: "file", name: "main.ts", content: this.generateMainEntry(context) }
2184
- ]
2185
- },
2186
- {
2187
- type: "directory",
2188
- name: "tests",
2189
- children: [
2190
- {
2191
- type: "directory",
2192
- name: "Modules",
2193
- children: [
2194
- {
2195
- type: "directory",
2196
- name: "Ordering",
2197
- children: [
2198
- {
2199
- type: "directory",
2200
- name: "Unit",
2201
- children: [{ type: "file", name: ".gitkeep", content: "" }]
2202
- },
2203
- {
2204
- type: "directory",
2205
- name: "Integration",
2206
- children: [{ type: "file", name: ".gitkeep", content: "" }]
2207
- }
2208
- ]
2209
- },
2210
- {
2211
- type: "directory",
2212
- name: "Catalog",
2213
- children: [
2214
- {
2215
- type: "directory",
2216
- name: "Unit",
2217
- children: [{ type: "file", name: ".gitkeep", content: "" }]
2218
- }
2219
- ]
2220
- }
2221
- ]
2222
- },
2223
- {
2224
- type: "directory",
2225
- name: "Shared",
2226
- children: [{ type: "file", name: ".gitkeep", content: "" }]
2227
- }
2228
- ]
2229
- }
2230
- ];
2231
- }
2232
- // ─────────────────────────────────────────────────────────────
2233
- // Bootstrap Directory
2234
- // ─────────────────────────────────────────────────────────────
2235
- generateBootstrapDirectory(context) {
1933
+ // src/generators/ddd/BootstrapGenerator.ts
1934
+ var BootstrapGenerator = class {
1935
+ generate(context) {
2236
1936
  return {
2237
1937
  type: "directory",
2238
1938
  name: "Bootstrap",
@@ -2244,60 +1944,227 @@ var DddGenerator = class extends BaseGenerator {
2244
1944
  ]
2245
1945
  };
2246
1946
  }
2247
- // ─────────────────────────────────────────────────────────────
2248
- // Shared Directory (replaces SharedKernel with user's structure)
2249
- // ─────────────────────────────────────────────────────────────
2250
- generateShared() {
2251
- return {
2252
- type: "directory",
2253
- name: "Shared",
2254
- children: [
2255
- {
2256
- type: "directory",
2257
- name: "Domain",
2258
- children: [
2259
- {
2260
- type: "directory",
2261
- name: "ValueObjects",
2262
- children: [
2263
- { type: "file", name: "Id.ts", content: this.generateIdValueObject() },
2264
- { type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
2265
- { type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
2266
- ]
2267
- }
2268
- ]
1947
+ generateConfigDirectory(context) {
1948
+ return {
1949
+ type: "directory",
1950
+ name: "config",
1951
+ children: [
1952
+ { type: "file", name: "app.ts", content: this.generateAppConfig(context) },
1953
+ { type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
1954
+ { type: "file", name: "modules.ts", content: this.generateModulesConfig() },
1955
+ { type: "file", name: "cache.ts", content: this.generateCacheConfig() },
1956
+ { type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
1957
+ ]
1958
+ };
1959
+ }
1960
+ generateMainEntry(_context) {
1961
+ return `/**
1962
+ * Application Entry Point
1963
+ *
1964
+ * Start the HTTP server.
1965
+ */
1966
+
1967
+ import { createApp } from './Bootstrap/app'
1968
+
1969
+ const app = await createApp()
1970
+
1971
+ export default app.liftoff()
1972
+ `;
1973
+ }
1974
+ generateBootstrapApp(_context) {
1975
+ return `/**
1976
+ * Application Bootstrap
1977
+ *
1978
+ * Central configuration and initialization using the ServiceProvider pattern.
1979
+ *
1980
+ * Lifecycle:
1981
+ * 1. Configure: Load app config and orbits
1982
+ * 2. Boot: Initialize PlanetCore
1983
+ * 3. Register Providers: Bind services to container
1984
+ * 4. Bootstrap: Boot all providers
1985
+ */
1986
+
1987
+ import { defineConfig, PlanetCore } from '@gravito/core'
1988
+ import { OrbitAtlas } from '@gravito/atlas'
1989
+ import appConfig from '../../config/app'
1990
+ import { registerProviders } from './providers'
1991
+ import { registerRoutes } from './routes'
1992
+
1993
+ export async function createApp(): Promise<PlanetCore> {
1994
+ // 1. Configure
1995
+ const config = defineConfig({
1996
+ config: appConfig,
1997
+ orbits: [new OrbitAtlas()],
1998
+ })
1999
+
2000
+ // 2. Boot Core
2001
+ const core = await PlanetCore.boot(config)
2002
+ core.registerGlobalErrorHandlers()
2003
+
2004
+ // 3. Register Providers
2005
+ await registerProviders(core)
2006
+
2007
+ // 4. Bootstrap All Providers
2008
+ await core.bootstrap()
2009
+
2010
+ // Register routes after bootstrap
2011
+ registerRoutes(core.router)
2012
+
2013
+ return core
2014
+ }
2015
+ `;
2016
+ }
2017
+ generateProvidersRegistry(_context) {
2018
+ return `/**
2019
+ * Service Providers Registry
2020
+ *
2021
+ * Register all service providers here.
2022
+ * Include both global and module-specific providers.
2023
+ */
2024
+
2025
+ import {
2026
+ ServiceProvider,
2027
+ type Container,
2028
+ type PlanetCore,
2029
+ bodySizeLimit,
2030
+ securityHeaders,
2031
+ } from '@gravito/core'
2032
+ import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
2033
+ import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
2034
+
2035
+ /**
2036
+ * Middleware Provider - Global middleware registration
2037
+ */
2038
+ export class MiddlewareProvider extends ServiceProvider {
2039
+ register(_container: Container): void {}
2040
+
2041
+ boot(core: PlanetCore): void {
2042
+ const isDev = process.env.NODE_ENV !== 'production'
2043
+
2044
+ core.adapter.use('*', securityHeaders({
2045
+ contentSecurityPolicy: isDev ? false : undefined,
2046
+ }))
2047
+
2048
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
2049
+
2050
+ core.logger.info('\u{1F6E1}\uFE0F Global middleware registered')
2051
+ }
2052
+ }
2053
+
2054
+ export async function registerProviders(core: PlanetCore): Promise<void> {
2055
+ // Global Providers
2056
+ core.register(new MiddlewareProvider())
2057
+
2058
+ // Module Providers
2059
+ core.register(new OrderingServiceProvider())
2060
+ core.register(new CatalogServiceProvider())
2061
+
2062
+ // Add more providers as needed
2063
+ }
2064
+ `;
2065
+ }
2066
+ generateEventsRegistry() {
2067
+ return `/**
2068
+ * Domain Events Registry
2069
+ *
2070
+ * Register all domain event handlers here.
2071
+ */
2072
+
2073
+ import { EventDispatcher } from '../Shared/Infrastructure/EventBus/EventDispatcher'
2074
+
2075
+ export function registerEvents(dispatcher: EventDispatcher): void {
2076
+ // Register event handlers
2077
+ // dispatcher.subscribe('ordering.created', async (event) => { ... })
2078
+ }
2079
+ `;
2080
+ }
2081
+ generateRoutesRegistry(_context) {
2082
+ return `/**
2083
+ * Routes Registry
2084
+ *
2085
+ * Register all module routes here.
2086
+ */
2087
+
2088
+ export function registerRoutes(router: any): void {
2089
+ // Health check
2090
+ router.get('/health', (c: any) => c.json({ status: 'healthy' }))
2091
+
2092
+ // Ordering module
2093
+ router.get('/api/orders', (c: any) => c.json({ message: 'Order list' }))
2094
+ router.post('/api/orders', (c: any) => c.json({ message: 'Order created' }, 201))
2095
+
2096
+ // Catalog module
2097
+ router.get('/api/products', (c: any) => c.json({ message: 'Product list' }))
2098
+ }
2099
+ `;
2100
+ }
2101
+ generateModulesConfig() {
2102
+ return `/**
2103
+ * Modules Configuration
2104
+ *
2105
+ * Define module boundaries and their dependencies.
2106
+ */
2107
+
2108
+ export default {
2109
+ modules: {
2110
+ ordering: {
2111
+ name: 'Ordering',
2112
+ description: 'Order management module',
2113
+ prefix: '/api/orders',
2269
2114
  },
2270
- {
2271
- type: "directory",
2272
- name: "Infrastructure",
2273
- children: [
2274
- {
2275
- type: "directory",
2276
- name: "EventBus",
2277
- children: [
2278
- {
2279
- type: "file",
2280
- name: "EventDispatcher.ts",
2281
- content: this.generateEventDispatcher()
2282
- }
2283
- ]
2284
- }
2285
- ]
2115
+ catalog: {
2116
+ name: 'Catalog',
2117
+ description: 'Product catalog module',
2118
+ prefix: '/api/products',
2286
2119
  },
2287
- {
2288
- type: "directory",
2289
- name: "Exceptions",
2290
- children: [
2291
- { type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
2292
- ]
2293
- }
2294
- ]
2295
- };
2120
+ },
2121
+
2122
+ // Module dependencies
2123
+ dependencies: {
2124
+ ordering: ['catalog'], // Ordering depends on Catalog
2125
+ },
2126
+ }
2127
+ `;
2296
2128
  }
2297
- // ─────────────────────────────────────────────────────────────
2298
- // Module Generator (replaces Bounded Context)
2299
- // ─────────────────────────────────────────────────────────────
2300
- generateModule(name, context) {
2129
+ generateAppConfig(context) {
2130
+ return `export default {
2131
+ name: process.env.APP_NAME ?? '${context.name}',
2132
+ env: process.env.APP_ENV ?? 'development',
2133
+ port: Number.parseInt(process.env.PORT ?? '3000', 10),
2134
+ VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
2135
+ debug: process.env.APP_DEBUG === 'true',
2136
+ url: process.env.APP_URL ?? 'http://localhost:3000',
2137
+ }
2138
+ `;
2139
+ }
2140
+ generateDatabaseConfig() {
2141
+ return `export default {
2142
+ default: process.env.DB_CONNECTION ?? 'sqlite',
2143
+ connections: {
2144
+ sqlite: { driver: 'sqlite', database: 'database/database.sqlite' },
2145
+ },
2146
+ }
2147
+ `;
2148
+ }
2149
+ generateCacheConfig() {
2150
+ return `export default {
2151
+ default: process.env.CACHE_DRIVER ?? 'memory',
2152
+ stores: { memory: { driver: 'memory' } },
2153
+ }
2154
+ `;
2155
+ }
2156
+ generateLoggingConfig() {
2157
+ return `export default {
2158
+ default: 'console',
2159
+ channels: { console: { driver: 'console', level: 'debug' } },
2160
+ }
2161
+ `;
2162
+ }
2163
+ };
2164
+
2165
+ // src/generators/ddd/ModuleGenerator.ts
2166
+ var ModuleGenerator = class {
2167
+ generate(name, context) {
2301
2168
  return {
2302
2169
  type: "directory",
2303
2170
  name,
@@ -2442,175 +2309,224 @@ var DddGenerator = class extends BaseGenerator {
2442
2309
  ]
2443
2310
  };
2444
2311
  }
2445
- // ─────────────────────────────────────────────────────────────
2446
- // Bootstrap File Generators
2447
- // ─────────────────────────────────────────────────────────────
2448
- generateBootstrapApp(_context) {
2312
+ generateAggregate(name) {
2449
2313
  return `/**
2450
- * Application Bootstrap
2451
- *
2452
- * Central configuration and initialization using the ServiceProvider pattern.
2453
- *
2454
- * Lifecycle:
2455
- * 1. Configure: Load app config and orbits
2456
- * 2. Boot: Initialize PlanetCore
2457
- * 3. Register Providers: Bind services to container
2458
- * 4. Bootstrap: Boot all providers
2314
+ * ${name} Aggregate Root
2459
2315
  */
2460
2316
 
2461
- import { defineConfig, PlanetCore } from '@gravito/core'
2462
- import { OrbitAtlas } from '@gravito/atlas'
2463
- import appConfig from '../../config/app'
2464
- import { registerProviders } from './providers'
2465
- import { registerRoutes } from './routes'
2317
+ import { AggregateRoot } from '@gravito/enterprise'
2318
+ import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2319
+ import { ${name}Created } from '../../Events/${name}Created'
2320
+ import { ${name}Status } from './${name}Status'
2466
2321
 
2467
- export async function createApp(): Promise<PlanetCore> {
2468
- // 1. Configure
2469
- const config = defineConfig({
2470
- config: appConfig,
2471
- orbits: [new OrbitAtlas()],
2472
- })
2322
+ export interface ${name}Props {
2323
+ // Add properties here
2324
+ status: ${name}Status
2325
+ createdAt: Date
2326
+ }
2473
2327
 
2474
- // 2. Boot Core
2475
- const core = await PlanetCore.boot(config)
2476
- core.registerGlobalErrorHandlers()
2328
+ export class ${name} extends AggregateRoot<Id> {
2329
+ private props: ${name}Props
2477
2330
 
2478
- // 3. Register Providers
2479
- await registerProviders(core)
2331
+ private constructor(id: Id, props: ${name}Props) {
2332
+ super(id)
2333
+ this.props = props
2334
+ }
2480
2335
 
2481
- // 4. Bootstrap All Providers
2482
- await core.bootstrap()
2336
+ static create(id: Id): ${name} {
2337
+ const aggregate = new ${name}(id, {
2338
+ status: ${name}Status.PENDING,
2339
+ createdAt: new Date(),
2340
+ })
2483
2341
 
2484
- // Register routes after bootstrap
2485
- registerRoutes(core.router)
2342
+ aggregate.addDomainEvent(new ${name}Created(id.value))
2486
2343
 
2487
- return core
2344
+ return aggregate
2345
+ }
2346
+
2347
+ get status(): ${name}Status {
2348
+ return this.props.status
2349
+ }
2350
+
2351
+ // Add domain methods here
2488
2352
  }
2489
2353
  `;
2490
2354
  }
2491
- generateProvidersRegistry(_context) {
2355
+ generateAggregateStatus(name) {
2492
2356
  return `/**
2493
- * Service Providers Registry
2494
- *
2495
- * Register all service providers here.
2496
- * Include both global and module-specific providers.
2357
+ * ${name} Status
2497
2358
  */
2498
2359
 
2499
- import {
2500
- ServiceProvider,
2501
- type Container,
2502
- type PlanetCore,
2503
- bodySizeLimit,
2504
- securityHeaders,
2505
- } from '@gravito/core'
2506
- import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
2507
- import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
2360
+ export enum ${name}Status {
2361
+ PENDING = 'pending',
2362
+ ACTIVE = 'active',
2363
+ COMPLETED = 'completed',
2364
+ CANCELLED = 'cancelled',
2365
+ }
2366
+ `;
2367
+ }
2368
+ generateCreatedEvent(name) {
2369
+ return `/**
2370
+ * ${name} Created Event
2371
+ */
2508
2372
 
2509
- /**
2510
- * Middleware Provider - Global middleware registration
2373
+ import { DomainEvent } from '@gravito/enterprise'
2374
+
2375
+ export class ${name}Created extends DomainEvent {
2376
+ constructor(public readonly ${name.toLowerCase()}Id: string) {
2377
+ super()
2378
+ }
2379
+
2380
+ override get eventName(): string {
2381
+ return '${name.toLowerCase()}.created'
2382
+ }
2383
+
2384
+ get aggregateId(): string {
2385
+ return this.${name.toLowerCase()}Id
2386
+ }
2387
+ }
2388
+ `;
2389
+ }
2390
+ generateRepositoryInterface(name) {
2391
+ return `/**
2392
+ * ${name} Repository Interface
2511
2393
  */
2512
- export class MiddlewareProvider extends ServiceProvider {
2513
- register(_container: Container): void {}
2514
2394
 
2515
- boot(core: PlanetCore): void {
2516
- const isDev = process.env.NODE_ENV !== 'production'
2395
+ import { Repository } from '@gravito/enterprise'
2396
+ import type { ${name} } from '../Aggregates/${name}/${name}'
2397
+ import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2517
2398
 
2518
- core.adapter.use('*', securityHeaders({
2519
- contentSecurityPolicy: isDev ? false : undefined,
2520
- }))
2399
+ export interface I${name}Repository extends Repository<${name}, Id> {
2400
+ // Add specific methods for this repository if needed
2401
+ }
2402
+ `;
2403
+ }
2404
+ generateCommand(name) {
2405
+ return `/**
2406
+ * Create ${name} Command
2407
+ */
2521
2408
 
2522
- core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
2409
+ import { Command } from '@gravito/enterprise'
2523
2410
 
2524
- core.logger.info('\u{1F6E1}\uFE0F Global middleware registered')
2411
+ export class Create${name}Command extends Command {
2412
+ constructor(
2413
+ // Add command properties
2414
+ public readonly id?: string
2415
+ ) {
2416
+ super()
2525
2417
  }
2526
2418
  }
2419
+ `;
2420
+ }
2421
+ generateCommandHandler(name) {
2422
+ return `/**
2423
+ * Create ${name} Handler
2424
+ */
2527
2425
 
2528
- export async function registerProviders(core: PlanetCore): Promise<void> {
2529
- // Global Providers
2530
- core.register(new MiddlewareProvider())
2426
+ import { CommandHandler } from '@gravito/enterprise'
2427
+ import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
2428
+ import { ${name} } from '../../../Domain/Aggregates/${name}/${name}'
2429
+ import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2430
+ import type { Create${name}Command } from './Create${name}Command'
2531
2431
 
2532
- // Module Providers
2533
- core.register(new OrderingServiceProvider())
2534
- core.register(new CatalogServiceProvider())
2432
+ export class Create${name}Handler implements CommandHandler<Create${name}Command, string> {
2433
+ constructor(private repository: I${name}Repository) {}
2535
2434
 
2536
- // Add more providers as needed
2435
+ async handle(command: Create${name}Command): Promise<string> {
2436
+ const id = command.id ? Id.from(command.id) : Id.create()
2437
+ const aggregate = ${name}.create(id)
2438
+
2439
+ await this.repository.save(aggregate)
2440
+
2441
+ return id.value
2442
+ }
2537
2443
  }
2538
2444
  `;
2539
2445
  }
2540
- generateEventsRegistry() {
2446
+ generateQuery(name) {
2541
2447
  return `/**
2542
- * Domain Events Registry
2543
- *
2544
- * Register all domain event handlers here.
2448
+ * Get ${name} By Id Query
2545
2449
  */
2546
2450
 
2547
- import { EventDispatcher } from '../Shared/Infrastructure/EventBus/EventDispatcher'
2451
+ import { Query } from '@gravito/enterprise'
2548
2452
 
2549
- export function registerEvents(dispatcher: EventDispatcher): void {
2550
- // Register event handlers
2551
- // dispatcher.subscribe('ordering.created', async (event) => { ... })
2453
+ export class Get${name}ByIdQuery extends Query {
2454
+ constructor(public readonly id: string) {
2455
+ super()
2456
+ }
2552
2457
  }
2553
2458
  `;
2554
2459
  }
2555
- generateRoutesRegistry(_context) {
2460
+ generateQueryHandler(name) {
2556
2461
  return `/**
2557
- * Routes Registry
2558
- *
2559
- * Register all module routes here.
2462
+ * Get ${name} By Id Handler
2560
2463
  */
2561
2464
 
2562
- export function registerRoutes(router: any): void {
2563
- // Health check
2564
- router.get('/health', (c: any) => c.json({ status: 'healthy' }))
2465
+ import { QueryHandler } from '@gravito/enterprise'
2466
+ import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
2467
+ import type { ${name}DTO } from '../../DTOs/${name}DTO'
2468
+ import type { Get${name}ByIdQuery } from './Get${name}ByIdQuery'
2565
2469
 
2566
- // Ordering module
2567
- router.get('/api/orders', (c: any) => c.json({ message: 'Order list' }))
2568
- router.post('/api/orders', (c: any) => c.json({ message: 'Order created' }, 201))
2470
+ export class Get${name}ByIdHandler implements QueryHandler<Get${name}ByIdQuery, ${name}DTO | null> {
2471
+ constructor(private repository: I${name}Repository) {}
2569
2472
 
2570
- // Catalog module
2571
- router.get('/api/products', (c: any) => c.json({ message: 'Product list' }))
2473
+ async handle(query: Get${name}ByIdQuery): Promise<${name}DTO | null> {
2474
+ const aggregate = await this.repository.findById(query.id as any) // Simplified for demo
2475
+ if (!aggregate) return null
2476
+
2477
+ return {
2478
+ id: aggregate.id.value,
2479
+ status: aggregate.status,
2480
+ }
2481
+ }
2572
2482
  }
2573
2483
  `;
2574
2484
  }
2575
- generateMainEntry(_context) {
2485
+ generateDTO(name) {
2576
2486
  return `/**
2577
- * Application Entry Point
2578
- *
2579
- * Start the HTTP server.
2487
+ * ${name} DTO
2580
2488
  */
2581
2489
 
2582
- import { createApp } from './Bootstrap/app'
2583
-
2584
- const app = await createApp()
2490
+ import type { ${name}Status } from '../../Domain/Aggregates/${name}/${name}Status'
2585
2491
 
2586
- export default app.liftoff()
2492
+ export interface ${name}DTO {
2493
+ id: string
2494
+ status: ${name}Status
2495
+ // Add more fields
2496
+ }
2587
2497
  `;
2588
2498
  }
2589
- generateModulesConfig() {
2499
+ generateRepository(name) {
2590
2500
  return `/**
2591
- * Modules Configuration
2592
- *
2593
- * Define module boundaries and their dependencies.
2501
+ * ${name} Repository Implementation
2594
2502
  */
2595
2503
 
2596
- export default {
2597
- modules: {
2598
- ordering: {
2599
- name: 'Ordering',
2600
- description: 'Order management module',
2601
- prefix: '/api/orders',
2602
- },
2603
- catalog: {
2604
- name: 'Catalog',
2605
- description: 'Product catalog module',
2606
- prefix: '/api/products',
2607
- },
2608
- },
2504
+ import type { ${name} } from '../../Domain/Aggregates/${name}/${name}'
2505
+ import type { I${name}Repository } from '../../Domain/Repositories/I${name}Repository'
2506
+ import type { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2609
2507
 
2610
- // Module dependencies
2611
- dependencies: {
2612
- ordering: ['catalog'], // Ordering depends on Catalog
2613
- },
2508
+ const store = new Map<string, ${name}>()
2509
+
2510
+ export class ${name}Repository implements I${name}Repository {
2511
+ async findById(id: Id): Promise<${name} | null> {
2512
+ return store.get(id.value) ?? null
2513
+ }
2514
+
2515
+ async save(aggregate: ${name}): Promise<void> {
2516
+ store.set(aggregate.id.value, aggregate)
2517
+ }
2518
+
2519
+ async delete(id: Id): Promise<void> {
2520
+ store.delete(id.value)
2521
+ }
2522
+
2523
+ async findAll(): Promise<${name}[]> {
2524
+ return Array.from(store.values())
2525
+ }
2526
+
2527
+ async exists(id: Id): Promise<boolean> {
2528
+ return store.has(id.value)
2529
+ }
2614
2530
  }
2615
2531
  `;
2616
2532
  }
@@ -2633,78 +2549,57 @@ export class ${name}ServiceProvider extends ServiceProvider {
2633
2549
  }
2634
2550
  `;
2635
2551
  }
2636
- /**
2637
- * Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
2638
- */
2639
- generatePackageJson(context) {
2640
- const pkg = {
2641
- name: context.nameKebabCase,
2642
- version: "0.1.0",
2643
- type: "module",
2644
- scripts: {
2645
- dev: "bun run --watch src/main.ts",
2646
- build: "bun build ./src/main.ts --outdir ./dist --target bun",
2647
- start: "bun run dist/main.js",
2648
- test: "bun test",
2649
- typecheck: "tsc --noEmit",
2650
- check: "bun run typecheck && bun run test",
2651
- "check:deps": "bun run scripts/check-dependencies.ts",
2652
- validate: "bun run check && bun run check:deps",
2653
- precommit: "bun run validate",
2654
- "docker:build": `docker build -t ${context.nameKebabCase} .`,
2655
- "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
2656
- },
2657
- dependencies: {
2658
- "@gravito/core": "^1.0.0-beta.5",
2659
- "@gravito/enterprise": "workspace:*"
2660
- },
2661
- devDependencies: {
2662
- "bun-types": "latest",
2663
- typescript: "^5.0.0"
2664
- }
2552
+ };
2553
+
2554
+ // src/generators/ddd/SharedKernelGenerator.ts
2555
+ var SharedKernelGenerator = class {
2556
+ generate() {
2557
+ return {
2558
+ type: "directory",
2559
+ name: "Shared",
2560
+ children: [
2561
+ {
2562
+ type: "directory",
2563
+ name: "Domain",
2564
+ children: [
2565
+ {
2566
+ type: "directory",
2567
+ name: "ValueObjects",
2568
+ children: [
2569
+ { type: "file", name: "Id.ts", content: this.generateIdValueObject() },
2570
+ { type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
2571
+ { type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
2572
+ ]
2573
+ }
2574
+ ]
2575
+ },
2576
+ {
2577
+ type: "directory",
2578
+ name: "Infrastructure",
2579
+ children: [
2580
+ {
2581
+ type: "directory",
2582
+ name: "EventBus",
2583
+ children: [
2584
+ {
2585
+ type: "file",
2586
+ name: "EventDispatcher.ts",
2587
+ content: this.generateEventDispatcher()
2588
+ }
2589
+ ]
2590
+ }
2591
+ ]
2592
+ },
2593
+ {
2594
+ type: "directory",
2595
+ name: "Exceptions",
2596
+ children: [
2597
+ { type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
2598
+ ]
2599
+ }
2600
+ ]
2665
2601
  };
2666
- return JSON.stringify(pkg, null, 2);
2667
- }
2668
- // ─────────────────────────────────────────────────────────────
2669
- // Config Generators
2670
- // ─────────────────────────────────────────────────────────────
2671
- generateAppConfig(context) {
2672
- return `export default {
2673
- name: process.env.APP_NAME ?? '${context.name}',
2674
- env: process.env.APP_ENV ?? 'development',
2675
- port: Number.parseInt(process.env.PORT ?? '3000', 10),
2676
- VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
2677
- debug: process.env.APP_DEBUG === 'true',
2678
- url: process.env.APP_URL ?? 'http://localhost:3000',
2679
- }
2680
- `;
2681
- }
2682
- generateDatabaseConfig() {
2683
- return `export default {
2684
- default: process.env.DB_CONNECTION ?? 'sqlite',
2685
- connections: {
2686
- sqlite: { driver: 'sqlite', database: 'database/database.sqlite' },
2687
- },
2688
- }
2689
- `;
2690
- }
2691
- generateCacheConfig() {
2692
- return `export default {
2693
- default: process.env.CACHE_DRIVER ?? 'memory',
2694
- stores: { memory: { driver: 'memory' } },
2695
- }
2696
- `;
2697
- }
2698
- generateLoggingConfig() {
2699
- return `export default {
2700
- default: 'console',
2701
- channels: { console: { driver: 'console', level: 'debug' } },
2702
- }
2703
- `;
2704
2602
  }
2705
- // ─────────────────────────────────────────────────────────────
2706
- // Shared Kernel Files
2707
- // ─────────────────────────────────────────────────────────────
2708
2603
  generateIdValueObject() {
2709
2604
  return `/**
2710
2605
  * ID Value Object
@@ -2852,239 +2747,142 @@ export class EventDispatcher {
2852
2747
  }
2853
2748
  `;
2854
2749
  }
2855
- // ─────────────────────────────────────────────────────────────
2856
- // Bounded Context Templates
2857
- // ─────────────────────────────────────────────────────────────
2858
- generateAggregate(name) {
2859
- return `/**
2860
- * ${name} Aggregate Root
2861
- */
2862
-
2863
- import { AggregateRoot } from '@gravito/enterprise'
2864
- import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2865
- import { ${name}Created } from '../../Events/${name}Created'
2866
- import { ${name}Status } from './${name}Status'
2867
-
2868
- export interface ${name}Props {
2869
- // Add properties here
2870
- status: ${name}Status
2871
- createdAt: Date
2872
- }
2873
-
2874
- export class ${name} extends AggregateRoot<Id> {
2875
- private props: ${name}Props
2876
-
2877
- private constructor(id: Id, props: ${name}Props) {
2878
- super(id)
2879
- this.props = props
2880
- }
2881
-
2882
- static create(id: Id): ${name} {
2883
- const aggregate = new ${name}(id, {
2884
- status: ${name}Status.PENDING,
2885
- createdAt: new Date(),
2886
- })
2887
-
2888
- aggregate.addDomainEvent(new ${name}Created(id.value))
2889
-
2890
- return aggregate
2891
- }
2892
-
2893
- get status(): ${name}Status {
2894
- return this.props.status
2895
- }
2896
-
2897
- // Add domain methods here
2898
- }
2899
- `;
2900
- }
2901
- generateAggregateStatus(name) {
2902
- return `/**
2903
- * ${name} Status
2904
- */
2905
-
2906
- export enum ${name}Status {
2907
- PENDING = 'pending',
2908
- ACTIVE = 'active',
2909
- COMPLETED = 'completed',
2910
- CANCELLED = 'cancelled',
2911
- }
2912
- `;
2913
- }
2914
- generateCreatedEvent(name) {
2915
- return `/**
2916
- * ${name} Created Event
2917
- */
2918
-
2919
- import { DomainEvent } from '@gravito/enterprise'
2920
-
2921
- export class ${name}Created extends DomainEvent {
2922
- constructor(public readonly ${name.toLowerCase()}Id: string) {
2923
- super()
2924
- }
2925
-
2926
- override get eventName(): string {
2927
- return '${name.toLowerCase()}.created'
2928
- }
2929
-
2930
- get aggregateId(): string {
2931
- return this.${name.toLowerCase()}Id
2932
- }
2933
- }
2934
- `;
2935
- }
2936
- generateRepositoryInterface(name) {
2937
- return `/**
2938
- * ${name} Repository Interface
2939
- */
2940
-
2941
- import { Repository } from '@gravito/enterprise'
2942
- import type { ${name} } from '../Aggregates/${name}/${name}'
2943
- import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2944
-
2945
- export interface I${name}Repository extends Repository<${name}, Id> {
2946
- // Add specific methods for this repository if needed
2947
- }
2948
- `;
2949
- }
2950
- generateCommand(name) {
2951
- return `/**
2952
- * Create ${name} Command
2953
- */
2954
-
2955
- import { Command } from '@gravito/enterprise'
2956
-
2957
- export class Create${name}Command extends Command {
2958
- constructor(
2959
- // Add command properties
2960
- public readonly id?: string
2961
- ) {
2962
- super()
2963
- }
2964
- }
2965
- `;
2966
- }
2967
- generateCommandHandler(name) {
2968
- return `/**
2969
- * Create ${name} Handler
2970
- */
2971
-
2972
- import { CommandHandler } from '@gravito/enterprise'
2973
- import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
2974
- import { ${name} } from '../../../Domain/Aggregates/${name}/${name}'
2975
- import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
2976
- import type { Create${name}Command } from './Create${name}Command'
2977
-
2978
- export class Create${name}Handler implements CommandHandler<Create${name}Command, string> {
2979
- constructor(private repository: I${name}Repository) {}
2980
-
2981
- async handle(command: Create${name}Command): Promise<string> {
2982
- const id = command.id ? Id.from(command.id) : Id.create()
2983
- const aggregate = ${name}.create(id)
2984
-
2985
- await this.repository.save(aggregate)
2986
-
2987
- return id.value
2988
- }
2989
- }
2990
- `;
2991
- }
2992
- generateQuery(name) {
2993
- return `/**
2994
- * Get ${name} By Id Query
2995
- */
2996
-
2997
- import { Query } from '@gravito/enterprise'
2998
-
2999
- export class Get${name}ByIdQuery extends Query {
3000
- constructor(public readonly id: string) {
3001
- super()
3002
- }
3003
- }
3004
- `;
3005
- }
3006
- generateQueryHandler(name) {
3007
- return `/**
3008
- * Get ${name} By Id Handler
3009
- */
3010
-
3011
- import { QueryHandler } from '@gravito/enterprise'
3012
- import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
3013
- import type { ${name}DTO } from '../../DTOs/${name}DTO'
3014
- import type { Get${name}ByIdQuery } from './Get${name}ByIdQuery'
3015
-
3016
- export class Get${name}ByIdHandler implements QueryHandler<Get${name}ByIdQuery, ${name}DTO | null> {
3017
- constructor(private repository: I${name}Repository) {}
3018
-
3019
- async handle(query: Get${name}ByIdQuery): Promise<${name}DTO | null> {
3020
- const aggregate = await this.repository.findById(query.id as any) // Simplified for demo
3021
- if (!aggregate) return null
3022
-
3023
- return {
3024
- id: aggregate.id.value,
3025
- status: aggregate.status,
3026
- }
3027
- }
3028
- }
3029
- `;
3030
- }
3031
- generateDTO(name) {
2750
+ generateExceptionHandler() {
3032
2751
  return `/**
3033
- * ${name} DTO
2752
+ * Exception Handler
3034
2753
  */
3035
2754
 
3036
- import type { ${name}Status } from '../../Domain/Aggregates/${name}/${name}Status'
3037
-
3038
- export interface ${name}DTO {
3039
- id: string
3040
- status: ${name}Status
3041
- // Add more fields
2755
+ export function report(error: unknown): void {
2756
+ console.error('[Exception]', error)
3042
2757
  }
3043
2758
  `;
3044
2759
  }
3045
- generateRepository(name) {
3046
- return `/**
3047
- * ${name} Repository Implementation
3048
- */
3049
-
3050
- import type { ${name} } from '../../Domain/Aggregates/${name}/${name}'
3051
- import type { I${name}Repository } from '../../Domain/Repositories/I${name}Repository'
3052
- import type { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
3053
-
3054
- const store = new Map<string, ${name}>()
3055
-
3056
- export class ${name}Repository implements I${name}Repository {
3057
- async findById(id: Id): Promise<${name} | null> {
3058
- return store.get(id.value) ?? null
3059
- }
2760
+ };
3060
2761
 
3061
- async save(aggregate: ${name}): Promise<void> {
3062
- store.set(aggregate.id.value, aggregate)
2762
+ // src/generators/DddGenerator.ts
2763
+ var DddGenerator = class extends BaseGenerator {
2764
+ moduleGenerator;
2765
+ sharedKernelGenerator;
2766
+ bootstrapGenerator;
2767
+ constructor(config) {
2768
+ super(config);
2769
+ this.moduleGenerator = new ModuleGenerator();
2770
+ this.sharedKernelGenerator = new SharedKernelGenerator();
2771
+ this.bootstrapGenerator = new BootstrapGenerator();
3063
2772
  }
3064
-
3065
- async delete(id: Id): Promise<void> {
3066
- store.delete(id.value)
2773
+ get architectureType() {
2774
+ return "ddd";
3067
2775
  }
3068
-
3069
- async findAll(): Promise<${name}[]> {
3070
- return Array.from(store.values())
2776
+ get displayName() {
2777
+ return "Domain-Driven Design (DDD)";
3071
2778
  }
3072
-
3073
- async exists(id: Id): Promise<boolean> {
3074
- return store.has(id.value)
2779
+ get description() {
2780
+ return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
3075
2781
  }
3076
- }
3077
- `;
2782
+ getDirectoryStructure(context) {
2783
+ return [
2784
+ this.bootstrapGenerator.generateConfigDirectory(context),
2785
+ {
2786
+ type: "directory",
2787
+ name: "src",
2788
+ children: [
2789
+ // Bootstrap - Application startup and configuration
2790
+ this.bootstrapGenerator.generate(context),
2791
+ // Shared - Cross-module shared components
2792
+ this.sharedKernelGenerator.generate(),
2793
+ // Modules - Bounded Contexts
2794
+ {
2795
+ type: "directory",
2796
+ name: "Modules",
2797
+ children: [
2798
+ this.moduleGenerator.generate("Ordering", context),
2799
+ this.moduleGenerator.generate("Catalog", context)
2800
+ ]
2801
+ },
2802
+ {
2803
+ type: "file",
2804
+ name: "main.ts",
2805
+ content: this.bootstrapGenerator.generateMainEntry(context)
2806
+ }
2807
+ ]
2808
+ },
2809
+ {
2810
+ type: "directory",
2811
+ name: "tests",
2812
+ children: [
2813
+ {
2814
+ type: "directory",
2815
+ name: "Modules",
2816
+ children: [
2817
+ {
2818
+ type: "directory",
2819
+ name: "Ordering",
2820
+ children: [
2821
+ {
2822
+ type: "directory",
2823
+ name: "Unit",
2824
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2825
+ },
2826
+ {
2827
+ type: "directory",
2828
+ name: "Integration",
2829
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2830
+ }
2831
+ ]
2832
+ },
2833
+ {
2834
+ type: "directory",
2835
+ name: "Catalog",
2836
+ children: [
2837
+ {
2838
+ type: "directory",
2839
+ name: "Unit",
2840
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2841
+ }
2842
+ ]
2843
+ }
2844
+ ]
2845
+ },
2846
+ {
2847
+ type: "directory",
2848
+ name: "Shared",
2849
+ children: [{ type: "file", name: ".gitkeep", content: "" }]
2850
+ }
2851
+ ]
2852
+ }
2853
+ ];
3078
2854
  }
3079
- generateExceptionHandler() {
3080
- return `/**
3081
- * Exception Handler
3082
- */
3083
-
3084
- export function report(error: unknown): void {
3085
- console.error('[Exception]', error)
3086
- }
3087
- `;
2855
+ /**
2856
+ * Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
2857
+ */
2858
+ generatePackageJson(context) {
2859
+ const pkg = {
2860
+ name: context.nameKebabCase,
2861
+ version: "0.1.0",
2862
+ type: "module",
2863
+ scripts: {
2864
+ dev: "bun run --watch src/main.ts",
2865
+ build: "bun build ./src/main.ts --outdir ./dist --target bun",
2866
+ start: "bun run dist/main.js",
2867
+ test: "bun test",
2868
+ typecheck: "bun tsc --noEmit",
2869
+ check: "bun run typecheck && bun run test",
2870
+ "check:deps": "bun run scripts/check-dependencies.ts",
2871
+ validate: "bun run check && bun run check:deps",
2872
+ precommit: "bun run validate",
2873
+ "docker:build": `docker build -t ${context.nameKebabCase} .`,
2874
+ "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
2875
+ },
2876
+ dependencies: {
2877
+ "@gravito/core": "^1.0.0-beta.5",
2878
+ "@gravito/enterprise": "workspace:*"
2879
+ },
2880
+ devDependencies: {
2881
+ "bun-types": "latest",
2882
+ typescript: "^5.9.3"
2883
+ }
2884
+ };
2885
+ return JSON.stringify(pkg, null, 2);
3088
2886
  }
3089
2887
  generateArchitectureDoc(context) {
3090
2888
  return `# ${context.name} - DDD Architecture Guide
@@ -3313,123 +3111,13 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
3313
3111
  ];
3314
3112
  }
3315
3113
  // ─────────────────────────────────────────────────────────────
3316
- // Config Generators
3114
+ // Config Generators (using shared ConfigGenerator)
3317
3115
  // ─────────────────────────────────────────────────────────────
3318
3116
  generateAppConfig(context) {
3319
- return `/**
3320
- * Application Configuration
3321
- */
3322
- export default {
3323
- /**
3324
- * Application name
3325
- */
3326
- name: process.env.APP_NAME ?? '${context.name}',
3327
-
3328
- /**
3329
- * Application environment
3330
- */
3331
- env: process.env.APP_ENV ?? 'development',
3332
-
3333
- /**
3334
- * Application port
3335
- */
3336
- port: Number.parseInt(process.env.PORT ?? '3000', 10),
3337
-
3338
- /**
3339
- * View directory
3340
- */
3341
- VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
3342
-
3343
- /**
3344
- * Debug mode
3345
- */
3346
- debug: process.env.APP_DEBUG === 'true',
3347
-
3348
- /**
3349
- * Application URL
3350
- */
3351
- url: process.env.APP_URL ?? 'http://localhost:3000',
3352
-
3353
- /**
3354
- * Timezone
3355
- */
3356
- timezone: 'UTC',
3357
-
3358
- /**
3359
- * Locale
3360
- */
3361
- locale: 'en',
3362
-
3363
- /**
3364
- * Fallback locale
3365
- */
3366
- fallbackLocale: 'en',
3367
-
3368
- /**
3369
- * Encryption key
3370
- */
3371
- key: process.env.APP_KEY,
3372
-
3373
- /**
3374
- * Service providers to register
3375
- */
3376
- providers: [
3377
- // Framework providers
3378
- // 'RouteServiceProvider',
3379
-
3380
- // Application providers
3381
- // 'AppServiceProvider',
3382
- ],
3383
- }
3384
- `;
3117
+ return ConfigGenerator.generateDetailedAppConfig(context);
3385
3118
  }
3386
3119
  generateDatabaseConfig() {
3387
- return `/**
3388
- * Database Configuration
3389
- */
3390
- export default {
3391
- /**
3392
- * Default connection
3393
- */
3394
- default: process.env.DB_CONNECTION ?? 'sqlite',
3395
-
3396
- /**
3397
- * Database connections
3398
- */
3399
- connections: {
3400
- sqlite: {
3401
- driver: 'sqlite',
3402
- database: process.env.DB_DATABASE ?? 'database/database.sqlite',
3403
- },
3404
-
3405
- mysql: {
3406
- driver: 'mysql',
3407
- host: process.env.DB_HOST ?? 'localhost',
3408
- port: Number(process.env.DB_PORT ?? 3306),
3409
- database: process.env.DB_DATABASE ?? 'forge',
3410
- username: process.env.DB_USERNAME ?? 'forge',
3411
- password: process.env.DB_PASSWORD ?? '',
3412
- },
3413
-
3414
- postgres: {
3415
- driver: 'postgres',
3416
- host: process.env.DB_HOST ?? 'localhost',
3417
- port: Number(process.env.DB_PORT ?? 5432),
3418
- database: process.env.DB_DATABASE ?? 'forge',
3419
- username: process.env.DB_USERNAME ?? 'forge',
3420
- password: process.env.DB_PASSWORD ?? '',
3421
- },
3422
- },
3423
-
3424
- /**
3425
- * Migration settings
3426
- */
3427
- migrations: {
3428
- table: 'migrations',
3429
- path: 'database/migrations',
3430
- },
3431
- }
3432
- `;
3120
+ return ConfigGenerator.generateDetailedDatabaseConfig();
3433
3121
  }
3434
3122
  generateAuthConfig() {
3435
3123
  return `/**
@@ -3712,33 +3400,6 @@ export class AppServiceProvider extends ServiceProvider {
3712
3400
  console.log('${context.name} application booted!')
3713
3401
  }
3714
3402
  }
3715
- `;
3716
- }
3717
- generateRouteServiceProvider(_context) {
3718
- return `/**
3719
- * Route Service Provider
3720
- *
3721
- * Configures and registers application routes.
3722
- */
3723
-
3724
- import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
3725
- import { registerRoutes } from '../routes'
3726
-
3727
- export class RouteServiceProvider extends ServiceProvider {
3728
- /**
3729
- * Register any application services.
3730
- */
3731
- register(_container: Container): void {
3732
- // Routes are registered in boot
3733
- }
3734
-
3735
- /**
3736
- * Bootstrap any application services.
3737
- */
3738
- boot(core: PlanetCore): void {
3739
- registerRoutes(core.router)
3740
- }
3741
- }
3742
3403
  `;
3743
3404
  }
3744
3405
  // ─────────────────────────────────────────────────────────────
@@ -4409,11 +4070,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
4409
4070
  module: "dist/index.mjs",
4410
4071
  types: "dist/index.d.ts",
4411
4072
  scripts: {
4412
- build: "tsup src/index.ts --format cjs,esm --dts",
4073
+ build: "tsup src/index.ts --format esm --dts",
4413
4074
  test: "bun test",
4414
- typecheck: "tsc --noEmit",
4415
- check: "bun run typecheck && bun run test",
4416
- validate: "bun run check"
4075
+ typecheck: "bun tsc --noEmit"
4417
4076
  },
4418
4077
  dependencies: {
4419
4078
  "@gravito/core": depVersion,
@@ -4422,8 +4081,12 @@ export class ${name}ServiceProvider extends ServiceProvider {
4422
4081
  "@gravito/stasis": depVersion
4423
4082
  },
4424
4083
  devDependencies: {
4425
- tsup: "^8.0.0",
4426
- typescript: "^5.0.0"
4084
+ "bun-types": "latest",
4085
+ typescript: "^5.9.3",
4086
+ tsup: "^8.0.0"
4087
+ },
4088
+ peerDependencies: {
4089
+ "@gravito/core": ">=1.0.0"
4427
4090
  }
4428
4091
  };
4429
4092
  return JSON.stringify(pkg, null, 2);
@@ -4573,7 +4236,7 @@ var ProfileResolver = class _ProfileResolver {
4573
4236
  };
4574
4237
 
4575
4238
  // src/Scaffold.ts
4576
- import path3 from "path";
4239
+ import path6 from "path";
4577
4240
 
4578
4241
  // src/generators/ActionDomainGenerator.ts
4579
4242
  var ActionDomainGenerator = class extends BaseGenerator {
@@ -4780,6 +4443,9 @@ var ActionDomainGenerator = class extends BaseGenerator {
4780
4443
 
4781
4444
  import { Model, column } from '@gravito/atlas'
4782
4445
 
4446
+ /**
4447
+ * Represents a user in the system.
4448
+ */
4783
4449
  export class User extends Model {
4784
4450
  static table = 'users'
4785
4451
 
@@ -4803,6 +4469,11 @@ export class User extends Model {
4803
4469
  // ─────────────────────────────────────────────────────────────
4804
4470
  // Action Generators
4805
4471
  // ─────────────────────────────────────────────────────────────
4472
+ /**
4473
+ * Generates the base Action class source code.
4474
+ *
4475
+ * @returns The complete source code for the abstract Action class.
4476
+ */
4806
4477
  generateActionBase() {
4807
4478
  return `/**
4808
4479
  * Action Base Class
@@ -4819,6 +4490,11 @@ export abstract class Action<TInput = unknown, TOutput = unknown> {
4819
4490
  }
4820
4491
  `;
4821
4492
  }
4493
+ /**
4494
+ * Generates the GetServerStatusAction source code.
4495
+ *
4496
+ * @returns The complete source code for the example action.
4497
+ */
4822
4498
  generateGetServerStatusAction() {
4823
4499
  return `/**
4824
4500
  * Get Server Status Action
@@ -4841,6 +4517,11 @@ export class GetServerStatusAction extends Action<void, ServerStatusResponse> {
4841
4517
  // ─────────────────────────────────────────────────────────────
4842
4518
  // Controller Generators
4843
4519
  // ─────────────────────────────────────────────────────────────
4520
+ /**
4521
+ * Generates the Server Controller source code.
4522
+ *
4523
+ * @returns The complete source code for the ServerController class.
4524
+ */
4844
4525
  generateServerController() {
4845
4526
  return `/**
4846
4527
  * Server Controller
@@ -4868,6 +4549,11 @@ export class ServerController {
4868
4549
  // ─────────────────────────────────────────────────────────────
4869
4550
  // Type Generators
4870
4551
  // ─────────────────────────────────────────────────────────────
4552
+ /**
4553
+ * Generates the ServerStatusResponse type definition.
4554
+ *
4555
+ * @returns The complete source code for the response interface.
4556
+ */
4871
4557
  generateServerStatusResponse() {
4872
4558
  return `/**
4873
4559
  * Server Status Response Type
@@ -4883,6 +4569,11 @@ export interface ServerStatusResponse {
4883
4569
  // ─────────────────────────────────────────────────────────────
4884
4570
  // Routes & Bootstrap
4885
4571
  // ─────────────────────────────────────────────────────────────
4572
+ /**
4573
+ * Generates the API routes registration function.
4574
+ *
4575
+ * @returns The complete source code for the api.ts routes file.
4576
+ */
4886
4577
  generateApiRoutes() {
4887
4578
  return `/**
4888
4579
  * API Routes Registration
@@ -4901,6 +4592,12 @@ export function registerApiRoutes(router: Router) {
4901
4592
  }
4902
4593
  `;
4903
4594
  }
4595
+ /**
4596
+ * Generates the App Service Provider source code.
4597
+ *
4598
+ * @param context - The generator context containing project details.
4599
+ * @returns The complete source code for AppServiceProvider.
4600
+ */
4904
4601
  generateAppServiceProvider(context) {
4905
4602
  return `/**
4906
4603
  * App Service Provider
@@ -4929,6 +4626,11 @@ export { MiddlewareProvider } from './MiddlewareProvider'
4929
4626
  export { RouteProvider } from './RouteProvider'
4930
4627
  `;
4931
4628
  }
4629
+ /**
4630
+ * Generates the Middleware Service Provider source code.
4631
+ *
4632
+ * @returns The complete source code for MiddlewareProvider.
4633
+ */
4932
4634
  generateMiddlewareProvider() {
4933
4635
  return `/**
4934
4636
  * Middleware Service Provider
@@ -4953,6 +4655,11 @@ export class MiddlewareProvider extends ServiceProvider {
4953
4655
  }
4954
4656
  `;
4955
4657
  }
4658
+ /**
4659
+ * Generates the Route Service Provider source code.
4660
+ *
4661
+ * @returns The complete source code for RouteProvider.
4662
+ */
4956
4663
  generateRouteProvider() {
4957
4664
  return `/**
4958
4665
  * Route Service Provider
@@ -5064,7 +4771,7 @@ Created with \u2764\uFE0F using Gravito Framework
5064
4771
  build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
5065
4772
  start: "bun run dist/bootstrap.js",
5066
4773
  test: "bun test",
5067
- typecheck: "tsc --noEmit",
4774
+ typecheck: "bun tsc --noEmit",
5068
4775
  check: "bun run typecheck && bun run test",
5069
4776
  "check:deps": "bun run scripts/check-dependencies.ts",
5070
4777
  validate: "bun run check && bun run check:deps",
@@ -5078,7 +4785,7 @@ Created with \u2764\uFE0F using Gravito Framework
5078
4785
  },
5079
4786
  devDependencies: {
5080
4787
  "bun-types": "latest",
5081
- typescript: "^5.0.0"
4788
+ typescript: "^5.9.3"
5082
4789
  }
5083
4790
  };
5084
4791
  return JSON.stringify(pkg, null, 2);
@@ -5086,6 +4793,7 @@ Created with \u2764\uFE0F using Gravito Framework
5086
4793
  };
5087
4794
 
5088
4795
  // src/generators/StandaloneEngineGenerator.ts
4796
+ import path5 from "path";
5089
4797
  var StandaloneEngineGenerator = class extends BaseGenerator {
5090
4798
  get architectureType() {
5091
4799
  return "standalone-engine";
@@ -5117,9 +4825,16 @@ var StandaloneEngineGenerator = class extends BaseGenerator {
5117
4825
  ];
5118
4826
  }
5119
4827
  async generateCommonFiles(context) {
4828
+ const commonDir = path5.resolve(this.config.templatesDir, "common");
4829
+ const extendedContext = { ...context };
5120
4830
  await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
5121
- await this.writeFile(context.targetDir, "tsconfig.json", this.generateTsConfig());
5122
- await this.writeFile(context.targetDir, ".gitignore", this.generateGitignore());
4831
+ await this.generateFileFromTemplate(
4832
+ commonDir,
4833
+ "tsconfig.json.hbs",
4834
+ "tsconfig.json",
4835
+ extendedContext
4836
+ );
4837
+ await this.generateFileFromTemplate(commonDir, "gitignore.hbs", ".gitignore", extendedContext);
5123
4838
  }
5124
4839
  generatePackageJson(context) {
5125
4840
  const pkg = {
@@ -5174,23 +4889,17 @@ A high-performance web application powered by Gravito Engine.
5174
4889
 
5175
4890
  ### Install Dependencies
5176
4891
 
5177
- \`\`\`bash
5178
- bun install
5179
- \`\`\`
5180
-
4892
+ bun install
4893
+
5181
4894
  ### Run Development Server
5182
4895
 
5183
- \`\`\`bash
5184
- bun run dev
5185
- \`\`\`
5186
-
4896
+ bun run dev
4897
+
5187
4898
  ### Production Build
5188
4899
 
5189
- \`\`\`bash
5190
- bun run build
4900
+ bun run build
5191
4901
  bun start
5192
- \`\`\`
5193
- `;
4902
+ `;
5194
4903
  }
5195
4904
  };
5196
4905
 
@@ -5199,11 +4908,14 @@ var Scaffold = class {
5199
4908
  templatesDir;
5200
4909
  verbose;
5201
4910
  constructor(options = {}) {
5202
- this.templatesDir = options.templatesDir ?? path3.resolve(__dirname, "../templates");
4911
+ this.templatesDir = options.templatesDir ?? path6.resolve(__dirname, "../templates");
5203
4912
  this.verbose = options.verbose ?? false;
5204
4913
  }
5205
4914
  /**
5206
- * Get all available architecture types.
4915
+ * Returns a list of all architectural patterns supported by the engine,
4916
+ * along with human-readable names and descriptions.
4917
+ *
4918
+ * @returns {Array<{type: ArchitectureType, name: string, description: string}>}
5207
4919
  */
5208
4920
  getArchitectureTypes() {
5209
4921
  return [
@@ -5240,11 +4952,16 @@ var Scaffold = class {
5240
4952
  ];
5241
4953
  }
5242
4954
  /**
5243
- * Create a new project scaffold.
4955
+ * Orchestrates the complete project generation lifecycle.
4956
+ * This includes directory creation, file layout, profile resolution,
4957
+ * dependency mapping, and optional post-install hooks.
4958
+ *
4959
+ * @param {ScaffoldOptions} options - Detailed configuration for the new project.
4960
+ * @returns {Promise<ScaffoldResult>}
5244
4961
  */
5245
4962
  async create(options) {
5246
4963
  const generator = this.createGenerator(options.architecture);
5247
- const fs3 = await import("fs/promises");
4964
+ const fs5 = await import("fs/promises");
5248
4965
  const profileResolver = new ProfileResolver();
5249
4966
  const profileConfig = profileResolver.resolve(options.profile, options.features);
5250
4967
  const context = BaseGenerator.createContext(
@@ -5271,8 +4988,8 @@ var Scaffold = class {
5271
4988
  // Default template for now, should come from options if applicable
5272
4989
  "1.0.0"
5273
4990
  );
5274
- const lockPath = path3.resolve(options.targetDir, "gravito.lock.json");
5275
- await fs3.writeFile(lockPath, lockContent, "utf-8");
4991
+ const lockPath = path6.resolve(options.targetDir, "gravito.lock.json");
4992
+ await fs5.writeFile(lockPath, lockContent, "utf-8");
5276
4993
  filesCreated.push(lockPath);
5277
4994
  return {
5278
4995
  success: true,
@@ -5330,6 +5047,7 @@ export {
5330
5047
  BaseGenerator,
5331
5048
  CleanArchitectureGenerator,
5332
5049
  DddGenerator,
5050
+ DependencyValidator,
5333
5051
  EnterpriseMvcGenerator,
5334
5052
  EnvironmentDetector,
5335
5053
  FileMerger,