@gravito/scaffold 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1361 -1642
- package/dist/index.d.cts +378 -308
- package/dist/index.d.ts +378 -308
- package/dist/index.js +1360 -1642
- package/package.json +7 -5
- package/templates/common/Dockerfile.hbs +25 -0
- package/templates/common/env.example.hbs +13 -0
- package/templates/common/gitignore.hbs +10 -0
- package/templates/common/tsconfig.json.hbs +22 -0
- package/templates/features/otel/package.json +6 -6
- package/templates/features/redis/package.json +4 -4
- package/templates/overlays/core/package.json +4 -4
- package/templates/overlays/enterprise/package.json +6 -6
- package/templates/overlays/scale/package.json +5 -5
- package/templates/scripts/check-dependencies.ts.hbs +142 -0
- package/templates/scripts/check.sh.hbs +55 -0
- package/templates/scripts/pre-commit.sh.hbs +58 -0
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
|
|
102
|
-
import
|
|
242
|
+
import fs4 from "fs/promises";
|
|
243
|
+
import path4 from "path";
|
|
103
244
|
|
|
104
|
-
// src/
|
|
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 =
|
|
196
|
-
const template = await
|
|
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 =
|
|
203
|
-
await
|
|
204
|
-
await
|
|
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/
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
270
|
-
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// src/generators/BaseGenerator.ts
|
|
271
478
|
var BaseGenerator = class {
|
|
272
479
|
config;
|
|
273
|
-
|
|
480
|
+
templateManager;
|
|
274
481
|
fileMerger;
|
|
275
482
|
filesCreated = [];
|
|
276
483
|
constructor(config) {
|
|
277
484
|
this.config = config;
|
|
278
|
-
this.
|
|
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 =
|
|
499
|
+
const fullPath = path4.resolve(basePath, node.name);
|
|
306
500
|
if (node.type === "directory") {
|
|
307
|
-
await
|
|
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
|
|
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
|
|
318
|
-
const
|
|
319
|
-
|
|
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
|
-
|
|
515
|
+
content = node.content ?? "";
|
|
322
516
|
}
|
|
323
|
-
} else if (node.content) {
|
|
324
|
-
await fs2.writeFile(fullPath, node.content, "utf-8");
|
|
325
517
|
} else {
|
|
326
|
-
|
|
518
|
+
content = node.content ?? "";
|
|
327
519
|
}
|
|
328
|
-
|
|
329
|
-
|
|
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(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
|
|
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
|
-
|
|
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 (
|
|
381
|
-
|
|
382
|
-
|
|
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 =
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
|
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
|
-
|
|
446
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
713
|
+
* Generate app configuration (simple version for Clean Architecture)
|
|
478
714
|
*/
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
|
726
|
+
* Generate app configuration (detailed version for Enterprise MVC)
|
|
510
727
|
*/
|
|
511
|
-
|
|
512
|
-
return
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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
|
-
*
|
|
734
|
+
* Application name
|
|
524
735
|
*/
|
|
525
|
-
|
|
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
|
-
*
|
|
739
|
+
* Application environment
|
|
654
740
|
*/
|
|
655
|
-
|
|
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
|
-
|
|
678
|
-
|
|
679
|
-
|
|
743
|
+
/**
|
|
744
|
+
* Application port
|
|
745
|
+
*/
|
|
746
|
+
port: Number.parseInt(process.env.PORT ?? '3000', 10),
|
|
680
747
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
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
|
-
*
|
|
754
|
+
* Debug mode
|
|
691
755
|
*/
|
|
692
|
-
|
|
693
|
-
|
|
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
|
-
*
|
|
759
|
+
* Application URL
|
|
719
760
|
*/
|
|
720
|
-
|
|
721
|
-
|
|
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
|
-
*
|
|
764
|
+
* Timezone
|
|
734
765
|
*/
|
|
735
|
-
|
|
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
|
-
|
|
744
|
-
|
|
768
|
+
/**
|
|
769
|
+
* Locale
|
|
770
|
+
*/
|
|
771
|
+
locale: 'en',
|
|
745
772
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
-
|
|
767
|
-
|
|
768
|
-
|
|
778
|
+
/**
|
|
779
|
+
* Encryption key
|
|
780
|
+
*/
|
|
781
|
+
key: process.env.APP_KEY,
|
|
769
782
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
} catch {
|
|
777
|
-
return null
|
|
778
|
-
}
|
|
779
|
-
}
|
|
783
|
+
/**
|
|
784
|
+
* Service providers to register
|
|
785
|
+
*/
|
|
786
|
+
providers: [
|
|
787
|
+
// Framework providers
|
|
788
|
+
// 'RouteServiceProvider',
|
|
780
789
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
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
|
|
876
|
+
* Generate cache configuration
|
|
882
877
|
*/
|
|
883
|
-
|
|
884
|
-
return
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
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
|
|
888
|
+
* Generate logging configuration
|
|
943
889
|
*/
|
|
944
|
-
|
|
945
|
-
return
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
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
|
|
900
|
+
* Generate view configuration
|
|
1007
901
|
*/
|
|
1008
|
-
|
|
1009
|
-
return
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
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
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
912
|
+
/**
|
|
913
|
+
* View directory
|
|
914
|
+
*/
|
|
915
|
+
path: 'src/views',
|
|
1074
916
|
|
|
1075
|
-
|
|
917
|
+
/**
|
|
918
|
+
* Cache views in production
|
|
919
|
+
*/
|
|
920
|
+
cache: process.env.NODE_ENV === 'production',
|
|
921
|
+
}
|
|
922
|
+
`;
|
|
923
|
+
}
|
|
924
|
+
};
|
|
1076
925
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
938
|
+
export class AppServiceProvider extends ServiceProvider {
|
|
939
|
+
register(_container: Container): void {
|
|
940
|
+
// Register application services
|
|
941
|
+
}
|
|
1089
942
|
|
|
1090
|
-
|
|
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
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
957
|
+
import {
|
|
958
|
+
ServiceProvider,
|
|
959
|
+
type Container,
|
|
960
|
+
type PlanetCore,
|
|
961
|
+
bodySizeLimit,
|
|
962
|
+
securityHeaders,
|
|
963
|
+
} from '@gravito/core'
|
|
1097
964
|
|
|
1098
|
-
|
|
1099
|
-
|
|
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
|
-
|
|
968
|
+
boot(core: PlanetCore): void {
|
|
969
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
1105
970
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
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
|
-
|
|
975
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
1117
976
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
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
|
-
|
|
992
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
993
|
+
${importStatement}
|
|
1123
994
|
|
|
1124
|
-
|
|
1125
|
-
|
|
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
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
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
|
-
|
|
1136
|
-
\`\`\`bash
|
|
1137
|
-
rm .git/hooks/pre-commit
|
|
1138
|
-
\`\`\`
|
|
1014
|
+
${exports}
|
|
1139
1015
|
`;
|
|
1140
1016
|
}
|
|
1141
1017
|
/**
|
|
1142
|
-
*
|
|
1018
|
+
* Generate Repository Service Provider
|
|
1143
1019
|
*/
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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
|
-
*
|
|
1049
|
+
* Generate Database Provider
|
|
1151
1050
|
*/
|
|
1152
|
-
static
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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 (
|
|
1323
|
+
// Config Generators (using shared ConfigGenerator)
|
|
1425
1324
|
// ─────────────────────────────────────────────────────────────
|
|
1426
1325
|
generateAppConfig(context) {
|
|
1427
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1639
|
+
async findAll(): Promise<User[]> {
|
|
1640
|
+
return Array.from(users.values())
|
|
1641
|
+
}
|
|
1875
1642
|
|
|
1876
|
-
|
|
1643
|
+
async exists(id: string): Promise<boolean> {
|
|
1644
|
+
return users.has(id)
|
|
1877
1645
|
}
|
|
1878
1646
|
}
|
|
1879
1647
|
`;
|
|
1880
1648
|
}
|
|
1881
|
-
|
|
1649
|
+
generateMailService() {
|
|
1882
1650
|
return `/**
|
|
1883
|
-
*
|
|
1651
|
+
* Mail Service Implementation
|
|
1884
1652
|
*/
|
|
1885
1653
|
|
|
1886
|
-
import {
|
|
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
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
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.
|
|
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/
|
|
2143
|
-
var
|
|
2144
|
-
|
|
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
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
{
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
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
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
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
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
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
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
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
|
-
*
|
|
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 {
|
|
2462
|
-
import {
|
|
2463
|
-
import
|
|
2464
|
-
import {
|
|
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
|
|
2468
|
-
//
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
})
|
|
2322
|
+
export interface ${name}Props {
|
|
2323
|
+
// Add properties here
|
|
2324
|
+
status: ${name}Status
|
|
2325
|
+
createdAt: Date
|
|
2326
|
+
}
|
|
2473
2327
|
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
core.registerGlobalErrorHandlers()
|
|
2328
|
+
export class ${name} extends AggregateRoot<Id> {
|
|
2329
|
+
private props: ${name}Props
|
|
2477
2330
|
|
|
2478
|
-
|
|
2479
|
-
|
|
2331
|
+
private constructor(id: Id, props: ${name}Props) {
|
|
2332
|
+
super(id)
|
|
2333
|
+
this.props = props
|
|
2334
|
+
}
|
|
2480
2335
|
|
|
2481
|
-
|
|
2482
|
-
|
|
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
|
-
|
|
2485
|
-
registerRoutes(core.router)
|
|
2342
|
+
aggregate.addDomainEvent(new ${name}Created(id.value))
|
|
2486
2343
|
|
|
2487
|
-
|
|
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
|
-
|
|
2355
|
+
generateAggregateStatus(name) {
|
|
2492
2356
|
return `/**
|
|
2493
|
-
*
|
|
2494
|
-
*
|
|
2495
|
-
* Register all service providers here.
|
|
2496
|
-
* Include both global and module-specific providers.
|
|
2357
|
+
* ${name} Status
|
|
2497
2358
|
*/
|
|
2498
2359
|
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2516
|
-
|
|
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
|
-
|
|
2519
|
-
|
|
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
|
-
|
|
2409
|
+
import { Command } from '@gravito/enterprise'
|
|
2523
2410
|
|
|
2524
|
-
|
|
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
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
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
|
-
|
|
2533
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2446
|
+
generateQuery(name) {
|
|
2541
2447
|
return `/**
|
|
2542
|
-
*
|
|
2543
|
-
*
|
|
2544
|
-
* Register all domain event handlers here.
|
|
2448
|
+
* Get ${name} By Id Query
|
|
2545
2449
|
*/
|
|
2546
2450
|
|
|
2547
|
-
import {
|
|
2451
|
+
import { Query } from '@gravito/enterprise'
|
|
2548
2452
|
|
|
2549
|
-
export
|
|
2550
|
-
|
|
2551
|
-
|
|
2453
|
+
export class Get${name}ByIdQuery extends Query {
|
|
2454
|
+
constructor(public readonly id: string) {
|
|
2455
|
+
super()
|
|
2456
|
+
}
|
|
2552
2457
|
}
|
|
2553
2458
|
`;
|
|
2554
2459
|
}
|
|
2555
|
-
|
|
2460
|
+
generateQueryHandler(name) {
|
|
2556
2461
|
return `/**
|
|
2557
|
-
*
|
|
2558
|
-
*
|
|
2559
|
-
* Register all module routes here.
|
|
2462
|
+
* Get ${name} By Id Handler
|
|
2560
2463
|
*/
|
|
2561
2464
|
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
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
|
-
|
|
2567
|
-
|
|
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
|
-
|
|
2571
|
-
|
|
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
|
-
|
|
2485
|
+
generateDTO(name) {
|
|
2576
2486
|
return `/**
|
|
2577
|
-
*
|
|
2578
|
-
*
|
|
2579
|
-
* Start the HTTP server.
|
|
2487
|
+
* ${name} DTO
|
|
2580
2488
|
*/
|
|
2581
2489
|
|
|
2582
|
-
import {
|
|
2583
|
-
|
|
2584
|
-
const app = await createApp()
|
|
2490
|
+
import type { ${name}Status } from '../../Domain/Aggregates/${name}/${name}Status'
|
|
2585
2491
|
|
|
2586
|
-
export
|
|
2492
|
+
export interface ${name}DTO {
|
|
2493
|
+
id: string
|
|
2494
|
+
status: ${name}Status
|
|
2495
|
+
// Add more fields
|
|
2496
|
+
}
|
|
2587
2497
|
`;
|
|
2588
2498
|
}
|
|
2589
|
-
|
|
2499
|
+
generateRepository(name) {
|
|
2590
2500
|
return `/**
|
|
2591
|
-
*
|
|
2592
|
-
*
|
|
2593
|
-
* Define module boundaries and their dependencies.
|
|
2501
|
+
* ${name} Repository Implementation
|
|
2594
2502
|
*/
|
|
2595
2503
|
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
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
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
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
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
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
|
-
*
|
|
2752
|
+
* Exception Handler
|
|
3034
2753
|
*/
|
|
3035
2754
|
|
|
3036
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3062
|
-
|
|
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
|
-
|
|
3066
|
-
store.delete(id.value)
|
|
2773
|
+
get architectureType() {
|
|
2774
|
+
return "ddd";
|
|
3067
2775
|
}
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
return Array.from(store.values())
|
|
2776
|
+
get displayName() {
|
|
2777
|
+
return "Domain-Driven Design (DDD)";
|
|
3071
2778
|
}
|
|
3072
|
-
|
|
3073
|
-
|
|
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
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
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
|
|
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
|
-
|
|
4426
|
-
typescript: "^5.
|
|
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
|
|
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.
|
|
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.
|
|
5122
|
-
|
|
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
|
-
|
|
5178
|
-
|
|
5179
|
-
\`\`\`
|
|
5180
|
-
|
|
4892
|
+
bun install
|
|
4893
|
+
|
|
5181
4894
|
### Run Development Server
|
|
5182
4895
|
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
\`\`\`
|
|
5186
|
-
|
|
4896
|
+
bun run dev
|
|
4897
|
+
|
|
5187
4898
|
### Production Build
|
|
5188
4899
|
|
|
5189
|
-
|
|
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 ??
|
|
4911
|
+
this.templatesDir = options.templatesDir ?? path6.resolve(__dirname, "../templates");
|
|
5203
4912
|
this.verbose = options.verbose ?? false;
|
|
5204
4913
|
}
|
|
5205
4914
|
/**
|
|
5206
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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 =
|
|
5275
|
-
await
|
|
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,
|