@gravito/scaffold 3.0.1 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  BaseGenerator: () => BaseGenerator,
34
34
  CleanArchitectureGenerator: () => CleanArchitectureGenerator,
35
35
  DddGenerator: () => DddGenerator,
36
+ DependencyValidator: () => DependencyValidator,
36
37
  EnterpriseMvcGenerator: () => EnterpriseMvcGenerator,
37
38
  EnvironmentDetector: () => EnvironmentDetector,
38
39
  FileMerger: () => FileMerger,
@@ -44,6 +45,147 @@ __export(index_exports, {
44
45
  });
45
46
  module.exports = __toCommonJS(index_exports);
46
47
 
48
+ // src/DependencyValidator.ts
49
+ var DependencyValidator = class _DependencyValidator {
50
+ /**
51
+ * Driver 到 Package 的映射規則
52
+ */
53
+ static DRIVER_DEPENDENCIES = [
54
+ {
55
+ driver: "redis",
56
+ requiredPackages: ["@gravito/ion"],
57
+ description: "Redis cache/queue driver requires @gravito/ion"
58
+ },
59
+ {
60
+ driver: "postgresql",
61
+ requiredPackages: ["@gravito/atlas", "pg"],
62
+ description: "PostgreSQL driver requires @gravito/atlas and pg"
63
+ },
64
+ {
65
+ driver: "mysql",
66
+ requiredPackages: ["@gravito/atlas", "mysql2"],
67
+ description: "MySQL driver requires @gravito/atlas and mysql2"
68
+ },
69
+ {
70
+ driver: "sqlite",
71
+ requiredPackages: ["@gravito/atlas", "better-sqlite3"],
72
+ description: "SQLite driver requires @gravito/atlas and better-sqlite3"
73
+ },
74
+ {
75
+ driver: "s3",
76
+ requiredPackages: ["@gravito/stasis", "@aws-sdk/client-s3"],
77
+ description: "S3 storage driver requires @gravito/stasis and AWS SDK"
78
+ },
79
+ {
80
+ driver: "r2",
81
+ requiredPackages: ["@gravito/stasis", "@aws-sdk/client-s3"],
82
+ description: "R2 storage driver requires @gravito/stasis and AWS SDK"
83
+ }
84
+ ];
85
+ /**
86
+ * Feature 衝突規則
87
+ */
88
+ static CONFLICTS = [
89
+ {
90
+ features: ["postgres", "mysql", "sqlite"],
91
+ reason: "\u4E0D\u80FD\u540C\u6642\u4F7F\u7528\u591A\u500B\u8CC7\u6599\u5EAB driver (PostgreSQL, MySQL, SQLite)"
92
+ }
93
+ ];
94
+ /**
95
+ * Feature 依賴映射
96
+ */
97
+ static FEATURE_DEPENDENCIES = {
98
+ stream: ["@gravito/beam"],
99
+ monitor: ["@gravito/spectrum"],
100
+ graphql: ["@gravito/constellation"]
101
+ };
102
+ /**
103
+ * 驗證 Profile 配置
104
+ *
105
+ * @param config - Profile 配置
106
+ * @param packageJson - 專案的 package.json 內容
107
+ * @returns 驗證結果
108
+ */
109
+ validate(config, packageJson) {
110
+ const errors = [];
111
+ const warnings = [];
112
+ this.validateDriverDependencies(config, packageJson, errors);
113
+ this.validateFeatureConflicts(config, errors);
114
+ this.validateFeatureDependencies(config, packageJson, warnings);
115
+ return {
116
+ valid: errors.length === 0,
117
+ errors,
118
+ warnings
119
+ };
120
+ }
121
+ /**
122
+ * 驗證 driver 依賴
123
+ */
124
+ validateDriverDependencies(config, packageJson, errors) {
125
+ for (const [service, driver] of Object.entries(config.drivers)) {
126
+ const rule = _DependencyValidator.DRIVER_DEPENDENCIES.find((r) => r.driver === driver);
127
+ if (rule) {
128
+ for (const pkg of rule.requiredPackages) {
129
+ if (!this.hasPackage(packageJson, pkg)) {
130
+ errors.push(`\u7F3A\u5C11\u4F9D\u8CF4: ${pkg} (${service} driver '${driver}' \u6240\u9700)`);
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ /**
137
+ * 驗證 feature 衝突
138
+ */
139
+ validateFeatureConflicts(config, errors) {
140
+ for (const conflict of _DependencyValidator.CONFLICTS) {
141
+ const conflictingFeatures = conflict.features.filter((f) => config.features.includes(f));
142
+ if (conflictingFeatures.length > 1) {
143
+ errors.push(`Feature \u885D\u7A81: ${conflictingFeatures.join(", ")} - ${conflict.reason}`);
144
+ }
145
+ }
146
+ }
147
+ /**
148
+ * 驗證 feature 依賴
149
+ */
150
+ validateFeatureDependencies(config, packageJson, warnings) {
151
+ for (const feature of config.features) {
152
+ const requiredPackages = _DependencyValidator.FEATURE_DEPENDENCIES[feature];
153
+ if (requiredPackages) {
154
+ for (const pkg of requiredPackages) {
155
+ if (!this.hasPackage(packageJson, pkg)) {
156
+ warnings.push(`Feature "${feature}" \u9700\u8981 ${pkg}`);
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
162
+ /**
163
+ * 檢查 package.json 是否包含指定 package
164
+ */
165
+ hasPackage(packageJson, packageName) {
166
+ return packageJson.dependencies?.[packageName] !== void 0 || packageJson.devDependencies?.[packageName] !== void 0;
167
+ }
168
+ /**
169
+ * 建議安裝缺失的依賴
170
+ *
171
+ * @param result - 驗證結果
172
+ * @returns 安裝命令建議
173
+ */
174
+ static suggestInstallCommand(result) {
175
+ if (result.errors.length === 0) {
176
+ return null;
177
+ }
178
+ const missingPackages = result.errors.map((err) => {
179
+ const match = err.match(/缺少依賴: ([@\w/-]+)/);
180
+ return match ? match[1] : null;
181
+ }).filter((pkg) => pkg !== null);
182
+ if (missingPackages.length === 0) {
183
+ return null;
184
+ }
185
+ return `bun add ${missingPackages.join(" ")}`;
186
+ }
187
+ };
188
+
47
189
  // src/EnvironmentDetector.ts
48
190
  var EnvironmentDetector = class {
49
191
  detect() {
@@ -471,6 +613,18 @@ dist
471
613
  "ARCHITECTURE.md",
472
614
  this.generateArchitectureDoc(context)
473
615
  );
616
+ await this.writeFile(
617
+ context.targetDir,
618
+ "tests/Example.test.ts",
619
+ `import { describe, it, expect } from 'bun:test'
620
+
621
+ describe('Example Test', () => {
622
+ it('should pass', () => {
623
+ expect(true).toBe(true)
624
+ })
625
+ })
626
+ `
627
+ );
474
628
  await this.generateCheckScripts(context);
475
629
  await this.generateSkills(context);
476
630
  }
@@ -1702,7 +1856,9 @@ export async function bootstrap() {
1702
1856
  // 1. Configure
1703
1857
  const config = defineConfig({
1704
1858
  config: appConfig,
1705
- orbits: [new OrbitAtlas()],
1859
+ orbits: [
1860
+ new OrbitAtlas() as unknown as import('@gravito/core').GravitoOrbit,
1861
+ ],
1706
1862
  })
1707
1863
 
1708
1864
  // 2. Boot Core
@@ -1899,7 +2055,9 @@ export async function createApp(): Promise<PlanetCore> {
1899
2055
  // 1. Configure
1900
2056
  const config = defineConfig({
1901
2057
  config: appConfig,
1902
- orbits: [new OrbitAtlas()],
2058
+ orbits: [
2059
+ new OrbitAtlas() as unknown as import('@gravito/core').GravitoOrbit,
2060
+ ],
1903
2061
  })
1904
2062
 
1905
2063
  // 2. Boot Core
@@ -2988,7 +3146,21 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
2988
3146
  {
2989
3147
  type: "directory",
2990
3148
  name: "Unit",
2991
- children: [{ type: "file", name: ".gitkeep", content: "" }]
3149
+ children: [
3150
+ { type: "file", name: ".gitkeep", content: "" },
3151
+ {
3152
+ type: "file",
3153
+ name: "Example.test.ts",
3154
+ content: `import { describe, it, expect } from 'bun:test'
3155
+
3156
+ describe('Example Test', () => {
3157
+ it('should pass', () => {
3158
+ expect(true).toBe(true)
3159
+ })
3160
+ })
3161
+ `
3162
+ }
3163
+ ]
2992
3164
  },
2993
3165
  {
2994
3166
  type: "directory",
@@ -3520,7 +3692,7 @@ export async function bootstrap() {
3520
3692
  const config = defineConfig({
3521
3693
  config: appConfig,
3522
3694
  orbits: [
3523
- new OrbitAtlas(),
3695
+ new OrbitAtlas() as unknown as import('@gravito/core').GravitoOrbit,
3524
3696
  ${spectrumOrbit}
3525
3697
  ],
3526
3698
  })
@@ -4307,7 +4479,21 @@ var ActionDomainGenerator = class extends BaseGenerator {
4307
4479
  {
4308
4480
  type: "directory",
4309
4481
  name: "tests",
4310
- children: [{ type: "file", name: ".gitkeep", content: "" }]
4482
+ children: [
4483
+ { type: "file", name: ".gitkeep", content: "" },
4484
+ {
4485
+ type: "file",
4486
+ name: "Example.test.ts",
4487
+ content: `import { describe, it, expect } from 'bun:test'
4488
+
4489
+ describe('Example Test', () => {
4490
+ it('should pass', () => {
4491
+ expect(true).toBe(true)
4492
+ })
4493
+ })
4494
+ `
4495
+ }
4496
+ ]
4311
4497
  }
4312
4498
  ];
4313
4499
  }
@@ -4374,6 +4560,11 @@ export class User extends Model {
4374
4560
  // ─────────────────────────────────────────────────────────────
4375
4561
  // Action Generators
4376
4562
  // ─────────────────────────────────────────────────────────────
4563
+ /**
4564
+ * Generates the base Action class source code.
4565
+ *
4566
+ * @returns The complete source code for the abstract Action class.
4567
+ */
4377
4568
  generateActionBase() {
4378
4569
  return `/**
4379
4570
  * Action Base Class
@@ -4390,6 +4581,11 @@ export abstract class Action<TInput = unknown, TOutput = unknown> {
4390
4581
  }
4391
4582
  `;
4392
4583
  }
4584
+ /**
4585
+ * Generates the GetServerStatusAction source code.
4586
+ *
4587
+ * @returns The complete source code for the example action.
4588
+ */
4393
4589
  generateGetServerStatusAction() {
4394
4590
  return `/**
4395
4591
  * Get Server Status Action
@@ -4412,6 +4608,11 @@ export class GetServerStatusAction extends Action<void, ServerStatusResponse> {
4412
4608
  // ─────────────────────────────────────────────────────────────
4413
4609
  // Controller Generators
4414
4610
  // ─────────────────────────────────────────────────────────────
4611
+ /**
4612
+ * Generates the Server Controller source code.
4613
+ *
4614
+ * @returns The complete source code for the ServerController class.
4615
+ */
4415
4616
  generateServerController() {
4416
4617
  return `/**
4417
4618
  * Server Controller
@@ -4439,6 +4640,11 @@ export class ServerController {
4439
4640
  // ─────────────────────────────────────────────────────────────
4440
4641
  // Type Generators
4441
4642
  // ─────────────────────────────────────────────────────────────
4643
+ /**
4644
+ * Generates the ServerStatusResponse type definition.
4645
+ *
4646
+ * @returns The complete source code for the response interface.
4647
+ */
4442
4648
  generateServerStatusResponse() {
4443
4649
  return `/**
4444
4650
  * Server Status Response Type
@@ -4454,6 +4660,11 @@ export interface ServerStatusResponse {
4454
4660
  // ─────────────────────────────────────────────────────────────
4455
4661
  // Routes & Bootstrap
4456
4662
  // ─────────────────────────────────────────────────────────────
4663
+ /**
4664
+ * Generates the API routes registration function.
4665
+ *
4666
+ * @returns The complete source code for the api.ts routes file.
4667
+ */
4457
4668
  generateApiRoutes() {
4458
4669
  return `/**
4459
4670
  * API Routes Registration
@@ -4472,6 +4683,12 @@ export function registerApiRoutes(router: Router) {
4472
4683
  }
4473
4684
  `;
4474
4685
  }
4686
+ /**
4687
+ * Generates the App Service Provider source code.
4688
+ *
4689
+ * @param context - The generator context containing project details.
4690
+ * @returns The complete source code for AppServiceProvider.
4691
+ */
4475
4692
  generateAppServiceProvider(context) {
4476
4693
  return `/**
4477
4694
  * App Service Provider
@@ -4500,6 +4717,11 @@ export { MiddlewareProvider } from './MiddlewareProvider'
4500
4717
  export { RouteProvider } from './RouteProvider'
4501
4718
  `;
4502
4719
  }
4720
+ /**
4721
+ * Generates the Middleware Service Provider source code.
4722
+ *
4723
+ * @returns The complete source code for MiddlewareProvider.
4724
+ */
4503
4725
  generateMiddlewareProvider() {
4504
4726
  return `/**
4505
4727
  * Middleware Service Provider
@@ -4524,6 +4746,11 @@ export class MiddlewareProvider extends ServiceProvider {
4524
4746
  }
4525
4747
  `;
4526
4748
  }
4749
+ /**
4750
+ * Generates the Route Service Provider source code.
4751
+ *
4752
+ * @returns The complete source code for RouteProvider.
4753
+ */
4527
4754
  generateRouteProvider() {
4528
4755
  return `/**
4529
4756
  * Route Service Provider
@@ -4561,7 +4788,7 @@ import {
4561
4788
  export async function bootstrap() {
4562
4789
  const config = defineConfig({
4563
4790
  config: appConfig,
4564
- orbits: [new OrbitAtlas()],
4791
+ orbits: [new OrbitAtlas() as unknown as import('@gravito/core').GravitoOrbit],
4565
4792
  })
4566
4793
 
4567
4794
  const core = await PlanetCore.boot(config)
@@ -4912,6 +5139,7 @@ var Scaffold = class {
4912
5139
  BaseGenerator,
4913
5140
  CleanArchitectureGenerator,
4914
5141
  DddGenerator,
5142
+ DependencyValidator,
4915
5143
  EnterpriseMvcGenerator,
4916
5144
  EnvironmentDetector,
4917
5145
  FileMerger,
package/dist/index.d.cts CHANGED
@@ -49,6 +49,94 @@ declare class ProfileResolver {
49
49
  isValidFeature(feature: string): boolean;
50
50
  }
51
51
 
52
+ /**
53
+ * package.json 結構
54
+ */
55
+ interface PackageJson {
56
+ name?: string;
57
+ version?: string;
58
+ dependencies?: Record<string, string>;
59
+ devDependencies?: Record<string, string>;
60
+ [key: string]: unknown;
61
+ }
62
+ /**
63
+ * 驗證結果
64
+ */
65
+ interface ValidationResult {
66
+ /** 是否通過驗證 */
67
+ valid: boolean;
68
+ /** 錯誤訊息列表 (阻塞性問題) */
69
+ errors: string[];
70
+ /** 警告訊息列表 (非阻塞性問題) */
71
+ warnings: string[];
72
+ }
73
+ /**
74
+ * 依賴驗證器
75
+ *
76
+ * 負責驗證 Profile 配置的依賴完整性,包括:
77
+ * - Driver 必需的 packages
78
+ * - Feature 之間的衝突
79
+ * - Feature 的依賴關係
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const validator = new DependencyValidator()
84
+ * const result = validator.validate(profileConfig, packageJson)
85
+ *
86
+ * if (!result.valid) {
87
+ * console.error('依賴驗證失敗:', result.errors)
88
+ * }
89
+ * ```
90
+ *
91
+ * @since 3.1.0
92
+ * @public
93
+ */
94
+ declare class DependencyValidator {
95
+ /**
96
+ * Driver 到 Package 的映射規則
97
+ */
98
+ private static readonly DRIVER_DEPENDENCIES;
99
+ /**
100
+ * Feature 衝突規則
101
+ */
102
+ private static readonly CONFLICTS;
103
+ /**
104
+ * Feature 依賴映射
105
+ */
106
+ private static readonly FEATURE_DEPENDENCIES;
107
+ /**
108
+ * 驗證 Profile 配置
109
+ *
110
+ * @param config - Profile 配置
111
+ * @param packageJson - 專案的 package.json 內容
112
+ * @returns 驗證結果
113
+ */
114
+ validate(config: ProfileConfig, packageJson: PackageJson): ValidationResult;
115
+ /**
116
+ * 驗證 driver 依賴
117
+ */
118
+ private validateDriverDependencies;
119
+ /**
120
+ * 驗證 feature 衝突
121
+ */
122
+ private validateFeatureConflicts;
123
+ /**
124
+ * 驗證 feature 依賴
125
+ */
126
+ private validateFeatureDependencies;
127
+ /**
128
+ * 檢查 package.json 是否包含指定 package
129
+ */
130
+ private hasPackage;
131
+ /**
132
+ * 建議安裝缺失的依賴
133
+ *
134
+ * @param result - 驗證結果
135
+ * @returns 安裝命令建議
136
+ */
137
+ static suggestInstallCommand(result: ValidationResult): string | null;
138
+ }
139
+
52
140
  /**
53
141
  * Represents the results of an environment detection scan.
54
142
  *
@@ -595,4 +683,4 @@ declare class Scaffold {
595
683
  generateProvider(_targetDir: string, _providerName: string): Promise<ScaffoldResult>;
596
684
  }
597
685
 
598
- export { type ArchitectureType, BaseGenerator, CleanArchitectureGenerator, DddGenerator, type DetectedEnvironment, EnterpriseMvcGenerator, EnvironmentDetector, FileMerger, type GeneratorConfig, type GeneratorContext, type LockFile, LockGenerator, type ProfileConfig, ProfileResolver, type ProfileType, SatelliteGenerator, Scaffold, type ScaffoldOptions, type StubConfig, StubGenerator, type StubVariables };
686
+ export { type ArchitectureType, BaseGenerator, CleanArchitectureGenerator, DddGenerator, DependencyValidator, type DetectedEnvironment, EnterpriseMvcGenerator, EnvironmentDetector, FileMerger, type GeneratorConfig, type GeneratorContext, type LockFile, LockGenerator, type PackageJson, type ProfileConfig, ProfileResolver, type ProfileType, SatelliteGenerator, Scaffold, type ScaffoldOptions, type StubConfig, StubGenerator, type StubVariables, type ValidationResult };
package/dist/index.d.ts CHANGED
@@ -49,6 +49,94 @@ declare class ProfileResolver {
49
49
  isValidFeature(feature: string): boolean;
50
50
  }
51
51
 
52
+ /**
53
+ * package.json 結構
54
+ */
55
+ interface PackageJson {
56
+ name?: string;
57
+ version?: string;
58
+ dependencies?: Record<string, string>;
59
+ devDependencies?: Record<string, string>;
60
+ [key: string]: unknown;
61
+ }
62
+ /**
63
+ * 驗證結果
64
+ */
65
+ interface ValidationResult {
66
+ /** 是否通過驗證 */
67
+ valid: boolean;
68
+ /** 錯誤訊息列表 (阻塞性問題) */
69
+ errors: string[];
70
+ /** 警告訊息列表 (非阻塞性問題) */
71
+ warnings: string[];
72
+ }
73
+ /**
74
+ * 依賴驗證器
75
+ *
76
+ * 負責驗證 Profile 配置的依賴完整性,包括:
77
+ * - Driver 必需的 packages
78
+ * - Feature 之間的衝突
79
+ * - Feature 的依賴關係
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * const validator = new DependencyValidator()
84
+ * const result = validator.validate(profileConfig, packageJson)
85
+ *
86
+ * if (!result.valid) {
87
+ * console.error('依賴驗證失敗:', result.errors)
88
+ * }
89
+ * ```
90
+ *
91
+ * @since 3.1.0
92
+ * @public
93
+ */
94
+ declare class DependencyValidator {
95
+ /**
96
+ * Driver 到 Package 的映射規則
97
+ */
98
+ private static readonly DRIVER_DEPENDENCIES;
99
+ /**
100
+ * Feature 衝突規則
101
+ */
102
+ private static readonly CONFLICTS;
103
+ /**
104
+ * Feature 依賴映射
105
+ */
106
+ private static readonly FEATURE_DEPENDENCIES;
107
+ /**
108
+ * 驗證 Profile 配置
109
+ *
110
+ * @param config - Profile 配置
111
+ * @param packageJson - 專案的 package.json 內容
112
+ * @returns 驗證結果
113
+ */
114
+ validate(config: ProfileConfig, packageJson: PackageJson): ValidationResult;
115
+ /**
116
+ * 驗證 driver 依賴
117
+ */
118
+ private validateDriverDependencies;
119
+ /**
120
+ * 驗證 feature 衝突
121
+ */
122
+ private validateFeatureConflicts;
123
+ /**
124
+ * 驗證 feature 依賴
125
+ */
126
+ private validateFeatureDependencies;
127
+ /**
128
+ * 檢查 package.json 是否包含指定 package
129
+ */
130
+ private hasPackage;
131
+ /**
132
+ * 建議安裝缺失的依賴
133
+ *
134
+ * @param result - 驗證結果
135
+ * @returns 安裝命令建議
136
+ */
137
+ static suggestInstallCommand(result: ValidationResult): string | null;
138
+ }
139
+
52
140
  /**
53
141
  * Represents the results of an environment detection scan.
54
142
  *
@@ -595,4 +683,4 @@ declare class Scaffold {
595
683
  generateProvider(_targetDir: string, _providerName: string): Promise<ScaffoldResult>;
596
684
  }
597
685
 
598
- export { type ArchitectureType, BaseGenerator, CleanArchitectureGenerator, DddGenerator, type DetectedEnvironment, EnterpriseMvcGenerator, EnvironmentDetector, FileMerger, type GeneratorConfig, type GeneratorContext, type LockFile, LockGenerator, type ProfileConfig, ProfileResolver, type ProfileType, SatelliteGenerator, Scaffold, type ScaffoldOptions, type StubConfig, StubGenerator, type StubVariables };
686
+ export { type ArchitectureType, BaseGenerator, CleanArchitectureGenerator, DddGenerator, DependencyValidator, type DetectedEnvironment, EnterpriseMvcGenerator, EnvironmentDetector, FileMerger, type GeneratorConfig, type GeneratorContext, type LockFile, LockGenerator, type PackageJson, type ProfileConfig, ProfileResolver, type ProfileType, SatelliteGenerator, Scaffold, type ScaffoldOptions, type StubConfig, StubGenerator, type StubVariables, type ValidationResult };
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() {
@@ -425,6 +566,18 @@ dist
425
566
  "ARCHITECTURE.md",
426
567
  this.generateArchitectureDoc(context)
427
568
  );
569
+ await this.writeFile(
570
+ context.targetDir,
571
+ "tests/Example.test.ts",
572
+ `import { describe, it, expect } from 'bun:test'
573
+
574
+ describe('Example Test', () => {
575
+ it('should pass', () => {
576
+ expect(true).toBe(true)
577
+ })
578
+ })
579
+ `
580
+ );
428
581
  await this.generateCheckScripts(context);
429
582
  await this.generateSkills(context);
430
583
  }
@@ -1656,7 +1809,9 @@ export async function bootstrap() {
1656
1809
  // 1. Configure
1657
1810
  const config = defineConfig({
1658
1811
  config: appConfig,
1659
- orbits: [new OrbitAtlas()],
1812
+ orbits: [
1813
+ new OrbitAtlas() as unknown as import('@gravito/core').GravitoOrbit,
1814
+ ],
1660
1815
  })
1661
1816
 
1662
1817
  // 2. Boot Core
@@ -1853,7 +2008,9 @@ export async function createApp(): Promise<PlanetCore> {
1853
2008
  // 1. Configure
1854
2009
  const config = defineConfig({
1855
2010
  config: appConfig,
1856
- orbits: [new OrbitAtlas()],
2011
+ orbits: [
2012
+ new OrbitAtlas() as unknown as import('@gravito/core').GravitoOrbit,
2013
+ ],
1857
2014
  })
1858
2015
 
1859
2016
  // 2. Boot Core
@@ -2942,7 +3099,21 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
2942
3099
  {
2943
3100
  type: "directory",
2944
3101
  name: "Unit",
2945
- children: [{ type: "file", name: ".gitkeep", content: "" }]
3102
+ children: [
3103
+ { type: "file", name: ".gitkeep", content: "" },
3104
+ {
3105
+ type: "file",
3106
+ name: "Example.test.ts",
3107
+ content: `import { describe, it, expect } from 'bun:test'
3108
+
3109
+ describe('Example Test', () => {
3110
+ it('should pass', () => {
3111
+ expect(true).toBe(true)
3112
+ })
3113
+ })
3114
+ `
3115
+ }
3116
+ ]
2946
3117
  },
2947
3118
  {
2948
3119
  type: "directory",
@@ -3474,7 +3645,7 @@ export async function bootstrap() {
3474
3645
  const config = defineConfig({
3475
3646
  config: appConfig,
3476
3647
  orbits: [
3477
- new OrbitAtlas(),
3648
+ new OrbitAtlas() as unknown as import('@gravito/core').GravitoOrbit,
3478
3649
  ${spectrumOrbit}
3479
3650
  ],
3480
3651
  })
@@ -4261,7 +4432,21 @@ var ActionDomainGenerator = class extends BaseGenerator {
4261
4432
  {
4262
4433
  type: "directory",
4263
4434
  name: "tests",
4264
- children: [{ type: "file", name: ".gitkeep", content: "" }]
4435
+ children: [
4436
+ { type: "file", name: ".gitkeep", content: "" },
4437
+ {
4438
+ type: "file",
4439
+ name: "Example.test.ts",
4440
+ content: `import { describe, it, expect } from 'bun:test'
4441
+
4442
+ describe('Example Test', () => {
4443
+ it('should pass', () => {
4444
+ expect(true).toBe(true)
4445
+ })
4446
+ })
4447
+ `
4448
+ }
4449
+ ]
4265
4450
  }
4266
4451
  ];
4267
4452
  }
@@ -4328,6 +4513,11 @@ export class User extends Model {
4328
4513
  // ─────────────────────────────────────────────────────────────
4329
4514
  // Action Generators
4330
4515
  // ─────────────────────────────────────────────────────────────
4516
+ /**
4517
+ * Generates the base Action class source code.
4518
+ *
4519
+ * @returns The complete source code for the abstract Action class.
4520
+ */
4331
4521
  generateActionBase() {
4332
4522
  return `/**
4333
4523
  * Action Base Class
@@ -4344,6 +4534,11 @@ export abstract class Action<TInput = unknown, TOutput = unknown> {
4344
4534
  }
4345
4535
  `;
4346
4536
  }
4537
+ /**
4538
+ * Generates the GetServerStatusAction source code.
4539
+ *
4540
+ * @returns The complete source code for the example action.
4541
+ */
4347
4542
  generateGetServerStatusAction() {
4348
4543
  return `/**
4349
4544
  * Get Server Status Action
@@ -4366,6 +4561,11 @@ export class GetServerStatusAction extends Action<void, ServerStatusResponse> {
4366
4561
  // ─────────────────────────────────────────────────────────────
4367
4562
  // Controller Generators
4368
4563
  // ─────────────────────────────────────────────────────────────
4564
+ /**
4565
+ * Generates the Server Controller source code.
4566
+ *
4567
+ * @returns The complete source code for the ServerController class.
4568
+ */
4369
4569
  generateServerController() {
4370
4570
  return `/**
4371
4571
  * Server Controller
@@ -4393,6 +4593,11 @@ export class ServerController {
4393
4593
  // ─────────────────────────────────────────────────────────────
4394
4594
  // Type Generators
4395
4595
  // ─────────────────────────────────────────────────────────────
4596
+ /**
4597
+ * Generates the ServerStatusResponse type definition.
4598
+ *
4599
+ * @returns The complete source code for the response interface.
4600
+ */
4396
4601
  generateServerStatusResponse() {
4397
4602
  return `/**
4398
4603
  * Server Status Response Type
@@ -4408,6 +4613,11 @@ export interface ServerStatusResponse {
4408
4613
  // ─────────────────────────────────────────────────────────────
4409
4614
  // Routes & Bootstrap
4410
4615
  // ─────────────────────────────────────────────────────────────
4616
+ /**
4617
+ * Generates the API routes registration function.
4618
+ *
4619
+ * @returns The complete source code for the api.ts routes file.
4620
+ */
4411
4621
  generateApiRoutes() {
4412
4622
  return `/**
4413
4623
  * API Routes Registration
@@ -4426,6 +4636,12 @@ export function registerApiRoutes(router: Router) {
4426
4636
  }
4427
4637
  `;
4428
4638
  }
4639
+ /**
4640
+ * Generates the App Service Provider source code.
4641
+ *
4642
+ * @param context - The generator context containing project details.
4643
+ * @returns The complete source code for AppServiceProvider.
4644
+ */
4429
4645
  generateAppServiceProvider(context) {
4430
4646
  return `/**
4431
4647
  * App Service Provider
@@ -4454,6 +4670,11 @@ export { MiddlewareProvider } from './MiddlewareProvider'
4454
4670
  export { RouteProvider } from './RouteProvider'
4455
4671
  `;
4456
4672
  }
4673
+ /**
4674
+ * Generates the Middleware Service Provider source code.
4675
+ *
4676
+ * @returns The complete source code for MiddlewareProvider.
4677
+ */
4457
4678
  generateMiddlewareProvider() {
4458
4679
  return `/**
4459
4680
  * Middleware Service Provider
@@ -4478,6 +4699,11 @@ export class MiddlewareProvider extends ServiceProvider {
4478
4699
  }
4479
4700
  `;
4480
4701
  }
4702
+ /**
4703
+ * Generates the Route Service Provider source code.
4704
+ *
4705
+ * @returns The complete source code for RouteProvider.
4706
+ */
4481
4707
  generateRouteProvider() {
4482
4708
  return `/**
4483
4709
  * Route Service Provider
@@ -4515,7 +4741,7 @@ import {
4515
4741
  export async function bootstrap() {
4516
4742
  const config = defineConfig({
4517
4743
  config: appConfig,
4518
- orbits: [new OrbitAtlas()],
4744
+ orbits: [new OrbitAtlas() as unknown as import('@gravito/core').GravitoOrbit],
4519
4745
  })
4520
4746
 
4521
4747
  const core = await PlanetCore.boot(config)
@@ -4865,6 +5091,7 @@ export {
4865
5091
  BaseGenerator,
4866
5092
  CleanArchitectureGenerator,
4867
5093
  DddGenerator,
5094
+ DependencyValidator,
4868
5095
  EnterpriseMvcGenerator,
4869
5096
  EnvironmentDetector,
4870
5097
  FileMerger,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravito/scaffold",
3
- "version": "3.0.1",
3
+ "version": "3.1.1",
4
4
  "description": "Project scaffolding engine for Gravito - Generate enterprise-grade architecture templates",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,10 +16,12 @@
16
16
  "scripts": {
17
17
  "build": "bun run build.ts",
18
18
  "dev": "bun run --watch src/index.ts",
19
- "test": "bun test",
19
+ "test": "bun test --timeout=10000",
20
20
  "typecheck": "bun tsc -p tsconfig.json --noEmit --skipLibCheck",
21
- "test:coverage": "bun test --coverage --coverage-threshold=50",
22
- "test:ci": "bun test --coverage --coverage-threshold=50"
21
+ "test:coverage": "bun test --timeout=10000 --coverage --coverage-reporter=lcov --coverage-dir coverage && bun run --bun scripts/check-coverage.ts",
22
+ "test:ci": "bun test --timeout=10000 --coverage --coverage-reporter=lcov --coverage-dir coverage && bun run --bun scripts/check-coverage.ts",
23
+ "test:unit": "bun test tests/ --timeout=10000",
24
+ "test:integration": "test $(find tests -name '*.integration.test.ts' 2>/dev/null | wc -l) -gt 0 && find tests -name '*.integration.test.ts' -print0 | xargs -0 bun test --timeout=10000 || echo 'No integration tests found'"
23
25
  },
24
26
  "files": [
25
27
  "dist",
@@ -38,7 +40,7 @@
38
40
  "bun-types": "latest"
39
41
  },
40
42
  "peerDependencies": {
41
- "@gravito/core": "workspace:*"
43
+ "@gravito/core": "^1.6.1"
42
44
  },
43
45
  "author": "Carl Lee <carllee0520@gmail.com>",
44
46
  "license": "MIT",
@@ -48,4 +50,4 @@
48
50
  "url": "git+https://github.com/gravito-framework/gravito.git",
49
51
  "directory": "packages/scaffold"
50
52
  }
51
- }
53
+ }
@@ -11,12 +11,11 @@
11
11
  "emitDecoratorMetadata": true,
12
12
  "types": ["bun-types"],
13
13
  "outDir": "./dist",
14
- "rootDir": "./src",
15
14
  "baseUrl": ".",
16
15
  "paths": {
17
16
  "@/*": ["./src/*"]
18
17
  }
19
18
  },
20
- "include": ["src/**/*"],
19
+ "include": ["src/**/*", "config/**/*"],
21
20
  "exclude": ["node_modules", "dist"]
22
21
  }
@@ -1,7 +1,7 @@
1
1
  {
2
- "dependencies": {
3
- "@opentelemetry/sdk-node": "^0.45.0",
4
- "@opentelemetry/exporter-trace-otlp-http": "^0.45.0",
5
- "@gravito/telemetry": "workspace:*"
6
- }
7
- }
2
+ "dependencies": {
3
+ "@opentelemetry/sdk-node": "^0.45.0",
4
+ "@opentelemetry/exporter-trace-otlp-http": "^0.45.0",
5
+ "@gravito/telemetry": "workspace:*"
6
+ }
7
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "dependencies": {
3
- "ioredis": "^5.3.0"
4
- }
5
- }
2
+ "dependencies": {
3
+ "ioredis": "^5.3.0"
4
+ }
5
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "dependencies": {
3
- "better-sqlite3": "^9.0.0"
4
- }
5
- }
2
+ "dependencies": {
3
+ "better-sqlite3": "^9.0.0"
4
+ }
5
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
- "dependencies": {
3
- "ioredis": "^5.0.0",
4
- "pg": "^8.0.0",
5
- "@gravito/spectrum": "workspace:*"
6
- }
7
- }
2
+ "dependencies": {
3
+ "ioredis": "^5.0.0",
4
+ "pg": "^8.0.0",
5
+ "@gravito/spectrum": "workspace:*"
6
+ }
7
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
- "dependencies": {
3
- "ioredis": "^5.0.0",
4
- "pg": "^8.0.0"
5
- }
6
- }
2
+ "dependencies": {
3
+ "ioredis": "^5.0.0",
4
+ "pg": "^8.0.0"
5
+ }
6
+ }