@gravito/scaffold 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1115 -1580
- package/dist/index.d.cts +289 -307
- package/dist/index.d.ts +289 -307
- package/dist/index.js +1115 -1580
- package/package.json +3 -3
- 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/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.cjs
CHANGED
|
@@ -144,12 +144,52 @@ ${overlay}`;
|
|
|
144
144
|
};
|
|
145
145
|
|
|
146
146
|
// src/generators/BaseGenerator.ts
|
|
147
|
-
var
|
|
148
|
-
var
|
|
147
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
148
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
149
149
|
|
|
150
|
-
// src/
|
|
150
|
+
// src/utils/FileUtilities.ts
|
|
151
151
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
152
152
|
var import_node_path = __toESM(require("path"), 1);
|
|
153
|
+
var FileUtilities = class _FileUtilities {
|
|
154
|
+
static async walk(dir) {
|
|
155
|
+
const files = await import_promises.default.readdir(dir);
|
|
156
|
+
const paths = [];
|
|
157
|
+
for (const file of files) {
|
|
158
|
+
const filePath = import_node_path.default.join(dir, file);
|
|
159
|
+
const stat = await import_promises.default.stat(filePath);
|
|
160
|
+
if (stat.isDirectory()) {
|
|
161
|
+
paths.push(...await _FileUtilities.walk(filePath));
|
|
162
|
+
} else {
|
|
163
|
+
paths.push(filePath);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return paths;
|
|
167
|
+
}
|
|
168
|
+
static async writeFile(basePath, relativePath, content, fileMerger, log) {
|
|
169
|
+
const fullPath = import_node_path.default.resolve(basePath, relativePath);
|
|
170
|
+
await import_promises.default.mkdir(import_node_path.default.dirname(fullPath), { recursive: true });
|
|
171
|
+
let finalContent = content;
|
|
172
|
+
try {
|
|
173
|
+
const existingContent = await import_promises.default.readFile(fullPath, "utf-8");
|
|
174
|
+
finalContent = fileMerger.merge(relativePath, existingContent, content);
|
|
175
|
+
if (finalContent !== content) {
|
|
176
|
+
log?.(`\u{1F504} Merged file: ${relativePath}`);
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
await import_promises.default.writeFile(fullPath, finalContent, "utf-8");
|
|
181
|
+
log?.(`\u{1F4C4} Created file: ${relativePath}`);
|
|
182
|
+
return fullPath;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// src/utils/TemplateManager.ts
|
|
187
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
188
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
189
|
+
|
|
190
|
+
// src/generators/StubGenerator.ts
|
|
191
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
192
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
153
193
|
var import_handlebars = __toESM(require("handlebars"), 1);
|
|
154
194
|
var StubGenerator = class {
|
|
155
195
|
config;
|
|
@@ -238,16 +278,16 @@ var StubGenerator = class {
|
|
|
238
278
|
* @returns Path to the generated file
|
|
239
279
|
*/
|
|
240
280
|
async generate(stubName, outputPath, variables = {}) {
|
|
241
|
-
const stubPath =
|
|
242
|
-
const template = await
|
|
281
|
+
const stubPath = import_node_path2.default.resolve(this.config.stubsDir, stubName);
|
|
282
|
+
const template = await import_promises2.default.readFile(stubPath, "utf-8");
|
|
243
283
|
const compiled = this.handlebars.compile(template);
|
|
244
284
|
const content = compiled({
|
|
245
285
|
...this.config.defaultVariables,
|
|
246
286
|
...variables
|
|
247
287
|
});
|
|
248
|
-
const fullOutputPath =
|
|
249
|
-
await
|
|
250
|
-
await
|
|
288
|
+
const fullOutputPath = import_node_path2.default.resolve(this.config.outputDir, outputPath);
|
|
289
|
+
await import_promises2.default.mkdir(import_node_path2.default.dirname(fullOutputPath), { recursive: true });
|
|
290
|
+
await import_promises2.default.writeFile(fullOutputPath, content, "utf-8");
|
|
251
291
|
return fullOutputPath;
|
|
252
292
|
}
|
|
253
293
|
/**
|
|
@@ -299,41 +339,57 @@ var StubGenerator = class {
|
|
|
299
339
|
}
|
|
300
340
|
};
|
|
301
341
|
|
|
302
|
-
// src/
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
342
|
+
// src/utils/TemplateManager.ts
|
|
343
|
+
var TemplateManager = class {
|
|
344
|
+
stubGenerator;
|
|
345
|
+
constructor(templatesDir) {
|
|
346
|
+
this.stubGenerator = new StubGenerator({
|
|
347
|
+
stubsDir: templatesDir,
|
|
348
|
+
outputDir: ""
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
render(template, context) {
|
|
352
|
+
return this.stubGenerator.render(template, context);
|
|
353
|
+
}
|
|
354
|
+
async applyOverlay(sourceDir, targetDir, context, fileMerger, log) {
|
|
355
|
+
const createdFiles = [];
|
|
356
|
+
try {
|
|
357
|
+
await import_promises3.default.access(sourceDir);
|
|
358
|
+
} catch {
|
|
359
|
+
return [];
|
|
360
|
+
}
|
|
361
|
+
const files = await FileUtilities.walk(sourceDir);
|
|
362
|
+
for (const filePath of files) {
|
|
363
|
+
const relativePath = import_node_path3.default.relative(sourceDir, filePath);
|
|
364
|
+
let content = await import_promises3.default.readFile(filePath, "utf-8");
|
|
365
|
+
try {
|
|
366
|
+
content = this.render(content, context);
|
|
367
|
+
} catch {
|
|
368
|
+
}
|
|
369
|
+
const fullPath = await FileUtilities.writeFile(
|
|
370
|
+
targetDir,
|
|
371
|
+
relativePath,
|
|
372
|
+
content,
|
|
373
|
+
fileMerger,
|
|
374
|
+
log
|
|
375
|
+
);
|
|
376
|
+
createdFiles.push(fullPath);
|
|
313
377
|
}
|
|
378
|
+
return createdFiles;
|
|
314
379
|
}
|
|
315
|
-
|
|
316
|
-
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// src/generators/BaseGenerator.ts
|
|
317
383
|
var BaseGenerator = class {
|
|
318
384
|
config;
|
|
319
|
-
|
|
385
|
+
templateManager;
|
|
320
386
|
fileMerger;
|
|
321
387
|
filesCreated = [];
|
|
322
388
|
constructor(config) {
|
|
323
389
|
this.config = config;
|
|
324
|
-
this.
|
|
325
|
-
stubsDir: config.templatesDir,
|
|
326
|
-
outputDir: ""
|
|
327
|
-
// Set per-generation
|
|
328
|
-
});
|
|
390
|
+
this.templateManager = new TemplateManager(config.templatesDir);
|
|
329
391
|
this.fileMerger = new FileMerger();
|
|
330
392
|
}
|
|
331
|
-
/**
|
|
332
|
-
* Generate the project scaffold.
|
|
333
|
-
*
|
|
334
|
-
* @param context - Generator context
|
|
335
|
-
* @returns Array of created file paths
|
|
336
|
-
*/
|
|
337
393
|
async generate(context) {
|
|
338
394
|
this.filesCreated = [];
|
|
339
395
|
const structure = this.getDirectoryStructure(context);
|
|
@@ -343,50 +399,73 @@ var BaseGenerator = class {
|
|
|
343
399
|
await this.applyFeatureOverlays(context);
|
|
344
400
|
return this.filesCreated;
|
|
345
401
|
}
|
|
346
|
-
/**
|
|
347
|
-
* Create directory structure recursively.
|
|
348
|
-
*/
|
|
349
402
|
async createStructure(basePath, nodes, context) {
|
|
350
403
|
for (const node of nodes) {
|
|
351
|
-
const fullPath =
|
|
404
|
+
const fullPath = import_node_path4.default.resolve(basePath, node.name);
|
|
352
405
|
if (node.type === "directory") {
|
|
353
|
-
await
|
|
406
|
+
await import_promises4.default.mkdir(fullPath, { recursive: true });
|
|
354
407
|
this.log(`\u{1F4C1} Created directory: ${node.name}`);
|
|
355
408
|
if (node.children) {
|
|
356
409
|
await this.createStructure(fullPath, node.children, context);
|
|
357
410
|
}
|
|
358
411
|
} else {
|
|
359
|
-
await
|
|
412
|
+
await import_promises4.default.mkdir(import_node_path4.default.dirname(fullPath), { recursive: true });
|
|
413
|
+
let content = "";
|
|
360
414
|
if (node.template) {
|
|
361
|
-
const templatePath = import_node_path2.default.resolve(this.config.templatesDir, node.template);
|
|
362
415
|
try {
|
|
363
|
-
const
|
|
364
|
-
const
|
|
365
|
-
|
|
416
|
+
const templatePath = import_node_path4.default.resolve(this.config.templatesDir, node.template);
|
|
417
|
+
const template = await import_promises4.default.readFile(templatePath, "utf-8");
|
|
418
|
+
content = this.templateManager.render(template, context);
|
|
366
419
|
} catch {
|
|
367
|
-
|
|
420
|
+
content = node.content ?? "";
|
|
368
421
|
}
|
|
369
|
-
} else if (node.content) {
|
|
370
|
-
await import_promises2.default.writeFile(fullPath, node.content, "utf-8");
|
|
371
422
|
} else {
|
|
372
|
-
|
|
423
|
+
content = node.content ?? "";
|
|
373
424
|
}
|
|
374
|
-
|
|
375
|
-
|
|
425
|
+
const relativePath = import_node_path4.default.relative(context.targetDir, fullPath);
|
|
426
|
+
const writtenPath = await FileUtilities.writeFile(
|
|
427
|
+
context.targetDir,
|
|
428
|
+
relativePath,
|
|
429
|
+
content,
|
|
430
|
+
this.fileMerger,
|
|
431
|
+
(msg) => this.log(msg)
|
|
432
|
+
);
|
|
433
|
+
this.filesCreated.push(writtenPath);
|
|
376
434
|
}
|
|
377
435
|
}
|
|
378
436
|
}
|
|
379
|
-
/**
|
|
380
|
-
* Generate common files (package.json, .env, etc.)
|
|
381
|
-
*/
|
|
382
437
|
async generateCommonFiles(context) {
|
|
438
|
+
const commonDir = import_node_path4.default.resolve(this.config.templatesDir, "common");
|
|
439
|
+
const extendedContext = {
|
|
440
|
+
...context,
|
|
441
|
+
entrypoint: context.architecture === "ddd" ? "dist/main.js" : "dist/bootstrap.js",
|
|
442
|
+
dbConnection: context.profile === "core" ? "sqlite" : "postgres"
|
|
443
|
+
};
|
|
444
|
+
await this.generateFileFromTemplate(
|
|
445
|
+
commonDir,
|
|
446
|
+
"env.example.hbs",
|
|
447
|
+
".env.example",
|
|
448
|
+
extendedContext
|
|
449
|
+
);
|
|
450
|
+
await this.generateFileFromTemplate(commonDir, "env.example.hbs", ".env", extendedContext);
|
|
451
|
+
await this.generateFileFromTemplate(commonDir, "gitignore.hbs", ".gitignore", extendedContext);
|
|
452
|
+
await this.generateFileFromTemplate(
|
|
453
|
+
commonDir,
|
|
454
|
+
"tsconfig.json.hbs",
|
|
455
|
+
"tsconfig.json",
|
|
456
|
+
extendedContext
|
|
457
|
+
);
|
|
458
|
+
await this.generateFileFromTemplate(commonDir, "Dockerfile.hbs", "Dockerfile", extendedContext);
|
|
383
459
|
await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
|
|
384
|
-
await this.writeFile(
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
460
|
+
await this.writeFile(
|
|
461
|
+
context.targetDir,
|
|
462
|
+
".dockerignore",
|
|
463
|
+
`node_modules
|
|
464
|
+
dist
|
|
465
|
+
.git
|
|
466
|
+
.env
|
|
467
|
+
`
|
|
468
|
+
);
|
|
390
469
|
await this.writeFile(
|
|
391
470
|
context.targetDir,
|
|
392
471
|
"ARCHITECTURE.md",
|
|
@@ -395,104 +474,74 @@ var BaseGenerator = class {
|
|
|
395
474
|
await this.generateCheckScripts(context);
|
|
396
475
|
await this.generateSkills(context);
|
|
397
476
|
}
|
|
398
|
-
|
|
399
|
-
* Copy AI Skills to the project
|
|
400
|
-
*/
|
|
401
|
-
async generateSkills(context) {
|
|
402
|
-
const skillsDir = import_node_path2.default.resolve(this.config.templatesDir, "skills");
|
|
403
|
-
const targetSkillsDir = import_node_path2.default.join(".skills");
|
|
477
|
+
async generateFileFromTemplate(tplDir, tplName, targetName, context) {
|
|
404
478
|
try {
|
|
405
|
-
await
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
for (const filePath of files) {
|
|
411
|
-
const relativePath = import_node_path2.default.relative(skillsDir, filePath);
|
|
412
|
-
const targetPath = import_node_path2.default.join(targetSkillsDir, relativePath);
|
|
413
|
-
let content = await import_promises2.default.readFile(filePath, "utf-8");
|
|
414
|
-
try {
|
|
415
|
-
content = this.stubGenerator.render(content, context);
|
|
416
|
-
} catch {
|
|
417
|
-
}
|
|
418
|
-
await this.writeFile(context.targetDir, targetPath, content);
|
|
479
|
+
const template = await import_promises4.default.readFile(import_node_path4.default.join(tplDir, tplName), "utf-8");
|
|
480
|
+
const content = this.templateManager.render(template, context);
|
|
481
|
+
await this.writeFile(context.targetDir, targetName, content);
|
|
482
|
+
} catch (e) {
|
|
483
|
+
this.log(`\u26A0\uFE0F Failed to generate ${targetName}: ${e}`);
|
|
419
484
|
}
|
|
420
485
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
486
|
+
async generateSkills(context) {
|
|
487
|
+
const skillsDir = import_node_path4.default.resolve(this.config.templatesDir, "skills");
|
|
488
|
+
const created = await this.templateManager.applyOverlay(
|
|
489
|
+
skillsDir,
|
|
490
|
+
import_node_path4.default.join(context.targetDir, ".skills"),
|
|
491
|
+
context,
|
|
492
|
+
this.fileMerger,
|
|
493
|
+
(msg) => this.log(msg)
|
|
494
|
+
);
|
|
495
|
+
this.filesCreated.push(...created);
|
|
496
|
+
}
|
|
424
497
|
async applyOverlays(context) {
|
|
425
498
|
const profile = context.profile;
|
|
426
|
-
if (
|
|
427
|
-
|
|
428
|
-
|
|
499
|
+
if (profile) {
|
|
500
|
+
const overlayDir = import_node_path4.default.resolve(this.config.templatesDir, "overlays", profile);
|
|
501
|
+
await this.copyOverlayDirectory(overlayDir, context);
|
|
502
|
+
}
|
|
429
503
|
}
|
|
430
|
-
/**
|
|
431
|
-
* Apply feature-specific overlays
|
|
432
|
-
*/
|
|
433
504
|
async applyFeatureOverlays(context) {
|
|
434
505
|
const features = context.features || [];
|
|
435
506
|
for (const feature of features) {
|
|
436
|
-
const overlayDir =
|
|
507
|
+
const overlayDir = import_node_path4.default.resolve(this.config.templatesDir, "features", feature);
|
|
437
508
|
await this.copyOverlayDirectory(overlayDir, context);
|
|
438
509
|
}
|
|
439
510
|
}
|
|
440
|
-
/**
|
|
441
|
-
* Helper to copy/merge an overlay directory into the target
|
|
442
|
-
*/
|
|
443
511
|
async copyOverlayDirectory(sourceDir, context) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
let content = await import_promises2.default.readFile(filePath, "utf-8");
|
|
453
|
-
try {
|
|
454
|
-
content = this.stubGenerator.render(content, context);
|
|
455
|
-
} catch {
|
|
456
|
-
}
|
|
457
|
-
await this.writeFile(context.targetDir, relativePath, content);
|
|
458
|
-
}
|
|
512
|
+
const created = await this.templateManager.applyOverlay(
|
|
513
|
+
sourceDir,
|
|
514
|
+
context.targetDir,
|
|
515
|
+
context,
|
|
516
|
+
this.fileMerger,
|
|
517
|
+
(msg) => this.log(msg)
|
|
518
|
+
);
|
|
519
|
+
this.filesCreated.push(...created);
|
|
459
520
|
}
|
|
460
|
-
/**
|
|
461
|
-
* Write a file and track it.
|
|
462
|
-
*/
|
|
463
521
|
async writeFile(basePath, relativePath, content) {
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
} catch {
|
|
474
|
-
}
|
|
475
|
-
await import_promises2.default.writeFile(fullPath, finalContent, "utf-8");
|
|
476
|
-
this.filesCreated.push(fullPath);
|
|
477
|
-
this.log(`\u{1F4C4} Created file: ${relativePath}`);
|
|
522
|
+
const writtenPath = await FileUtilities.writeFile(
|
|
523
|
+
basePath,
|
|
524
|
+
relativePath,
|
|
525
|
+
content,
|
|
526
|
+
this.fileMerger,
|
|
527
|
+
(msg) => this.log(msg)
|
|
528
|
+
);
|
|
529
|
+
this.filesCreated.push(writtenPath);
|
|
478
530
|
}
|
|
479
|
-
/**
|
|
480
|
-
* Generate package.json content.
|
|
481
|
-
*/
|
|
482
531
|
generatePackageJson(context) {
|
|
483
532
|
const profile = context.profile || "core";
|
|
484
|
-
const
|
|
533
|
+
const deps = {
|
|
485
534
|
"@gravito/core": "^1.0.0-beta.5",
|
|
486
535
|
"@gravito/atlas": "^1.0.0-beta.5",
|
|
487
536
|
"@gravito/plasma": "^1.0.0-beta.5",
|
|
488
537
|
"@gravito/stream": "^1.0.0-beta.5"
|
|
489
538
|
};
|
|
490
539
|
if (profile === "enterprise" || profile === "scale") {
|
|
491
|
-
|
|
492
|
-
|
|
540
|
+
deps["@gravito/quasar"] = "^1.0.0-beta.5";
|
|
541
|
+
deps["@gravito/horizon"] = "^1.0.0-beta.5";
|
|
493
542
|
}
|
|
494
543
|
if (context.withSpectrum) {
|
|
495
|
-
|
|
544
|
+
deps["@gravito/spectrum"] = "^1.0.0-beta.5";
|
|
496
545
|
}
|
|
497
546
|
const pkg = {
|
|
498
547
|
name: context.nameKebabCase,
|
|
@@ -503,717 +552,426 @@ var BaseGenerator = class {
|
|
|
503
552
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
504
553
|
start: "bun run dist/bootstrap.js",
|
|
505
554
|
test: "bun test",
|
|
506
|
-
typecheck: "tsc --noEmit",
|
|
507
|
-
|
|
508
|
-
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
509
|
-
validate: "bun run check && bun run check:deps",
|
|
510
|
-
precommit: "bun run validate",
|
|
511
|
-
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
512
|
-
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
555
|
+
typecheck: "bun tsc --noEmit",
|
|
556
|
+
validate: "bun run typecheck && bun run test"
|
|
513
557
|
},
|
|
514
|
-
dependencies:
|
|
515
|
-
devDependencies: {
|
|
516
|
-
"bun-types": "latest",
|
|
517
|
-
typescript: "^5.0.0"
|
|
518
|
-
}
|
|
558
|
+
dependencies: deps,
|
|
559
|
+
devDependencies: { "bun-types": "latest", typescript: "^5.9.3" }
|
|
519
560
|
};
|
|
520
561
|
return JSON.stringify(pkg, null, 2);
|
|
521
562
|
}
|
|
563
|
+
async generateCheckScripts(context) {
|
|
564
|
+
const scriptsDir = import_node_path4.default.resolve(context.targetDir, "scripts");
|
|
565
|
+
await import_promises4.default.mkdir(scriptsDir, { recursive: true });
|
|
566
|
+
const templatesDir = import_node_path4.default.resolve(this.config.templatesDir, "scripts");
|
|
567
|
+
await this.generateFileFromTemplate(
|
|
568
|
+
templatesDir,
|
|
569
|
+
"check-dependencies.ts.hbs",
|
|
570
|
+
"scripts/check-dependencies.ts",
|
|
571
|
+
context
|
|
572
|
+
);
|
|
573
|
+
await this.generateFileFromTemplate(templatesDir, "check.sh.hbs", "scripts/check.sh", context);
|
|
574
|
+
await this.generateFileFromTemplate(
|
|
575
|
+
templatesDir,
|
|
576
|
+
"pre-commit.sh.hbs",
|
|
577
|
+
"scripts/pre-commit.sh",
|
|
578
|
+
context
|
|
579
|
+
);
|
|
580
|
+
await this.writeFile(
|
|
581
|
+
context.targetDir,
|
|
582
|
+
"CHECK_SYSTEM.md",
|
|
583
|
+
"# Project Check System\n\nRun `bun run validate` to check everything.\n"
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
log(message) {
|
|
587
|
+
if (this.config.verbose) {
|
|
588
|
+
console.log(message);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
|
|
592
|
+
const toPascalCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_ ]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
593
|
+
const toCamelCase = (str) => {
|
|
594
|
+
const pascal = toPascalCase(str);
|
|
595
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
596
|
+
};
|
|
597
|
+
const toSnakeCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1_$2").split(/[-_ ]+/).map((word) => word.toLowerCase()).join("_");
|
|
598
|
+
const toKebabCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").split(/[-_ ]+/).map((word) => word.toLowerCase()).join("-");
|
|
599
|
+
return {
|
|
600
|
+
name,
|
|
601
|
+
namePascalCase: toPascalCase(name),
|
|
602
|
+
nameCamelCase: toCamelCase(name),
|
|
603
|
+
nameSnakeCase: toSnakeCase(name),
|
|
604
|
+
nameKebabCase: toKebabCase(name),
|
|
605
|
+
targetDir,
|
|
606
|
+
architecture,
|
|
607
|
+
packageManager,
|
|
608
|
+
year: (/* @__PURE__ */ new Date()).getFullYear().toString(),
|
|
609
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
610
|
+
...extra
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// src/utils/ConfigGenerator.ts
|
|
616
|
+
var ConfigGenerator = class {
|
|
522
617
|
/**
|
|
523
|
-
* Generate
|
|
618
|
+
* Generate app configuration (simple version for Clean Architecture)
|
|
524
619
|
*/
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
COPY package.json bun.lockb /temp/dev/
|
|
534
|
-
RUN cd /temp/dev && bun install --frozen-lockfile
|
|
535
|
-
|
|
536
|
-
# Build application
|
|
537
|
-
FROM base AS build
|
|
538
|
-
COPY --from=install /temp/dev/node_modules node_modules
|
|
539
|
-
COPY . .
|
|
540
|
-
ENV NODE_ENV=production
|
|
541
|
-
RUN bun run build
|
|
542
|
-
|
|
543
|
-
# Final production image
|
|
544
|
-
FROM base AS release
|
|
545
|
-
COPY --from=build /usr/src/app/${entrypoint} index.js
|
|
546
|
-
COPY --from=build /usr/src/app/package.json .
|
|
547
|
-
|
|
548
|
-
# Create a non-root user for security
|
|
549
|
-
USER bun
|
|
550
|
-
EXPOSE 3000/tcp
|
|
551
|
-
ENTRYPOINT [ "bun", "run", "index.js" ]
|
|
620
|
+
static generateSimpleAppConfig(context) {
|
|
621
|
+
return `export default {
|
|
622
|
+
name: process.env.APP_NAME ?? '${context.name}',
|
|
623
|
+
env: process.env.APP_ENV ?? 'development',
|
|
624
|
+
debug: process.env.APP_DEBUG === 'true',
|
|
625
|
+
url: process.env.APP_URL ?? 'http://localhost:3000',
|
|
626
|
+
key: process.env.APP_KEY,
|
|
627
|
+
}
|
|
552
628
|
`;
|
|
553
629
|
}
|
|
554
630
|
/**
|
|
555
|
-
* Generate
|
|
631
|
+
* Generate app configuration (detailed version for Enterprise MVC)
|
|
556
632
|
*/
|
|
557
|
-
|
|
558
|
-
return
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
*.log
|
|
563
|
-
.vscode
|
|
564
|
-
.idea
|
|
565
|
-
tests
|
|
566
|
-
`;
|
|
567
|
-
}
|
|
633
|
+
static generateDetailedAppConfig(context) {
|
|
634
|
+
return `/**
|
|
635
|
+
* Application Configuration
|
|
636
|
+
*/
|
|
637
|
+
export default {
|
|
568
638
|
/**
|
|
569
|
-
*
|
|
639
|
+
* Application name
|
|
570
640
|
*/
|
|
571
|
-
|
|
572
|
-
const profile = context.profile || "core";
|
|
573
|
-
let envContent = `# ============================================================================
|
|
574
|
-
# Application Configuration
|
|
575
|
-
# ============================================================================
|
|
576
|
-
|
|
577
|
-
APP_NAME=${context.name}
|
|
578
|
-
APP_ENV=development
|
|
579
|
-
APP_DEBUG=true
|
|
580
|
-
APP_URL=http://localhost:3000
|
|
581
|
-
APP_KEY=
|
|
582
|
-
|
|
583
|
-
# ============================================================================
|
|
584
|
-
# Database Configuration
|
|
585
|
-
# ============================================================================
|
|
586
|
-
|
|
587
|
-
# Database Connection (sqlite, postgres, mysql)
|
|
588
|
-
DB_CONNECTION=${profile === "core" ? "sqlite" : "postgres"}
|
|
589
|
-
|
|
590
|
-
# SQLite Configuration (when DB_CONNECTION=sqlite)
|
|
591
|
-
DB_DATABASE=database/database.sqlite
|
|
592
|
-
|
|
593
|
-
# PostgreSQL Configuration (when DB_CONNECTION=postgres)
|
|
594
|
-
${profile !== "core" ? `DB_HOST=127.0.0.1
|
|
595
|
-
DB_PORT=5432
|
|
596
|
-
DB_DATABASE=${context.name}
|
|
597
|
-
DB_USERNAME=postgres
|
|
598
|
-
DB_PASSWORD=
|
|
599
|
-
DB_SSLMODE=prefer` : `# DB_HOST=127.0.0.1
|
|
600
|
-
# DB_PORT=5432
|
|
601
|
-
# DB_DATABASE=${context.name}
|
|
602
|
-
# DB_USERNAME=postgres
|
|
603
|
-
# DB_PASSWORD=
|
|
604
|
-
# DB_SSLMODE=prefer`}
|
|
605
|
-
|
|
606
|
-
# MySQL Configuration (when DB_CONNECTION=mysql)
|
|
607
|
-
# DB_HOST=127.0.0.1
|
|
608
|
-
# DB_PORT=3306
|
|
609
|
-
# DB_DATABASE=${context.name}
|
|
610
|
-
# DB_USERNAME=root
|
|
611
|
-
# DB_PASSWORD=
|
|
612
|
-
|
|
613
|
-
# ============================================================================
|
|
614
|
-
# Redis Configuration (@gravito/plasma)
|
|
615
|
-
# ============================================================================
|
|
616
|
-
|
|
617
|
-
# Default Redis Connection
|
|
618
|
-
REDIS_CONNECTION=default
|
|
619
|
-
REDIS_HOST=127.0.0.1
|
|
620
|
-
REDIS_PORT=6379
|
|
621
|
-
REDIS_PASSWORD=
|
|
622
|
-
REDIS_DB=0
|
|
623
|
-
|
|
624
|
-
# Redis Connection Options
|
|
625
|
-
REDIS_CONNECT_TIMEOUT=10000
|
|
626
|
-
REDIS_COMMAND_TIMEOUT=5000
|
|
627
|
-
REDIS_KEY_PREFIX=
|
|
628
|
-
REDIS_MAX_RETRIES=3
|
|
629
|
-
REDIS_RETRY_DELAY=1000
|
|
630
|
-
|
|
631
|
-
# Cache-specific Redis Connection (optional, falls back to default)
|
|
632
|
-
# REDIS_CACHE_HOST=127.0.0.1
|
|
633
|
-
# REDIS_CACHE_PORT=6379
|
|
634
|
-
# REDIS_CACHE_PASSWORD=
|
|
635
|
-
REDIS_CACHE_DB=1
|
|
636
|
-
|
|
637
|
-
# Queue-specific Redis Connection (optional, falls back to default)
|
|
638
|
-
# REDIS_QUEUE_HOST=127.0.0.1
|
|
639
|
-
# REDIS_QUEUE_PORT=6379
|
|
640
|
-
# REDIS_QUEUE_PASSWORD=
|
|
641
|
-
REDIS_QUEUE_DB=2
|
|
642
|
-
|
|
643
|
-
# ============================================================================
|
|
644
|
-
# Cache Configuration (@gravito/stasis)
|
|
645
|
-
# ============================================================================
|
|
646
|
-
|
|
647
|
-
# Cache Driver (memory, file, redis)
|
|
648
|
-
CACHE_DRIVER=${profile === "core" ? "memory" : "redis"}
|
|
649
|
-
|
|
650
|
-
# File Cache Path (when CACHE_DRIVER=file)
|
|
651
|
-
CACHE_PATH=storage/framework/cache
|
|
652
|
-
|
|
653
|
-
# Redis Cache Configuration (when CACHE_DRIVER=redis)
|
|
654
|
-
REDIS_CACHE_CONNECTION=cache
|
|
655
|
-
REDIS_CACHE_PREFIX=cache:
|
|
656
|
-
|
|
657
|
-
# ============================================================================
|
|
658
|
-
# Queue Configuration (@gravito/stream)
|
|
659
|
-
# ============================================================================
|
|
660
|
-
|
|
661
|
-
# Queue Connection (sync, memory, database, redis, kafka, sqs, rabbitmq)
|
|
662
|
-
QUEUE_CONNECTION=${profile === "core" ? "sync" : "redis"}
|
|
663
|
-
|
|
664
|
-
# Database Queue Configuration (when QUEUE_CONNECTION=database)
|
|
665
|
-
QUEUE_TABLE=jobs
|
|
666
|
-
|
|
667
|
-
# Redis Queue Configuration (when QUEUE_CONNECTION=redis)
|
|
668
|
-
REDIS_PREFIX=queue:
|
|
641
|
+
name: process.env.APP_NAME ?? '${context.name}',
|
|
669
642
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
# KAFKA_CONSUMER_GROUP_ID=gravito-workers
|
|
675
|
-
# KAFKA_CLIENT_ID=${context.name}
|
|
676
|
-
|
|
677
|
-
# AWS SQS Queue Configuration (when QUEUE_CONNECTION=sqs)
|
|
678
|
-
# AWS_REGION=us-east-1
|
|
679
|
-
# SQS_QUEUE_URL_PREFIX=
|
|
680
|
-
# SQS_VISIBILITY_TIMEOUT=30
|
|
681
|
-
# SQS_WAIT_TIME_SECONDS=20
|
|
682
|
-
|
|
683
|
-
# RabbitMQ Queue Configuration (when QUEUE_CONNECTION=rabbitmq)
|
|
684
|
-
# RABBITMQ_URL=amqp://localhost
|
|
685
|
-
# RABBITMQ_EXCHANGE=gravito.events
|
|
686
|
-
# RABBITMQ_EXCHANGE_TYPE=fanout
|
|
643
|
+
/**
|
|
644
|
+
* Application environment
|
|
645
|
+
*/
|
|
646
|
+
env: process.env.APP_ENV ?? 'development',
|
|
687
647
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
# ============================================================================
|
|
648
|
+
/**
|
|
649
|
+
* Application port
|
|
650
|
+
*/
|
|
651
|
+
port: Number.parseInt(process.env.PORT ?? '3000', 10),
|
|
693
652
|
|
|
694
|
-
LOG_LEVEL=debug
|
|
695
|
-
`;
|
|
696
|
-
return envContent;
|
|
697
|
-
}
|
|
698
653
|
/**
|
|
699
|
-
*
|
|
654
|
+
* View directory
|
|
700
655
|
*/
|
|
701
|
-
|
|
702
|
-
return `# Dependencies
|
|
703
|
-
node_modules/
|
|
656
|
+
VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
|
|
704
657
|
|
|
705
|
-
|
|
706
|
-
|
|
658
|
+
/**
|
|
659
|
+
* Debug mode
|
|
660
|
+
*/
|
|
661
|
+
debug: process.env.APP_DEBUG === 'true',
|
|
707
662
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
.env
|
|
663
|
+
/**
|
|
664
|
+
* Application URL
|
|
665
|
+
*/
|
|
666
|
+
url: process.env.APP_URL ?? 'http://localhost:3000',
|
|
712
667
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
*.swo
|
|
668
|
+
/**
|
|
669
|
+
* Timezone
|
|
670
|
+
*/
|
|
671
|
+
timezone: 'UTC',
|
|
718
672
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
673
|
+
/**
|
|
674
|
+
* Locale
|
|
675
|
+
*/
|
|
676
|
+
locale: 'en',
|
|
722
677
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
678
|
+
/**
|
|
679
|
+
* Fallback locale
|
|
680
|
+
*/
|
|
681
|
+
fallbackLocale: 'en',
|
|
726
682
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
683
|
+
/**
|
|
684
|
+
* Encryption key
|
|
685
|
+
*/
|
|
686
|
+
key: process.env.APP_KEY,
|
|
730
687
|
|
|
731
|
-
# Coverage
|
|
732
|
-
coverage/
|
|
733
|
-
`;
|
|
734
|
-
}
|
|
735
688
|
/**
|
|
736
|
-
*
|
|
689
|
+
* Service providers to register
|
|
737
690
|
*/
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
declaration: true,
|
|
748
|
-
experimentalDecorators: true,
|
|
749
|
-
emitDecoratorMetadata: true,
|
|
750
|
-
types: ["bun-types"],
|
|
751
|
-
outDir: "./dist",
|
|
752
|
-
rootDir: "./src",
|
|
753
|
-
baseUrl: ".",
|
|
754
|
-
paths: {
|
|
755
|
-
"@/*": ["./src/*"]
|
|
756
|
-
}
|
|
757
|
-
},
|
|
758
|
-
include: ["src/**/*"],
|
|
759
|
-
exclude: ["node_modules", "dist"]
|
|
760
|
-
};
|
|
761
|
-
return JSON.stringify(config, null, 2);
|
|
691
|
+
providers: [
|
|
692
|
+
// Framework providers
|
|
693
|
+
// 'RouteServiceProvider',
|
|
694
|
+
|
|
695
|
+
// Application providers
|
|
696
|
+
// 'AppServiceProvider',
|
|
697
|
+
],
|
|
698
|
+
}
|
|
699
|
+
`;
|
|
762
700
|
}
|
|
763
701
|
/**
|
|
764
|
-
* Generate
|
|
702
|
+
* Generate database configuration (simple version)
|
|
765
703
|
*/
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
704
|
+
static generateSimpleDatabaseConfig() {
|
|
705
|
+
return `export default {
|
|
706
|
+
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
707
|
+
connections: {
|
|
708
|
+
sqlite: {
|
|
709
|
+
driver: 'sqlite',
|
|
710
|
+
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
711
|
+
},
|
|
712
|
+
},
|
|
713
|
+
}
|
|
714
|
+
`;
|
|
777
715
|
}
|
|
778
716
|
/**
|
|
779
|
-
* Generate
|
|
717
|
+
* Generate database configuration (detailed version)
|
|
780
718
|
*/
|
|
781
|
-
|
|
719
|
+
static generateDetailedDatabaseConfig() {
|
|
782
720
|
return `/**
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
* \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
|
|
786
|
-
* \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
721
|
+
* Database Configuration
|
|
787
722
|
*/
|
|
723
|
+
export default {
|
|
724
|
+
/**
|
|
725
|
+
* Default connection
|
|
726
|
+
*/
|
|
727
|
+
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
788
728
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
interface PackageInfo {
|
|
798
|
-
name: string
|
|
799
|
-
current: string
|
|
800
|
-
latest: string
|
|
801
|
-
outdated: boolean
|
|
802
|
-
}
|
|
729
|
+
/**
|
|
730
|
+
* Database connections
|
|
731
|
+
*/
|
|
732
|
+
connections: {
|
|
733
|
+
sqlite: {
|
|
734
|
+
driver: 'sqlite',
|
|
735
|
+
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
736
|
+
},
|
|
803
737
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
738
|
+
mysql: {
|
|
739
|
+
driver: 'mysql',
|
|
740
|
+
host: process.env.DB_HOST ?? 'localhost',
|
|
741
|
+
port: Number(process.env.DB_PORT ?? 3306),
|
|
742
|
+
database: process.env.DB_DATABASE ?? 'forge',
|
|
743
|
+
username: process.env.DB_USERNAME ?? 'forge',
|
|
744
|
+
password: process.env.DB_PASSWORD ?? '',
|
|
745
|
+
},
|
|
811
746
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
747
|
+
postgres: {
|
|
748
|
+
driver: 'postgres',
|
|
749
|
+
host: process.env.DB_HOST ?? 'localhost',
|
|
750
|
+
port: Number(process.env.DB_PORT ?? 5432),
|
|
751
|
+
database: process.env.DB_DATABASE ?? 'forge',
|
|
752
|
+
username: process.env.DB_USERNAME ?? 'forge',
|
|
753
|
+
password: process.env.DB_PASSWORD ?? '',
|
|
754
|
+
},
|
|
755
|
+
},
|
|
815
756
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
}
|
|
823
|
-
|
|
757
|
+
/**
|
|
758
|
+
* Migration settings
|
|
759
|
+
*/
|
|
760
|
+
migrations: {
|
|
761
|
+
table: 'migrations',
|
|
762
|
+
path: 'database/migrations',
|
|
763
|
+
},
|
|
764
|
+
}
|
|
765
|
+
`;
|
|
824
766
|
}
|
|
767
|
+
/**
|
|
768
|
+
* Generate auth configuration
|
|
769
|
+
*/
|
|
770
|
+
static generateAuthConfig() {
|
|
771
|
+
return `export default {
|
|
772
|
+
defaults: { guard: 'web' },
|
|
773
|
+
guards: {
|
|
774
|
+
web: { driver: 'session', provider: 'users' },
|
|
775
|
+
api: { driver: 'token', provider: 'users' },
|
|
776
|
+
},
|
|
825
777
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
778
|
+
`;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Generate cache configuration
|
|
782
|
+
*/
|
|
783
|
+
static generateCacheConfig() {
|
|
784
|
+
return `export default {
|
|
785
|
+
default: process.env.CACHE_DRIVER ?? 'memory',
|
|
786
|
+
stores: {
|
|
787
|
+
memory: { driver: 'memory' },
|
|
788
|
+
},
|
|
789
|
+
}
|
|
790
|
+
`;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Generate logging configuration
|
|
794
|
+
*/
|
|
795
|
+
static generateLoggingConfig() {
|
|
796
|
+
return `export default {
|
|
797
|
+
default: process.env.LOG_CHANNEL ?? 'console',
|
|
798
|
+
channels: {
|
|
799
|
+
console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
|
|
800
|
+
},
|
|
830
801
|
}
|
|
802
|
+
`;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Generate view configuration
|
|
806
|
+
*/
|
|
807
|
+
static generateViewConfig() {
|
|
808
|
+
return `/**
|
|
809
|
+
* View Configuration
|
|
810
|
+
*/
|
|
811
|
+
export default {
|
|
812
|
+
/**
|
|
813
|
+
* View engine
|
|
814
|
+
*/
|
|
815
|
+
engine: 'html',
|
|
831
816
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
817
|
+
/**
|
|
818
|
+
* View directory
|
|
819
|
+
*/
|
|
820
|
+
path: 'src/views',
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Cache views in production
|
|
824
|
+
*/
|
|
825
|
+
cache: process.env.NODE_ENV === 'production',
|
|
826
|
+
}
|
|
827
|
+
`;
|
|
839
828
|
}
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
// src/utils/ServiceProviderGenerator.ts
|
|
832
|
+
var ServiceProviderGenerator = class {
|
|
833
|
+
/**
|
|
834
|
+
* Generate App Service Provider
|
|
835
|
+
*/
|
|
836
|
+
static generateAppServiceProvider(context, architectureName) {
|
|
837
|
+
return `/**
|
|
838
|
+
* App Service Provider
|
|
839
|
+
*/
|
|
840
840
|
|
|
841
|
-
|
|
842
|
-
const latest = await getLatestVersion(name)
|
|
841
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
843
842
|
|
|
844
|
-
|
|
845
|
-
|
|
843
|
+
export class AppServiceProvider extends ServiceProvider {
|
|
844
|
+
register(_container: Container): void {
|
|
845
|
+
// Register application services
|
|
846
846
|
}
|
|
847
847
|
|
|
848
|
-
|
|
849
|
-
name
|
|
850
|
-
current,
|
|
851
|
-
latest,
|
|
852
|
-
outdated: current !== latest,
|
|
848
|
+
boot(_core: PlanetCore): void {
|
|
849
|
+
console.log('${context.name} (${architectureName}) booted!')
|
|
853
850
|
}
|
|
854
851
|
}
|
|
852
|
+
`;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Generate Middleware Provider
|
|
856
|
+
*/
|
|
857
|
+
static generateMiddlewareProvider() {
|
|
858
|
+
return `/**
|
|
859
|
+
* Middleware Service Provider
|
|
860
|
+
*/
|
|
855
861
|
|
|
856
|
-
|
|
857
|
-
|
|
862
|
+
import {
|
|
863
|
+
ServiceProvider,
|
|
864
|
+
type Container,
|
|
865
|
+
type PlanetCore,
|
|
866
|
+
bodySizeLimit,
|
|
867
|
+
securityHeaders,
|
|
868
|
+
} from '@gravito/core'
|
|
858
869
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
readFileSync(packageJsonPath, 'utf-8')
|
|
862
|
-
)
|
|
870
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
871
|
+
register(_container: Container): void {}
|
|
863
872
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
...packageJson.devDependencies,
|
|
867
|
-
}
|
|
873
|
+
boot(core: PlanetCore): void {
|
|
874
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
868
875
|
|
|
869
|
-
|
|
876
|
+
core.adapter.use('*', securityHeaders({
|
|
877
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
878
|
+
}))
|
|
870
879
|
|
|
871
|
-
|
|
872
|
-
const outdated: PackageInfo[] = []
|
|
873
|
-
const upToDate: PackageInfo[] = []
|
|
880
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
874
881
|
|
|
875
|
-
|
|
876
|
-
for (const [name, version] of Object.entries(allDependencies)) {
|
|
877
|
-
const info = await checkPackage(name, version)
|
|
878
|
-
if (info) {
|
|
879
|
-
results.push(info)
|
|
880
|
-
if (info.outdated) {
|
|
881
|
-
outdated.push(info)
|
|
882
|
-
} else {
|
|
883
|
-
upToDate.push(info)
|
|
884
|
-
}
|
|
885
|
-
}
|
|
882
|
+
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
886
883
|
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
if (upToDate.length > 0) {
|
|
890
|
-
log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
|
|
891
|
-
upToDate.forEach((pkg) => {
|
|
892
|
-
log(\` \${pkg.name}: \${pkg.current}\`, 'green')
|
|
893
|
-
})
|
|
884
|
+
}
|
|
885
|
+
`;
|
|
894
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Generate Route Provider
|
|
889
|
+
*/
|
|
890
|
+
static generateRouteProvider(routePath = "../../routes/api", importType = "default") {
|
|
891
|
+
const importStatement = importType === "default" ? `import routes from '${routePath}'` : `import { registerApiRoutes } from '${routePath}'`;
|
|
892
|
+
const routeCall = importType === "default" ? "routes(core.router)" : "registerApiRoutes(core.router)";
|
|
893
|
+
return `/**
|
|
894
|
+
* Route Service Provider
|
|
895
|
+
*/
|
|
895
896
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
outdated.forEach((pkg) => {
|
|
899
|
-
log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
|
|
900
|
-
})
|
|
901
|
-
}
|
|
897
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
898
|
+
${importStatement}
|
|
902
899
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
|
|
906
|
-
log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
|
|
907
|
-
log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
|
|
900
|
+
export class RouteProvider extends ServiceProvider {
|
|
901
|
+
register(_container: Container): void {}
|
|
908
902
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
log(' bun update', 'yellow')
|
|
913
|
-
process.exit(1)
|
|
914
|
-
} else {
|
|
915
|
-
log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
|
|
916
|
-
process.exit(0)
|
|
903
|
+
boot(core: PlanetCore): void {
|
|
904
|
+
${routeCall}
|
|
905
|
+
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
917
906
|
}
|
|
918
907
|
}
|
|
919
|
-
|
|
920
|
-
main().catch((error) => {
|
|
921
|
-
log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
|
|
922
|
-
process.exit(1)
|
|
923
|
-
})
|
|
924
908
|
`;
|
|
925
909
|
}
|
|
926
910
|
/**
|
|
927
|
-
* Generate
|
|
911
|
+
* Generate Providers Index
|
|
928
912
|
*/
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
# \u984F\u8272\u5B9A\u7FA9
|
|
938
|
-
GREEN='\\033[0;32m'
|
|
939
|
-
YELLOW='\\033[1;33m'
|
|
940
|
-
RED='\\033[0;31m'
|
|
941
|
-
BLUE='\\033[0;34m'
|
|
942
|
-
NC='\\033[0m' # No Color
|
|
943
|
-
|
|
944
|
-
echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
|
|
945
|
-
|
|
946
|
-
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
947
|
-
if [ ! -f "package.json" ]; then
|
|
948
|
-
echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
|
|
949
|
-
exit 1
|
|
950
|
-
fi
|
|
951
|
-
|
|
952
|
-
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
953
|
-
if ! command -v bun &> /dev/null; then
|
|
954
|
-
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
955
|
-
exit 1
|
|
956
|
-
fi
|
|
957
|
-
|
|
958
|
-
# 1. \u985E\u578B\u6AA2\u67E5
|
|
959
|
-
echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
960
|
-
if bun run typecheck; then
|
|
961
|
-
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
962
|
-
else
|
|
963
|
-
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
964
|
-
exit 1
|
|
965
|
-
fi
|
|
966
|
-
|
|
967
|
-
# 2. \u57F7\u884C\u6E2C\u8A66
|
|
968
|
-
echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
969
|
-
if bun test; then
|
|
970
|
-
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
971
|
-
else
|
|
972
|
-
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
973
|
-
exit 1
|
|
974
|
-
fi
|
|
975
|
-
|
|
976
|
-
# 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
|
|
977
|
-
echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
|
|
978
|
-
if bun run check:deps; then
|
|
979
|
-
echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
|
|
980
|
-
else
|
|
981
|
-
echo -e "\${YELLOW}\u26A0 \u4F9D\u8CF4\u6AA2\u67E5\u6709\u8B66\u544A\uFF08\u67D0\u4E9B\u5957\u4EF6\u53EF\u80FD\u9700\u8981\u66F4\u65B0\uFF09\${NC}\\n"
|
|
982
|
-
fi
|
|
983
|
-
|
|
984
|
-
echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
|
|
913
|
+
static generateProvidersIndex(providers = ["AppServiceProvider", "MiddlewareProvider", "RouteProvider"]) {
|
|
914
|
+
const exports2 = providers.map((p) => `export { ${p} } from './${p}'`).join("\n");
|
|
915
|
+
return `/**
|
|
916
|
+
* Application Service Providers
|
|
917
|
+
*/
|
|
918
|
+
|
|
919
|
+
${exports2}
|
|
985
920
|
`;
|
|
986
921
|
}
|
|
987
922
|
/**
|
|
988
|
-
* Generate
|
|
923
|
+
* Generate Repository Service Provider
|
|
989
924
|
*/
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
1017
|
-
if [ ! -f "package.json" ]; then
|
|
1018
|
-
echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
|
|
1019
|
-
exit 1
|
|
1020
|
-
fi
|
|
1021
|
-
|
|
1022
|
-
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
1023
|
-
if ! command -v bun &> /dev/null; then
|
|
1024
|
-
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
1025
|
-
exit 1
|
|
1026
|
-
fi
|
|
1027
|
-
|
|
1028
|
-
# 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
|
|
1029
|
-
echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
1030
|
-
if bun run typecheck; then
|
|
1031
|
-
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
1032
|
-
else
|
|
1033
|
-
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
1034
|
-
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
1035
|
-
exit 1
|
|
1036
|
-
fi
|
|
1037
|
-
|
|
1038
|
-
# 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
|
|
1039
|
-
echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
1040
|
-
if bun test; then
|
|
1041
|
-
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
1042
|
-
else
|
|
1043
|
-
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
1044
|
-
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
1045
|
-
exit 1
|
|
1046
|
-
fi
|
|
1047
|
-
|
|
1048
|
-
echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
|
|
925
|
+
static generateRepositoryServiceProvider(repositories = [], additionalServices = []) {
|
|
926
|
+
const repositoryRegistrations = repositories.map((repo) => ` container.singleton('${repo}', () => new ${repo}())`).join("\n");
|
|
927
|
+
const serviceRegistrations = additionalServices.map((service) => ` container.singleton('${service}', () => new ${service}())`).join("\n");
|
|
928
|
+
const imports = [
|
|
929
|
+
...repositories.map(
|
|
930
|
+
(repo) => `import { ${repo} } from '../Persistence/Repositories/${repo}'`
|
|
931
|
+
),
|
|
932
|
+
...additionalServices.map(
|
|
933
|
+
(service) => `import { ${service} } from '../ExternalServices/${service}'`
|
|
934
|
+
)
|
|
935
|
+
].join("\n");
|
|
936
|
+
return `/**
|
|
937
|
+
* Repository Service Provider
|
|
938
|
+
*
|
|
939
|
+
* Binds repository interfaces to implementations.
|
|
940
|
+
*/
|
|
941
|
+
|
|
942
|
+
import { ServiceProvider, type Container } from '@gravito/core'
|
|
943
|
+
${imports}
|
|
944
|
+
|
|
945
|
+
export class RepositoryServiceProvider extends ServiceProvider {
|
|
946
|
+
register(container: Container): void {
|
|
947
|
+
${repositoryRegistrations || " // Bind repositories here"}
|
|
948
|
+
${serviceRegistrations || " // Bind external services here"}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
1049
951
|
`;
|
|
1050
952
|
}
|
|
1051
953
|
/**
|
|
1052
|
-
* Generate
|
|
954
|
+
* Generate Database Provider
|
|
1053
955
|
*/
|
|
1054
|
-
|
|
1055
|
-
return
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
## \u5FEB\u901F\u958B\u59CB
|
|
1060
|
-
|
|
1061
|
-
### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
|
|
1062
|
-
\`\`\`bash
|
|
1063
|
-
bun run validate
|
|
1064
|
-
\`\`\`
|
|
1065
|
-
|
|
1066
|
-
### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
|
|
1067
|
-
\`\`\`bash
|
|
1068
|
-
# \u985E\u578B\u6AA2\u67E5
|
|
1069
|
-
bun run typecheck
|
|
1070
|
-
|
|
1071
|
-
# \u6E2C\u8A66
|
|
1072
|
-
bun run test
|
|
1073
|
-
|
|
1074
|
-
# \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
1075
|
-
bun run check:deps
|
|
1076
|
-
\`\`\`
|
|
1077
|
-
|
|
1078
|
-
## \u53EF\u7528\u547D\u4EE4
|
|
1079
|
-
|
|
1080
|
-
### Package.json \u8173\u672C
|
|
1081
|
-
|
|
1082
|
-
| \u547D\u4EE4 | \u8AAA\u660E |
|
|
1083
|
-
|------|------|
|
|
1084
|
-
| \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
|
|
1085
|
-
| \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
|
|
1086
|
-
| \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
|
|
1087
|
-
| \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
|
|
1088
|
-
| \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
|
|
1089
|
-
| \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
|
|
1090
|
-
|
|
1091
|
-
### Shell \u8173\u672C
|
|
1092
|
-
|
|
1093
|
-
| \u8173\u672C | \u8AAA\u660E |
|
|
1094
|
-
|------|------|
|
|
1095
|
-
| \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
|
|
1096
|
-
| \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
|
|
1097
|
-
|
|
1098
|
-
## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
1099
|
-
|
|
1100
|
-
\u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
|
|
1101
|
-
|
|
1102
|
-
\`\`\`bash
|
|
1103
|
-
# \u5B89\u88DD pre-commit hook
|
|
1104
|
-
ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
|
|
1105
|
-
|
|
1106
|
-
# \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
|
|
1107
|
-
cp scripts/pre-commit.sh .git/hooks/pre-commit
|
|
1108
|
-
chmod +x .git/hooks/pre-commit
|
|
1109
|
-
\`\`\`
|
|
1110
|
-
|
|
1111
|
-
**\u529F\u80FD\uFF1A**
|
|
1112
|
-
- \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
|
|
1113
|
-
- \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
|
|
1114
|
-
- \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
|
|
1115
|
-
|
|
1116
|
-
**\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
|
|
1117
|
-
\`\`\`bash
|
|
1118
|
-
git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
|
|
1119
|
-
\`\`\`
|
|
1120
|
-
|
|
1121
|
-
## \u6AA2\u67E5\u9805\u76EE
|
|
1122
|
-
|
|
1123
|
-
### 1. \u985E\u578B\u6AA2\u67E5
|
|
1124
|
-
- \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
|
|
1125
|
-
- \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
|
|
1126
|
-
|
|
1127
|
-
### 2. \u6E2C\u8A66
|
|
1128
|
-
- \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
|
|
1129
|
-
- \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
|
|
1130
|
-
|
|
1131
|
-
### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
|
|
1132
|
-
- \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
|
|
1133
|
-
- \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
1134
|
-
- \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
|
|
1135
|
-
|
|
1136
|
-
## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
|
|
1137
|
-
|
|
1138
|
-
### \u958B\u767C\u6642
|
|
1139
|
-
1. \u958B\u767C\u529F\u80FD
|
|
1140
|
-
2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
|
|
1141
|
-
3. \u4FEE\u6B63\u554F\u984C
|
|
1142
|
-
4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
|
|
1143
|
-
|
|
1144
|
-
### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
1145
|
-
1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
|
|
1146
|
-
2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
|
|
1147
|
-
3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
|
|
1148
|
-
4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
|
|
1149
|
-
|
|
1150
|
-
## \u6A94\u6848\u7D50\u69CB
|
|
1151
|
-
|
|
1152
|
-
\`\`\`
|
|
1153
|
-
${context.nameKebabCase}/
|
|
1154
|
-
\u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
|
|
1155
|
-
\u251C\u2500\u2500 scripts/
|
|
1156
|
-
\u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
|
|
1157
|
-
\u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
1158
|
-
\u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
|
|
1159
|
-
\u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
|
|
1160
|
-
\`\`\`
|
|
1161
|
-
|
|
1162
|
-
## \u6CE8\u610F\u4E8B\u9805
|
|
1163
|
-
|
|
1164
|
-
1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
|
|
1165
|
-
2. **\u6E2C\u8A66\u6642\u9593**\uFF1A\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\uFF0C\u53EF\u4EE5\u7DE8\u8F2F \`pre-commit.sh\` \u8A3B\u89E3\u6389\u6E2C\u8A66\u90E8\u5206
|
|
1166
|
-
3. **\u985E\u578B\u932F\u8AA4**\uFF1A\u5C08\u6848\u4E2D\u53EF\u80FD\u9084\u6709\u4E00\u4E9B\u65E2\u6709\u7684\u985E\u578B\u932F\u8AA4\uFF0C\u5EFA\u8B70\u9010\u6B65\u4FEE\u6B63
|
|
1167
|
-
|
|
1168
|
-
## \u6545\u969C\u6392\u9664
|
|
1169
|
-
|
|
1170
|
-
### \u6AA2\u67E5\u5931\u6557
|
|
1171
|
-
1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
|
|
1172
|
-
2. \u4FEE\u6B63\u554F\u984C
|
|
1173
|
-
3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
|
|
956
|
+
static generateDatabaseProvider() {
|
|
957
|
+
return `/**
|
|
958
|
+
* Database Service Provider
|
|
959
|
+
*/
|
|
1174
960
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
\`\`\`bash
|
|
1178
|
-
git commit --no-verify
|
|
1179
|
-
\`\`\`
|
|
961
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
962
|
+
import { OrbitAtlas } from '@gravito/atlas'
|
|
1180
963
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
\`\`\`
|
|
1185
|
-
`;
|
|
964
|
+
export class DatabaseProvider extends ServiceProvider {
|
|
965
|
+
register(_container: Container): void {
|
|
966
|
+
// Register database connections
|
|
1186
967
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
if (this.config.verbose) {
|
|
1192
|
-
console.log(message);
|
|
1193
|
-
}
|
|
968
|
+
|
|
969
|
+
boot(core: PlanetCore): void {
|
|
970
|
+
// Initialize database
|
|
971
|
+
core.logger.info('\u{1F5C4}\uFE0F Database initialized')
|
|
1194
972
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
*/
|
|
1198
|
-
static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
|
|
1199
|
-
const now = /* @__PURE__ */ new Date();
|
|
1200
|
-
const pascalCase = name.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toUpperCase());
|
|
1201
|
-
const camelCase = pascalCase.replace(/^./, (c) => c.toLowerCase());
|
|
1202
|
-
const snakeCase = name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[-\s]+/g, "_");
|
|
1203
|
-
const kebabCase = name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "").replace(/[_\s]+/g, "-");
|
|
1204
|
-
return {
|
|
1205
|
-
name,
|
|
1206
|
-
namePascalCase: pascalCase,
|
|
1207
|
-
nameCamelCase: camelCase,
|
|
1208
|
-
nameSnakeCase: snakeCase,
|
|
1209
|
-
nameKebabCase: kebabCase,
|
|
1210
|
-
targetDir,
|
|
1211
|
-
architecture,
|
|
1212
|
-
packageManager,
|
|
1213
|
-
year: now.getFullYear().toString(),
|
|
1214
|
-
date: now.toISOString().split("T")[0] ?? now.toISOString().slice(0, 10),
|
|
1215
|
-
...extra
|
|
1216
|
-
};
|
|
973
|
+
}
|
|
974
|
+
`;
|
|
1217
975
|
}
|
|
1218
976
|
};
|
|
1219
977
|
|
|
@@ -1467,57 +1225,22 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
|
|
|
1467
1225
|
];
|
|
1468
1226
|
}
|
|
1469
1227
|
// ─────────────────────────────────────────────────────────────
|
|
1470
|
-
// Config Generators (
|
|
1228
|
+
// Config Generators (using shared ConfigGenerator)
|
|
1471
1229
|
// ─────────────────────────────────────────────────────────────
|
|
1472
1230
|
generateAppConfig(context) {
|
|
1473
|
-
return
|
|
1474
|
-
name: process.env.APP_NAME ?? '${context.name}',
|
|
1475
|
-
env: process.env.APP_ENV ?? 'development',
|
|
1476
|
-
debug: process.env.APP_DEBUG === 'true',
|
|
1477
|
-
url: process.env.APP_URL ?? 'http://localhost:3000',
|
|
1478
|
-
key: process.env.APP_KEY,
|
|
1479
|
-
}
|
|
1480
|
-
`;
|
|
1231
|
+
return ConfigGenerator.generateSimpleAppConfig(context);
|
|
1481
1232
|
}
|
|
1482
1233
|
generateDatabaseConfig() {
|
|
1483
|
-
return
|
|
1484
|
-
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
1485
|
-
connections: {
|
|
1486
|
-
sqlite: {
|
|
1487
|
-
driver: 'sqlite',
|
|
1488
|
-
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
1489
|
-
},
|
|
1490
|
-
},
|
|
1491
|
-
}
|
|
1492
|
-
`;
|
|
1234
|
+
return ConfigGenerator.generateSimpleDatabaseConfig();
|
|
1493
1235
|
}
|
|
1494
1236
|
generateAuthConfig() {
|
|
1495
|
-
return
|
|
1496
|
-
defaults: { guard: 'web' },
|
|
1497
|
-
guards: {
|
|
1498
|
-
web: { driver: 'session', provider: 'users' },
|
|
1499
|
-
api: { driver: 'token', provider: 'users' },
|
|
1500
|
-
},
|
|
1501
|
-
}
|
|
1502
|
-
`;
|
|
1237
|
+
return ConfigGenerator.generateAuthConfig();
|
|
1503
1238
|
}
|
|
1504
1239
|
generateCacheConfig() {
|
|
1505
|
-
return
|
|
1506
|
-
default: process.env.CACHE_DRIVER ?? 'memory',
|
|
1507
|
-
stores: {
|
|
1508
|
-
memory: { driver: 'memory' },
|
|
1509
|
-
},
|
|
1510
|
-
}
|
|
1511
|
-
`;
|
|
1240
|
+
return ConfigGenerator.generateCacheConfig();
|
|
1512
1241
|
}
|
|
1513
1242
|
generateLoggingConfig() {
|
|
1514
|
-
return
|
|
1515
|
-
default: process.env.LOG_CHANNEL ?? 'console',
|
|
1516
|
-
channels: {
|
|
1517
|
-
console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
|
|
1518
|
-
},
|
|
1519
|
-
}
|
|
1520
|
-
`;
|
|
1243
|
+
return ConfigGenerator.generateLoggingConfig();
|
|
1521
1244
|
}
|
|
1522
1245
|
generateUserEntity() {
|
|
1523
1246
|
return `/**
|
|
@@ -1844,103 +1567,30 @@ export class MailService implements IMailService {
|
|
|
1844
1567
|
`;
|
|
1845
1568
|
}
|
|
1846
1569
|
generateAppServiceProvider(context) {
|
|
1847
|
-
return
|
|
1848
|
-
* App Service Provider
|
|
1849
|
-
*/
|
|
1850
|
-
|
|
1851
|
-
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
1852
|
-
|
|
1853
|
-
export class AppServiceProvider extends ServiceProvider {
|
|
1854
|
-
register(_container: Container): void {
|
|
1855
|
-
// Register application services
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
boot(_core: PlanetCore): void {
|
|
1859
|
-
console.log('${context.name} (Clean Architecture) booted!')
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
`;
|
|
1570
|
+
return ServiceProviderGenerator.generateAppServiceProvider(context, "Clean Architecture");
|
|
1863
1571
|
}
|
|
1864
1572
|
generateRepositoryServiceProvider() {
|
|
1865
|
-
return
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
*/
|
|
1870
|
-
|
|
1871
|
-
import { ServiceProvider, type Container } from '@gravito/core'
|
|
1872
|
-
import { UserRepository } from '../Persistence/Repositories/UserRepository'
|
|
1873
|
-
import { MailService } from '../ExternalServices/MailService'
|
|
1874
|
-
|
|
1875
|
-
export class RepositoryServiceProvider extends ServiceProvider {
|
|
1876
|
-
register(container: Container): void {
|
|
1877
|
-
// Bind repositories
|
|
1878
|
-
container.singleton('userRepository', () => new UserRepository())
|
|
1879
|
-
|
|
1880
|
-
// Bind external services
|
|
1881
|
-
container.singleton('mailService', () => new MailService())
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
`;
|
|
1573
|
+
return ServiceProviderGenerator.generateRepositoryServiceProvider(
|
|
1574
|
+
["UserRepository"],
|
|
1575
|
+
["MailService"]
|
|
1576
|
+
);
|
|
1885
1577
|
}
|
|
1886
1578
|
generateProvidersIndex() {
|
|
1887
|
-
return
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
1894
|
-
export { RouteProvider } from './RouteProvider'
|
|
1895
|
-
`;
|
|
1579
|
+
return ServiceProviderGenerator.generateProvidersIndex([
|
|
1580
|
+
"AppServiceProvider",
|
|
1581
|
+
"RepositoryServiceProvider",
|
|
1582
|
+
"MiddlewareProvider",
|
|
1583
|
+
"RouteProvider"
|
|
1584
|
+
]);
|
|
1896
1585
|
}
|
|
1897
1586
|
generateMiddlewareProvider() {
|
|
1898
|
-
return
|
|
1899
|
-
* Middleware Service Provider
|
|
1900
|
-
*/
|
|
1901
|
-
|
|
1902
|
-
import {
|
|
1903
|
-
ServiceProvider,
|
|
1904
|
-
type Container,
|
|
1905
|
-
type PlanetCore,
|
|
1906
|
-
bodySizeLimit,
|
|
1907
|
-
securityHeaders,
|
|
1908
|
-
} from '@gravito/core'
|
|
1909
|
-
|
|
1910
|
-
export class MiddlewareProvider extends ServiceProvider {
|
|
1911
|
-
register(_container: Container): void {}
|
|
1912
|
-
|
|
1913
|
-
boot(core: PlanetCore): void {
|
|
1914
|
-
const isDev = process.env.NODE_ENV !== 'production'
|
|
1915
|
-
|
|
1916
|
-
core.adapter.use('*', securityHeaders({
|
|
1917
|
-
contentSecurityPolicy: isDev ? false : undefined,
|
|
1918
|
-
}))
|
|
1919
|
-
|
|
1920
|
-
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
1921
|
-
|
|
1922
|
-
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
1923
|
-
}
|
|
1924
|
-
}
|
|
1925
|
-
`;
|
|
1587
|
+
return ServiceProviderGenerator.generateMiddlewareProvider();
|
|
1926
1588
|
}
|
|
1927
1589
|
generateRouteProvider() {
|
|
1928
|
-
return
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
1933
|
-
import { registerApiRoutes } from '../../Interface/Http/Routes/api'
|
|
1934
|
-
|
|
1935
|
-
export class RouteProvider extends ServiceProvider {
|
|
1936
|
-
register(_container: Container): void {}
|
|
1937
|
-
|
|
1938
|
-
boot(core: PlanetCore): void {
|
|
1939
|
-
registerApiRoutes(core.router)
|
|
1940
|
-
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
`;
|
|
1590
|
+
return ServiceProviderGenerator.generateRouteProvider(
|
|
1591
|
+
"../../Interface/Http/Routes/api",
|
|
1592
|
+
"named"
|
|
1593
|
+
);
|
|
1944
1594
|
}
|
|
1945
1595
|
// ─────────────────────────────────────────────────────────────
|
|
1946
1596
|
// Interface Layer
|
|
@@ -2163,7 +1813,7 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
2163
1813
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
2164
1814
|
start: "bun run dist/bootstrap.js",
|
|
2165
1815
|
test: "bun test",
|
|
2166
|
-
typecheck: "tsc --noEmit",
|
|
1816
|
+
typecheck: "bun tsc --noEmit",
|
|
2167
1817
|
check: "bun run typecheck && bun run test",
|
|
2168
1818
|
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2169
1819
|
validate: "bun run check && bun run check:deps",
|
|
@@ -2178,107 +1828,16 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
2178
1828
|
},
|
|
2179
1829
|
devDependencies: {
|
|
2180
1830
|
"bun-types": "latest",
|
|
2181
|
-
typescript: "^5.
|
|
1831
|
+
typescript: "^5.9.3"
|
|
2182
1832
|
}
|
|
2183
1833
|
};
|
|
2184
1834
|
return JSON.stringify(pkg, null, 2);
|
|
2185
1835
|
}
|
|
2186
1836
|
};
|
|
2187
1837
|
|
|
2188
|
-
// src/generators/
|
|
2189
|
-
var
|
|
2190
|
-
|
|
2191
|
-
return "ddd";
|
|
2192
|
-
}
|
|
2193
|
-
get displayName() {
|
|
2194
|
-
return "Domain-Driven Design (DDD)";
|
|
2195
|
-
}
|
|
2196
|
-
get description() {
|
|
2197
|
-
return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
|
|
2198
|
-
}
|
|
2199
|
-
getDirectoryStructure(context) {
|
|
2200
|
-
return [
|
|
2201
|
-
{
|
|
2202
|
-
type: "directory",
|
|
2203
|
-
name: "config",
|
|
2204
|
-
children: [
|
|
2205
|
-
{ type: "file", name: "app.ts", content: this.generateAppConfig(context) },
|
|
2206
|
-
{ type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
|
|
2207
|
-
{ type: "file", name: "modules.ts", content: this.generateModulesConfig() },
|
|
2208
|
-
{ type: "file", name: "cache.ts", content: this.generateCacheConfig() },
|
|
2209
|
-
{ type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
|
|
2210
|
-
]
|
|
2211
|
-
},
|
|
2212
|
-
{
|
|
2213
|
-
type: "directory",
|
|
2214
|
-
name: "src",
|
|
2215
|
-
children: [
|
|
2216
|
-
// Bootstrap - Application startup and configuration
|
|
2217
|
-
this.generateBootstrapDirectory(context),
|
|
2218
|
-
// Shared - Cross-module shared components
|
|
2219
|
-
this.generateShared(),
|
|
2220
|
-
// Modules - Bounded Contexts
|
|
2221
|
-
{
|
|
2222
|
-
type: "directory",
|
|
2223
|
-
name: "Modules",
|
|
2224
|
-
children: [
|
|
2225
|
-
this.generateModule("Ordering", context),
|
|
2226
|
-
this.generateModule("Catalog", context)
|
|
2227
|
-
]
|
|
2228
|
-
},
|
|
2229
|
-
{ type: "file", name: "main.ts", content: this.generateMainEntry(context) }
|
|
2230
|
-
]
|
|
2231
|
-
},
|
|
2232
|
-
{
|
|
2233
|
-
type: "directory",
|
|
2234
|
-
name: "tests",
|
|
2235
|
-
children: [
|
|
2236
|
-
{
|
|
2237
|
-
type: "directory",
|
|
2238
|
-
name: "Modules",
|
|
2239
|
-
children: [
|
|
2240
|
-
{
|
|
2241
|
-
type: "directory",
|
|
2242
|
-
name: "Ordering",
|
|
2243
|
-
children: [
|
|
2244
|
-
{
|
|
2245
|
-
type: "directory",
|
|
2246
|
-
name: "Unit",
|
|
2247
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2248
|
-
},
|
|
2249
|
-
{
|
|
2250
|
-
type: "directory",
|
|
2251
|
-
name: "Integration",
|
|
2252
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2253
|
-
}
|
|
2254
|
-
]
|
|
2255
|
-
},
|
|
2256
|
-
{
|
|
2257
|
-
type: "directory",
|
|
2258
|
-
name: "Catalog",
|
|
2259
|
-
children: [
|
|
2260
|
-
{
|
|
2261
|
-
type: "directory",
|
|
2262
|
-
name: "Unit",
|
|
2263
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2264
|
-
}
|
|
2265
|
-
]
|
|
2266
|
-
}
|
|
2267
|
-
]
|
|
2268
|
-
},
|
|
2269
|
-
{
|
|
2270
|
-
type: "directory",
|
|
2271
|
-
name: "Shared",
|
|
2272
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2273
|
-
}
|
|
2274
|
-
]
|
|
2275
|
-
}
|
|
2276
|
-
];
|
|
2277
|
-
}
|
|
2278
|
-
// ─────────────────────────────────────────────────────────────
|
|
2279
|
-
// Bootstrap Directory
|
|
2280
|
-
// ─────────────────────────────────────────────────────────────
|
|
2281
|
-
generateBootstrapDirectory(context) {
|
|
1838
|
+
// src/generators/ddd/BootstrapGenerator.ts
|
|
1839
|
+
var BootstrapGenerator = class {
|
|
1840
|
+
generate(context) {
|
|
2282
1841
|
return {
|
|
2283
1842
|
type: "directory",
|
|
2284
1843
|
name: "Bootstrap",
|
|
@@ -2290,207 +1849,33 @@ var DddGenerator = class extends BaseGenerator {
|
|
|
2290
1849
|
]
|
|
2291
1850
|
};
|
|
2292
1851
|
}
|
|
2293
|
-
|
|
2294
|
-
// Shared Directory (replaces SharedKernel with user's structure)
|
|
2295
|
-
// ─────────────────────────────────────────────────────────────
|
|
2296
|
-
generateShared() {
|
|
1852
|
+
generateConfigDirectory(context) {
|
|
2297
1853
|
return {
|
|
2298
1854
|
type: "directory",
|
|
2299
|
-
name: "
|
|
1855
|
+
name: "config",
|
|
2300
1856
|
children: [
|
|
2301
|
-
{
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
type: "directory",
|
|
2307
|
-
name: "ValueObjects",
|
|
2308
|
-
children: [
|
|
2309
|
-
{ type: "file", name: "Id.ts", content: this.generateIdValueObject() },
|
|
2310
|
-
{ type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
|
|
2311
|
-
{ type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
|
|
2312
|
-
]
|
|
2313
|
-
}
|
|
2314
|
-
]
|
|
2315
|
-
},
|
|
2316
|
-
{
|
|
2317
|
-
type: "directory",
|
|
2318
|
-
name: "Infrastructure",
|
|
2319
|
-
children: [
|
|
2320
|
-
{
|
|
2321
|
-
type: "directory",
|
|
2322
|
-
name: "EventBus",
|
|
2323
|
-
children: [
|
|
2324
|
-
{
|
|
2325
|
-
type: "file",
|
|
2326
|
-
name: "EventDispatcher.ts",
|
|
2327
|
-
content: this.generateEventDispatcher()
|
|
2328
|
-
}
|
|
2329
|
-
]
|
|
2330
|
-
}
|
|
2331
|
-
]
|
|
2332
|
-
},
|
|
2333
|
-
{
|
|
2334
|
-
type: "directory",
|
|
2335
|
-
name: "Exceptions",
|
|
2336
|
-
children: [
|
|
2337
|
-
{ type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
|
|
2338
|
-
]
|
|
2339
|
-
}
|
|
1857
|
+
{ type: "file", name: "app.ts", content: this.generateAppConfig(context) },
|
|
1858
|
+
{ type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
|
|
1859
|
+
{ type: "file", name: "modules.ts", content: this.generateModulesConfig() },
|
|
1860
|
+
{ type: "file", name: "cache.ts", content: this.generateCacheConfig() },
|
|
1861
|
+
{ type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
|
|
2340
1862
|
]
|
|
2341
1863
|
};
|
|
2342
1864
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
{
|
|
2357
|
-
type: "directory",
|
|
2358
|
-
name: "Aggregates",
|
|
2359
|
-
children: [
|
|
2360
|
-
{
|
|
2361
|
-
type: "directory",
|
|
2362
|
-
name,
|
|
2363
|
-
children: [
|
|
2364
|
-
{ type: "file", name: `${name}.ts`, content: this.generateAggregate(name) },
|
|
2365
|
-
{
|
|
2366
|
-
type: "file",
|
|
2367
|
-
name: `${name}Status.ts`,
|
|
2368
|
-
content: this.generateAggregateStatus(name)
|
|
2369
|
-
}
|
|
2370
|
-
]
|
|
2371
|
-
}
|
|
2372
|
-
]
|
|
2373
|
-
},
|
|
2374
|
-
{
|
|
2375
|
-
type: "directory",
|
|
2376
|
-
name: "Events",
|
|
2377
|
-
children: [
|
|
2378
|
-
{
|
|
2379
|
-
type: "file",
|
|
2380
|
-
name: `${name}Created.ts`,
|
|
2381
|
-
content: this.generateCreatedEvent(name)
|
|
2382
|
-
}
|
|
2383
|
-
]
|
|
2384
|
-
},
|
|
2385
|
-
{
|
|
2386
|
-
type: "directory",
|
|
2387
|
-
name: "Repositories",
|
|
2388
|
-
children: [
|
|
2389
|
-
{
|
|
2390
|
-
type: "file",
|
|
2391
|
-
name: `I${name}Repository.ts`,
|
|
2392
|
-
content: this.generateRepositoryInterface(name)
|
|
2393
|
-
}
|
|
2394
|
-
]
|
|
2395
|
-
},
|
|
2396
|
-
{
|
|
2397
|
-
type: "directory",
|
|
2398
|
-
name: "Services",
|
|
2399
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2400
|
-
}
|
|
2401
|
-
]
|
|
2402
|
-
},
|
|
2403
|
-
// Application Layer
|
|
2404
|
-
{
|
|
2405
|
-
type: "directory",
|
|
2406
|
-
name: "Application",
|
|
2407
|
-
children: [
|
|
2408
|
-
{
|
|
2409
|
-
type: "directory",
|
|
2410
|
-
name: "Commands",
|
|
2411
|
-
children: [
|
|
2412
|
-
{
|
|
2413
|
-
type: "directory",
|
|
2414
|
-
name: `Create${name}`,
|
|
2415
|
-
children: [
|
|
2416
|
-
{
|
|
2417
|
-
type: "file",
|
|
2418
|
-
name: `Create${name}Command.ts`,
|
|
2419
|
-
content: this.generateCommand(name)
|
|
2420
|
-
},
|
|
2421
|
-
{
|
|
2422
|
-
type: "file",
|
|
2423
|
-
name: `Create${name}Handler.ts`,
|
|
2424
|
-
content: this.generateCommandHandler(name)
|
|
2425
|
-
}
|
|
2426
|
-
]
|
|
2427
|
-
}
|
|
2428
|
-
]
|
|
2429
|
-
},
|
|
2430
|
-
{
|
|
2431
|
-
type: "directory",
|
|
2432
|
-
name: "Queries",
|
|
2433
|
-
children: [
|
|
2434
|
-
{
|
|
2435
|
-
type: "directory",
|
|
2436
|
-
name: `Get${name}ById`,
|
|
2437
|
-
children: [
|
|
2438
|
-
{
|
|
2439
|
-
type: "file",
|
|
2440
|
-
name: `Get${name}ByIdQuery.ts`,
|
|
2441
|
-
content: this.generateQuery(name)
|
|
2442
|
-
},
|
|
2443
|
-
{
|
|
2444
|
-
type: "file",
|
|
2445
|
-
name: `Get${name}ByIdHandler.ts`,
|
|
2446
|
-
content: this.generateQueryHandler(name)
|
|
2447
|
-
}
|
|
2448
|
-
]
|
|
2449
|
-
}
|
|
2450
|
-
]
|
|
2451
|
-
},
|
|
2452
|
-
{
|
|
2453
|
-
type: "directory",
|
|
2454
|
-
name: "DTOs",
|
|
2455
|
-
children: [{ type: "file", name: `${name}DTO.ts`, content: this.generateDTO(name) }]
|
|
2456
|
-
}
|
|
2457
|
-
]
|
|
2458
|
-
},
|
|
2459
|
-
// Infrastructure Layer
|
|
2460
|
-
{
|
|
2461
|
-
type: "directory",
|
|
2462
|
-
name: "Infrastructure",
|
|
2463
|
-
children: [
|
|
2464
|
-
{
|
|
2465
|
-
type: "directory",
|
|
2466
|
-
name: "Persistence",
|
|
2467
|
-
children: [
|
|
2468
|
-
{
|
|
2469
|
-
type: "file",
|
|
2470
|
-
name: `${name}Repository.ts`,
|
|
2471
|
-
content: this.generateRepository(name)
|
|
2472
|
-
}
|
|
2473
|
-
]
|
|
2474
|
-
},
|
|
2475
|
-
{
|
|
2476
|
-
type: "directory",
|
|
2477
|
-
name: "Providers",
|
|
2478
|
-
children: [
|
|
2479
|
-
{
|
|
2480
|
-
type: "file",
|
|
2481
|
-
name: `${name}ServiceProvider.ts`,
|
|
2482
|
-
content: this.generateModuleServiceProvider(name, context)
|
|
2483
|
-
}
|
|
2484
|
-
]
|
|
2485
|
-
}
|
|
2486
|
-
]
|
|
2487
|
-
}
|
|
2488
|
-
]
|
|
2489
|
-
};
|
|
1865
|
+
generateMainEntry(_context) {
|
|
1866
|
+
return `/**
|
|
1867
|
+
* Application Entry Point
|
|
1868
|
+
*
|
|
1869
|
+
* Start the HTTP server.
|
|
1870
|
+
*/
|
|
1871
|
+
|
|
1872
|
+
import { createApp } from './Bootstrap/app'
|
|
1873
|
+
|
|
1874
|
+
const app = await createApp()
|
|
1875
|
+
|
|
1876
|
+
export default app.liftoff()
|
|
1877
|
+
`;
|
|
2490
1878
|
}
|
|
2491
|
-
// ─────────────────────────────────────────────────────────────
|
|
2492
|
-
// Bootstrap File Generators
|
|
2493
|
-
// ─────────────────────────────────────────────────────────────
|
|
2494
1879
|
generateBootstrapApp(_context) {
|
|
2495
1880
|
return `/**
|
|
2496
1881
|
* Application Bootstrap
|
|
@@ -2616,20 +2001,6 @@ export function registerRoutes(router: any): void {
|
|
|
2616
2001
|
// Catalog module
|
|
2617
2002
|
router.get('/api/products', (c: any) => c.json({ message: 'Product list' }))
|
|
2618
2003
|
}
|
|
2619
|
-
`;
|
|
2620
|
-
}
|
|
2621
|
-
generateMainEntry(_context) {
|
|
2622
|
-
return `/**
|
|
2623
|
-
* Application Entry Point
|
|
2624
|
-
*
|
|
2625
|
-
* Start the HTTP server.
|
|
2626
|
-
*/
|
|
2627
|
-
|
|
2628
|
-
import { createApp } from './Bootstrap/app'
|
|
2629
|
-
|
|
2630
|
-
const app = await createApp()
|
|
2631
|
-
|
|
2632
|
-
export default app.liftoff()
|
|
2633
2004
|
`;
|
|
2634
2005
|
}
|
|
2635
2006
|
generateModulesConfig() {
|
|
@@ -2660,60 +2031,6 @@ export default {
|
|
|
2660
2031
|
}
|
|
2661
2032
|
`;
|
|
2662
2033
|
}
|
|
2663
|
-
generateModuleServiceProvider(name, _context) {
|
|
2664
|
-
return `/**
|
|
2665
|
-
* ${name} Service Provider
|
|
2666
|
-
*/
|
|
2667
|
-
|
|
2668
|
-
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
2669
|
-
import { ${name}Repository } from '../Persistence/${name}Repository'
|
|
2670
|
-
|
|
2671
|
-
export class ${name}ServiceProvider extends ServiceProvider {
|
|
2672
|
-
register(container: Container): void {
|
|
2673
|
-
container.singleton('${name.toLowerCase()}Repository', () => new ${name}Repository())
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
boot(_core: PlanetCore): void {
|
|
2677
|
-
console.log('[${name}] Module loaded')
|
|
2678
|
-
}
|
|
2679
|
-
}
|
|
2680
|
-
`;
|
|
2681
|
-
}
|
|
2682
|
-
/**
|
|
2683
|
-
* Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
|
|
2684
|
-
*/
|
|
2685
|
-
generatePackageJson(context) {
|
|
2686
|
-
const pkg = {
|
|
2687
|
-
name: context.nameKebabCase,
|
|
2688
|
-
version: "0.1.0",
|
|
2689
|
-
type: "module",
|
|
2690
|
-
scripts: {
|
|
2691
|
-
dev: "bun run --watch src/main.ts",
|
|
2692
|
-
build: "bun build ./src/main.ts --outdir ./dist --target bun",
|
|
2693
|
-
start: "bun run dist/main.js",
|
|
2694
|
-
test: "bun test",
|
|
2695
|
-
typecheck: "tsc --noEmit",
|
|
2696
|
-
check: "bun run typecheck && bun run test",
|
|
2697
|
-
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2698
|
-
validate: "bun run check && bun run check:deps",
|
|
2699
|
-
precommit: "bun run validate",
|
|
2700
|
-
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
2701
|
-
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
2702
|
-
},
|
|
2703
|
-
dependencies: {
|
|
2704
|
-
"@gravito/core": "^1.0.0-beta.5",
|
|
2705
|
-
"@gravito/enterprise": "workspace:*"
|
|
2706
|
-
},
|
|
2707
|
-
devDependencies: {
|
|
2708
|
-
"bun-types": "latest",
|
|
2709
|
-
typescript: "^5.0.0"
|
|
2710
|
-
}
|
|
2711
|
-
};
|
|
2712
|
-
return JSON.stringify(pkg, null, 2);
|
|
2713
|
-
}
|
|
2714
|
-
// ─────────────────────────────────────────────────────────────
|
|
2715
|
-
// Config Generators
|
|
2716
|
-
// ─────────────────────────────────────────────────────────────
|
|
2717
2034
|
generateAppConfig(context) {
|
|
2718
2035
|
return `export default {
|
|
2719
2036
|
name: process.env.APP_NAME ?? '${context.name}',
|
|
@@ -2748,159 +2065,155 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
2748
2065
|
}
|
|
2749
2066
|
`;
|
|
2750
2067
|
}
|
|
2751
|
-
|
|
2752
|
-
// Shared Kernel Files
|
|
2753
|
-
// ─────────────────────────────────────────────────────────────
|
|
2754
|
-
generateIdValueObject() {
|
|
2755
|
-
return `/**
|
|
2756
|
-
* ID Value Object
|
|
2757
|
-
*
|
|
2758
|
-
* Shared identifier across all contexts.
|
|
2759
|
-
*/
|
|
2760
|
-
|
|
2761
|
-
import { ValueObject } from '@gravito/enterprise'
|
|
2762
|
-
|
|
2763
|
-
interface IdProps {
|
|
2764
|
-
value: string
|
|
2765
|
-
}
|
|
2766
|
-
|
|
2767
|
-
export class Id extends ValueObject<IdProps> {
|
|
2768
|
-
private constructor(value: string) {
|
|
2769
|
-
super({ value })
|
|
2770
|
-
}
|
|
2771
|
-
|
|
2772
|
-
static create(): Id {
|
|
2773
|
-
return new Id(crypto.randomUUID())
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
|
-
static from(value: string): Id {
|
|
2777
|
-
if (!value) throw new Error('Id cannot be empty')
|
|
2778
|
-
return new Id(value)
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
get value(): string {
|
|
2782
|
-
return this.props.value
|
|
2783
|
-
}
|
|
2784
|
-
|
|
2785
|
-
toString(): string {
|
|
2786
|
-
return this.props.value
|
|
2787
|
-
}
|
|
2788
|
-
}
|
|
2789
|
-
`;
|
|
2790
|
-
}
|
|
2791
|
-
generateMoneyValueObject() {
|
|
2792
|
-
return `/**
|
|
2793
|
-
* Money Value Object
|
|
2794
|
-
*/
|
|
2795
|
-
|
|
2796
|
-
import { ValueObject } from '@gravito/enterprise'
|
|
2797
|
-
|
|
2798
|
-
interface MoneyProps {
|
|
2799
|
-
amount: number
|
|
2800
|
-
currency: string
|
|
2801
|
-
}
|
|
2802
|
-
|
|
2803
|
-
export class Money extends ValueObject<MoneyProps> {
|
|
2804
|
-
constructor(amount: number, currency: string = 'USD') {
|
|
2805
|
-
if (amount < 0) throw new Error('Amount cannot be negative')
|
|
2806
|
-
super({ amount, currency })
|
|
2807
|
-
}
|
|
2808
|
-
|
|
2809
|
-
get amount(): number {
|
|
2810
|
-
return this.props.amount
|
|
2811
|
-
}
|
|
2812
|
-
|
|
2813
|
-
get currency(): string {
|
|
2814
|
-
return this.props.currency
|
|
2815
|
-
}
|
|
2816
|
-
|
|
2817
|
-
add(other: Money): Money {
|
|
2818
|
-
this.assertSameCurrency(other)
|
|
2819
|
-
return new Money(this.amount + other.amount, this.currency)
|
|
2820
|
-
}
|
|
2821
|
-
|
|
2822
|
-
subtract(other: Money): Money {
|
|
2823
|
-
this.assertSameCurrency(other)
|
|
2824
|
-
return new Money(this.amount - other.amount, this.currency)
|
|
2825
|
-
}
|
|
2826
|
-
|
|
2827
|
-
private assertSameCurrency(other: Money): void {
|
|
2828
|
-
if (this.currency !== other.currency) {
|
|
2829
|
-
throw new Error('Cannot operate on different currencies')
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
|
-
`;
|
|
2834
|
-
}
|
|
2835
|
-
generateEmailValueObject() {
|
|
2836
|
-
return `/**
|
|
2837
|
-
* Email Value Object
|
|
2838
|
-
*/
|
|
2839
|
-
|
|
2840
|
-
import { ValueObject } from '@gravito/enterprise'
|
|
2841
|
-
|
|
2842
|
-
interface EmailProps {
|
|
2843
|
-
value: string
|
|
2844
|
-
}
|
|
2845
|
-
|
|
2846
|
-
export class Email extends ValueObject<EmailProps> {
|
|
2847
|
-
private constructor(value: string) {
|
|
2848
|
-
super({ value: value.toLowerCase().trim() })
|
|
2849
|
-
}
|
|
2850
|
-
|
|
2851
|
-
static create(email: string): Email {
|
|
2852
|
-
if (!Email.isValid(email)) {
|
|
2853
|
-
throw new Error(\`Invalid email: \${email}\`)
|
|
2854
|
-
}
|
|
2855
|
-
return new Email(email)
|
|
2856
|
-
}
|
|
2857
|
-
|
|
2858
|
-
static isValid(email: string): boolean {
|
|
2859
|
-
return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)
|
|
2860
|
-
}
|
|
2861
|
-
|
|
2862
|
-
get value(): string {
|
|
2863
|
-
return this.props.value
|
|
2864
|
-
}
|
|
2865
|
-
}
|
|
2866
|
-
`;
|
|
2867
|
-
}
|
|
2868
|
-
generateEventDispatcher() {
|
|
2869
|
-
return `/**
|
|
2870
|
-
* Event Dispatcher
|
|
2871
|
-
*/
|
|
2872
|
-
|
|
2873
|
-
import type { DomainEvent } from '@gravito/enterprise'
|
|
2874
|
-
|
|
2875
|
-
type EventHandler = (event: DomainEvent) => void | Promise<void>
|
|
2876
|
-
|
|
2877
|
-
export class EventDispatcher {
|
|
2878
|
-
private handlers: Map<string, EventHandler[]> = new Map()
|
|
2879
|
-
|
|
2880
|
-
subscribe(eventName: string, handler: EventHandler): void {
|
|
2881
|
-
const handlers = this.handlers.get(eventName) ?? []
|
|
2882
|
-
handlers.push(handler)
|
|
2883
|
-
this.handlers.set(eventName, handlers)
|
|
2884
|
-
}
|
|
2885
|
-
|
|
2886
|
-
async dispatch(event: DomainEvent): Promise<void> {
|
|
2887
|
-
const handlers = this.handlers.get(event.eventName) ?? []
|
|
2888
|
-
for (const handler of handlers) {
|
|
2889
|
-
await handler(event)
|
|
2890
|
-
}
|
|
2891
|
-
}
|
|
2068
|
+
};
|
|
2892
2069
|
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2070
|
+
// src/generators/ddd/ModuleGenerator.ts
|
|
2071
|
+
var ModuleGenerator = class {
|
|
2072
|
+
generate(name, context) {
|
|
2073
|
+
return {
|
|
2074
|
+
type: "directory",
|
|
2075
|
+
name,
|
|
2076
|
+
children: [
|
|
2077
|
+
// Domain Layer
|
|
2078
|
+
{
|
|
2079
|
+
type: "directory",
|
|
2080
|
+
name: "Domain",
|
|
2081
|
+
children: [
|
|
2082
|
+
{
|
|
2083
|
+
type: "directory",
|
|
2084
|
+
name: "Aggregates",
|
|
2085
|
+
children: [
|
|
2086
|
+
{
|
|
2087
|
+
type: "directory",
|
|
2088
|
+
name,
|
|
2089
|
+
children: [
|
|
2090
|
+
{ type: "file", name: `${name}.ts`, content: this.generateAggregate(name) },
|
|
2091
|
+
{
|
|
2092
|
+
type: "file",
|
|
2093
|
+
name: `${name}Status.ts`,
|
|
2094
|
+
content: this.generateAggregateStatus(name)
|
|
2095
|
+
}
|
|
2096
|
+
]
|
|
2097
|
+
}
|
|
2098
|
+
]
|
|
2099
|
+
},
|
|
2100
|
+
{
|
|
2101
|
+
type: "directory",
|
|
2102
|
+
name: "Events",
|
|
2103
|
+
children: [
|
|
2104
|
+
{
|
|
2105
|
+
type: "file",
|
|
2106
|
+
name: `${name}Created.ts`,
|
|
2107
|
+
content: this.generateCreatedEvent(name)
|
|
2108
|
+
}
|
|
2109
|
+
]
|
|
2110
|
+
},
|
|
2111
|
+
{
|
|
2112
|
+
type: "directory",
|
|
2113
|
+
name: "Repositories",
|
|
2114
|
+
children: [
|
|
2115
|
+
{
|
|
2116
|
+
type: "file",
|
|
2117
|
+
name: `I${name}Repository.ts`,
|
|
2118
|
+
content: this.generateRepositoryInterface(name)
|
|
2119
|
+
}
|
|
2120
|
+
]
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
type: "directory",
|
|
2124
|
+
name: "Services",
|
|
2125
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2126
|
+
}
|
|
2127
|
+
]
|
|
2128
|
+
},
|
|
2129
|
+
// Application Layer
|
|
2130
|
+
{
|
|
2131
|
+
type: "directory",
|
|
2132
|
+
name: "Application",
|
|
2133
|
+
children: [
|
|
2134
|
+
{
|
|
2135
|
+
type: "directory",
|
|
2136
|
+
name: "Commands",
|
|
2137
|
+
children: [
|
|
2138
|
+
{
|
|
2139
|
+
type: "directory",
|
|
2140
|
+
name: `Create${name}`,
|
|
2141
|
+
children: [
|
|
2142
|
+
{
|
|
2143
|
+
type: "file",
|
|
2144
|
+
name: `Create${name}Command.ts`,
|
|
2145
|
+
content: this.generateCommand(name)
|
|
2146
|
+
},
|
|
2147
|
+
{
|
|
2148
|
+
type: "file",
|
|
2149
|
+
name: `Create${name}Handler.ts`,
|
|
2150
|
+
content: this.generateCommandHandler(name)
|
|
2151
|
+
}
|
|
2152
|
+
]
|
|
2153
|
+
}
|
|
2154
|
+
]
|
|
2155
|
+
},
|
|
2156
|
+
{
|
|
2157
|
+
type: "directory",
|
|
2158
|
+
name: "Queries",
|
|
2159
|
+
children: [
|
|
2160
|
+
{
|
|
2161
|
+
type: "directory",
|
|
2162
|
+
name: `Get${name}ById`,
|
|
2163
|
+
children: [
|
|
2164
|
+
{
|
|
2165
|
+
type: "file",
|
|
2166
|
+
name: `Get${name}ByIdQuery.ts`,
|
|
2167
|
+
content: this.generateQuery(name)
|
|
2168
|
+
},
|
|
2169
|
+
{
|
|
2170
|
+
type: "file",
|
|
2171
|
+
name: `Get${name}ByIdHandler.ts`,
|
|
2172
|
+
content: this.generateQueryHandler(name)
|
|
2173
|
+
}
|
|
2174
|
+
]
|
|
2175
|
+
}
|
|
2176
|
+
]
|
|
2177
|
+
},
|
|
2178
|
+
{
|
|
2179
|
+
type: "directory",
|
|
2180
|
+
name: "DTOs",
|
|
2181
|
+
children: [{ type: "file", name: `${name}DTO.ts`, content: this.generateDTO(name) }]
|
|
2182
|
+
}
|
|
2183
|
+
]
|
|
2184
|
+
},
|
|
2185
|
+
// Infrastructure Layer
|
|
2186
|
+
{
|
|
2187
|
+
type: "directory",
|
|
2188
|
+
name: "Infrastructure",
|
|
2189
|
+
children: [
|
|
2190
|
+
{
|
|
2191
|
+
type: "directory",
|
|
2192
|
+
name: "Persistence",
|
|
2193
|
+
children: [
|
|
2194
|
+
{
|
|
2195
|
+
type: "file",
|
|
2196
|
+
name: `${name}Repository.ts`,
|
|
2197
|
+
content: this.generateRepository(name)
|
|
2198
|
+
}
|
|
2199
|
+
]
|
|
2200
|
+
},
|
|
2201
|
+
{
|
|
2202
|
+
type: "directory",
|
|
2203
|
+
name: "Providers",
|
|
2204
|
+
children: [
|
|
2205
|
+
{
|
|
2206
|
+
type: "file",
|
|
2207
|
+
name: `${name}ServiceProvider.ts`,
|
|
2208
|
+
content: this.generateModuleServiceProvider(name, context)
|
|
2209
|
+
}
|
|
2210
|
+
]
|
|
2211
|
+
}
|
|
2212
|
+
]
|
|
2213
|
+
}
|
|
2214
|
+
]
|
|
2215
|
+
};
|
|
2900
2216
|
}
|
|
2901
|
-
// ─────────────────────────────────────────────────────────────
|
|
2902
|
-
// Bounded Context Templates
|
|
2903
|
-
// ─────────────────────────────────────────────────────────────
|
|
2904
2217
|
generateAggregate(name) {
|
|
2905
2218
|
return `/**
|
|
2906
2219
|
* ${name} Aggregate Root
|
|
@@ -3122,15 +2435,359 @@ export class ${name}Repository implements I${name}Repository {
|
|
|
3122
2435
|
}
|
|
3123
2436
|
`;
|
|
3124
2437
|
}
|
|
3125
|
-
|
|
3126
|
-
return `/**
|
|
3127
|
-
*
|
|
3128
|
-
*/
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
2438
|
+
generateModuleServiceProvider(name, _context) {
|
|
2439
|
+
return `/**
|
|
2440
|
+
* ${name} Service Provider
|
|
2441
|
+
*/
|
|
2442
|
+
|
|
2443
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
2444
|
+
import { ${name}Repository } from '../Persistence/${name}Repository'
|
|
2445
|
+
|
|
2446
|
+
export class ${name}ServiceProvider extends ServiceProvider {
|
|
2447
|
+
register(container: Container): void {
|
|
2448
|
+
container.singleton('${name.toLowerCase()}Repository', () => new ${name}Repository())
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
boot(_core: PlanetCore): void {
|
|
2452
|
+
console.log('[${name}] Module loaded')
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
`;
|
|
2456
|
+
}
|
|
2457
|
+
};
|
|
2458
|
+
|
|
2459
|
+
// src/generators/ddd/SharedKernelGenerator.ts
|
|
2460
|
+
var SharedKernelGenerator = class {
|
|
2461
|
+
generate() {
|
|
2462
|
+
return {
|
|
2463
|
+
type: "directory",
|
|
2464
|
+
name: "Shared",
|
|
2465
|
+
children: [
|
|
2466
|
+
{
|
|
2467
|
+
type: "directory",
|
|
2468
|
+
name: "Domain",
|
|
2469
|
+
children: [
|
|
2470
|
+
{
|
|
2471
|
+
type: "directory",
|
|
2472
|
+
name: "ValueObjects",
|
|
2473
|
+
children: [
|
|
2474
|
+
{ type: "file", name: "Id.ts", content: this.generateIdValueObject() },
|
|
2475
|
+
{ type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
|
|
2476
|
+
{ type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
|
|
2477
|
+
]
|
|
2478
|
+
}
|
|
2479
|
+
]
|
|
2480
|
+
},
|
|
2481
|
+
{
|
|
2482
|
+
type: "directory",
|
|
2483
|
+
name: "Infrastructure",
|
|
2484
|
+
children: [
|
|
2485
|
+
{
|
|
2486
|
+
type: "directory",
|
|
2487
|
+
name: "EventBus",
|
|
2488
|
+
children: [
|
|
2489
|
+
{
|
|
2490
|
+
type: "file",
|
|
2491
|
+
name: "EventDispatcher.ts",
|
|
2492
|
+
content: this.generateEventDispatcher()
|
|
2493
|
+
}
|
|
2494
|
+
]
|
|
2495
|
+
}
|
|
2496
|
+
]
|
|
2497
|
+
},
|
|
2498
|
+
{
|
|
2499
|
+
type: "directory",
|
|
2500
|
+
name: "Exceptions",
|
|
2501
|
+
children: [
|
|
2502
|
+
{ type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
|
|
2503
|
+
]
|
|
2504
|
+
}
|
|
2505
|
+
]
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
generateIdValueObject() {
|
|
2509
|
+
return `/**
|
|
2510
|
+
* ID Value Object
|
|
2511
|
+
*
|
|
2512
|
+
* Shared identifier across all contexts.
|
|
2513
|
+
*/
|
|
2514
|
+
|
|
2515
|
+
import { ValueObject } from '@gravito/enterprise'
|
|
2516
|
+
|
|
2517
|
+
interface IdProps {
|
|
2518
|
+
value: string
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
export class Id extends ValueObject<IdProps> {
|
|
2522
|
+
private constructor(value: string) {
|
|
2523
|
+
super({ value })
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
static create(): Id {
|
|
2527
|
+
return new Id(crypto.randomUUID())
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
static from(value: string): Id {
|
|
2531
|
+
if (!value) throw new Error('Id cannot be empty')
|
|
2532
|
+
return new Id(value)
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
get value(): string {
|
|
2536
|
+
return this.props.value
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
toString(): string {
|
|
2540
|
+
return this.props.value
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
`;
|
|
2544
|
+
}
|
|
2545
|
+
generateMoneyValueObject() {
|
|
2546
|
+
return `/**
|
|
2547
|
+
* Money Value Object
|
|
2548
|
+
*/
|
|
2549
|
+
|
|
2550
|
+
import { ValueObject } from '@gravito/enterprise'
|
|
2551
|
+
|
|
2552
|
+
interface MoneyProps {
|
|
2553
|
+
amount: number
|
|
2554
|
+
currency: string
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
export class Money extends ValueObject<MoneyProps> {
|
|
2558
|
+
constructor(amount: number, currency: string = 'USD') {
|
|
2559
|
+
if (amount < 0) throw new Error('Amount cannot be negative')
|
|
2560
|
+
super({ amount, currency })
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
get amount(): number {
|
|
2564
|
+
return this.props.amount
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
get currency(): string {
|
|
2568
|
+
return this.props.currency
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
add(other: Money): Money {
|
|
2572
|
+
this.assertSameCurrency(other)
|
|
2573
|
+
return new Money(this.amount + other.amount, this.currency)
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
subtract(other: Money): Money {
|
|
2577
|
+
this.assertSameCurrency(other)
|
|
2578
|
+
return new Money(this.amount - other.amount, this.currency)
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
private assertSameCurrency(other: Money): void {
|
|
2582
|
+
if (this.currency !== other.currency) {
|
|
2583
|
+
throw new Error('Cannot operate on different currencies')
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
`;
|
|
2588
|
+
}
|
|
2589
|
+
generateEmailValueObject() {
|
|
2590
|
+
return `/**
|
|
2591
|
+
* Email Value Object
|
|
2592
|
+
*/
|
|
2593
|
+
|
|
2594
|
+
import { ValueObject } from '@gravito/enterprise'
|
|
2595
|
+
|
|
2596
|
+
interface EmailProps {
|
|
2597
|
+
value: string
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
export class Email extends ValueObject<EmailProps> {
|
|
2601
|
+
private constructor(value: string) {
|
|
2602
|
+
super({ value: value.toLowerCase().trim() })
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
static create(email: string): Email {
|
|
2606
|
+
if (!Email.isValid(email)) {
|
|
2607
|
+
throw new Error(\`Invalid email: \${email}\`)
|
|
2608
|
+
}
|
|
2609
|
+
return new Email(email)
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
static isValid(email: string): boolean {
|
|
2613
|
+
return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
get value(): string {
|
|
2617
|
+
return this.props.value
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
`;
|
|
2621
|
+
}
|
|
2622
|
+
generateEventDispatcher() {
|
|
2623
|
+
return `/**
|
|
2624
|
+
* Event Dispatcher
|
|
2625
|
+
*/
|
|
2626
|
+
|
|
2627
|
+
import type { DomainEvent } from '@gravito/enterprise'
|
|
2628
|
+
|
|
2629
|
+
type EventHandler = (event: DomainEvent) => void | Promise<void>
|
|
2630
|
+
|
|
2631
|
+
export class EventDispatcher {
|
|
2632
|
+
private handlers: Map<string, EventHandler[]> = new Map()
|
|
2633
|
+
|
|
2634
|
+
subscribe(eventName: string, handler: EventHandler): void {
|
|
2635
|
+
const handlers = this.handlers.get(eventName) ?? []
|
|
2636
|
+
handlers.push(handler)
|
|
2637
|
+
this.handlers.set(eventName, handlers)
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
async dispatch(event: DomainEvent): Promise<void> {
|
|
2641
|
+
const handlers = this.handlers.get(event.eventName) ?? []
|
|
2642
|
+
for (const handler of handlers) {
|
|
2643
|
+
await handler(event)
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
async dispatchAll(events: DomainEvent[]): Promise<void> {
|
|
2648
|
+
for (const event of events) {
|
|
2649
|
+
await this.dispatch(event)
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
`;
|
|
2654
|
+
}
|
|
2655
|
+
generateExceptionHandler() {
|
|
2656
|
+
return `/**
|
|
2657
|
+
* Exception Handler
|
|
2658
|
+
*/
|
|
2659
|
+
|
|
2660
|
+
export function report(error: unknown): void {
|
|
2661
|
+
console.error('[Exception]', error)
|
|
2662
|
+
}
|
|
2663
|
+
`;
|
|
2664
|
+
}
|
|
2665
|
+
};
|
|
2666
|
+
|
|
2667
|
+
// src/generators/DddGenerator.ts
|
|
2668
|
+
var DddGenerator = class extends BaseGenerator {
|
|
2669
|
+
moduleGenerator;
|
|
2670
|
+
sharedKernelGenerator;
|
|
2671
|
+
bootstrapGenerator;
|
|
2672
|
+
constructor(config) {
|
|
2673
|
+
super(config);
|
|
2674
|
+
this.moduleGenerator = new ModuleGenerator();
|
|
2675
|
+
this.sharedKernelGenerator = new SharedKernelGenerator();
|
|
2676
|
+
this.bootstrapGenerator = new BootstrapGenerator();
|
|
2677
|
+
}
|
|
2678
|
+
get architectureType() {
|
|
2679
|
+
return "ddd";
|
|
2680
|
+
}
|
|
2681
|
+
get displayName() {
|
|
2682
|
+
return "Domain-Driven Design (DDD)";
|
|
2683
|
+
}
|
|
2684
|
+
get description() {
|
|
2685
|
+
return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
|
|
2686
|
+
}
|
|
2687
|
+
getDirectoryStructure(context) {
|
|
2688
|
+
return [
|
|
2689
|
+
this.bootstrapGenerator.generateConfigDirectory(context),
|
|
2690
|
+
{
|
|
2691
|
+
type: "directory",
|
|
2692
|
+
name: "src",
|
|
2693
|
+
children: [
|
|
2694
|
+
// Bootstrap - Application startup and configuration
|
|
2695
|
+
this.bootstrapGenerator.generate(context),
|
|
2696
|
+
// Shared - Cross-module shared components
|
|
2697
|
+
this.sharedKernelGenerator.generate(),
|
|
2698
|
+
// Modules - Bounded Contexts
|
|
2699
|
+
{
|
|
2700
|
+
type: "directory",
|
|
2701
|
+
name: "Modules",
|
|
2702
|
+
children: [
|
|
2703
|
+
this.moduleGenerator.generate("Ordering", context),
|
|
2704
|
+
this.moduleGenerator.generate("Catalog", context)
|
|
2705
|
+
]
|
|
2706
|
+
},
|
|
2707
|
+
{
|
|
2708
|
+
type: "file",
|
|
2709
|
+
name: "main.ts",
|
|
2710
|
+
content: this.bootstrapGenerator.generateMainEntry(context)
|
|
2711
|
+
}
|
|
2712
|
+
]
|
|
2713
|
+
},
|
|
2714
|
+
{
|
|
2715
|
+
type: "directory",
|
|
2716
|
+
name: "tests",
|
|
2717
|
+
children: [
|
|
2718
|
+
{
|
|
2719
|
+
type: "directory",
|
|
2720
|
+
name: "Modules",
|
|
2721
|
+
children: [
|
|
2722
|
+
{
|
|
2723
|
+
type: "directory",
|
|
2724
|
+
name: "Ordering",
|
|
2725
|
+
children: [
|
|
2726
|
+
{
|
|
2727
|
+
type: "directory",
|
|
2728
|
+
name: "Unit",
|
|
2729
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2730
|
+
},
|
|
2731
|
+
{
|
|
2732
|
+
type: "directory",
|
|
2733
|
+
name: "Integration",
|
|
2734
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2735
|
+
}
|
|
2736
|
+
]
|
|
2737
|
+
},
|
|
2738
|
+
{
|
|
2739
|
+
type: "directory",
|
|
2740
|
+
name: "Catalog",
|
|
2741
|
+
children: [
|
|
2742
|
+
{
|
|
2743
|
+
type: "directory",
|
|
2744
|
+
name: "Unit",
|
|
2745
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2746
|
+
}
|
|
2747
|
+
]
|
|
2748
|
+
}
|
|
2749
|
+
]
|
|
2750
|
+
},
|
|
2751
|
+
{
|
|
2752
|
+
type: "directory",
|
|
2753
|
+
name: "Shared",
|
|
2754
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2755
|
+
}
|
|
2756
|
+
]
|
|
2757
|
+
}
|
|
2758
|
+
];
|
|
2759
|
+
}
|
|
2760
|
+
/**
|
|
2761
|
+
* Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
|
|
2762
|
+
*/
|
|
2763
|
+
generatePackageJson(context) {
|
|
2764
|
+
const pkg = {
|
|
2765
|
+
name: context.nameKebabCase,
|
|
2766
|
+
version: "0.1.0",
|
|
2767
|
+
type: "module",
|
|
2768
|
+
scripts: {
|
|
2769
|
+
dev: "bun run --watch src/main.ts",
|
|
2770
|
+
build: "bun build ./src/main.ts --outdir ./dist --target bun",
|
|
2771
|
+
start: "bun run dist/main.js",
|
|
2772
|
+
test: "bun test",
|
|
2773
|
+
typecheck: "bun tsc --noEmit",
|
|
2774
|
+
check: "bun run typecheck && bun run test",
|
|
2775
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2776
|
+
validate: "bun run check && bun run check:deps",
|
|
2777
|
+
precommit: "bun run validate",
|
|
2778
|
+
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
2779
|
+
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
2780
|
+
},
|
|
2781
|
+
dependencies: {
|
|
2782
|
+
"@gravito/core": "^1.0.0-beta.5",
|
|
2783
|
+
"@gravito/enterprise": "workspace:*"
|
|
2784
|
+
},
|
|
2785
|
+
devDependencies: {
|
|
2786
|
+
"bun-types": "latest",
|
|
2787
|
+
typescript: "^5.9.3"
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
return JSON.stringify(pkg, null, 2);
|
|
3134
2791
|
}
|
|
3135
2792
|
generateArchitectureDoc(context) {
|
|
3136
2793
|
return `# ${context.name} - DDD Architecture Guide
|
|
@@ -3359,123 +3016,13 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
|
|
|
3359
3016
|
];
|
|
3360
3017
|
}
|
|
3361
3018
|
// ─────────────────────────────────────────────────────────────
|
|
3362
|
-
// Config Generators
|
|
3019
|
+
// Config Generators (using shared ConfigGenerator)
|
|
3363
3020
|
// ─────────────────────────────────────────────────────────────
|
|
3364
3021
|
generateAppConfig(context) {
|
|
3365
|
-
return
|
|
3366
|
-
* Application Configuration
|
|
3367
|
-
*/
|
|
3368
|
-
export default {
|
|
3369
|
-
/**
|
|
3370
|
-
* Application name
|
|
3371
|
-
*/
|
|
3372
|
-
name: process.env.APP_NAME ?? '${context.name}',
|
|
3373
|
-
|
|
3374
|
-
/**
|
|
3375
|
-
* Application environment
|
|
3376
|
-
*/
|
|
3377
|
-
env: process.env.APP_ENV ?? 'development',
|
|
3378
|
-
|
|
3379
|
-
/**
|
|
3380
|
-
* Application port
|
|
3381
|
-
*/
|
|
3382
|
-
port: Number.parseInt(process.env.PORT ?? '3000', 10),
|
|
3383
|
-
|
|
3384
|
-
/**
|
|
3385
|
-
* View directory
|
|
3386
|
-
*/
|
|
3387
|
-
VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
|
|
3388
|
-
|
|
3389
|
-
/**
|
|
3390
|
-
* Debug mode
|
|
3391
|
-
*/
|
|
3392
|
-
debug: process.env.APP_DEBUG === 'true',
|
|
3393
|
-
|
|
3394
|
-
/**
|
|
3395
|
-
* Application URL
|
|
3396
|
-
*/
|
|
3397
|
-
url: process.env.APP_URL ?? 'http://localhost:3000',
|
|
3398
|
-
|
|
3399
|
-
/**
|
|
3400
|
-
* Timezone
|
|
3401
|
-
*/
|
|
3402
|
-
timezone: 'UTC',
|
|
3403
|
-
|
|
3404
|
-
/**
|
|
3405
|
-
* Locale
|
|
3406
|
-
*/
|
|
3407
|
-
locale: 'en',
|
|
3408
|
-
|
|
3409
|
-
/**
|
|
3410
|
-
* Fallback locale
|
|
3411
|
-
*/
|
|
3412
|
-
fallbackLocale: 'en',
|
|
3413
|
-
|
|
3414
|
-
/**
|
|
3415
|
-
* Encryption key
|
|
3416
|
-
*/
|
|
3417
|
-
key: process.env.APP_KEY,
|
|
3418
|
-
|
|
3419
|
-
/**
|
|
3420
|
-
* Service providers to register
|
|
3421
|
-
*/
|
|
3422
|
-
providers: [
|
|
3423
|
-
// Framework providers
|
|
3424
|
-
// 'RouteServiceProvider',
|
|
3425
|
-
|
|
3426
|
-
// Application providers
|
|
3427
|
-
// 'AppServiceProvider',
|
|
3428
|
-
],
|
|
3429
|
-
}
|
|
3430
|
-
`;
|
|
3022
|
+
return ConfigGenerator.generateDetailedAppConfig(context);
|
|
3431
3023
|
}
|
|
3432
3024
|
generateDatabaseConfig() {
|
|
3433
|
-
return
|
|
3434
|
-
* Database Configuration
|
|
3435
|
-
*/
|
|
3436
|
-
export default {
|
|
3437
|
-
/**
|
|
3438
|
-
* Default connection
|
|
3439
|
-
*/
|
|
3440
|
-
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
3441
|
-
|
|
3442
|
-
/**
|
|
3443
|
-
* Database connections
|
|
3444
|
-
*/
|
|
3445
|
-
connections: {
|
|
3446
|
-
sqlite: {
|
|
3447
|
-
driver: 'sqlite',
|
|
3448
|
-
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
3449
|
-
},
|
|
3450
|
-
|
|
3451
|
-
mysql: {
|
|
3452
|
-
driver: 'mysql',
|
|
3453
|
-
host: process.env.DB_HOST ?? 'localhost',
|
|
3454
|
-
port: Number(process.env.DB_PORT ?? 3306),
|
|
3455
|
-
database: process.env.DB_DATABASE ?? 'forge',
|
|
3456
|
-
username: process.env.DB_USERNAME ?? 'forge',
|
|
3457
|
-
password: process.env.DB_PASSWORD ?? '',
|
|
3458
|
-
},
|
|
3459
|
-
|
|
3460
|
-
postgres: {
|
|
3461
|
-
driver: 'postgres',
|
|
3462
|
-
host: process.env.DB_HOST ?? 'localhost',
|
|
3463
|
-
port: Number(process.env.DB_PORT ?? 5432),
|
|
3464
|
-
database: process.env.DB_DATABASE ?? 'forge',
|
|
3465
|
-
username: process.env.DB_USERNAME ?? 'forge',
|
|
3466
|
-
password: process.env.DB_PASSWORD ?? '',
|
|
3467
|
-
},
|
|
3468
|
-
},
|
|
3469
|
-
|
|
3470
|
-
/**
|
|
3471
|
-
* Migration settings
|
|
3472
|
-
*/
|
|
3473
|
-
migrations: {
|
|
3474
|
-
table: 'migrations',
|
|
3475
|
-
path: 'database/migrations',
|
|
3476
|
-
},
|
|
3477
|
-
}
|
|
3478
|
-
`;
|
|
3025
|
+
return ConfigGenerator.generateDetailedDatabaseConfig();
|
|
3479
3026
|
}
|
|
3480
3027
|
generateAuthConfig() {
|
|
3481
3028
|
return `/**
|
|
@@ -3758,33 +3305,6 @@ export class AppServiceProvider extends ServiceProvider {
|
|
|
3758
3305
|
console.log('${context.name} application booted!')
|
|
3759
3306
|
}
|
|
3760
3307
|
}
|
|
3761
|
-
`;
|
|
3762
|
-
}
|
|
3763
|
-
generateRouteServiceProvider(_context) {
|
|
3764
|
-
return `/**
|
|
3765
|
-
* Route Service Provider
|
|
3766
|
-
*
|
|
3767
|
-
* Configures and registers application routes.
|
|
3768
|
-
*/
|
|
3769
|
-
|
|
3770
|
-
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
3771
|
-
import { registerRoutes } from '../routes'
|
|
3772
|
-
|
|
3773
|
-
export class RouteServiceProvider extends ServiceProvider {
|
|
3774
|
-
/**
|
|
3775
|
-
* Register any application services.
|
|
3776
|
-
*/
|
|
3777
|
-
register(_container: Container): void {
|
|
3778
|
-
// Routes are registered in boot
|
|
3779
|
-
}
|
|
3780
|
-
|
|
3781
|
-
/**
|
|
3782
|
-
* Bootstrap any application services.
|
|
3783
|
-
*/
|
|
3784
|
-
boot(core: PlanetCore): void {
|
|
3785
|
-
registerRoutes(core.router)
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
3308
|
`;
|
|
3789
3309
|
}
|
|
3790
3310
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -4455,11 +3975,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
4455
3975
|
module: "dist/index.mjs",
|
|
4456
3976
|
types: "dist/index.d.ts",
|
|
4457
3977
|
scripts: {
|
|
4458
|
-
build: "tsup src/index.ts --format
|
|
3978
|
+
build: "tsup src/index.ts --format esm --dts",
|
|
4459
3979
|
test: "bun test",
|
|
4460
|
-
typecheck: "tsc --noEmit"
|
|
4461
|
-
check: "bun run typecheck && bun run test",
|
|
4462
|
-
validate: "bun run check"
|
|
3980
|
+
typecheck: "bun tsc --noEmit"
|
|
4463
3981
|
},
|
|
4464
3982
|
dependencies: {
|
|
4465
3983
|
"@gravito/core": depVersion,
|
|
@@ -4468,8 +3986,12 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
4468
3986
|
"@gravito/stasis": depVersion
|
|
4469
3987
|
},
|
|
4470
3988
|
devDependencies: {
|
|
4471
|
-
|
|
4472
|
-
typescript: "^5.
|
|
3989
|
+
"bun-types": "latest",
|
|
3990
|
+
typescript: "^5.9.3",
|
|
3991
|
+
tsup: "^8.0.0"
|
|
3992
|
+
},
|
|
3993
|
+
peerDependencies: {
|
|
3994
|
+
"@gravito/core": ">=1.0.0"
|
|
4473
3995
|
}
|
|
4474
3996
|
};
|
|
4475
3997
|
return JSON.stringify(pkg, null, 2);
|
|
@@ -4619,7 +4141,7 @@ var ProfileResolver = class _ProfileResolver {
|
|
|
4619
4141
|
};
|
|
4620
4142
|
|
|
4621
4143
|
// src/Scaffold.ts
|
|
4622
|
-
var
|
|
4144
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
4623
4145
|
|
|
4624
4146
|
// src/generators/ActionDomainGenerator.ts
|
|
4625
4147
|
var ActionDomainGenerator = class extends BaseGenerator {
|
|
@@ -4826,6 +4348,9 @@ var ActionDomainGenerator = class extends BaseGenerator {
|
|
|
4826
4348
|
|
|
4827
4349
|
import { Model, column } from '@gravito/atlas'
|
|
4828
4350
|
|
|
4351
|
+
/**
|
|
4352
|
+
* Represents a user in the system.
|
|
4353
|
+
*/
|
|
4829
4354
|
export class User extends Model {
|
|
4830
4355
|
static table = 'users'
|
|
4831
4356
|
|
|
@@ -5110,7 +4635,7 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
5110
4635
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
5111
4636
|
start: "bun run dist/bootstrap.js",
|
|
5112
4637
|
test: "bun test",
|
|
5113
|
-
typecheck: "tsc --noEmit",
|
|
4638
|
+
typecheck: "bun tsc --noEmit",
|
|
5114
4639
|
check: "bun run typecheck && bun run test",
|
|
5115
4640
|
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
5116
4641
|
validate: "bun run check && bun run check:deps",
|
|
@@ -5124,7 +4649,7 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
5124
4649
|
},
|
|
5125
4650
|
devDependencies: {
|
|
5126
4651
|
"bun-types": "latest",
|
|
5127
|
-
typescript: "^5.
|
|
4652
|
+
typescript: "^5.9.3"
|
|
5128
4653
|
}
|
|
5129
4654
|
};
|
|
5130
4655
|
return JSON.stringify(pkg, null, 2);
|
|
@@ -5132,6 +4657,7 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
5132
4657
|
};
|
|
5133
4658
|
|
|
5134
4659
|
// src/generators/StandaloneEngineGenerator.ts
|
|
4660
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
5135
4661
|
var StandaloneEngineGenerator = class extends BaseGenerator {
|
|
5136
4662
|
get architectureType() {
|
|
5137
4663
|
return "standalone-engine";
|
|
@@ -5163,9 +4689,16 @@ var StandaloneEngineGenerator = class extends BaseGenerator {
|
|
|
5163
4689
|
];
|
|
5164
4690
|
}
|
|
5165
4691
|
async generateCommonFiles(context) {
|
|
4692
|
+
const commonDir = import_node_path5.default.resolve(this.config.templatesDir, "common");
|
|
4693
|
+
const extendedContext = { ...context };
|
|
5166
4694
|
await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
|
|
5167
|
-
await this.
|
|
5168
|
-
|
|
4695
|
+
await this.generateFileFromTemplate(
|
|
4696
|
+
commonDir,
|
|
4697
|
+
"tsconfig.json.hbs",
|
|
4698
|
+
"tsconfig.json",
|
|
4699
|
+
extendedContext
|
|
4700
|
+
);
|
|
4701
|
+
await this.generateFileFromTemplate(commonDir, "gitignore.hbs", ".gitignore", extendedContext);
|
|
5169
4702
|
}
|
|
5170
4703
|
generatePackageJson(context) {
|
|
5171
4704
|
const pkg = {
|
|
@@ -5220,23 +4753,17 @@ A high-performance web application powered by Gravito Engine.
|
|
|
5220
4753
|
|
|
5221
4754
|
### Install Dependencies
|
|
5222
4755
|
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
\`\`\`
|
|
5226
|
-
|
|
4756
|
+
bun install
|
|
4757
|
+
|
|
5227
4758
|
### Run Development Server
|
|
5228
4759
|
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
\`\`\`
|
|
5232
|
-
|
|
4760
|
+
bun run dev
|
|
4761
|
+
|
|
5233
4762
|
### Production Build
|
|
5234
4763
|
|
|
5235
|
-
|
|
5236
|
-
bun run build
|
|
4764
|
+
bun run build
|
|
5237
4765
|
bun start
|
|
5238
|
-
|
|
5239
|
-
`;
|
|
4766
|
+
`;
|
|
5240
4767
|
}
|
|
5241
4768
|
};
|
|
5242
4769
|
|
|
@@ -5245,11 +4772,14 @@ var Scaffold = class {
|
|
|
5245
4772
|
templatesDir;
|
|
5246
4773
|
verbose;
|
|
5247
4774
|
constructor(options = {}) {
|
|
5248
|
-
this.templatesDir = options.templatesDir ??
|
|
4775
|
+
this.templatesDir = options.templatesDir ?? import_node_path6.default.resolve(__dirname, "../templates");
|
|
5249
4776
|
this.verbose = options.verbose ?? false;
|
|
5250
4777
|
}
|
|
5251
4778
|
/**
|
|
5252
|
-
*
|
|
4779
|
+
* Returns a list of all architectural patterns supported by the engine,
|
|
4780
|
+
* along with human-readable names and descriptions.
|
|
4781
|
+
*
|
|
4782
|
+
* @returns {Array<{type: ArchitectureType, name: string, description: string}>}
|
|
5253
4783
|
*/
|
|
5254
4784
|
getArchitectureTypes() {
|
|
5255
4785
|
return [
|
|
@@ -5286,11 +4816,16 @@ var Scaffold = class {
|
|
|
5286
4816
|
];
|
|
5287
4817
|
}
|
|
5288
4818
|
/**
|
|
5289
|
-
*
|
|
4819
|
+
* Orchestrates the complete project generation lifecycle.
|
|
4820
|
+
* This includes directory creation, file layout, profile resolution,
|
|
4821
|
+
* dependency mapping, and optional post-install hooks.
|
|
4822
|
+
*
|
|
4823
|
+
* @param {ScaffoldOptions} options - Detailed configuration for the new project.
|
|
4824
|
+
* @returns {Promise<ScaffoldResult>}
|
|
5290
4825
|
*/
|
|
5291
4826
|
async create(options) {
|
|
5292
4827
|
const generator = this.createGenerator(options.architecture);
|
|
5293
|
-
const
|
|
4828
|
+
const fs5 = await import("fs/promises");
|
|
5294
4829
|
const profileResolver = new ProfileResolver();
|
|
5295
4830
|
const profileConfig = profileResolver.resolve(options.profile, options.features);
|
|
5296
4831
|
const context = BaseGenerator.createContext(
|
|
@@ -5317,8 +4852,8 @@ var Scaffold = class {
|
|
|
5317
4852
|
// Default template for now, should come from options if applicable
|
|
5318
4853
|
"1.0.0"
|
|
5319
4854
|
);
|
|
5320
|
-
const lockPath =
|
|
5321
|
-
await
|
|
4855
|
+
const lockPath = import_node_path6.default.resolve(options.targetDir, "gravito.lock.json");
|
|
4856
|
+
await fs5.writeFile(lockPath, lockContent, "utf-8");
|
|
5322
4857
|
filesCreated.push(lockPath);
|
|
5323
4858
|
return {
|
|
5324
4859
|
success: true,
|