@gravito/scaffold 2.1.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.js
CHANGED
|
@@ -98,12 +98,52 @@ ${overlay}`;
|
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
// src/generators/BaseGenerator.ts
|
|
101
|
-
import
|
|
102
|
-
import
|
|
101
|
+
import fs4 from "fs/promises";
|
|
102
|
+
import path4 from "path";
|
|
103
103
|
|
|
104
|
-
// src/
|
|
104
|
+
// src/utils/FileUtilities.ts
|
|
105
105
|
import fs from "fs/promises";
|
|
106
106
|
import path from "path";
|
|
107
|
+
var FileUtilities = class _FileUtilities {
|
|
108
|
+
static async walk(dir) {
|
|
109
|
+
const files = await fs.readdir(dir);
|
|
110
|
+
const paths = [];
|
|
111
|
+
for (const file of files) {
|
|
112
|
+
const filePath = path.join(dir, file);
|
|
113
|
+
const stat = await fs.stat(filePath);
|
|
114
|
+
if (stat.isDirectory()) {
|
|
115
|
+
paths.push(...await _FileUtilities.walk(filePath));
|
|
116
|
+
} else {
|
|
117
|
+
paths.push(filePath);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return paths;
|
|
121
|
+
}
|
|
122
|
+
static async writeFile(basePath, relativePath, content, fileMerger, log) {
|
|
123
|
+
const fullPath = path.resolve(basePath, relativePath);
|
|
124
|
+
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
125
|
+
let finalContent = content;
|
|
126
|
+
try {
|
|
127
|
+
const existingContent = await fs.readFile(fullPath, "utf-8");
|
|
128
|
+
finalContent = fileMerger.merge(relativePath, existingContent, content);
|
|
129
|
+
if (finalContent !== content) {
|
|
130
|
+
log?.(`\u{1F504} Merged file: ${relativePath}`);
|
|
131
|
+
}
|
|
132
|
+
} catch {
|
|
133
|
+
}
|
|
134
|
+
await fs.writeFile(fullPath, finalContent, "utf-8");
|
|
135
|
+
log?.(`\u{1F4C4} Created file: ${relativePath}`);
|
|
136
|
+
return fullPath;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// src/utils/TemplateManager.ts
|
|
141
|
+
import fs3 from "fs/promises";
|
|
142
|
+
import path3 from "path";
|
|
143
|
+
|
|
144
|
+
// src/generators/StubGenerator.ts
|
|
145
|
+
import fs2 from "fs/promises";
|
|
146
|
+
import path2 from "path";
|
|
107
147
|
import Handlebars from "handlebars";
|
|
108
148
|
var StubGenerator = class {
|
|
109
149
|
config;
|
|
@@ -192,16 +232,16 @@ var StubGenerator = class {
|
|
|
192
232
|
* @returns Path to the generated file
|
|
193
233
|
*/
|
|
194
234
|
async generate(stubName, outputPath, variables = {}) {
|
|
195
|
-
const stubPath =
|
|
196
|
-
const template = await
|
|
235
|
+
const stubPath = path2.resolve(this.config.stubsDir, stubName);
|
|
236
|
+
const template = await fs2.readFile(stubPath, "utf-8");
|
|
197
237
|
const compiled = this.handlebars.compile(template);
|
|
198
238
|
const content = compiled({
|
|
199
239
|
...this.config.defaultVariables,
|
|
200
240
|
...variables
|
|
201
241
|
});
|
|
202
|
-
const fullOutputPath =
|
|
203
|
-
await
|
|
204
|
-
await
|
|
242
|
+
const fullOutputPath = path2.resolve(this.config.outputDir, outputPath);
|
|
243
|
+
await fs2.mkdir(path2.dirname(fullOutputPath), { recursive: true });
|
|
244
|
+
await fs2.writeFile(fullOutputPath, content, "utf-8");
|
|
205
245
|
return fullOutputPath;
|
|
206
246
|
}
|
|
207
247
|
/**
|
|
@@ -253,41 +293,57 @@ var StubGenerator = class {
|
|
|
253
293
|
}
|
|
254
294
|
};
|
|
255
295
|
|
|
256
|
-
// src/
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
296
|
+
// src/utils/TemplateManager.ts
|
|
297
|
+
var TemplateManager = class {
|
|
298
|
+
stubGenerator;
|
|
299
|
+
constructor(templatesDir) {
|
|
300
|
+
this.stubGenerator = new StubGenerator({
|
|
301
|
+
stubsDir: templatesDir,
|
|
302
|
+
outputDir: ""
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
render(template, context) {
|
|
306
|
+
return this.stubGenerator.render(template, context);
|
|
307
|
+
}
|
|
308
|
+
async applyOverlay(sourceDir, targetDir, context, fileMerger, log) {
|
|
309
|
+
const createdFiles = [];
|
|
310
|
+
try {
|
|
311
|
+
await fs3.access(sourceDir);
|
|
312
|
+
} catch {
|
|
313
|
+
return [];
|
|
314
|
+
}
|
|
315
|
+
const files = await FileUtilities.walk(sourceDir);
|
|
316
|
+
for (const filePath of files) {
|
|
317
|
+
const relativePath = path3.relative(sourceDir, filePath);
|
|
318
|
+
let content = await fs3.readFile(filePath, "utf-8");
|
|
319
|
+
try {
|
|
320
|
+
content = this.render(content, context);
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
const fullPath = await FileUtilities.writeFile(
|
|
324
|
+
targetDir,
|
|
325
|
+
relativePath,
|
|
326
|
+
content,
|
|
327
|
+
fileMerger,
|
|
328
|
+
log
|
|
329
|
+
);
|
|
330
|
+
createdFiles.push(fullPath);
|
|
267
331
|
}
|
|
332
|
+
return createdFiles;
|
|
268
333
|
}
|
|
269
|
-
|
|
270
|
-
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// src/generators/BaseGenerator.ts
|
|
271
337
|
var BaseGenerator = class {
|
|
272
338
|
config;
|
|
273
|
-
|
|
339
|
+
templateManager;
|
|
274
340
|
fileMerger;
|
|
275
341
|
filesCreated = [];
|
|
276
342
|
constructor(config) {
|
|
277
343
|
this.config = config;
|
|
278
|
-
this.
|
|
279
|
-
stubsDir: config.templatesDir,
|
|
280
|
-
outputDir: ""
|
|
281
|
-
// Set per-generation
|
|
282
|
-
});
|
|
344
|
+
this.templateManager = new TemplateManager(config.templatesDir);
|
|
283
345
|
this.fileMerger = new FileMerger();
|
|
284
346
|
}
|
|
285
|
-
/**
|
|
286
|
-
* Generate the project scaffold.
|
|
287
|
-
*
|
|
288
|
-
* @param context - Generator context
|
|
289
|
-
* @returns Array of created file paths
|
|
290
|
-
*/
|
|
291
347
|
async generate(context) {
|
|
292
348
|
this.filesCreated = [];
|
|
293
349
|
const structure = this.getDirectoryStructure(context);
|
|
@@ -297,50 +353,73 @@ var BaseGenerator = class {
|
|
|
297
353
|
await this.applyFeatureOverlays(context);
|
|
298
354
|
return this.filesCreated;
|
|
299
355
|
}
|
|
300
|
-
/**
|
|
301
|
-
* Create directory structure recursively.
|
|
302
|
-
*/
|
|
303
356
|
async createStructure(basePath, nodes, context) {
|
|
304
357
|
for (const node of nodes) {
|
|
305
|
-
const fullPath =
|
|
358
|
+
const fullPath = path4.resolve(basePath, node.name);
|
|
306
359
|
if (node.type === "directory") {
|
|
307
|
-
await
|
|
360
|
+
await fs4.mkdir(fullPath, { recursive: true });
|
|
308
361
|
this.log(`\u{1F4C1} Created directory: ${node.name}`);
|
|
309
362
|
if (node.children) {
|
|
310
363
|
await this.createStructure(fullPath, node.children, context);
|
|
311
364
|
}
|
|
312
365
|
} else {
|
|
313
|
-
await
|
|
366
|
+
await fs4.mkdir(path4.dirname(fullPath), { recursive: true });
|
|
367
|
+
let content = "";
|
|
314
368
|
if (node.template) {
|
|
315
|
-
const templatePath = path2.resolve(this.config.templatesDir, node.template);
|
|
316
369
|
try {
|
|
317
|
-
const
|
|
318
|
-
const
|
|
319
|
-
|
|
370
|
+
const templatePath = path4.resolve(this.config.templatesDir, node.template);
|
|
371
|
+
const template = await fs4.readFile(templatePath, "utf-8");
|
|
372
|
+
content = this.templateManager.render(template, context);
|
|
320
373
|
} catch {
|
|
321
|
-
|
|
374
|
+
content = node.content ?? "";
|
|
322
375
|
}
|
|
323
|
-
} else if (node.content) {
|
|
324
|
-
await fs2.writeFile(fullPath, node.content, "utf-8");
|
|
325
376
|
} else {
|
|
326
|
-
|
|
377
|
+
content = node.content ?? "";
|
|
327
378
|
}
|
|
328
|
-
|
|
329
|
-
|
|
379
|
+
const relativePath = path4.relative(context.targetDir, fullPath);
|
|
380
|
+
const writtenPath = await FileUtilities.writeFile(
|
|
381
|
+
context.targetDir,
|
|
382
|
+
relativePath,
|
|
383
|
+
content,
|
|
384
|
+
this.fileMerger,
|
|
385
|
+
(msg) => this.log(msg)
|
|
386
|
+
);
|
|
387
|
+
this.filesCreated.push(writtenPath);
|
|
330
388
|
}
|
|
331
389
|
}
|
|
332
390
|
}
|
|
333
|
-
/**
|
|
334
|
-
* Generate common files (package.json, .env, etc.)
|
|
335
|
-
*/
|
|
336
391
|
async generateCommonFiles(context) {
|
|
392
|
+
const commonDir = path4.resolve(this.config.templatesDir, "common");
|
|
393
|
+
const extendedContext = {
|
|
394
|
+
...context,
|
|
395
|
+
entrypoint: context.architecture === "ddd" ? "dist/main.js" : "dist/bootstrap.js",
|
|
396
|
+
dbConnection: context.profile === "core" ? "sqlite" : "postgres"
|
|
397
|
+
};
|
|
398
|
+
await this.generateFileFromTemplate(
|
|
399
|
+
commonDir,
|
|
400
|
+
"env.example.hbs",
|
|
401
|
+
".env.example",
|
|
402
|
+
extendedContext
|
|
403
|
+
);
|
|
404
|
+
await this.generateFileFromTemplate(commonDir, "env.example.hbs", ".env", extendedContext);
|
|
405
|
+
await this.generateFileFromTemplate(commonDir, "gitignore.hbs", ".gitignore", extendedContext);
|
|
406
|
+
await this.generateFileFromTemplate(
|
|
407
|
+
commonDir,
|
|
408
|
+
"tsconfig.json.hbs",
|
|
409
|
+
"tsconfig.json",
|
|
410
|
+
extendedContext
|
|
411
|
+
);
|
|
412
|
+
await this.generateFileFromTemplate(commonDir, "Dockerfile.hbs", "Dockerfile", extendedContext);
|
|
337
413
|
await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
|
|
338
|
-
await this.writeFile(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
414
|
+
await this.writeFile(
|
|
415
|
+
context.targetDir,
|
|
416
|
+
".dockerignore",
|
|
417
|
+
`node_modules
|
|
418
|
+
dist
|
|
419
|
+
.git
|
|
420
|
+
.env
|
|
421
|
+
`
|
|
422
|
+
);
|
|
344
423
|
await this.writeFile(
|
|
345
424
|
context.targetDir,
|
|
346
425
|
"ARCHITECTURE.md",
|
|
@@ -349,104 +428,74 @@ var BaseGenerator = class {
|
|
|
349
428
|
await this.generateCheckScripts(context);
|
|
350
429
|
await this.generateSkills(context);
|
|
351
430
|
}
|
|
352
|
-
|
|
353
|
-
* Copy AI Skills to the project
|
|
354
|
-
*/
|
|
355
|
-
async generateSkills(context) {
|
|
356
|
-
const skillsDir = path2.resolve(this.config.templatesDir, "skills");
|
|
357
|
-
const targetSkillsDir = path2.join(".skills");
|
|
431
|
+
async generateFileFromTemplate(tplDir, tplName, targetName, context) {
|
|
358
432
|
try {
|
|
359
|
-
await
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
for (const filePath of files) {
|
|
365
|
-
const relativePath = path2.relative(skillsDir, filePath);
|
|
366
|
-
const targetPath = path2.join(targetSkillsDir, relativePath);
|
|
367
|
-
let content = await fs2.readFile(filePath, "utf-8");
|
|
368
|
-
try {
|
|
369
|
-
content = this.stubGenerator.render(content, context);
|
|
370
|
-
} catch {
|
|
371
|
-
}
|
|
372
|
-
await this.writeFile(context.targetDir, targetPath, content);
|
|
433
|
+
const template = await fs4.readFile(path4.join(tplDir, tplName), "utf-8");
|
|
434
|
+
const content = this.templateManager.render(template, context);
|
|
435
|
+
await this.writeFile(context.targetDir, targetName, content);
|
|
436
|
+
} catch (e) {
|
|
437
|
+
this.log(`\u26A0\uFE0F Failed to generate ${targetName}: ${e}`);
|
|
373
438
|
}
|
|
374
439
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
440
|
+
async generateSkills(context) {
|
|
441
|
+
const skillsDir = path4.resolve(this.config.templatesDir, "skills");
|
|
442
|
+
const created = await this.templateManager.applyOverlay(
|
|
443
|
+
skillsDir,
|
|
444
|
+
path4.join(context.targetDir, ".skills"),
|
|
445
|
+
context,
|
|
446
|
+
this.fileMerger,
|
|
447
|
+
(msg) => this.log(msg)
|
|
448
|
+
);
|
|
449
|
+
this.filesCreated.push(...created);
|
|
450
|
+
}
|
|
378
451
|
async applyOverlays(context) {
|
|
379
452
|
const profile = context.profile;
|
|
380
|
-
if (
|
|
381
|
-
|
|
382
|
-
|
|
453
|
+
if (profile) {
|
|
454
|
+
const overlayDir = path4.resolve(this.config.templatesDir, "overlays", profile);
|
|
455
|
+
await this.copyOverlayDirectory(overlayDir, context);
|
|
456
|
+
}
|
|
383
457
|
}
|
|
384
|
-
/**
|
|
385
|
-
* Apply feature-specific overlays
|
|
386
|
-
*/
|
|
387
458
|
async applyFeatureOverlays(context) {
|
|
388
459
|
const features = context.features || [];
|
|
389
460
|
for (const feature of features) {
|
|
390
|
-
const overlayDir =
|
|
461
|
+
const overlayDir = path4.resolve(this.config.templatesDir, "features", feature);
|
|
391
462
|
await this.copyOverlayDirectory(overlayDir, context);
|
|
392
463
|
}
|
|
393
464
|
}
|
|
394
|
-
/**
|
|
395
|
-
* Helper to copy/merge an overlay directory into the target
|
|
396
|
-
*/
|
|
397
465
|
async copyOverlayDirectory(sourceDir, context) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
let content = await fs2.readFile(filePath, "utf-8");
|
|
407
|
-
try {
|
|
408
|
-
content = this.stubGenerator.render(content, context);
|
|
409
|
-
} catch {
|
|
410
|
-
}
|
|
411
|
-
await this.writeFile(context.targetDir, relativePath, content);
|
|
412
|
-
}
|
|
466
|
+
const created = await this.templateManager.applyOverlay(
|
|
467
|
+
sourceDir,
|
|
468
|
+
context.targetDir,
|
|
469
|
+
context,
|
|
470
|
+
this.fileMerger,
|
|
471
|
+
(msg) => this.log(msg)
|
|
472
|
+
);
|
|
473
|
+
this.filesCreated.push(...created);
|
|
413
474
|
}
|
|
414
|
-
/**
|
|
415
|
-
* Write a file and track it.
|
|
416
|
-
*/
|
|
417
475
|
async writeFile(basePath, relativePath, content) {
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
} catch {
|
|
428
|
-
}
|
|
429
|
-
await fs2.writeFile(fullPath, finalContent, "utf-8");
|
|
430
|
-
this.filesCreated.push(fullPath);
|
|
431
|
-
this.log(`\u{1F4C4} Created file: ${relativePath}`);
|
|
476
|
+
const writtenPath = await FileUtilities.writeFile(
|
|
477
|
+
basePath,
|
|
478
|
+
relativePath,
|
|
479
|
+
content,
|
|
480
|
+
this.fileMerger,
|
|
481
|
+
(msg) => this.log(msg)
|
|
482
|
+
);
|
|
483
|
+
this.filesCreated.push(writtenPath);
|
|
432
484
|
}
|
|
433
|
-
/**
|
|
434
|
-
* Generate package.json content.
|
|
435
|
-
*/
|
|
436
485
|
generatePackageJson(context) {
|
|
437
486
|
const profile = context.profile || "core";
|
|
438
|
-
const
|
|
487
|
+
const deps = {
|
|
439
488
|
"@gravito/core": "^1.0.0-beta.5",
|
|
440
489
|
"@gravito/atlas": "^1.0.0-beta.5",
|
|
441
490
|
"@gravito/plasma": "^1.0.0-beta.5",
|
|
442
491
|
"@gravito/stream": "^1.0.0-beta.5"
|
|
443
492
|
};
|
|
444
493
|
if (profile === "enterprise" || profile === "scale") {
|
|
445
|
-
|
|
446
|
-
|
|
494
|
+
deps["@gravito/quasar"] = "^1.0.0-beta.5";
|
|
495
|
+
deps["@gravito/horizon"] = "^1.0.0-beta.5";
|
|
447
496
|
}
|
|
448
497
|
if (context.withSpectrum) {
|
|
449
|
-
|
|
498
|
+
deps["@gravito/spectrum"] = "^1.0.0-beta.5";
|
|
450
499
|
}
|
|
451
500
|
const pkg = {
|
|
452
501
|
name: context.nameKebabCase,
|
|
@@ -457,717 +506,426 @@ var BaseGenerator = class {
|
|
|
457
506
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
458
507
|
start: "bun run dist/bootstrap.js",
|
|
459
508
|
test: "bun test",
|
|
460
|
-
typecheck: "tsc --noEmit",
|
|
461
|
-
|
|
462
|
-
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
463
|
-
validate: "bun run check && bun run check:deps",
|
|
464
|
-
precommit: "bun run validate",
|
|
465
|
-
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
466
|
-
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
509
|
+
typecheck: "bun tsc --noEmit",
|
|
510
|
+
validate: "bun run typecheck && bun run test"
|
|
467
511
|
},
|
|
468
|
-
dependencies:
|
|
469
|
-
devDependencies: {
|
|
470
|
-
"bun-types": "latest",
|
|
471
|
-
typescript: "^5.0.0"
|
|
472
|
-
}
|
|
512
|
+
dependencies: deps,
|
|
513
|
+
devDependencies: { "bun-types": "latest", typescript: "^5.9.3" }
|
|
473
514
|
};
|
|
474
515
|
return JSON.stringify(pkg, null, 2);
|
|
475
516
|
}
|
|
517
|
+
async generateCheckScripts(context) {
|
|
518
|
+
const scriptsDir = path4.resolve(context.targetDir, "scripts");
|
|
519
|
+
await fs4.mkdir(scriptsDir, { recursive: true });
|
|
520
|
+
const templatesDir = path4.resolve(this.config.templatesDir, "scripts");
|
|
521
|
+
await this.generateFileFromTemplate(
|
|
522
|
+
templatesDir,
|
|
523
|
+
"check-dependencies.ts.hbs",
|
|
524
|
+
"scripts/check-dependencies.ts",
|
|
525
|
+
context
|
|
526
|
+
);
|
|
527
|
+
await this.generateFileFromTemplate(templatesDir, "check.sh.hbs", "scripts/check.sh", context);
|
|
528
|
+
await this.generateFileFromTemplate(
|
|
529
|
+
templatesDir,
|
|
530
|
+
"pre-commit.sh.hbs",
|
|
531
|
+
"scripts/pre-commit.sh",
|
|
532
|
+
context
|
|
533
|
+
);
|
|
534
|
+
await this.writeFile(
|
|
535
|
+
context.targetDir,
|
|
536
|
+
"CHECK_SYSTEM.md",
|
|
537
|
+
"# Project Check System\n\nRun `bun run validate` to check everything.\n"
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
log(message) {
|
|
541
|
+
if (this.config.verbose) {
|
|
542
|
+
console.log(message);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
|
|
546
|
+
const toPascalCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1 $2").split(/[-_ ]+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
547
|
+
const toCamelCase = (str) => {
|
|
548
|
+
const pascal = toPascalCase(str);
|
|
549
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
550
|
+
};
|
|
551
|
+
const toSnakeCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1_$2").split(/[-_ ]+/).map((word) => word.toLowerCase()).join("_");
|
|
552
|
+
const toKebabCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").split(/[-_ ]+/).map((word) => word.toLowerCase()).join("-");
|
|
553
|
+
return {
|
|
554
|
+
name,
|
|
555
|
+
namePascalCase: toPascalCase(name),
|
|
556
|
+
nameCamelCase: toCamelCase(name),
|
|
557
|
+
nameSnakeCase: toSnakeCase(name),
|
|
558
|
+
nameKebabCase: toKebabCase(name),
|
|
559
|
+
targetDir,
|
|
560
|
+
architecture,
|
|
561
|
+
packageManager,
|
|
562
|
+
year: (/* @__PURE__ */ new Date()).getFullYear().toString(),
|
|
563
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
564
|
+
...extra
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// src/utils/ConfigGenerator.ts
|
|
570
|
+
var ConfigGenerator = class {
|
|
476
571
|
/**
|
|
477
|
-
* Generate
|
|
572
|
+
* Generate app configuration (simple version for Clean Architecture)
|
|
478
573
|
*/
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
COPY package.json bun.lockb /temp/dev/
|
|
488
|
-
RUN cd /temp/dev && bun install --frozen-lockfile
|
|
489
|
-
|
|
490
|
-
# Build application
|
|
491
|
-
FROM base AS build
|
|
492
|
-
COPY --from=install /temp/dev/node_modules node_modules
|
|
493
|
-
COPY . .
|
|
494
|
-
ENV NODE_ENV=production
|
|
495
|
-
RUN bun run build
|
|
496
|
-
|
|
497
|
-
# Final production image
|
|
498
|
-
FROM base AS release
|
|
499
|
-
COPY --from=build /usr/src/app/${entrypoint} index.js
|
|
500
|
-
COPY --from=build /usr/src/app/package.json .
|
|
501
|
-
|
|
502
|
-
# Create a non-root user for security
|
|
503
|
-
USER bun
|
|
504
|
-
EXPOSE 3000/tcp
|
|
505
|
-
ENTRYPOINT [ "bun", "run", "index.js" ]
|
|
574
|
+
static generateSimpleAppConfig(context) {
|
|
575
|
+
return `export default {
|
|
576
|
+
name: process.env.APP_NAME ?? '${context.name}',
|
|
577
|
+
env: process.env.APP_ENV ?? 'development',
|
|
578
|
+
debug: process.env.APP_DEBUG === 'true',
|
|
579
|
+
url: process.env.APP_URL ?? 'http://localhost:3000',
|
|
580
|
+
key: process.env.APP_KEY,
|
|
581
|
+
}
|
|
506
582
|
`;
|
|
507
583
|
}
|
|
508
584
|
/**
|
|
509
|
-
* Generate
|
|
585
|
+
* Generate app configuration (detailed version for Enterprise MVC)
|
|
510
586
|
*/
|
|
511
|
-
|
|
512
|
-
return
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
*.log
|
|
517
|
-
.vscode
|
|
518
|
-
.idea
|
|
519
|
-
tests
|
|
520
|
-
`;
|
|
521
|
-
}
|
|
587
|
+
static generateDetailedAppConfig(context) {
|
|
588
|
+
return `/**
|
|
589
|
+
* Application Configuration
|
|
590
|
+
*/
|
|
591
|
+
export default {
|
|
522
592
|
/**
|
|
523
|
-
*
|
|
593
|
+
* Application name
|
|
524
594
|
*/
|
|
525
|
-
|
|
526
|
-
const profile = context.profile || "core";
|
|
527
|
-
let envContent = `# ============================================================================
|
|
528
|
-
# Application Configuration
|
|
529
|
-
# ============================================================================
|
|
530
|
-
|
|
531
|
-
APP_NAME=${context.name}
|
|
532
|
-
APP_ENV=development
|
|
533
|
-
APP_DEBUG=true
|
|
534
|
-
APP_URL=http://localhost:3000
|
|
535
|
-
APP_KEY=
|
|
536
|
-
|
|
537
|
-
# ============================================================================
|
|
538
|
-
# Database Configuration
|
|
539
|
-
# ============================================================================
|
|
540
|
-
|
|
541
|
-
# Database Connection (sqlite, postgres, mysql)
|
|
542
|
-
DB_CONNECTION=${profile === "core" ? "sqlite" : "postgres"}
|
|
543
|
-
|
|
544
|
-
# SQLite Configuration (when DB_CONNECTION=sqlite)
|
|
545
|
-
DB_DATABASE=database/database.sqlite
|
|
546
|
-
|
|
547
|
-
# PostgreSQL Configuration (when DB_CONNECTION=postgres)
|
|
548
|
-
${profile !== "core" ? `DB_HOST=127.0.0.1
|
|
549
|
-
DB_PORT=5432
|
|
550
|
-
DB_DATABASE=${context.name}
|
|
551
|
-
DB_USERNAME=postgres
|
|
552
|
-
DB_PASSWORD=
|
|
553
|
-
DB_SSLMODE=prefer` : `# DB_HOST=127.0.0.1
|
|
554
|
-
# DB_PORT=5432
|
|
555
|
-
# DB_DATABASE=${context.name}
|
|
556
|
-
# DB_USERNAME=postgres
|
|
557
|
-
# DB_PASSWORD=
|
|
558
|
-
# DB_SSLMODE=prefer`}
|
|
559
|
-
|
|
560
|
-
# MySQL Configuration (when DB_CONNECTION=mysql)
|
|
561
|
-
# DB_HOST=127.0.0.1
|
|
562
|
-
# DB_PORT=3306
|
|
563
|
-
# DB_DATABASE=${context.name}
|
|
564
|
-
# DB_USERNAME=root
|
|
565
|
-
# DB_PASSWORD=
|
|
566
|
-
|
|
567
|
-
# ============================================================================
|
|
568
|
-
# Redis Configuration (@gravito/plasma)
|
|
569
|
-
# ============================================================================
|
|
570
|
-
|
|
571
|
-
# Default Redis Connection
|
|
572
|
-
REDIS_CONNECTION=default
|
|
573
|
-
REDIS_HOST=127.0.0.1
|
|
574
|
-
REDIS_PORT=6379
|
|
575
|
-
REDIS_PASSWORD=
|
|
576
|
-
REDIS_DB=0
|
|
577
|
-
|
|
578
|
-
# Redis Connection Options
|
|
579
|
-
REDIS_CONNECT_TIMEOUT=10000
|
|
580
|
-
REDIS_COMMAND_TIMEOUT=5000
|
|
581
|
-
REDIS_KEY_PREFIX=
|
|
582
|
-
REDIS_MAX_RETRIES=3
|
|
583
|
-
REDIS_RETRY_DELAY=1000
|
|
584
|
-
|
|
585
|
-
# Cache-specific Redis Connection (optional, falls back to default)
|
|
586
|
-
# REDIS_CACHE_HOST=127.0.0.1
|
|
587
|
-
# REDIS_CACHE_PORT=6379
|
|
588
|
-
# REDIS_CACHE_PASSWORD=
|
|
589
|
-
REDIS_CACHE_DB=1
|
|
590
|
-
|
|
591
|
-
# Queue-specific Redis Connection (optional, falls back to default)
|
|
592
|
-
# REDIS_QUEUE_HOST=127.0.0.1
|
|
593
|
-
# REDIS_QUEUE_PORT=6379
|
|
594
|
-
# REDIS_QUEUE_PASSWORD=
|
|
595
|
-
REDIS_QUEUE_DB=2
|
|
596
|
-
|
|
597
|
-
# ============================================================================
|
|
598
|
-
# Cache Configuration (@gravito/stasis)
|
|
599
|
-
# ============================================================================
|
|
600
|
-
|
|
601
|
-
# Cache Driver (memory, file, redis)
|
|
602
|
-
CACHE_DRIVER=${profile === "core" ? "memory" : "redis"}
|
|
603
|
-
|
|
604
|
-
# File Cache Path (when CACHE_DRIVER=file)
|
|
605
|
-
CACHE_PATH=storage/framework/cache
|
|
606
|
-
|
|
607
|
-
# Redis Cache Configuration (when CACHE_DRIVER=redis)
|
|
608
|
-
REDIS_CACHE_CONNECTION=cache
|
|
609
|
-
REDIS_CACHE_PREFIX=cache:
|
|
610
|
-
|
|
611
|
-
# ============================================================================
|
|
612
|
-
# Queue Configuration (@gravito/stream)
|
|
613
|
-
# ============================================================================
|
|
614
|
-
|
|
615
|
-
# Queue Connection (sync, memory, database, redis, kafka, sqs, rabbitmq)
|
|
616
|
-
QUEUE_CONNECTION=${profile === "core" ? "sync" : "redis"}
|
|
617
|
-
|
|
618
|
-
# Database Queue Configuration (when QUEUE_CONNECTION=database)
|
|
619
|
-
QUEUE_TABLE=jobs
|
|
620
|
-
|
|
621
|
-
# Redis Queue Configuration (when QUEUE_CONNECTION=redis)
|
|
622
|
-
REDIS_PREFIX=queue:
|
|
595
|
+
name: process.env.APP_NAME ?? '${context.name}',
|
|
623
596
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
# KAFKA_CONSUMER_GROUP_ID=gravito-workers
|
|
629
|
-
# KAFKA_CLIENT_ID=${context.name}
|
|
630
|
-
|
|
631
|
-
# AWS SQS Queue Configuration (when QUEUE_CONNECTION=sqs)
|
|
632
|
-
# AWS_REGION=us-east-1
|
|
633
|
-
# SQS_QUEUE_URL_PREFIX=
|
|
634
|
-
# SQS_VISIBILITY_TIMEOUT=30
|
|
635
|
-
# SQS_WAIT_TIME_SECONDS=20
|
|
636
|
-
|
|
637
|
-
# RabbitMQ Queue Configuration (when QUEUE_CONNECTION=rabbitmq)
|
|
638
|
-
# RABBITMQ_URL=amqp://localhost
|
|
639
|
-
# RABBITMQ_EXCHANGE=gravito.events
|
|
640
|
-
# RABBITMQ_EXCHANGE_TYPE=fanout
|
|
597
|
+
/**
|
|
598
|
+
* Application environment
|
|
599
|
+
*/
|
|
600
|
+
env: process.env.APP_ENV ?? 'development',
|
|
641
601
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
# ============================================================================
|
|
602
|
+
/**
|
|
603
|
+
* Application port
|
|
604
|
+
*/
|
|
605
|
+
port: Number.parseInt(process.env.PORT ?? '3000', 10),
|
|
647
606
|
|
|
648
|
-
LOG_LEVEL=debug
|
|
649
|
-
`;
|
|
650
|
-
return envContent;
|
|
651
|
-
}
|
|
652
607
|
/**
|
|
653
|
-
*
|
|
608
|
+
* View directory
|
|
654
609
|
*/
|
|
655
|
-
|
|
656
|
-
return `# Dependencies
|
|
657
|
-
node_modules/
|
|
610
|
+
VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
|
|
658
611
|
|
|
659
|
-
|
|
660
|
-
|
|
612
|
+
/**
|
|
613
|
+
* Debug mode
|
|
614
|
+
*/
|
|
615
|
+
debug: process.env.APP_DEBUG === 'true',
|
|
661
616
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
.env
|
|
617
|
+
/**
|
|
618
|
+
* Application URL
|
|
619
|
+
*/
|
|
620
|
+
url: process.env.APP_URL ?? 'http://localhost:3000',
|
|
666
621
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
*.swo
|
|
622
|
+
/**
|
|
623
|
+
* Timezone
|
|
624
|
+
*/
|
|
625
|
+
timezone: 'UTC',
|
|
672
626
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
627
|
+
/**
|
|
628
|
+
* Locale
|
|
629
|
+
*/
|
|
630
|
+
locale: 'en',
|
|
676
631
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
632
|
+
/**
|
|
633
|
+
* Fallback locale
|
|
634
|
+
*/
|
|
635
|
+
fallbackLocale: 'en',
|
|
680
636
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
637
|
+
/**
|
|
638
|
+
* Encryption key
|
|
639
|
+
*/
|
|
640
|
+
key: process.env.APP_KEY,
|
|
684
641
|
|
|
685
|
-
# Coverage
|
|
686
|
-
coverage/
|
|
687
|
-
`;
|
|
688
|
-
}
|
|
689
642
|
/**
|
|
690
|
-
*
|
|
643
|
+
* Service providers to register
|
|
691
644
|
*/
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
declaration: true,
|
|
702
|
-
experimentalDecorators: true,
|
|
703
|
-
emitDecoratorMetadata: true,
|
|
704
|
-
types: ["bun-types"],
|
|
705
|
-
outDir: "./dist",
|
|
706
|
-
rootDir: "./src",
|
|
707
|
-
baseUrl: ".",
|
|
708
|
-
paths: {
|
|
709
|
-
"@/*": ["./src/*"]
|
|
710
|
-
}
|
|
711
|
-
},
|
|
712
|
-
include: ["src/**/*"],
|
|
713
|
-
exclude: ["node_modules", "dist"]
|
|
714
|
-
};
|
|
715
|
-
return JSON.stringify(config, null, 2);
|
|
645
|
+
providers: [
|
|
646
|
+
// Framework providers
|
|
647
|
+
// 'RouteServiceProvider',
|
|
648
|
+
|
|
649
|
+
// Application providers
|
|
650
|
+
// 'AppServiceProvider',
|
|
651
|
+
],
|
|
652
|
+
}
|
|
653
|
+
`;
|
|
716
654
|
}
|
|
717
655
|
/**
|
|
718
|
-
* Generate
|
|
656
|
+
* Generate database configuration (simple version)
|
|
719
657
|
*/
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
658
|
+
static generateSimpleDatabaseConfig() {
|
|
659
|
+
return `export default {
|
|
660
|
+
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
661
|
+
connections: {
|
|
662
|
+
sqlite: {
|
|
663
|
+
driver: 'sqlite',
|
|
664
|
+
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
}
|
|
668
|
+
`;
|
|
731
669
|
}
|
|
732
670
|
/**
|
|
733
|
-
* Generate
|
|
671
|
+
* Generate database configuration (detailed version)
|
|
734
672
|
*/
|
|
735
|
-
|
|
673
|
+
static generateDetailedDatabaseConfig() {
|
|
736
674
|
return `/**
|
|
737
|
-
*
|
|
738
|
-
*
|
|
739
|
-
* \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
|
|
740
|
-
* \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
675
|
+
* Database Configuration
|
|
741
676
|
*/
|
|
677
|
+
export default {
|
|
678
|
+
/**
|
|
679
|
+
* Default connection
|
|
680
|
+
*/
|
|
681
|
+
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
742
682
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
interface PackageInfo {
|
|
752
|
-
name: string
|
|
753
|
-
current: string
|
|
754
|
-
latest: string
|
|
755
|
-
outdated: boolean
|
|
756
|
-
}
|
|
683
|
+
/**
|
|
684
|
+
* Database connections
|
|
685
|
+
*/
|
|
686
|
+
connections: {
|
|
687
|
+
sqlite: {
|
|
688
|
+
driver: 'sqlite',
|
|
689
|
+
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
690
|
+
},
|
|
757
691
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
692
|
+
mysql: {
|
|
693
|
+
driver: 'mysql',
|
|
694
|
+
host: process.env.DB_HOST ?? 'localhost',
|
|
695
|
+
port: Number(process.env.DB_PORT ?? 3306),
|
|
696
|
+
database: process.env.DB_DATABASE ?? 'forge',
|
|
697
|
+
username: process.env.DB_USERNAME ?? 'forge',
|
|
698
|
+
password: process.env.DB_PASSWORD ?? '',
|
|
699
|
+
},
|
|
765
700
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
701
|
+
postgres: {
|
|
702
|
+
driver: 'postgres',
|
|
703
|
+
host: process.env.DB_HOST ?? 'localhost',
|
|
704
|
+
port: Number(process.env.DB_PORT ?? 5432),
|
|
705
|
+
database: process.env.DB_DATABASE ?? 'forge',
|
|
706
|
+
username: process.env.DB_USERNAME ?? 'forge',
|
|
707
|
+
password: process.env.DB_PASSWORD ?? '',
|
|
708
|
+
},
|
|
709
|
+
},
|
|
769
710
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
}
|
|
777
|
-
|
|
711
|
+
/**
|
|
712
|
+
* Migration settings
|
|
713
|
+
*/
|
|
714
|
+
migrations: {
|
|
715
|
+
table: 'migrations',
|
|
716
|
+
path: 'database/migrations',
|
|
717
|
+
},
|
|
718
|
+
}
|
|
719
|
+
`;
|
|
778
720
|
}
|
|
721
|
+
/**
|
|
722
|
+
* Generate auth configuration
|
|
723
|
+
*/
|
|
724
|
+
static generateAuthConfig() {
|
|
725
|
+
return `export default {
|
|
726
|
+
defaults: { guard: 'web' },
|
|
727
|
+
guards: {
|
|
728
|
+
web: { driver: 'session', provider: 'users' },
|
|
729
|
+
api: { driver: 'token', provider: 'users' },
|
|
730
|
+
},
|
|
779
731
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
732
|
+
`;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Generate cache configuration
|
|
736
|
+
*/
|
|
737
|
+
static generateCacheConfig() {
|
|
738
|
+
return `export default {
|
|
739
|
+
default: process.env.CACHE_DRIVER ?? 'memory',
|
|
740
|
+
stores: {
|
|
741
|
+
memory: { driver: 'memory' },
|
|
742
|
+
},
|
|
743
|
+
}
|
|
744
|
+
`;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Generate logging configuration
|
|
748
|
+
*/
|
|
749
|
+
static generateLoggingConfig() {
|
|
750
|
+
return `export default {
|
|
751
|
+
default: process.env.LOG_CHANNEL ?? 'console',
|
|
752
|
+
channels: {
|
|
753
|
+
console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
|
|
754
|
+
},
|
|
784
755
|
}
|
|
756
|
+
`;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Generate view configuration
|
|
760
|
+
*/
|
|
761
|
+
static generateViewConfig() {
|
|
762
|
+
return `/**
|
|
763
|
+
* View Configuration
|
|
764
|
+
*/
|
|
765
|
+
export default {
|
|
766
|
+
/**
|
|
767
|
+
* View engine
|
|
768
|
+
*/
|
|
769
|
+
engine: 'html',
|
|
785
770
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
771
|
+
/**
|
|
772
|
+
* View directory
|
|
773
|
+
*/
|
|
774
|
+
path: 'src/views',
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Cache views in production
|
|
778
|
+
*/
|
|
779
|
+
cache: process.env.NODE_ENV === 'production',
|
|
780
|
+
}
|
|
781
|
+
`;
|
|
793
782
|
}
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
// src/utils/ServiceProviderGenerator.ts
|
|
786
|
+
var ServiceProviderGenerator = class {
|
|
787
|
+
/**
|
|
788
|
+
* Generate App Service Provider
|
|
789
|
+
*/
|
|
790
|
+
static generateAppServiceProvider(context, architectureName) {
|
|
791
|
+
return `/**
|
|
792
|
+
* App Service Provider
|
|
793
|
+
*/
|
|
794
794
|
|
|
795
|
-
|
|
796
|
-
const latest = await getLatestVersion(name)
|
|
795
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
797
796
|
|
|
798
|
-
|
|
799
|
-
|
|
797
|
+
export class AppServiceProvider extends ServiceProvider {
|
|
798
|
+
register(_container: Container): void {
|
|
799
|
+
// Register application services
|
|
800
800
|
}
|
|
801
801
|
|
|
802
|
-
|
|
803
|
-
name
|
|
804
|
-
current,
|
|
805
|
-
latest,
|
|
806
|
-
outdated: current !== latest,
|
|
802
|
+
boot(_core: PlanetCore): void {
|
|
803
|
+
console.log('${context.name} (${architectureName}) booted!')
|
|
807
804
|
}
|
|
808
805
|
}
|
|
806
|
+
`;
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Generate Middleware Provider
|
|
810
|
+
*/
|
|
811
|
+
static generateMiddlewareProvider() {
|
|
812
|
+
return `/**
|
|
813
|
+
* Middleware Service Provider
|
|
814
|
+
*/
|
|
809
815
|
|
|
810
|
-
|
|
811
|
-
|
|
816
|
+
import {
|
|
817
|
+
ServiceProvider,
|
|
818
|
+
type Container,
|
|
819
|
+
type PlanetCore,
|
|
820
|
+
bodySizeLimit,
|
|
821
|
+
securityHeaders,
|
|
822
|
+
} from '@gravito/core'
|
|
812
823
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
readFileSync(packageJsonPath, 'utf-8')
|
|
816
|
-
)
|
|
824
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
825
|
+
register(_container: Container): void {}
|
|
817
826
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
...packageJson.devDependencies,
|
|
821
|
-
}
|
|
827
|
+
boot(core: PlanetCore): void {
|
|
828
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
822
829
|
|
|
823
|
-
|
|
830
|
+
core.adapter.use('*', securityHeaders({
|
|
831
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
832
|
+
}))
|
|
824
833
|
|
|
825
|
-
|
|
826
|
-
const outdated: PackageInfo[] = []
|
|
827
|
-
const upToDate: PackageInfo[] = []
|
|
834
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
828
835
|
|
|
829
|
-
|
|
830
|
-
for (const [name, version] of Object.entries(allDependencies)) {
|
|
831
|
-
const info = await checkPackage(name, version)
|
|
832
|
-
if (info) {
|
|
833
|
-
results.push(info)
|
|
834
|
-
if (info.outdated) {
|
|
835
|
-
outdated.push(info)
|
|
836
|
-
} else {
|
|
837
|
-
upToDate.push(info)
|
|
838
|
-
}
|
|
839
|
-
}
|
|
836
|
+
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
840
837
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (upToDate.length > 0) {
|
|
844
|
-
log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
|
|
845
|
-
upToDate.forEach((pkg) => {
|
|
846
|
-
log(\` \${pkg.name}: \${pkg.current}\`, 'green')
|
|
847
|
-
})
|
|
838
|
+
}
|
|
839
|
+
`;
|
|
848
840
|
}
|
|
841
|
+
/**
|
|
842
|
+
* Generate Route Provider
|
|
843
|
+
*/
|
|
844
|
+
static generateRouteProvider(routePath = "../../routes/api", importType = "default") {
|
|
845
|
+
const importStatement = importType === "default" ? `import routes from '${routePath}'` : `import { registerApiRoutes } from '${routePath}'`;
|
|
846
|
+
const routeCall = importType === "default" ? "routes(core.router)" : "registerApiRoutes(core.router)";
|
|
847
|
+
return `/**
|
|
848
|
+
* Route Service Provider
|
|
849
|
+
*/
|
|
849
850
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
outdated.forEach((pkg) => {
|
|
853
|
-
log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
|
|
854
|
-
})
|
|
855
|
-
}
|
|
851
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
852
|
+
${importStatement}
|
|
856
853
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
|
|
860
|
-
log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
|
|
861
|
-
log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
|
|
854
|
+
export class RouteProvider extends ServiceProvider {
|
|
855
|
+
register(_container: Container): void {}
|
|
862
856
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
log(' bun update', 'yellow')
|
|
867
|
-
process.exit(1)
|
|
868
|
-
} else {
|
|
869
|
-
log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
|
|
870
|
-
process.exit(0)
|
|
857
|
+
boot(core: PlanetCore): void {
|
|
858
|
+
${routeCall}
|
|
859
|
+
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
871
860
|
}
|
|
872
861
|
}
|
|
873
|
-
|
|
874
|
-
main().catch((error) => {
|
|
875
|
-
log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
|
|
876
|
-
process.exit(1)
|
|
877
|
-
})
|
|
878
862
|
`;
|
|
879
863
|
}
|
|
880
864
|
/**
|
|
881
|
-
* Generate
|
|
865
|
+
* Generate Providers Index
|
|
882
866
|
*/
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
# \u984F\u8272\u5B9A\u7FA9
|
|
892
|
-
GREEN='\\033[0;32m'
|
|
893
|
-
YELLOW='\\033[1;33m'
|
|
894
|
-
RED='\\033[0;31m'
|
|
895
|
-
BLUE='\\033[0;34m'
|
|
896
|
-
NC='\\033[0m' # No Color
|
|
897
|
-
|
|
898
|
-
echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
|
|
899
|
-
|
|
900
|
-
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
901
|
-
if [ ! -f "package.json" ]; then
|
|
902
|
-
echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
|
|
903
|
-
exit 1
|
|
904
|
-
fi
|
|
905
|
-
|
|
906
|
-
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
907
|
-
if ! command -v bun &> /dev/null; then
|
|
908
|
-
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
909
|
-
exit 1
|
|
910
|
-
fi
|
|
911
|
-
|
|
912
|
-
# 1. \u985E\u578B\u6AA2\u67E5
|
|
913
|
-
echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
914
|
-
if bun run typecheck; then
|
|
915
|
-
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
916
|
-
else
|
|
917
|
-
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
918
|
-
exit 1
|
|
919
|
-
fi
|
|
920
|
-
|
|
921
|
-
# 2. \u57F7\u884C\u6E2C\u8A66
|
|
922
|
-
echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
923
|
-
if bun test; then
|
|
924
|
-
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
925
|
-
else
|
|
926
|
-
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
927
|
-
exit 1
|
|
928
|
-
fi
|
|
929
|
-
|
|
930
|
-
# 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
|
|
931
|
-
echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
|
|
932
|
-
if bun run check:deps; then
|
|
933
|
-
echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
|
|
934
|
-
else
|
|
935
|
-
echo -e "\${YELLOW}\u26A0 \u4F9D\u8CF4\u6AA2\u67E5\u6709\u8B66\u544A\uFF08\u67D0\u4E9B\u5957\u4EF6\u53EF\u80FD\u9700\u8981\u66F4\u65B0\uFF09\${NC}\\n"
|
|
936
|
-
fi
|
|
937
|
-
|
|
938
|
-
echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
|
|
867
|
+
static generateProvidersIndex(providers = ["AppServiceProvider", "MiddlewareProvider", "RouteProvider"]) {
|
|
868
|
+
const exports = providers.map((p) => `export { ${p} } from './${p}'`).join("\n");
|
|
869
|
+
return `/**
|
|
870
|
+
* Application Service Providers
|
|
871
|
+
*/
|
|
872
|
+
|
|
873
|
+
${exports}
|
|
939
874
|
`;
|
|
940
875
|
}
|
|
941
876
|
/**
|
|
942
|
-
* Generate
|
|
877
|
+
* Generate Repository Service Provider
|
|
943
878
|
*/
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
971
|
-
if [ ! -f "package.json" ]; then
|
|
972
|
-
echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
|
|
973
|
-
exit 1
|
|
974
|
-
fi
|
|
975
|
-
|
|
976
|
-
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
977
|
-
if ! command -v bun &> /dev/null; then
|
|
978
|
-
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
979
|
-
exit 1
|
|
980
|
-
fi
|
|
981
|
-
|
|
982
|
-
# 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
|
|
983
|
-
echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
984
|
-
if bun run typecheck; then
|
|
985
|
-
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
986
|
-
else
|
|
987
|
-
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
988
|
-
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
989
|
-
exit 1
|
|
990
|
-
fi
|
|
991
|
-
|
|
992
|
-
# 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
|
|
993
|
-
echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
994
|
-
if bun test; then
|
|
995
|
-
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
996
|
-
else
|
|
997
|
-
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
998
|
-
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
999
|
-
exit 1
|
|
1000
|
-
fi
|
|
1001
|
-
|
|
1002
|
-
echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
|
|
879
|
+
static generateRepositoryServiceProvider(repositories = [], additionalServices = []) {
|
|
880
|
+
const repositoryRegistrations = repositories.map((repo) => ` container.singleton('${repo}', () => new ${repo}())`).join("\n");
|
|
881
|
+
const serviceRegistrations = additionalServices.map((service) => ` container.singleton('${service}', () => new ${service}())`).join("\n");
|
|
882
|
+
const imports = [
|
|
883
|
+
...repositories.map(
|
|
884
|
+
(repo) => `import { ${repo} } from '../Persistence/Repositories/${repo}'`
|
|
885
|
+
),
|
|
886
|
+
...additionalServices.map(
|
|
887
|
+
(service) => `import { ${service} } from '../ExternalServices/${service}'`
|
|
888
|
+
)
|
|
889
|
+
].join("\n");
|
|
890
|
+
return `/**
|
|
891
|
+
* Repository Service Provider
|
|
892
|
+
*
|
|
893
|
+
* Binds repository interfaces to implementations.
|
|
894
|
+
*/
|
|
895
|
+
|
|
896
|
+
import { ServiceProvider, type Container } from '@gravito/core'
|
|
897
|
+
${imports}
|
|
898
|
+
|
|
899
|
+
export class RepositoryServiceProvider extends ServiceProvider {
|
|
900
|
+
register(container: Container): void {
|
|
901
|
+
${repositoryRegistrations || " // Bind repositories here"}
|
|
902
|
+
${serviceRegistrations || " // Bind external services here"}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
1003
905
|
`;
|
|
1004
906
|
}
|
|
1005
907
|
/**
|
|
1006
|
-
* Generate
|
|
908
|
+
* Generate Database Provider
|
|
1007
909
|
*/
|
|
1008
|
-
|
|
1009
|
-
return
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
## \u5FEB\u901F\u958B\u59CB
|
|
1014
|
-
|
|
1015
|
-
### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
|
|
1016
|
-
\`\`\`bash
|
|
1017
|
-
bun run validate
|
|
1018
|
-
\`\`\`
|
|
1019
|
-
|
|
1020
|
-
### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
|
|
1021
|
-
\`\`\`bash
|
|
1022
|
-
# \u985E\u578B\u6AA2\u67E5
|
|
1023
|
-
bun run typecheck
|
|
1024
|
-
|
|
1025
|
-
# \u6E2C\u8A66
|
|
1026
|
-
bun run test
|
|
1027
|
-
|
|
1028
|
-
# \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
1029
|
-
bun run check:deps
|
|
1030
|
-
\`\`\`
|
|
1031
|
-
|
|
1032
|
-
## \u53EF\u7528\u547D\u4EE4
|
|
1033
|
-
|
|
1034
|
-
### Package.json \u8173\u672C
|
|
1035
|
-
|
|
1036
|
-
| \u547D\u4EE4 | \u8AAA\u660E |
|
|
1037
|
-
|------|------|
|
|
1038
|
-
| \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
|
|
1039
|
-
| \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
|
|
1040
|
-
| \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
|
|
1041
|
-
| \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
|
|
1042
|
-
| \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
|
|
1043
|
-
| \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
|
|
1044
|
-
|
|
1045
|
-
### Shell \u8173\u672C
|
|
1046
|
-
|
|
1047
|
-
| \u8173\u672C | \u8AAA\u660E |
|
|
1048
|
-
|------|------|
|
|
1049
|
-
| \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
|
|
1050
|
-
| \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
|
|
1051
|
-
|
|
1052
|
-
## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
1053
|
-
|
|
1054
|
-
\u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
|
|
1055
|
-
|
|
1056
|
-
\`\`\`bash
|
|
1057
|
-
# \u5B89\u88DD pre-commit hook
|
|
1058
|
-
ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
|
|
1059
|
-
|
|
1060
|
-
# \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
|
|
1061
|
-
cp scripts/pre-commit.sh .git/hooks/pre-commit
|
|
1062
|
-
chmod +x .git/hooks/pre-commit
|
|
1063
|
-
\`\`\`
|
|
1064
|
-
|
|
1065
|
-
**\u529F\u80FD\uFF1A**
|
|
1066
|
-
- \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
|
|
1067
|
-
- \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
|
|
1068
|
-
- \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
|
|
1069
|
-
|
|
1070
|
-
**\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
|
|
1071
|
-
\`\`\`bash
|
|
1072
|
-
git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
|
|
1073
|
-
\`\`\`
|
|
1074
|
-
|
|
1075
|
-
## \u6AA2\u67E5\u9805\u76EE
|
|
1076
|
-
|
|
1077
|
-
### 1. \u985E\u578B\u6AA2\u67E5
|
|
1078
|
-
- \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
|
|
1079
|
-
- \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
|
|
1080
|
-
|
|
1081
|
-
### 2. \u6E2C\u8A66
|
|
1082
|
-
- \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
|
|
1083
|
-
- \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
|
|
1084
|
-
|
|
1085
|
-
### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
|
|
1086
|
-
- \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
|
|
1087
|
-
- \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
1088
|
-
- \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
|
|
1089
|
-
|
|
1090
|
-
## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
|
|
1091
|
-
|
|
1092
|
-
### \u958B\u767C\u6642
|
|
1093
|
-
1. \u958B\u767C\u529F\u80FD
|
|
1094
|
-
2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
|
|
1095
|
-
3. \u4FEE\u6B63\u554F\u984C
|
|
1096
|
-
4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
|
|
1097
|
-
|
|
1098
|
-
### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
1099
|
-
1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
|
|
1100
|
-
2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
|
|
1101
|
-
3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
|
|
1102
|
-
4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
|
|
1103
|
-
|
|
1104
|
-
## \u6A94\u6848\u7D50\u69CB
|
|
1105
|
-
|
|
1106
|
-
\`\`\`
|
|
1107
|
-
${context.nameKebabCase}/
|
|
1108
|
-
\u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
|
|
1109
|
-
\u251C\u2500\u2500 scripts/
|
|
1110
|
-
\u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
|
|
1111
|
-
\u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
1112
|
-
\u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
|
|
1113
|
-
\u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
|
|
1114
|
-
\`\`\`
|
|
1115
|
-
|
|
1116
|
-
## \u6CE8\u610F\u4E8B\u9805
|
|
1117
|
-
|
|
1118
|
-
1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
|
|
1119
|
-
2. **\u6E2C\u8A66\u6642\u9593**\uFF1A\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\uFF0C\u53EF\u4EE5\u7DE8\u8F2F \`pre-commit.sh\` \u8A3B\u89E3\u6389\u6E2C\u8A66\u90E8\u5206
|
|
1120
|
-
3. **\u985E\u578B\u932F\u8AA4**\uFF1A\u5C08\u6848\u4E2D\u53EF\u80FD\u9084\u6709\u4E00\u4E9B\u65E2\u6709\u7684\u985E\u578B\u932F\u8AA4\uFF0C\u5EFA\u8B70\u9010\u6B65\u4FEE\u6B63
|
|
1121
|
-
|
|
1122
|
-
## \u6545\u969C\u6392\u9664
|
|
1123
|
-
|
|
1124
|
-
### \u6AA2\u67E5\u5931\u6557
|
|
1125
|
-
1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
|
|
1126
|
-
2. \u4FEE\u6B63\u554F\u984C
|
|
1127
|
-
3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
|
|
910
|
+
static generateDatabaseProvider() {
|
|
911
|
+
return `/**
|
|
912
|
+
* Database Service Provider
|
|
913
|
+
*/
|
|
1128
914
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
\`\`\`bash
|
|
1132
|
-
git commit --no-verify
|
|
1133
|
-
\`\`\`
|
|
915
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
916
|
+
import { OrbitAtlas } from '@gravito/atlas'
|
|
1134
917
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
\`\`\`
|
|
1139
|
-
`;
|
|
918
|
+
export class DatabaseProvider extends ServiceProvider {
|
|
919
|
+
register(_container: Container): void {
|
|
920
|
+
// Register database connections
|
|
1140
921
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
if (this.config.verbose) {
|
|
1146
|
-
console.log(message);
|
|
1147
|
-
}
|
|
922
|
+
|
|
923
|
+
boot(core: PlanetCore): void {
|
|
924
|
+
// Initialize database
|
|
925
|
+
core.logger.info('\u{1F5C4}\uFE0F Database initialized')
|
|
1148
926
|
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
*/
|
|
1152
|
-
static createContext(name, targetDir, architecture, packageManager = "bun", extra = {}) {
|
|
1153
|
-
const now = /* @__PURE__ */ new Date();
|
|
1154
|
-
const pascalCase = name.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toUpperCase());
|
|
1155
|
-
const camelCase = pascalCase.replace(/^./, (c) => c.toLowerCase());
|
|
1156
|
-
const snakeCase = name.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "").replace(/[-\s]+/g, "_");
|
|
1157
|
-
const kebabCase = name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "").replace(/[_\s]+/g, "-");
|
|
1158
|
-
return {
|
|
1159
|
-
name,
|
|
1160
|
-
namePascalCase: pascalCase,
|
|
1161
|
-
nameCamelCase: camelCase,
|
|
1162
|
-
nameSnakeCase: snakeCase,
|
|
1163
|
-
nameKebabCase: kebabCase,
|
|
1164
|
-
targetDir,
|
|
1165
|
-
architecture,
|
|
1166
|
-
packageManager,
|
|
1167
|
-
year: now.getFullYear().toString(),
|
|
1168
|
-
date: now.toISOString().split("T")[0] ?? now.toISOString().slice(0, 10),
|
|
1169
|
-
...extra
|
|
1170
|
-
};
|
|
927
|
+
}
|
|
928
|
+
`;
|
|
1171
929
|
}
|
|
1172
930
|
};
|
|
1173
931
|
|
|
@@ -1421,57 +1179,22 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
|
|
|
1421
1179
|
];
|
|
1422
1180
|
}
|
|
1423
1181
|
// ─────────────────────────────────────────────────────────────
|
|
1424
|
-
// Config Generators (
|
|
1182
|
+
// Config Generators (using shared ConfigGenerator)
|
|
1425
1183
|
// ─────────────────────────────────────────────────────────────
|
|
1426
1184
|
generateAppConfig(context) {
|
|
1427
|
-
return
|
|
1428
|
-
name: process.env.APP_NAME ?? '${context.name}',
|
|
1429
|
-
env: process.env.APP_ENV ?? 'development',
|
|
1430
|
-
debug: process.env.APP_DEBUG === 'true',
|
|
1431
|
-
url: process.env.APP_URL ?? 'http://localhost:3000',
|
|
1432
|
-
key: process.env.APP_KEY,
|
|
1433
|
-
}
|
|
1434
|
-
`;
|
|
1185
|
+
return ConfigGenerator.generateSimpleAppConfig(context);
|
|
1435
1186
|
}
|
|
1436
1187
|
generateDatabaseConfig() {
|
|
1437
|
-
return
|
|
1438
|
-
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
1439
|
-
connections: {
|
|
1440
|
-
sqlite: {
|
|
1441
|
-
driver: 'sqlite',
|
|
1442
|
-
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
1443
|
-
},
|
|
1444
|
-
},
|
|
1445
|
-
}
|
|
1446
|
-
`;
|
|
1188
|
+
return ConfigGenerator.generateSimpleDatabaseConfig();
|
|
1447
1189
|
}
|
|
1448
1190
|
generateAuthConfig() {
|
|
1449
|
-
return
|
|
1450
|
-
defaults: { guard: 'web' },
|
|
1451
|
-
guards: {
|
|
1452
|
-
web: { driver: 'session', provider: 'users' },
|
|
1453
|
-
api: { driver: 'token', provider: 'users' },
|
|
1454
|
-
},
|
|
1455
|
-
}
|
|
1456
|
-
`;
|
|
1191
|
+
return ConfigGenerator.generateAuthConfig();
|
|
1457
1192
|
}
|
|
1458
1193
|
generateCacheConfig() {
|
|
1459
|
-
return
|
|
1460
|
-
default: process.env.CACHE_DRIVER ?? 'memory',
|
|
1461
|
-
stores: {
|
|
1462
|
-
memory: { driver: 'memory' },
|
|
1463
|
-
},
|
|
1464
|
-
}
|
|
1465
|
-
`;
|
|
1194
|
+
return ConfigGenerator.generateCacheConfig();
|
|
1466
1195
|
}
|
|
1467
1196
|
generateLoggingConfig() {
|
|
1468
|
-
return
|
|
1469
|
-
default: process.env.LOG_CHANNEL ?? 'console',
|
|
1470
|
-
channels: {
|
|
1471
|
-
console: { driver: 'console', level: process.env.LOG_LEVEL ?? 'debug' },
|
|
1472
|
-
},
|
|
1473
|
-
}
|
|
1474
|
-
`;
|
|
1197
|
+
return ConfigGenerator.generateLoggingConfig();
|
|
1475
1198
|
}
|
|
1476
1199
|
generateUserEntity() {
|
|
1477
1200
|
return `/**
|
|
@@ -1798,103 +1521,30 @@ export class MailService implements IMailService {
|
|
|
1798
1521
|
`;
|
|
1799
1522
|
}
|
|
1800
1523
|
generateAppServiceProvider(context) {
|
|
1801
|
-
return
|
|
1802
|
-
* App Service Provider
|
|
1803
|
-
*/
|
|
1804
|
-
|
|
1805
|
-
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
1806
|
-
|
|
1807
|
-
export class AppServiceProvider extends ServiceProvider {
|
|
1808
|
-
register(_container: Container): void {
|
|
1809
|
-
// Register application services
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
boot(_core: PlanetCore): void {
|
|
1813
|
-
console.log('${context.name} (Clean Architecture) booted!')
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
`;
|
|
1524
|
+
return ServiceProviderGenerator.generateAppServiceProvider(context, "Clean Architecture");
|
|
1817
1525
|
}
|
|
1818
1526
|
generateRepositoryServiceProvider() {
|
|
1819
|
-
return
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
*/
|
|
1824
|
-
|
|
1825
|
-
import { ServiceProvider, type Container } from '@gravito/core'
|
|
1826
|
-
import { UserRepository } from '../Persistence/Repositories/UserRepository'
|
|
1827
|
-
import { MailService } from '../ExternalServices/MailService'
|
|
1828
|
-
|
|
1829
|
-
export class RepositoryServiceProvider extends ServiceProvider {
|
|
1830
|
-
register(container: Container): void {
|
|
1831
|
-
// Bind repositories
|
|
1832
|
-
container.singleton('userRepository', () => new UserRepository())
|
|
1833
|
-
|
|
1834
|
-
// Bind external services
|
|
1835
|
-
container.singleton('mailService', () => new MailService())
|
|
1836
|
-
}
|
|
1837
|
-
}
|
|
1838
|
-
`;
|
|
1527
|
+
return ServiceProviderGenerator.generateRepositoryServiceProvider(
|
|
1528
|
+
["UserRepository"],
|
|
1529
|
+
["MailService"]
|
|
1530
|
+
);
|
|
1839
1531
|
}
|
|
1840
1532
|
generateProvidersIndex() {
|
|
1841
|
-
return
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
1848
|
-
export { RouteProvider } from './RouteProvider'
|
|
1849
|
-
`;
|
|
1533
|
+
return ServiceProviderGenerator.generateProvidersIndex([
|
|
1534
|
+
"AppServiceProvider",
|
|
1535
|
+
"RepositoryServiceProvider",
|
|
1536
|
+
"MiddlewareProvider",
|
|
1537
|
+
"RouteProvider"
|
|
1538
|
+
]);
|
|
1850
1539
|
}
|
|
1851
1540
|
generateMiddlewareProvider() {
|
|
1852
|
-
return
|
|
1853
|
-
* Middleware Service Provider
|
|
1854
|
-
*/
|
|
1855
|
-
|
|
1856
|
-
import {
|
|
1857
|
-
ServiceProvider,
|
|
1858
|
-
type Container,
|
|
1859
|
-
type PlanetCore,
|
|
1860
|
-
bodySizeLimit,
|
|
1861
|
-
securityHeaders,
|
|
1862
|
-
} from '@gravito/core'
|
|
1863
|
-
|
|
1864
|
-
export class MiddlewareProvider extends ServiceProvider {
|
|
1865
|
-
register(_container: Container): void {}
|
|
1866
|
-
|
|
1867
|
-
boot(core: PlanetCore): void {
|
|
1868
|
-
const isDev = process.env.NODE_ENV !== 'production'
|
|
1869
|
-
|
|
1870
|
-
core.adapter.use('*', securityHeaders({
|
|
1871
|
-
contentSecurityPolicy: isDev ? false : undefined,
|
|
1872
|
-
}))
|
|
1873
|
-
|
|
1874
|
-
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
1875
|
-
|
|
1876
|
-
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
`;
|
|
1541
|
+
return ServiceProviderGenerator.generateMiddlewareProvider();
|
|
1880
1542
|
}
|
|
1881
1543
|
generateRouteProvider() {
|
|
1882
|
-
return
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
1887
|
-
import { registerApiRoutes } from '../../Interface/Http/Routes/api'
|
|
1888
|
-
|
|
1889
|
-
export class RouteProvider extends ServiceProvider {
|
|
1890
|
-
register(_container: Container): void {}
|
|
1891
|
-
|
|
1892
|
-
boot(core: PlanetCore): void {
|
|
1893
|
-
registerApiRoutes(core.router)
|
|
1894
|
-
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
`;
|
|
1544
|
+
return ServiceProviderGenerator.generateRouteProvider(
|
|
1545
|
+
"../../Interface/Http/Routes/api",
|
|
1546
|
+
"named"
|
|
1547
|
+
);
|
|
1898
1548
|
}
|
|
1899
1549
|
// ─────────────────────────────────────────────────────────────
|
|
1900
1550
|
// Interface Layer
|
|
@@ -2117,7 +1767,7 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
2117
1767
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
2118
1768
|
start: "bun run dist/bootstrap.js",
|
|
2119
1769
|
test: "bun test",
|
|
2120
|
-
typecheck: "tsc --noEmit",
|
|
1770
|
+
typecheck: "bun tsc --noEmit",
|
|
2121
1771
|
check: "bun run typecheck && bun run test",
|
|
2122
1772
|
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2123
1773
|
validate: "bun run check && bun run check:deps",
|
|
@@ -2132,107 +1782,16 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
2132
1782
|
},
|
|
2133
1783
|
devDependencies: {
|
|
2134
1784
|
"bun-types": "latest",
|
|
2135
|
-
typescript: "^5.
|
|
1785
|
+
typescript: "^5.9.3"
|
|
2136
1786
|
}
|
|
2137
1787
|
};
|
|
2138
1788
|
return JSON.stringify(pkg, null, 2);
|
|
2139
1789
|
}
|
|
2140
1790
|
};
|
|
2141
1791
|
|
|
2142
|
-
// src/generators/
|
|
2143
|
-
var
|
|
2144
|
-
|
|
2145
|
-
return "ddd";
|
|
2146
|
-
}
|
|
2147
|
-
get displayName() {
|
|
2148
|
-
return "Domain-Driven Design (DDD)";
|
|
2149
|
-
}
|
|
2150
|
-
get description() {
|
|
2151
|
-
return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
|
|
2152
|
-
}
|
|
2153
|
-
getDirectoryStructure(context) {
|
|
2154
|
-
return [
|
|
2155
|
-
{
|
|
2156
|
-
type: "directory",
|
|
2157
|
-
name: "config",
|
|
2158
|
-
children: [
|
|
2159
|
-
{ type: "file", name: "app.ts", content: this.generateAppConfig(context) },
|
|
2160
|
-
{ type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
|
|
2161
|
-
{ type: "file", name: "modules.ts", content: this.generateModulesConfig() },
|
|
2162
|
-
{ type: "file", name: "cache.ts", content: this.generateCacheConfig() },
|
|
2163
|
-
{ type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
|
|
2164
|
-
]
|
|
2165
|
-
},
|
|
2166
|
-
{
|
|
2167
|
-
type: "directory",
|
|
2168
|
-
name: "src",
|
|
2169
|
-
children: [
|
|
2170
|
-
// Bootstrap - Application startup and configuration
|
|
2171
|
-
this.generateBootstrapDirectory(context),
|
|
2172
|
-
// Shared - Cross-module shared components
|
|
2173
|
-
this.generateShared(),
|
|
2174
|
-
// Modules - Bounded Contexts
|
|
2175
|
-
{
|
|
2176
|
-
type: "directory",
|
|
2177
|
-
name: "Modules",
|
|
2178
|
-
children: [
|
|
2179
|
-
this.generateModule("Ordering", context),
|
|
2180
|
-
this.generateModule("Catalog", context)
|
|
2181
|
-
]
|
|
2182
|
-
},
|
|
2183
|
-
{ type: "file", name: "main.ts", content: this.generateMainEntry(context) }
|
|
2184
|
-
]
|
|
2185
|
-
},
|
|
2186
|
-
{
|
|
2187
|
-
type: "directory",
|
|
2188
|
-
name: "tests",
|
|
2189
|
-
children: [
|
|
2190
|
-
{
|
|
2191
|
-
type: "directory",
|
|
2192
|
-
name: "Modules",
|
|
2193
|
-
children: [
|
|
2194
|
-
{
|
|
2195
|
-
type: "directory",
|
|
2196
|
-
name: "Ordering",
|
|
2197
|
-
children: [
|
|
2198
|
-
{
|
|
2199
|
-
type: "directory",
|
|
2200
|
-
name: "Unit",
|
|
2201
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2202
|
-
},
|
|
2203
|
-
{
|
|
2204
|
-
type: "directory",
|
|
2205
|
-
name: "Integration",
|
|
2206
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2207
|
-
}
|
|
2208
|
-
]
|
|
2209
|
-
},
|
|
2210
|
-
{
|
|
2211
|
-
type: "directory",
|
|
2212
|
-
name: "Catalog",
|
|
2213
|
-
children: [
|
|
2214
|
-
{
|
|
2215
|
-
type: "directory",
|
|
2216
|
-
name: "Unit",
|
|
2217
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2218
|
-
}
|
|
2219
|
-
]
|
|
2220
|
-
}
|
|
2221
|
-
]
|
|
2222
|
-
},
|
|
2223
|
-
{
|
|
2224
|
-
type: "directory",
|
|
2225
|
-
name: "Shared",
|
|
2226
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2227
|
-
}
|
|
2228
|
-
]
|
|
2229
|
-
}
|
|
2230
|
-
];
|
|
2231
|
-
}
|
|
2232
|
-
// ─────────────────────────────────────────────────────────────
|
|
2233
|
-
// Bootstrap Directory
|
|
2234
|
-
// ─────────────────────────────────────────────────────────────
|
|
2235
|
-
generateBootstrapDirectory(context) {
|
|
1792
|
+
// src/generators/ddd/BootstrapGenerator.ts
|
|
1793
|
+
var BootstrapGenerator = class {
|
|
1794
|
+
generate(context) {
|
|
2236
1795
|
return {
|
|
2237
1796
|
type: "directory",
|
|
2238
1797
|
name: "Bootstrap",
|
|
@@ -2244,207 +1803,33 @@ var DddGenerator = class extends BaseGenerator {
|
|
|
2244
1803
|
]
|
|
2245
1804
|
};
|
|
2246
1805
|
}
|
|
2247
|
-
|
|
2248
|
-
// Shared Directory (replaces SharedKernel with user's structure)
|
|
2249
|
-
// ─────────────────────────────────────────────────────────────
|
|
2250
|
-
generateShared() {
|
|
1806
|
+
generateConfigDirectory(context) {
|
|
2251
1807
|
return {
|
|
2252
1808
|
type: "directory",
|
|
2253
|
-
name: "
|
|
1809
|
+
name: "config",
|
|
2254
1810
|
children: [
|
|
2255
|
-
{
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
type: "directory",
|
|
2261
|
-
name: "ValueObjects",
|
|
2262
|
-
children: [
|
|
2263
|
-
{ type: "file", name: "Id.ts", content: this.generateIdValueObject() },
|
|
2264
|
-
{ type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
|
|
2265
|
-
{ type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
|
|
2266
|
-
]
|
|
2267
|
-
}
|
|
2268
|
-
]
|
|
2269
|
-
},
|
|
2270
|
-
{
|
|
2271
|
-
type: "directory",
|
|
2272
|
-
name: "Infrastructure",
|
|
2273
|
-
children: [
|
|
2274
|
-
{
|
|
2275
|
-
type: "directory",
|
|
2276
|
-
name: "EventBus",
|
|
2277
|
-
children: [
|
|
2278
|
-
{
|
|
2279
|
-
type: "file",
|
|
2280
|
-
name: "EventDispatcher.ts",
|
|
2281
|
-
content: this.generateEventDispatcher()
|
|
2282
|
-
}
|
|
2283
|
-
]
|
|
2284
|
-
}
|
|
2285
|
-
]
|
|
2286
|
-
},
|
|
2287
|
-
{
|
|
2288
|
-
type: "directory",
|
|
2289
|
-
name: "Exceptions",
|
|
2290
|
-
children: [
|
|
2291
|
-
{ type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
|
|
2292
|
-
]
|
|
2293
|
-
}
|
|
1811
|
+
{ type: "file", name: "app.ts", content: this.generateAppConfig(context) },
|
|
1812
|
+
{ type: "file", name: "database.ts", content: this.generateDatabaseConfig() },
|
|
1813
|
+
{ type: "file", name: "modules.ts", content: this.generateModulesConfig() },
|
|
1814
|
+
{ type: "file", name: "cache.ts", content: this.generateCacheConfig() },
|
|
1815
|
+
{ type: "file", name: "logging.ts", content: this.generateLoggingConfig() }
|
|
2294
1816
|
]
|
|
2295
1817
|
};
|
|
2296
1818
|
}
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
{
|
|
2311
|
-
type: "directory",
|
|
2312
|
-
name: "Aggregates",
|
|
2313
|
-
children: [
|
|
2314
|
-
{
|
|
2315
|
-
type: "directory",
|
|
2316
|
-
name,
|
|
2317
|
-
children: [
|
|
2318
|
-
{ type: "file", name: `${name}.ts`, content: this.generateAggregate(name) },
|
|
2319
|
-
{
|
|
2320
|
-
type: "file",
|
|
2321
|
-
name: `${name}Status.ts`,
|
|
2322
|
-
content: this.generateAggregateStatus(name)
|
|
2323
|
-
}
|
|
2324
|
-
]
|
|
2325
|
-
}
|
|
2326
|
-
]
|
|
2327
|
-
},
|
|
2328
|
-
{
|
|
2329
|
-
type: "directory",
|
|
2330
|
-
name: "Events",
|
|
2331
|
-
children: [
|
|
2332
|
-
{
|
|
2333
|
-
type: "file",
|
|
2334
|
-
name: `${name}Created.ts`,
|
|
2335
|
-
content: this.generateCreatedEvent(name)
|
|
2336
|
-
}
|
|
2337
|
-
]
|
|
2338
|
-
},
|
|
2339
|
-
{
|
|
2340
|
-
type: "directory",
|
|
2341
|
-
name: "Repositories",
|
|
2342
|
-
children: [
|
|
2343
|
-
{
|
|
2344
|
-
type: "file",
|
|
2345
|
-
name: `I${name}Repository.ts`,
|
|
2346
|
-
content: this.generateRepositoryInterface(name)
|
|
2347
|
-
}
|
|
2348
|
-
]
|
|
2349
|
-
},
|
|
2350
|
-
{
|
|
2351
|
-
type: "directory",
|
|
2352
|
-
name: "Services",
|
|
2353
|
-
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2354
|
-
}
|
|
2355
|
-
]
|
|
2356
|
-
},
|
|
2357
|
-
// Application Layer
|
|
2358
|
-
{
|
|
2359
|
-
type: "directory",
|
|
2360
|
-
name: "Application",
|
|
2361
|
-
children: [
|
|
2362
|
-
{
|
|
2363
|
-
type: "directory",
|
|
2364
|
-
name: "Commands",
|
|
2365
|
-
children: [
|
|
2366
|
-
{
|
|
2367
|
-
type: "directory",
|
|
2368
|
-
name: `Create${name}`,
|
|
2369
|
-
children: [
|
|
2370
|
-
{
|
|
2371
|
-
type: "file",
|
|
2372
|
-
name: `Create${name}Command.ts`,
|
|
2373
|
-
content: this.generateCommand(name)
|
|
2374
|
-
},
|
|
2375
|
-
{
|
|
2376
|
-
type: "file",
|
|
2377
|
-
name: `Create${name}Handler.ts`,
|
|
2378
|
-
content: this.generateCommandHandler(name)
|
|
2379
|
-
}
|
|
2380
|
-
]
|
|
2381
|
-
}
|
|
2382
|
-
]
|
|
2383
|
-
},
|
|
2384
|
-
{
|
|
2385
|
-
type: "directory",
|
|
2386
|
-
name: "Queries",
|
|
2387
|
-
children: [
|
|
2388
|
-
{
|
|
2389
|
-
type: "directory",
|
|
2390
|
-
name: `Get${name}ById`,
|
|
2391
|
-
children: [
|
|
2392
|
-
{
|
|
2393
|
-
type: "file",
|
|
2394
|
-
name: `Get${name}ByIdQuery.ts`,
|
|
2395
|
-
content: this.generateQuery(name)
|
|
2396
|
-
},
|
|
2397
|
-
{
|
|
2398
|
-
type: "file",
|
|
2399
|
-
name: `Get${name}ByIdHandler.ts`,
|
|
2400
|
-
content: this.generateQueryHandler(name)
|
|
2401
|
-
}
|
|
2402
|
-
]
|
|
2403
|
-
}
|
|
2404
|
-
]
|
|
2405
|
-
},
|
|
2406
|
-
{
|
|
2407
|
-
type: "directory",
|
|
2408
|
-
name: "DTOs",
|
|
2409
|
-
children: [{ type: "file", name: `${name}DTO.ts`, content: this.generateDTO(name) }]
|
|
2410
|
-
}
|
|
2411
|
-
]
|
|
2412
|
-
},
|
|
2413
|
-
// Infrastructure Layer
|
|
2414
|
-
{
|
|
2415
|
-
type: "directory",
|
|
2416
|
-
name: "Infrastructure",
|
|
2417
|
-
children: [
|
|
2418
|
-
{
|
|
2419
|
-
type: "directory",
|
|
2420
|
-
name: "Persistence",
|
|
2421
|
-
children: [
|
|
2422
|
-
{
|
|
2423
|
-
type: "file",
|
|
2424
|
-
name: `${name}Repository.ts`,
|
|
2425
|
-
content: this.generateRepository(name)
|
|
2426
|
-
}
|
|
2427
|
-
]
|
|
2428
|
-
},
|
|
2429
|
-
{
|
|
2430
|
-
type: "directory",
|
|
2431
|
-
name: "Providers",
|
|
2432
|
-
children: [
|
|
2433
|
-
{
|
|
2434
|
-
type: "file",
|
|
2435
|
-
name: `${name}ServiceProvider.ts`,
|
|
2436
|
-
content: this.generateModuleServiceProvider(name, context)
|
|
2437
|
-
}
|
|
2438
|
-
]
|
|
2439
|
-
}
|
|
2440
|
-
]
|
|
2441
|
-
}
|
|
2442
|
-
]
|
|
2443
|
-
};
|
|
1819
|
+
generateMainEntry(_context) {
|
|
1820
|
+
return `/**
|
|
1821
|
+
* Application Entry Point
|
|
1822
|
+
*
|
|
1823
|
+
* Start the HTTP server.
|
|
1824
|
+
*/
|
|
1825
|
+
|
|
1826
|
+
import { createApp } from './Bootstrap/app'
|
|
1827
|
+
|
|
1828
|
+
const app = await createApp()
|
|
1829
|
+
|
|
1830
|
+
export default app.liftoff()
|
|
1831
|
+
`;
|
|
2444
1832
|
}
|
|
2445
|
-
// ─────────────────────────────────────────────────────────────
|
|
2446
|
-
// Bootstrap File Generators
|
|
2447
|
-
// ─────────────────────────────────────────────────────────────
|
|
2448
1833
|
generateBootstrapApp(_context) {
|
|
2449
1834
|
return `/**
|
|
2450
1835
|
* Application Bootstrap
|
|
@@ -2570,20 +1955,6 @@ export function registerRoutes(router: any): void {
|
|
|
2570
1955
|
// Catalog module
|
|
2571
1956
|
router.get('/api/products', (c: any) => c.json({ message: 'Product list' }))
|
|
2572
1957
|
}
|
|
2573
|
-
`;
|
|
2574
|
-
}
|
|
2575
|
-
generateMainEntry(_context) {
|
|
2576
|
-
return `/**
|
|
2577
|
-
* Application Entry Point
|
|
2578
|
-
*
|
|
2579
|
-
* Start the HTTP server.
|
|
2580
|
-
*/
|
|
2581
|
-
|
|
2582
|
-
import { createApp } from './Bootstrap/app'
|
|
2583
|
-
|
|
2584
|
-
const app = await createApp()
|
|
2585
|
-
|
|
2586
|
-
export default app.liftoff()
|
|
2587
1958
|
`;
|
|
2588
1959
|
}
|
|
2589
1960
|
generateModulesConfig() {
|
|
@@ -2614,60 +1985,6 @@ export default {
|
|
|
2614
1985
|
}
|
|
2615
1986
|
`;
|
|
2616
1987
|
}
|
|
2617
|
-
generateModuleServiceProvider(name, _context) {
|
|
2618
|
-
return `/**
|
|
2619
|
-
* ${name} Service Provider
|
|
2620
|
-
*/
|
|
2621
|
-
|
|
2622
|
-
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
2623
|
-
import { ${name}Repository } from '../Persistence/${name}Repository'
|
|
2624
|
-
|
|
2625
|
-
export class ${name}ServiceProvider extends ServiceProvider {
|
|
2626
|
-
register(container: Container): void {
|
|
2627
|
-
container.singleton('${name.toLowerCase()}Repository', () => new ${name}Repository())
|
|
2628
|
-
}
|
|
2629
|
-
|
|
2630
|
-
boot(_core: PlanetCore): void {
|
|
2631
|
-
console.log('[${name}] Module loaded')
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
`;
|
|
2635
|
-
}
|
|
2636
|
-
/**
|
|
2637
|
-
* Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
|
|
2638
|
-
*/
|
|
2639
|
-
generatePackageJson(context) {
|
|
2640
|
-
const pkg = {
|
|
2641
|
-
name: context.nameKebabCase,
|
|
2642
|
-
version: "0.1.0",
|
|
2643
|
-
type: "module",
|
|
2644
|
-
scripts: {
|
|
2645
|
-
dev: "bun run --watch src/main.ts",
|
|
2646
|
-
build: "bun build ./src/main.ts --outdir ./dist --target bun",
|
|
2647
|
-
start: "bun run dist/main.js",
|
|
2648
|
-
test: "bun test",
|
|
2649
|
-
typecheck: "tsc --noEmit",
|
|
2650
|
-
check: "bun run typecheck && bun run test",
|
|
2651
|
-
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2652
|
-
validate: "bun run check && bun run check:deps",
|
|
2653
|
-
precommit: "bun run validate",
|
|
2654
|
-
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
2655
|
-
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
2656
|
-
},
|
|
2657
|
-
dependencies: {
|
|
2658
|
-
"@gravito/core": "^1.0.0-beta.5",
|
|
2659
|
-
"@gravito/enterprise": "workspace:*"
|
|
2660
|
-
},
|
|
2661
|
-
devDependencies: {
|
|
2662
|
-
"bun-types": "latest",
|
|
2663
|
-
typescript: "^5.0.0"
|
|
2664
|
-
}
|
|
2665
|
-
};
|
|
2666
|
-
return JSON.stringify(pkg, null, 2);
|
|
2667
|
-
}
|
|
2668
|
-
// ─────────────────────────────────────────────────────────────
|
|
2669
|
-
// Config Generators
|
|
2670
|
-
// ─────────────────────────────────────────────────────────────
|
|
2671
1988
|
generateAppConfig(context) {
|
|
2672
1989
|
return `export default {
|
|
2673
1990
|
name: process.env.APP_NAME ?? '${context.name}',
|
|
@@ -2702,159 +2019,155 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
2702
2019
|
}
|
|
2703
2020
|
`;
|
|
2704
2021
|
}
|
|
2705
|
-
|
|
2706
|
-
// Shared Kernel Files
|
|
2707
|
-
// ─────────────────────────────────────────────────────────────
|
|
2708
|
-
generateIdValueObject() {
|
|
2709
|
-
return `/**
|
|
2710
|
-
* ID Value Object
|
|
2711
|
-
*
|
|
2712
|
-
* Shared identifier across all contexts.
|
|
2713
|
-
*/
|
|
2714
|
-
|
|
2715
|
-
import { ValueObject } from '@gravito/enterprise'
|
|
2716
|
-
|
|
2717
|
-
interface IdProps {
|
|
2718
|
-
value: string
|
|
2719
|
-
}
|
|
2720
|
-
|
|
2721
|
-
export class Id extends ValueObject<IdProps> {
|
|
2722
|
-
private constructor(value: string) {
|
|
2723
|
-
super({ value })
|
|
2724
|
-
}
|
|
2725
|
-
|
|
2726
|
-
static create(): Id {
|
|
2727
|
-
return new Id(crypto.randomUUID())
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
static from(value: string): Id {
|
|
2731
|
-
if (!value) throw new Error('Id cannot be empty')
|
|
2732
|
-
return new Id(value)
|
|
2733
|
-
}
|
|
2734
|
-
|
|
2735
|
-
get value(): string {
|
|
2736
|
-
return this.props.value
|
|
2737
|
-
}
|
|
2738
|
-
|
|
2739
|
-
toString(): string {
|
|
2740
|
-
return this.props.value
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
`;
|
|
2744
|
-
}
|
|
2745
|
-
generateMoneyValueObject() {
|
|
2746
|
-
return `/**
|
|
2747
|
-
* Money Value Object
|
|
2748
|
-
*/
|
|
2749
|
-
|
|
2750
|
-
import { ValueObject } from '@gravito/enterprise'
|
|
2751
|
-
|
|
2752
|
-
interface MoneyProps {
|
|
2753
|
-
amount: number
|
|
2754
|
-
currency: string
|
|
2755
|
-
}
|
|
2756
|
-
|
|
2757
|
-
export class Money extends ValueObject<MoneyProps> {
|
|
2758
|
-
constructor(amount: number, currency: string = 'USD') {
|
|
2759
|
-
if (amount < 0) throw new Error('Amount cannot be negative')
|
|
2760
|
-
super({ amount, currency })
|
|
2761
|
-
}
|
|
2762
|
-
|
|
2763
|
-
get amount(): number {
|
|
2764
|
-
return this.props.amount
|
|
2765
|
-
}
|
|
2766
|
-
|
|
2767
|
-
get currency(): string {
|
|
2768
|
-
return this.props.currency
|
|
2769
|
-
}
|
|
2770
|
-
|
|
2771
|
-
add(other: Money): Money {
|
|
2772
|
-
this.assertSameCurrency(other)
|
|
2773
|
-
return new Money(this.amount + other.amount, this.currency)
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
|
-
subtract(other: Money): Money {
|
|
2777
|
-
this.assertSameCurrency(other)
|
|
2778
|
-
return new Money(this.amount - other.amount, this.currency)
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
private assertSameCurrency(other: Money): void {
|
|
2782
|
-
if (this.currency !== other.currency) {
|
|
2783
|
-
throw new Error('Cannot operate on different currencies')
|
|
2784
|
-
}
|
|
2785
|
-
}
|
|
2786
|
-
}
|
|
2787
|
-
`;
|
|
2788
|
-
}
|
|
2789
|
-
generateEmailValueObject() {
|
|
2790
|
-
return `/**
|
|
2791
|
-
* Email Value Object
|
|
2792
|
-
*/
|
|
2793
|
-
|
|
2794
|
-
import { ValueObject } from '@gravito/enterprise'
|
|
2795
|
-
|
|
2796
|
-
interface EmailProps {
|
|
2797
|
-
value: string
|
|
2798
|
-
}
|
|
2799
|
-
|
|
2800
|
-
export class Email extends ValueObject<EmailProps> {
|
|
2801
|
-
private constructor(value: string) {
|
|
2802
|
-
super({ value: value.toLowerCase().trim() })
|
|
2803
|
-
}
|
|
2804
|
-
|
|
2805
|
-
static create(email: string): Email {
|
|
2806
|
-
if (!Email.isValid(email)) {
|
|
2807
|
-
throw new Error(\`Invalid email: \${email}\`)
|
|
2808
|
-
}
|
|
2809
|
-
return new Email(email)
|
|
2810
|
-
}
|
|
2811
|
-
|
|
2812
|
-
static isValid(email: string): boolean {
|
|
2813
|
-
return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)
|
|
2814
|
-
}
|
|
2815
|
-
|
|
2816
|
-
get value(): string {
|
|
2817
|
-
return this.props.value
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
`;
|
|
2821
|
-
}
|
|
2822
|
-
generateEventDispatcher() {
|
|
2823
|
-
return `/**
|
|
2824
|
-
* Event Dispatcher
|
|
2825
|
-
*/
|
|
2826
|
-
|
|
2827
|
-
import type { DomainEvent } from '@gravito/enterprise'
|
|
2828
|
-
|
|
2829
|
-
type EventHandler = (event: DomainEvent) => void | Promise<void>
|
|
2830
|
-
|
|
2831
|
-
export class EventDispatcher {
|
|
2832
|
-
private handlers: Map<string, EventHandler[]> = new Map()
|
|
2833
|
-
|
|
2834
|
-
subscribe(eventName: string, handler: EventHandler): void {
|
|
2835
|
-
const handlers = this.handlers.get(eventName) ?? []
|
|
2836
|
-
handlers.push(handler)
|
|
2837
|
-
this.handlers.set(eventName, handlers)
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
async dispatch(event: DomainEvent): Promise<void> {
|
|
2841
|
-
const handlers = this.handlers.get(event.eventName) ?? []
|
|
2842
|
-
for (const handler of handlers) {
|
|
2843
|
-
await handler(event)
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2022
|
+
};
|
|
2846
2023
|
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2024
|
+
// src/generators/ddd/ModuleGenerator.ts
|
|
2025
|
+
var ModuleGenerator = class {
|
|
2026
|
+
generate(name, context) {
|
|
2027
|
+
return {
|
|
2028
|
+
type: "directory",
|
|
2029
|
+
name,
|
|
2030
|
+
children: [
|
|
2031
|
+
// Domain Layer
|
|
2032
|
+
{
|
|
2033
|
+
type: "directory",
|
|
2034
|
+
name: "Domain",
|
|
2035
|
+
children: [
|
|
2036
|
+
{
|
|
2037
|
+
type: "directory",
|
|
2038
|
+
name: "Aggregates",
|
|
2039
|
+
children: [
|
|
2040
|
+
{
|
|
2041
|
+
type: "directory",
|
|
2042
|
+
name,
|
|
2043
|
+
children: [
|
|
2044
|
+
{ type: "file", name: `${name}.ts`, content: this.generateAggregate(name) },
|
|
2045
|
+
{
|
|
2046
|
+
type: "file",
|
|
2047
|
+
name: `${name}Status.ts`,
|
|
2048
|
+
content: this.generateAggregateStatus(name)
|
|
2049
|
+
}
|
|
2050
|
+
]
|
|
2051
|
+
}
|
|
2052
|
+
]
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
type: "directory",
|
|
2056
|
+
name: "Events",
|
|
2057
|
+
children: [
|
|
2058
|
+
{
|
|
2059
|
+
type: "file",
|
|
2060
|
+
name: `${name}Created.ts`,
|
|
2061
|
+
content: this.generateCreatedEvent(name)
|
|
2062
|
+
}
|
|
2063
|
+
]
|
|
2064
|
+
},
|
|
2065
|
+
{
|
|
2066
|
+
type: "directory",
|
|
2067
|
+
name: "Repositories",
|
|
2068
|
+
children: [
|
|
2069
|
+
{
|
|
2070
|
+
type: "file",
|
|
2071
|
+
name: `I${name}Repository.ts`,
|
|
2072
|
+
content: this.generateRepositoryInterface(name)
|
|
2073
|
+
}
|
|
2074
|
+
]
|
|
2075
|
+
},
|
|
2076
|
+
{
|
|
2077
|
+
type: "directory",
|
|
2078
|
+
name: "Services",
|
|
2079
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2080
|
+
}
|
|
2081
|
+
]
|
|
2082
|
+
},
|
|
2083
|
+
// Application Layer
|
|
2084
|
+
{
|
|
2085
|
+
type: "directory",
|
|
2086
|
+
name: "Application",
|
|
2087
|
+
children: [
|
|
2088
|
+
{
|
|
2089
|
+
type: "directory",
|
|
2090
|
+
name: "Commands",
|
|
2091
|
+
children: [
|
|
2092
|
+
{
|
|
2093
|
+
type: "directory",
|
|
2094
|
+
name: `Create${name}`,
|
|
2095
|
+
children: [
|
|
2096
|
+
{
|
|
2097
|
+
type: "file",
|
|
2098
|
+
name: `Create${name}Command.ts`,
|
|
2099
|
+
content: this.generateCommand(name)
|
|
2100
|
+
},
|
|
2101
|
+
{
|
|
2102
|
+
type: "file",
|
|
2103
|
+
name: `Create${name}Handler.ts`,
|
|
2104
|
+
content: this.generateCommandHandler(name)
|
|
2105
|
+
}
|
|
2106
|
+
]
|
|
2107
|
+
}
|
|
2108
|
+
]
|
|
2109
|
+
},
|
|
2110
|
+
{
|
|
2111
|
+
type: "directory",
|
|
2112
|
+
name: "Queries",
|
|
2113
|
+
children: [
|
|
2114
|
+
{
|
|
2115
|
+
type: "directory",
|
|
2116
|
+
name: `Get${name}ById`,
|
|
2117
|
+
children: [
|
|
2118
|
+
{
|
|
2119
|
+
type: "file",
|
|
2120
|
+
name: `Get${name}ByIdQuery.ts`,
|
|
2121
|
+
content: this.generateQuery(name)
|
|
2122
|
+
},
|
|
2123
|
+
{
|
|
2124
|
+
type: "file",
|
|
2125
|
+
name: `Get${name}ByIdHandler.ts`,
|
|
2126
|
+
content: this.generateQueryHandler(name)
|
|
2127
|
+
}
|
|
2128
|
+
]
|
|
2129
|
+
}
|
|
2130
|
+
]
|
|
2131
|
+
},
|
|
2132
|
+
{
|
|
2133
|
+
type: "directory",
|
|
2134
|
+
name: "DTOs",
|
|
2135
|
+
children: [{ type: "file", name: `${name}DTO.ts`, content: this.generateDTO(name) }]
|
|
2136
|
+
}
|
|
2137
|
+
]
|
|
2138
|
+
},
|
|
2139
|
+
// Infrastructure Layer
|
|
2140
|
+
{
|
|
2141
|
+
type: "directory",
|
|
2142
|
+
name: "Infrastructure",
|
|
2143
|
+
children: [
|
|
2144
|
+
{
|
|
2145
|
+
type: "directory",
|
|
2146
|
+
name: "Persistence",
|
|
2147
|
+
children: [
|
|
2148
|
+
{
|
|
2149
|
+
type: "file",
|
|
2150
|
+
name: `${name}Repository.ts`,
|
|
2151
|
+
content: this.generateRepository(name)
|
|
2152
|
+
}
|
|
2153
|
+
]
|
|
2154
|
+
},
|
|
2155
|
+
{
|
|
2156
|
+
type: "directory",
|
|
2157
|
+
name: "Providers",
|
|
2158
|
+
children: [
|
|
2159
|
+
{
|
|
2160
|
+
type: "file",
|
|
2161
|
+
name: `${name}ServiceProvider.ts`,
|
|
2162
|
+
content: this.generateModuleServiceProvider(name, context)
|
|
2163
|
+
}
|
|
2164
|
+
]
|
|
2165
|
+
}
|
|
2166
|
+
]
|
|
2167
|
+
}
|
|
2168
|
+
]
|
|
2169
|
+
};
|
|
2854
2170
|
}
|
|
2855
|
-
// ─────────────────────────────────────────────────────────────
|
|
2856
|
-
// Bounded Context Templates
|
|
2857
|
-
// ─────────────────────────────────────────────────────────────
|
|
2858
2171
|
generateAggregate(name) {
|
|
2859
2172
|
return `/**
|
|
2860
2173
|
* ${name} Aggregate Root
|
|
@@ -3076,15 +2389,359 @@ export class ${name}Repository implements I${name}Repository {
|
|
|
3076
2389
|
}
|
|
3077
2390
|
`;
|
|
3078
2391
|
}
|
|
3079
|
-
|
|
3080
|
-
return `/**
|
|
3081
|
-
*
|
|
3082
|
-
*/
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
2392
|
+
generateModuleServiceProvider(name, _context) {
|
|
2393
|
+
return `/**
|
|
2394
|
+
* ${name} Service Provider
|
|
2395
|
+
*/
|
|
2396
|
+
|
|
2397
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
2398
|
+
import { ${name}Repository } from '../Persistence/${name}Repository'
|
|
2399
|
+
|
|
2400
|
+
export class ${name}ServiceProvider extends ServiceProvider {
|
|
2401
|
+
register(container: Container): void {
|
|
2402
|
+
container.singleton('${name.toLowerCase()}Repository', () => new ${name}Repository())
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
boot(_core: PlanetCore): void {
|
|
2406
|
+
console.log('[${name}] Module loaded')
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
`;
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
|
|
2413
|
+
// src/generators/ddd/SharedKernelGenerator.ts
|
|
2414
|
+
var SharedKernelGenerator = class {
|
|
2415
|
+
generate() {
|
|
2416
|
+
return {
|
|
2417
|
+
type: "directory",
|
|
2418
|
+
name: "Shared",
|
|
2419
|
+
children: [
|
|
2420
|
+
{
|
|
2421
|
+
type: "directory",
|
|
2422
|
+
name: "Domain",
|
|
2423
|
+
children: [
|
|
2424
|
+
{
|
|
2425
|
+
type: "directory",
|
|
2426
|
+
name: "ValueObjects",
|
|
2427
|
+
children: [
|
|
2428
|
+
{ type: "file", name: "Id.ts", content: this.generateIdValueObject() },
|
|
2429
|
+
{ type: "file", name: "Money.ts", content: this.generateMoneyValueObject() },
|
|
2430
|
+
{ type: "file", name: "Email.ts", content: this.generateEmailValueObject() }
|
|
2431
|
+
]
|
|
2432
|
+
}
|
|
2433
|
+
]
|
|
2434
|
+
},
|
|
2435
|
+
{
|
|
2436
|
+
type: "directory",
|
|
2437
|
+
name: "Infrastructure",
|
|
2438
|
+
children: [
|
|
2439
|
+
{
|
|
2440
|
+
type: "directory",
|
|
2441
|
+
name: "EventBus",
|
|
2442
|
+
children: [
|
|
2443
|
+
{
|
|
2444
|
+
type: "file",
|
|
2445
|
+
name: "EventDispatcher.ts",
|
|
2446
|
+
content: this.generateEventDispatcher()
|
|
2447
|
+
}
|
|
2448
|
+
]
|
|
2449
|
+
}
|
|
2450
|
+
]
|
|
2451
|
+
},
|
|
2452
|
+
{
|
|
2453
|
+
type: "directory",
|
|
2454
|
+
name: "Exceptions",
|
|
2455
|
+
children: [
|
|
2456
|
+
{ type: "file", name: "Handler.ts", content: this.generateExceptionHandler() }
|
|
2457
|
+
]
|
|
2458
|
+
}
|
|
2459
|
+
]
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
generateIdValueObject() {
|
|
2463
|
+
return `/**
|
|
2464
|
+
* ID Value Object
|
|
2465
|
+
*
|
|
2466
|
+
* Shared identifier across all contexts.
|
|
2467
|
+
*/
|
|
2468
|
+
|
|
2469
|
+
import { ValueObject } from '@gravito/enterprise'
|
|
2470
|
+
|
|
2471
|
+
interface IdProps {
|
|
2472
|
+
value: string
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
export class Id extends ValueObject<IdProps> {
|
|
2476
|
+
private constructor(value: string) {
|
|
2477
|
+
super({ value })
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
static create(): Id {
|
|
2481
|
+
return new Id(crypto.randomUUID())
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
static from(value: string): Id {
|
|
2485
|
+
if (!value) throw new Error('Id cannot be empty')
|
|
2486
|
+
return new Id(value)
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
get value(): string {
|
|
2490
|
+
return this.props.value
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
toString(): string {
|
|
2494
|
+
return this.props.value
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
`;
|
|
2498
|
+
}
|
|
2499
|
+
generateMoneyValueObject() {
|
|
2500
|
+
return `/**
|
|
2501
|
+
* Money Value Object
|
|
2502
|
+
*/
|
|
2503
|
+
|
|
2504
|
+
import { ValueObject } from '@gravito/enterprise'
|
|
2505
|
+
|
|
2506
|
+
interface MoneyProps {
|
|
2507
|
+
amount: number
|
|
2508
|
+
currency: string
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
export class Money extends ValueObject<MoneyProps> {
|
|
2512
|
+
constructor(amount: number, currency: string = 'USD') {
|
|
2513
|
+
if (amount < 0) throw new Error('Amount cannot be negative')
|
|
2514
|
+
super({ amount, currency })
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
get amount(): number {
|
|
2518
|
+
return this.props.amount
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
get currency(): string {
|
|
2522
|
+
return this.props.currency
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
add(other: Money): Money {
|
|
2526
|
+
this.assertSameCurrency(other)
|
|
2527
|
+
return new Money(this.amount + other.amount, this.currency)
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
subtract(other: Money): Money {
|
|
2531
|
+
this.assertSameCurrency(other)
|
|
2532
|
+
return new Money(this.amount - other.amount, this.currency)
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
private assertSameCurrency(other: Money): void {
|
|
2536
|
+
if (this.currency !== other.currency) {
|
|
2537
|
+
throw new Error('Cannot operate on different currencies')
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
`;
|
|
2542
|
+
}
|
|
2543
|
+
generateEmailValueObject() {
|
|
2544
|
+
return `/**
|
|
2545
|
+
* Email Value Object
|
|
2546
|
+
*/
|
|
2547
|
+
|
|
2548
|
+
import { ValueObject } from '@gravito/enterprise'
|
|
2549
|
+
|
|
2550
|
+
interface EmailProps {
|
|
2551
|
+
value: string
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
export class Email extends ValueObject<EmailProps> {
|
|
2555
|
+
private constructor(value: string) {
|
|
2556
|
+
super({ value: value.toLowerCase().trim() })
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
static create(email: string): Email {
|
|
2560
|
+
if (!Email.isValid(email)) {
|
|
2561
|
+
throw new Error(\`Invalid email: \${email}\`)
|
|
2562
|
+
}
|
|
2563
|
+
return new Email(email)
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
static isValid(email: string): boolean {
|
|
2567
|
+
return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
get value(): string {
|
|
2571
|
+
return this.props.value
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
`;
|
|
2575
|
+
}
|
|
2576
|
+
generateEventDispatcher() {
|
|
2577
|
+
return `/**
|
|
2578
|
+
* Event Dispatcher
|
|
2579
|
+
*/
|
|
2580
|
+
|
|
2581
|
+
import type { DomainEvent } from '@gravito/enterprise'
|
|
2582
|
+
|
|
2583
|
+
type EventHandler = (event: DomainEvent) => void | Promise<void>
|
|
2584
|
+
|
|
2585
|
+
export class EventDispatcher {
|
|
2586
|
+
private handlers: Map<string, EventHandler[]> = new Map()
|
|
2587
|
+
|
|
2588
|
+
subscribe(eventName: string, handler: EventHandler): void {
|
|
2589
|
+
const handlers = this.handlers.get(eventName) ?? []
|
|
2590
|
+
handlers.push(handler)
|
|
2591
|
+
this.handlers.set(eventName, handlers)
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
async dispatch(event: DomainEvent): Promise<void> {
|
|
2595
|
+
const handlers = this.handlers.get(event.eventName) ?? []
|
|
2596
|
+
for (const handler of handlers) {
|
|
2597
|
+
await handler(event)
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
async dispatchAll(events: DomainEvent[]): Promise<void> {
|
|
2602
|
+
for (const event of events) {
|
|
2603
|
+
await this.dispatch(event)
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
`;
|
|
2608
|
+
}
|
|
2609
|
+
generateExceptionHandler() {
|
|
2610
|
+
return `/**
|
|
2611
|
+
* Exception Handler
|
|
2612
|
+
*/
|
|
2613
|
+
|
|
2614
|
+
export function report(error: unknown): void {
|
|
2615
|
+
console.error('[Exception]', error)
|
|
2616
|
+
}
|
|
2617
|
+
`;
|
|
2618
|
+
}
|
|
2619
|
+
};
|
|
2620
|
+
|
|
2621
|
+
// src/generators/DddGenerator.ts
|
|
2622
|
+
var DddGenerator = class extends BaseGenerator {
|
|
2623
|
+
moduleGenerator;
|
|
2624
|
+
sharedKernelGenerator;
|
|
2625
|
+
bootstrapGenerator;
|
|
2626
|
+
constructor(config) {
|
|
2627
|
+
super(config);
|
|
2628
|
+
this.moduleGenerator = new ModuleGenerator();
|
|
2629
|
+
this.sharedKernelGenerator = new SharedKernelGenerator();
|
|
2630
|
+
this.bootstrapGenerator = new BootstrapGenerator();
|
|
2631
|
+
}
|
|
2632
|
+
get architectureType() {
|
|
2633
|
+
return "ddd";
|
|
2634
|
+
}
|
|
2635
|
+
get displayName() {
|
|
2636
|
+
return "Domain-Driven Design (DDD)";
|
|
2637
|
+
}
|
|
2638
|
+
get description() {
|
|
2639
|
+
return "Full DDD with Bounded Contexts, Aggregates, and Event-Driven patterns";
|
|
2640
|
+
}
|
|
2641
|
+
getDirectoryStructure(context) {
|
|
2642
|
+
return [
|
|
2643
|
+
this.bootstrapGenerator.generateConfigDirectory(context),
|
|
2644
|
+
{
|
|
2645
|
+
type: "directory",
|
|
2646
|
+
name: "src",
|
|
2647
|
+
children: [
|
|
2648
|
+
// Bootstrap - Application startup and configuration
|
|
2649
|
+
this.bootstrapGenerator.generate(context),
|
|
2650
|
+
// Shared - Cross-module shared components
|
|
2651
|
+
this.sharedKernelGenerator.generate(),
|
|
2652
|
+
// Modules - Bounded Contexts
|
|
2653
|
+
{
|
|
2654
|
+
type: "directory",
|
|
2655
|
+
name: "Modules",
|
|
2656
|
+
children: [
|
|
2657
|
+
this.moduleGenerator.generate("Ordering", context),
|
|
2658
|
+
this.moduleGenerator.generate("Catalog", context)
|
|
2659
|
+
]
|
|
2660
|
+
},
|
|
2661
|
+
{
|
|
2662
|
+
type: "file",
|
|
2663
|
+
name: "main.ts",
|
|
2664
|
+
content: this.bootstrapGenerator.generateMainEntry(context)
|
|
2665
|
+
}
|
|
2666
|
+
]
|
|
2667
|
+
},
|
|
2668
|
+
{
|
|
2669
|
+
type: "directory",
|
|
2670
|
+
name: "tests",
|
|
2671
|
+
children: [
|
|
2672
|
+
{
|
|
2673
|
+
type: "directory",
|
|
2674
|
+
name: "Modules",
|
|
2675
|
+
children: [
|
|
2676
|
+
{
|
|
2677
|
+
type: "directory",
|
|
2678
|
+
name: "Ordering",
|
|
2679
|
+
children: [
|
|
2680
|
+
{
|
|
2681
|
+
type: "directory",
|
|
2682
|
+
name: "Unit",
|
|
2683
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2684
|
+
},
|
|
2685
|
+
{
|
|
2686
|
+
type: "directory",
|
|
2687
|
+
name: "Integration",
|
|
2688
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2689
|
+
}
|
|
2690
|
+
]
|
|
2691
|
+
},
|
|
2692
|
+
{
|
|
2693
|
+
type: "directory",
|
|
2694
|
+
name: "Catalog",
|
|
2695
|
+
children: [
|
|
2696
|
+
{
|
|
2697
|
+
type: "directory",
|
|
2698
|
+
name: "Unit",
|
|
2699
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2700
|
+
}
|
|
2701
|
+
]
|
|
2702
|
+
}
|
|
2703
|
+
]
|
|
2704
|
+
},
|
|
2705
|
+
{
|
|
2706
|
+
type: "directory",
|
|
2707
|
+
name: "Shared",
|
|
2708
|
+
children: [{ type: "file", name: ".gitkeep", content: "" }]
|
|
2709
|
+
}
|
|
2710
|
+
]
|
|
2711
|
+
}
|
|
2712
|
+
];
|
|
2713
|
+
}
|
|
2714
|
+
/**
|
|
2715
|
+
* Override package.json for DDD architecture (uses main.ts instead of bootstrap.ts)
|
|
2716
|
+
*/
|
|
2717
|
+
generatePackageJson(context) {
|
|
2718
|
+
const pkg = {
|
|
2719
|
+
name: context.nameKebabCase,
|
|
2720
|
+
version: "0.1.0",
|
|
2721
|
+
type: "module",
|
|
2722
|
+
scripts: {
|
|
2723
|
+
dev: "bun run --watch src/main.ts",
|
|
2724
|
+
build: "bun build ./src/main.ts --outdir ./dist --target bun",
|
|
2725
|
+
start: "bun run dist/main.js",
|
|
2726
|
+
test: "bun test",
|
|
2727
|
+
typecheck: "bun tsc --noEmit",
|
|
2728
|
+
check: "bun run typecheck && bun run test",
|
|
2729
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2730
|
+
validate: "bun run check && bun run check:deps",
|
|
2731
|
+
precommit: "bun run validate",
|
|
2732
|
+
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
2733
|
+
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
2734
|
+
},
|
|
2735
|
+
dependencies: {
|
|
2736
|
+
"@gravito/core": "^1.0.0-beta.5",
|
|
2737
|
+
"@gravito/enterprise": "workspace:*"
|
|
2738
|
+
},
|
|
2739
|
+
devDependencies: {
|
|
2740
|
+
"bun-types": "latest",
|
|
2741
|
+
typescript: "^5.9.3"
|
|
2742
|
+
}
|
|
2743
|
+
};
|
|
2744
|
+
return JSON.stringify(pkg, null, 2);
|
|
3088
2745
|
}
|
|
3089
2746
|
generateArchitectureDoc(context) {
|
|
3090
2747
|
return `# ${context.name} - DDD Architecture Guide
|
|
@@ -3313,123 +2970,13 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
|
|
|
3313
2970
|
];
|
|
3314
2971
|
}
|
|
3315
2972
|
// ─────────────────────────────────────────────────────────────
|
|
3316
|
-
// Config Generators
|
|
2973
|
+
// Config Generators (using shared ConfigGenerator)
|
|
3317
2974
|
// ─────────────────────────────────────────────────────────────
|
|
3318
2975
|
generateAppConfig(context) {
|
|
3319
|
-
return
|
|
3320
|
-
* Application Configuration
|
|
3321
|
-
*/
|
|
3322
|
-
export default {
|
|
3323
|
-
/**
|
|
3324
|
-
* Application name
|
|
3325
|
-
*/
|
|
3326
|
-
name: process.env.APP_NAME ?? '${context.name}',
|
|
3327
|
-
|
|
3328
|
-
/**
|
|
3329
|
-
* Application environment
|
|
3330
|
-
*/
|
|
3331
|
-
env: process.env.APP_ENV ?? 'development',
|
|
3332
|
-
|
|
3333
|
-
/**
|
|
3334
|
-
* Application port
|
|
3335
|
-
*/
|
|
3336
|
-
port: Number.parseInt(process.env.PORT ?? '3000', 10),
|
|
3337
|
-
|
|
3338
|
-
/**
|
|
3339
|
-
* View directory
|
|
3340
|
-
*/
|
|
3341
|
-
VIEW_DIR: process.env.VIEW_DIR ?? 'src/views',
|
|
3342
|
-
|
|
3343
|
-
/**
|
|
3344
|
-
* Debug mode
|
|
3345
|
-
*/
|
|
3346
|
-
debug: process.env.APP_DEBUG === 'true',
|
|
3347
|
-
|
|
3348
|
-
/**
|
|
3349
|
-
* Application URL
|
|
3350
|
-
*/
|
|
3351
|
-
url: process.env.APP_URL ?? 'http://localhost:3000',
|
|
3352
|
-
|
|
3353
|
-
/**
|
|
3354
|
-
* Timezone
|
|
3355
|
-
*/
|
|
3356
|
-
timezone: 'UTC',
|
|
3357
|
-
|
|
3358
|
-
/**
|
|
3359
|
-
* Locale
|
|
3360
|
-
*/
|
|
3361
|
-
locale: 'en',
|
|
3362
|
-
|
|
3363
|
-
/**
|
|
3364
|
-
* Fallback locale
|
|
3365
|
-
*/
|
|
3366
|
-
fallbackLocale: 'en',
|
|
3367
|
-
|
|
3368
|
-
/**
|
|
3369
|
-
* Encryption key
|
|
3370
|
-
*/
|
|
3371
|
-
key: process.env.APP_KEY,
|
|
3372
|
-
|
|
3373
|
-
/**
|
|
3374
|
-
* Service providers to register
|
|
3375
|
-
*/
|
|
3376
|
-
providers: [
|
|
3377
|
-
// Framework providers
|
|
3378
|
-
// 'RouteServiceProvider',
|
|
3379
|
-
|
|
3380
|
-
// Application providers
|
|
3381
|
-
// 'AppServiceProvider',
|
|
3382
|
-
],
|
|
3383
|
-
}
|
|
3384
|
-
`;
|
|
2976
|
+
return ConfigGenerator.generateDetailedAppConfig(context);
|
|
3385
2977
|
}
|
|
3386
2978
|
generateDatabaseConfig() {
|
|
3387
|
-
return
|
|
3388
|
-
* Database Configuration
|
|
3389
|
-
*/
|
|
3390
|
-
export default {
|
|
3391
|
-
/**
|
|
3392
|
-
* Default connection
|
|
3393
|
-
*/
|
|
3394
|
-
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
3395
|
-
|
|
3396
|
-
/**
|
|
3397
|
-
* Database connections
|
|
3398
|
-
*/
|
|
3399
|
-
connections: {
|
|
3400
|
-
sqlite: {
|
|
3401
|
-
driver: 'sqlite',
|
|
3402
|
-
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
3403
|
-
},
|
|
3404
|
-
|
|
3405
|
-
mysql: {
|
|
3406
|
-
driver: 'mysql',
|
|
3407
|
-
host: process.env.DB_HOST ?? 'localhost',
|
|
3408
|
-
port: Number(process.env.DB_PORT ?? 3306),
|
|
3409
|
-
database: process.env.DB_DATABASE ?? 'forge',
|
|
3410
|
-
username: process.env.DB_USERNAME ?? 'forge',
|
|
3411
|
-
password: process.env.DB_PASSWORD ?? '',
|
|
3412
|
-
},
|
|
3413
|
-
|
|
3414
|
-
postgres: {
|
|
3415
|
-
driver: 'postgres',
|
|
3416
|
-
host: process.env.DB_HOST ?? 'localhost',
|
|
3417
|
-
port: Number(process.env.DB_PORT ?? 5432),
|
|
3418
|
-
database: process.env.DB_DATABASE ?? 'forge',
|
|
3419
|
-
username: process.env.DB_USERNAME ?? 'forge',
|
|
3420
|
-
password: process.env.DB_PASSWORD ?? '',
|
|
3421
|
-
},
|
|
3422
|
-
},
|
|
3423
|
-
|
|
3424
|
-
/**
|
|
3425
|
-
* Migration settings
|
|
3426
|
-
*/
|
|
3427
|
-
migrations: {
|
|
3428
|
-
table: 'migrations',
|
|
3429
|
-
path: 'database/migrations',
|
|
3430
|
-
},
|
|
3431
|
-
}
|
|
3432
|
-
`;
|
|
2979
|
+
return ConfigGenerator.generateDetailedDatabaseConfig();
|
|
3433
2980
|
}
|
|
3434
2981
|
generateAuthConfig() {
|
|
3435
2982
|
return `/**
|
|
@@ -3712,33 +3259,6 @@ export class AppServiceProvider extends ServiceProvider {
|
|
|
3712
3259
|
console.log('${context.name} application booted!')
|
|
3713
3260
|
}
|
|
3714
3261
|
}
|
|
3715
|
-
`;
|
|
3716
|
-
}
|
|
3717
|
-
generateRouteServiceProvider(_context) {
|
|
3718
|
-
return `/**
|
|
3719
|
-
* Route Service Provider
|
|
3720
|
-
*
|
|
3721
|
-
* Configures and registers application routes.
|
|
3722
|
-
*/
|
|
3723
|
-
|
|
3724
|
-
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
3725
|
-
import { registerRoutes } from '../routes'
|
|
3726
|
-
|
|
3727
|
-
export class RouteServiceProvider extends ServiceProvider {
|
|
3728
|
-
/**
|
|
3729
|
-
* Register any application services.
|
|
3730
|
-
*/
|
|
3731
|
-
register(_container: Container): void {
|
|
3732
|
-
// Routes are registered in boot
|
|
3733
|
-
}
|
|
3734
|
-
|
|
3735
|
-
/**
|
|
3736
|
-
* Bootstrap any application services.
|
|
3737
|
-
*/
|
|
3738
|
-
boot(core: PlanetCore): void {
|
|
3739
|
-
registerRoutes(core.router)
|
|
3740
|
-
}
|
|
3741
|
-
}
|
|
3742
3262
|
`;
|
|
3743
3263
|
}
|
|
3744
3264
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -4409,11 +3929,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
4409
3929
|
module: "dist/index.mjs",
|
|
4410
3930
|
types: "dist/index.d.ts",
|
|
4411
3931
|
scripts: {
|
|
4412
|
-
build: "tsup src/index.ts --format
|
|
3932
|
+
build: "tsup src/index.ts --format esm --dts",
|
|
4413
3933
|
test: "bun test",
|
|
4414
|
-
typecheck: "tsc --noEmit"
|
|
4415
|
-
check: "bun run typecheck && bun run test",
|
|
4416
|
-
validate: "bun run check"
|
|
3934
|
+
typecheck: "bun tsc --noEmit"
|
|
4417
3935
|
},
|
|
4418
3936
|
dependencies: {
|
|
4419
3937
|
"@gravito/core": depVersion,
|
|
@@ -4422,8 +3940,12 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
4422
3940
|
"@gravito/stasis": depVersion
|
|
4423
3941
|
},
|
|
4424
3942
|
devDependencies: {
|
|
4425
|
-
|
|
4426
|
-
typescript: "^5.
|
|
3943
|
+
"bun-types": "latest",
|
|
3944
|
+
typescript: "^5.9.3",
|
|
3945
|
+
tsup: "^8.0.0"
|
|
3946
|
+
},
|
|
3947
|
+
peerDependencies: {
|
|
3948
|
+
"@gravito/core": ">=1.0.0"
|
|
4427
3949
|
}
|
|
4428
3950
|
};
|
|
4429
3951
|
return JSON.stringify(pkg, null, 2);
|
|
@@ -4573,7 +4095,7 @@ var ProfileResolver = class _ProfileResolver {
|
|
|
4573
4095
|
};
|
|
4574
4096
|
|
|
4575
4097
|
// src/Scaffold.ts
|
|
4576
|
-
import
|
|
4098
|
+
import path6 from "path";
|
|
4577
4099
|
|
|
4578
4100
|
// src/generators/ActionDomainGenerator.ts
|
|
4579
4101
|
var ActionDomainGenerator = class extends BaseGenerator {
|
|
@@ -4780,6 +4302,9 @@ var ActionDomainGenerator = class extends BaseGenerator {
|
|
|
4780
4302
|
|
|
4781
4303
|
import { Model, column } from '@gravito/atlas'
|
|
4782
4304
|
|
|
4305
|
+
/**
|
|
4306
|
+
* Represents a user in the system.
|
|
4307
|
+
*/
|
|
4783
4308
|
export class User extends Model {
|
|
4784
4309
|
static table = 'users'
|
|
4785
4310
|
|
|
@@ -5064,7 +4589,7 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
5064
4589
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
5065
4590
|
start: "bun run dist/bootstrap.js",
|
|
5066
4591
|
test: "bun test",
|
|
5067
|
-
typecheck: "tsc --noEmit",
|
|
4592
|
+
typecheck: "bun tsc --noEmit",
|
|
5068
4593
|
check: "bun run typecheck && bun run test",
|
|
5069
4594
|
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
5070
4595
|
validate: "bun run check && bun run check:deps",
|
|
@@ -5078,7 +4603,7 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
5078
4603
|
},
|
|
5079
4604
|
devDependencies: {
|
|
5080
4605
|
"bun-types": "latest",
|
|
5081
|
-
typescript: "^5.
|
|
4606
|
+
typescript: "^5.9.3"
|
|
5082
4607
|
}
|
|
5083
4608
|
};
|
|
5084
4609
|
return JSON.stringify(pkg, null, 2);
|
|
@@ -5086,6 +4611,7 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
5086
4611
|
};
|
|
5087
4612
|
|
|
5088
4613
|
// src/generators/StandaloneEngineGenerator.ts
|
|
4614
|
+
import path5 from "path";
|
|
5089
4615
|
var StandaloneEngineGenerator = class extends BaseGenerator {
|
|
5090
4616
|
get architectureType() {
|
|
5091
4617
|
return "standalone-engine";
|
|
@@ -5117,9 +4643,16 @@ var StandaloneEngineGenerator = class extends BaseGenerator {
|
|
|
5117
4643
|
];
|
|
5118
4644
|
}
|
|
5119
4645
|
async generateCommonFiles(context) {
|
|
4646
|
+
const commonDir = path5.resolve(this.config.templatesDir, "common");
|
|
4647
|
+
const extendedContext = { ...context };
|
|
5120
4648
|
await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
|
|
5121
|
-
await this.
|
|
5122
|
-
|
|
4649
|
+
await this.generateFileFromTemplate(
|
|
4650
|
+
commonDir,
|
|
4651
|
+
"tsconfig.json.hbs",
|
|
4652
|
+
"tsconfig.json",
|
|
4653
|
+
extendedContext
|
|
4654
|
+
);
|
|
4655
|
+
await this.generateFileFromTemplate(commonDir, "gitignore.hbs", ".gitignore", extendedContext);
|
|
5123
4656
|
}
|
|
5124
4657
|
generatePackageJson(context) {
|
|
5125
4658
|
const pkg = {
|
|
@@ -5174,23 +4707,17 @@ A high-performance web application powered by Gravito Engine.
|
|
|
5174
4707
|
|
|
5175
4708
|
### Install Dependencies
|
|
5176
4709
|
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
\`\`\`
|
|
5180
|
-
|
|
4710
|
+
bun install
|
|
4711
|
+
|
|
5181
4712
|
### Run Development Server
|
|
5182
4713
|
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
\`\`\`
|
|
5186
|
-
|
|
4714
|
+
bun run dev
|
|
4715
|
+
|
|
5187
4716
|
### Production Build
|
|
5188
4717
|
|
|
5189
|
-
|
|
5190
|
-
bun run build
|
|
4718
|
+
bun run build
|
|
5191
4719
|
bun start
|
|
5192
|
-
|
|
5193
|
-
`;
|
|
4720
|
+
`;
|
|
5194
4721
|
}
|
|
5195
4722
|
};
|
|
5196
4723
|
|
|
@@ -5199,11 +4726,14 @@ var Scaffold = class {
|
|
|
5199
4726
|
templatesDir;
|
|
5200
4727
|
verbose;
|
|
5201
4728
|
constructor(options = {}) {
|
|
5202
|
-
this.templatesDir = options.templatesDir ??
|
|
4729
|
+
this.templatesDir = options.templatesDir ?? path6.resolve(__dirname, "../templates");
|
|
5203
4730
|
this.verbose = options.verbose ?? false;
|
|
5204
4731
|
}
|
|
5205
4732
|
/**
|
|
5206
|
-
*
|
|
4733
|
+
* Returns a list of all architectural patterns supported by the engine,
|
|
4734
|
+
* along with human-readable names and descriptions.
|
|
4735
|
+
*
|
|
4736
|
+
* @returns {Array<{type: ArchitectureType, name: string, description: string}>}
|
|
5207
4737
|
*/
|
|
5208
4738
|
getArchitectureTypes() {
|
|
5209
4739
|
return [
|
|
@@ -5240,11 +4770,16 @@ var Scaffold = class {
|
|
|
5240
4770
|
];
|
|
5241
4771
|
}
|
|
5242
4772
|
/**
|
|
5243
|
-
*
|
|
4773
|
+
* Orchestrates the complete project generation lifecycle.
|
|
4774
|
+
* This includes directory creation, file layout, profile resolution,
|
|
4775
|
+
* dependency mapping, and optional post-install hooks.
|
|
4776
|
+
*
|
|
4777
|
+
* @param {ScaffoldOptions} options - Detailed configuration for the new project.
|
|
4778
|
+
* @returns {Promise<ScaffoldResult>}
|
|
5244
4779
|
*/
|
|
5245
4780
|
async create(options) {
|
|
5246
4781
|
const generator = this.createGenerator(options.architecture);
|
|
5247
|
-
const
|
|
4782
|
+
const fs5 = await import("fs/promises");
|
|
5248
4783
|
const profileResolver = new ProfileResolver();
|
|
5249
4784
|
const profileConfig = profileResolver.resolve(options.profile, options.features);
|
|
5250
4785
|
const context = BaseGenerator.createContext(
|
|
@@ -5271,8 +4806,8 @@ var Scaffold = class {
|
|
|
5271
4806
|
// Default template for now, should come from options if applicable
|
|
5272
4807
|
"1.0.0"
|
|
5273
4808
|
);
|
|
5274
|
-
const lockPath =
|
|
5275
|
-
await
|
|
4809
|
+
const lockPath = path6.resolve(options.targetDir, "gravito.lock.json");
|
|
4810
|
+
await fs5.writeFile(lockPath, lockContent, "utf-8");
|
|
5276
4811
|
filesCreated.push(lockPath);
|
|
5277
4812
|
return {
|
|
5278
4813
|
success: true,
|