@gravito/pulse 1.0.0 → 1.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.js +987 -244
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -81110,6 +81110,7 @@ class BaseGenerator {
|
|
|
81110
81110
|
await this.writeFile(context.targetDir, "Dockerfile", this.generateDockerfile(context));
|
|
81111
81111
|
await this.writeFile(context.targetDir, ".dockerignore", this.generateDockerIgnore());
|
|
81112
81112
|
await this.writeFile(context.targetDir, "ARCHITECTURE.md", this.generateArchitectureDoc(context));
|
|
81113
|
+
await this.generateCheckScripts(context);
|
|
81113
81114
|
}
|
|
81114
81115
|
async applyOverlays(context) {
|
|
81115
81116
|
const profile = context.profile;
|
|
@@ -81181,6 +81182,10 @@ class BaseGenerator {
|
|
|
81181
81182
|
start: "bun run dist/bootstrap.js",
|
|
81182
81183
|
test: "bun test",
|
|
81183
81184
|
typecheck: "tsc --noEmit",
|
|
81185
|
+
check: "bun run typecheck && bun run test",
|
|
81186
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
81187
|
+
validate: "bun run check && bun run check:deps",
|
|
81188
|
+
precommit: "bun run validate",
|
|
81184
81189
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
81185
81190
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
81186
81191
|
},
|
|
@@ -81418,6 +81423,411 @@ coverage/
|
|
|
81418
81423
|
};
|
|
81419
81424
|
return JSON.stringify(config, null, 2);
|
|
81420
81425
|
}
|
|
81426
|
+
async generateCheckScripts(context) {
|
|
81427
|
+
const scriptsDir = path2.resolve(context.targetDir, "scripts");
|
|
81428
|
+
await fs2.mkdir(scriptsDir, { recursive: true });
|
|
81429
|
+
await this.writeFile(scriptsDir, "check-dependencies.ts", this.generateCheckDependenciesScript());
|
|
81430
|
+
await this.writeFile(scriptsDir, "check.sh", this.generateCheckShellScript());
|
|
81431
|
+
await this.writeFile(scriptsDir, "pre-commit.sh", this.generatePreCommitScript());
|
|
81432
|
+
await this.writeFile(context.targetDir, "CHECK_SYSTEM.md", this.generateCheckSystemDoc(context));
|
|
81433
|
+
}
|
|
81434
|
+
generateCheckDependenciesScript() {
|
|
81435
|
+
return `/**
|
|
81436
|
+
* \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5\u8173\u672C
|
|
81437
|
+
*
|
|
81438
|
+
* \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
|
|
81439
|
+
* \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
81440
|
+
*/
|
|
81441
|
+
|
|
81442
|
+
import { readFileSync } from 'fs'
|
|
81443
|
+
import { join } from 'path'
|
|
81444
|
+
|
|
81445
|
+
interface PackageJson {
|
|
81446
|
+
dependencies?: Record<string, string>
|
|
81447
|
+
devDependencies?: Record<string, string>
|
|
81448
|
+
}
|
|
81449
|
+
|
|
81450
|
+
interface PackageInfo {
|
|
81451
|
+
name: string
|
|
81452
|
+
current: string
|
|
81453
|
+
latest: string
|
|
81454
|
+
outdated: boolean
|
|
81455
|
+
}
|
|
81456
|
+
|
|
81457
|
+
const colors = {
|
|
81458
|
+
reset: '\\x1b[0m',
|
|
81459
|
+
green: '\\x1b[32m',
|
|
81460
|
+
yellow: '\\x1b[33m',
|
|
81461
|
+
red: '\\x1b[31m',
|
|
81462
|
+
blue: '\\x1b[36m',
|
|
81463
|
+
}
|
|
81464
|
+
|
|
81465
|
+
function log(message: string, color: keyof typeof colors = 'reset') {
|
|
81466
|
+
console.log(\`\${colors[color]}\${message}\${colors.reset}\`)
|
|
81467
|
+
}
|
|
81468
|
+
|
|
81469
|
+
async function getLatestVersion(packageName: string): Promise<string | null> {
|
|
81470
|
+
try {
|
|
81471
|
+
const response = await fetch(\`https://registry.npmjs.org/\${packageName}/latest\`)
|
|
81472
|
+
if (!response.ok) return null
|
|
81473
|
+
const data = await response.json()
|
|
81474
|
+
return data.version
|
|
81475
|
+
} catch {
|
|
81476
|
+
return null
|
|
81477
|
+
}
|
|
81478
|
+
}
|
|
81479
|
+
|
|
81480
|
+
function parseVersion(version: string): string {
|
|
81481
|
+
// \u79FB\u9664 ^, ~, >= \u7B49\u524D\u7DB4
|
|
81482
|
+
return version.replace(/^[\\^~>=<]/, '')
|
|
81483
|
+
}
|
|
81484
|
+
|
|
81485
|
+
async function checkPackage(
|
|
81486
|
+
name: string,
|
|
81487
|
+
currentVersion: string
|
|
81488
|
+
): Promise<PackageInfo | null> {
|
|
81489
|
+
// \u8DF3\u904E\u672C\u5730\u9023\u7D50\u7684\u5957\u4EF6
|
|
81490
|
+
if (currentVersion.startsWith('link:') || currentVersion.startsWith('workspace:')) {
|
|
81491
|
+
return null
|
|
81492
|
+
}
|
|
81493
|
+
|
|
81494
|
+
const current = parseVersion(currentVersion)
|
|
81495
|
+
const latest = await getLatestVersion(name)
|
|
81496
|
+
|
|
81497
|
+
if (!latest) {
|
|
81498
|
+
return null
|
|
81499
|
+
}
|
|
81500
|
+
|
|
81501
|
+
return {
|
|
81502
|
+
name,
|
|
81503
|
+
current,
|
|
81504
|
+
latest,
|
|
81505
|
+
outdated: current !== latest,
|
|
81506
|
+
}
|
|
81507
|
+
}
|
|
81508
|
+
|
|
81509
|
+
async function main() {
|
|
81510
|
+
log('\\n=== \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5 ===\\n', 'blue')
|
|
81511
|
+
|
|
81512
|
+
const packageJsonPath = join(process.cwd(), 'package.json')
|
|
81513
|
+
const packageJson: PackageJson = JSON.parse(
|
|
81514
|
+
readFileSync(packageJsonPath, 'utf-8')
|
|
81515
|
+
)
|
|
81516
|
+
|
|
81517
|
+
const allDependencies = {
|
|
81518
|
+
...packageJson.dependencies,
|
|
81519
|
+
...packageJson.devDependencies,
|
|
81520
|
+
}
|
|
81521
|
+
|
|
81522
|
+
log(\`\u6AA2\u67E5 \${Object.keys(allDependencies).length} \u500B\u5957\u4EF6...\\n\`, 'yellow')
|
|
81523
|
+
|
|
81524
|
+
const results: PackageInfo[] = []
|
|
81525
|
+
const outdated: PackageInfo[] = []
|
|
81526
|
+
const upToDate: PackageInfo[] = []
|
|
81527
|
+
|
|
81528
|
+
// \u6AA2\u67E5\u6240\u6709\u5957\u4EF6
|
|
81529
|
+
for (const [name, version] of Object.entries(allDependencies)) {
|
|
81530
|
+
const info = await checkPackage(name, version)
|
|
81531
|
+
if (info) {
|
|
81532
|
+
results.push(info)
|
|
81533
|
+
if (info.outdated) {
|
|
81534
|
+
outdated.push(info)
|
|
81535
|
+
} else {
|
|
81536
|
+
upToDate.push(info)
|
|
81537
|
+
}
|
|
81538
|
+
}
|
|
81539
|
+
}
|
|
81540
|
+
|
|
81541
|
+
// \u986F\u793A\u7D50\u679C
|
|
81542
|
+
if (upToDate.length > 0) {
|
|
81543
|
+
log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
|
|
81544
|
+
upToDate.forEach((pkg) => {
|
|
81545
|
+
log(\` \${pkg.name}: \${pkg.current}\`, 'green')
|
|
81546
|
+
})
|
|
81547
|
+
}
|
|
81548
|
+
|
|
81549
|
+
if (outdated.length > 0) {
|
|
81550
|
+
log(\`\\n\u26A0 \u9700\u8981\u66F4\u65B0 (\${outdated.length}):\`, 'yellow')
|
|
81551
|
+
outdated.forEach((pkg) => {
|
|
81552
|
+
log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
|
|
81553
|
+
})
|
|
81554
|
+
}
|
|
81555
|
+
|
|
81556
|
+
// \u7E3D\u7D50
|
|
81557
|
+
log('\\n=== \u6AA2\u67E5\u7D50\u679C ===', 'blue')
|
|
81558
|
+
log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
|
|
81559
|
+
log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
|
|
81560
|
+
log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
|
|
81561
|
+
|
|
81562
|
+
// \u5982\u679C\u6709\u9700\u8981\u66F4\u65B0\u7684\u5957\u4EF6\uFF0C\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u78BC
|
|
81563
|
+
if (outdated.length > 0) {
|
|
81564
|
+
log('\\n\u5EFA\u8B70\u57F7\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0\u5957\u4EF6\uFF1A', 'yellow')
|
|
81565
|
+
log(' bun update', 'yellow')
|
|
81566
|
+
process.exit(1)
|
|
81567
|
+
} else {
|
|
81568
|
+
log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
|
|
81569
|
+
process.exit(0)
|
|
81570
|
+
}
|
|
81571
|
+
}
|
|
81572
|
+
|
|
81573
|
+
main().catch((error) => {
|
|
81574
|
+
log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
|
|
81575
|
+
process.exit(1)
|
|
81576
|
+
})
|
|
81577
|
+
`;
|
|
81578
|
+
}
|
|
81579
|
+
generateCheckShellScript() {
|
|
81580
|
+
return `#!/bin/bash
|
|
81581
|
+
|
|
81582
|
+
# \u5C08\u6848\u6AA2\u67E5\u8173\u672C
|
|
81583
|
+
# \u57F7\u884C\u6240\u6709\u5FC5\u8981\u7684\u6AA2\u67E5\uFF1A\u985E\u578B\u6AA2\u67E5\u3001\u6E2C\u8A66\u3001\u4F9D\u8CF4\u6AA2\u67E5\u7B49
|
|
81584
|
+
|
|
81585
|
+
set -e
|
|
81586
|
+
|
|
81587
|
+
# \u984F\u8272\u5B9A\u7FA9
|
|
81588
|
+
GREEN='\\033[0;32m'
|
|
81589
|
+
YELLOW='\\033[1;33m'
|
|
81590
|
+
RED='\\033[0;31m'
|
|
81591
|
+
BLUE='\\033[0;34m'
|
|
81592
|
+
NC='\\033[0m' # No Color
|
|
81593
|
+
|
|
81594
|
+
echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
|
|
81595
|
+
|
|
81596
|
+
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
81597
|
+
if [ ! -f "package.json" ]; then
|
|
81598
|
+
echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
|
|
81599
|
+
exit 1
|
|
81600
|
+
fi
|
|
81601
|
+
|
|
81602
|
+
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
81603
|
+
if ! command -v bun &> /dev/null; then
|
|
81604
|
+
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
81605
|
+
exit 1
|
|
81606
|
+
fi
|
|
81607
|
+
|
|
81608
|
+
# 1. \u985E\u578B\u6AA2\u67E5
|
|
81609
|
+
echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
81610
|
+
if bun run typecheck; then
|
|
81611
|
+
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
81612
|
+
else
|
|
81613
|
+
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
81614
|
+
exit 1
|
|
81615
|
+
fi
|
|
81616
|
+
|
|
81617
|
+
# 2. \u57F7\u884C\u6E2C\u8A66
|
|
81618
|
+
echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
81619
|
+
if bun test; then
|
|
81620
|
+
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
81621
|
+
else
|
|
81622
|
+
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
81623
|
+
exit 1
|
|
81624
|
+
fi
|
|
81625
|
+
|
|
81626
|
+
# 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
|
|
81627
|
+
echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
|
|
81628
|
+
if bun run check:deps; then
|
|
81629
|
+
echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
|
|
81630
|
+
else
|
|
81631
|
+
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"
|
|
81632
|
+
fi
|
|
81633
|
+
|
|
81634
|
+
echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
|
|
81635
|
+
`;
|
|
81636
|
+
}
|
|
81637
|
+
generatePreCommitScript() {
|
|
81638
|
+
return `#!/bin/bash
|
|
81639
|
+
|
|
81640
|
+
# Pre-commit Hook
|
|
81641
|
+
# \u5728 git commit \u524D\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5
|
|
81642
|
+
#
|
|
81643
|
+
# \u5B89\u88DD\u65B9\u5F0F\uFF1A
|
|
81644
|
+
# ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
|
|
81645
|
+
# \u6216
|
|
81646
|
+
# cp scripts/pre-commit.sh .git/hooks/pre-commit
|
|
81647
|
+
# chmod +x .git/hooks/pre-commit
|
|
81648
|
+
|
|
81649
|
+
set -e
|
|
81650
|
+
|
|
81651
|
+
# \u984F\u8272\u5B9A\u7FA9
|
|
81652
|
+
GREEN='\\033[0;32m'
|
|
81653
|
+
YELLOW='\\033[1;33m'
|
|
81654
|
+
RED='\\033[0;31m'
|
|
81655
|
+
BLUE='\\033[0;34m'
|
|
81656
|
+
NC='\\033[0m' # No Color
|
|
81657
|
+
|
|
81658
|
+
echo -e "\${BLUE}=== Pre-commit \u6AA2\u67E5 ===\${NC}\\n"
|
|
81659
|
+
|
|
81660
|
+
# \u5207\u63DB\u5230\u5C08\u6848\u6839\u76EE\u9304
|
|
81661
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
81662
|
+
|
|
81663
|
+
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
81664
|
+
if [ ! -f "package.json" ]; then
|
|
81665
|
+
echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
|
|
81666
|
+
exit 1
|
|
81667
|
+
fi
|
|
81668
|
+
|
|
81669
|
+
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
81670
|
+
if ! command -v bun &> /dev/null; then
|
|
81671
|
+
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
81672
|
+
exit 1
|
|
81673
|
+
fi
|
|
81674
|
+
|
|
81675
|
+
# 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
|
|
81676
|
+
echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
81677
|
+
if bun run typecheck; then
|
|
81678
|
+
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
81679
|
+
else
|
|
81680
|
+
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
81681
|
+
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
81682
|
+
exit 1
|
|
81683
|
+
fi
|
|
81684
|
+
|
|
81685
|
+
# 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
|
|
81686
|
+
echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
81687
|
+
if bun test; then
|
|
81688
|
+
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
81689
|
+
else
|
|
81690
|
+
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
81691
|
+
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
81692
|
+
exit 1
|
|
81693
|
+
fi
|
|
81694
|
+
|
|
81695
|
+
echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
|
|
81696
|
+
`;
|
|
81697
|
+
}
|
|
81698
|
+
generateCheckSystemDoc(context) {
|
|
81699
|
+
return `# \u5C08\u6848\u6AA2\u67E5\u7CFB\u7D71
|
|
81700
|
+
|
|
81701
|
+
\u672C\u5C08\u6848\u5DF2\u5EFA\u7ACB\u5B8C\u6574\u7684\u672C\u5730\u6AA2\u67E5\u6A5F\u5236\uFF0C\u7121\u9700\u4F9D\u8CF4 GitHub CI\u3002
|
|
81702
|
+
|
|
81703
|
+
## \u5FEB\u901F\u958B\u59CB
|
|
81704
|
+
|
|
81705
|
+
### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
|
|
81706
|
+
\`\`\`bash
|
|
81707
|
+
bun run validate
|
|
81708
|
+
\`\`\`
|
|
81709
|
+
|
|
81710
|
+
### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
|
|
81711
|
+
\`\`\`bash
|
|
81712
|
+
# \u985E\u578B\u6AA2\u67E5
|
|
81713
|
+
bun run typecheck
|
|
81714
|
+
|
|
81715
|
+
# \u6E2C\u8A66
|
|
81716
|
+
bun run test
|
|
81717
|
+
|
|
81718
|
+
# \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
81719
|
+
bun run check:deps
|
|
81720
|
+
\`\`\`
|
|
81721
|
+
|
|
81722
|
+
## \u53EF\u7528\u547D\u4EE4
|
|
81723
|
+
|
|
81724
|
+
### Package.json \u8173\u672C
|
|
81725
|
+
|
|
81726
|
+
| \u547D\u4EE4 | \u8AAA\u660E |
|
|
81727
|
+
|------|------|
|
|
81728
|
+
| \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
|
|
81729
|
+
| \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
|
|
81730
|
+
| \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
|
|
81731
|
+
| \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
|
|
81732
|
+
| \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
|
|
81733
|
+
| \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
|
|
81734
|
+
|
|
81735
|
+
### Shell \u8173\u672C
|
|
81736
|
+
|
|
81737
|
+
| \u8173\u672C | \u8AAA\u660E |
|
|
81738
|
+
|------|------|
|
|
81739
|
+
| \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
|
|
81740
|
+
| \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
|
|
81741
|
+
|
|
81742
|
+
## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
81743
|
+
|
|
81744
|
+
\u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
|
|
81745
|
+
|
|
81746
|
+
\`\`\`bash
|
|
81747
|
+
# \u5B89\u88DD pre-commit hook
|
|
81748
|
+
ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
|
|
81749
|
+
|
|
81750
|
+
# \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
|
|
81751
|
+
cp scripts/pre-commit.sh .git/hooks/pre-commit
|
|
81752
|
+
chmod +x .git/hooks/pre-commit
|
|
81753
|
+
\`\`\`
|
|
81754
|
+
|
|
81755
|
+
**\u529F\u80FD\uFF1A**
|
|
81756
|
+
- \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
|
|
81757
|
+
- \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
|
|
81758
|
+
- \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
|
|
81759
|
+
|
|
81760
|
+
**\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
|
|
81761
|
+
\`\`\`bash
|
|
81762
|
+
git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
|
|
81763
|
+
\`\`\`
|
|
81764
|
+
|
|
81765
|
+
## \u6AA2\u67E5\u9805\u76EE
|
|
81766
|
+
|
|
81767
|
+
### 1. \u985E\u578B\u6AA2\u67E5
|
|
81768
|
+
- \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
|
|
81769
|
+
- \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
|
|
81770
|
+
|
|
81771
|
+
### 2. \u6E2C\u8A66
|
|
81772
|
+
- \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
|
|
81773
|
+
- \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
|
|
81774
|
+
|
|
81775
|
+
### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
|
|
81776
|
+
- \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
|
|
81777
|
+
- \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
81778
|
+
- \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
|
|
81779
|
+
|
|
81780
|
+
## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
|
|
81781
|
+
|
|
81782
|
+
### \u958B\u767C\u6642
|
|
81783
|
+
1. \u958B\u767C\u529F\u80FD
|
|
81784
|
+
2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
|
|
81785
|
+
3. \u4FEE\u6B63\u554F\u984C
|
|
81786
|
+
4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
|
|
81787
|
+
|
|
81788
|
+
### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
81789
|
+
1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
|
|
81790
|
+
2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
|
|
81791
|
+
3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
|
|
81792
|
+
4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
|
|
81793
|
+
|
|
81794
|
+
## \u6A94\u6848\u7D50\u69CB
|
|
81795
|
+
|
|
81796
|
+
\`\`\`
|
|
81797
|
+
${context.nameKebabCase}/
|
|
81798
|
+
\u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
|
|
81799
|
+
\u251C\u2500\u2500 scripts/
|
|
81800
|
+
\u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
|
|
81801
|
+
\u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
81802
|
+
\u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
|
|
81803
|
+
\u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
|
|
81804
|
+
\`\`\`
|
|
81805
|
+
|
|
81806
|
+
## \u6CE8\u610F\u4E8B\u9805
|
|
81807
|
+
|
|
81808
|
+
1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
|
|
81809
|
+
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
|
|
81810
|
+
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
|
|
81811
|
+
|
|
81812
|
+
## \u6545\u969C\u6392\u9664
|
|
81813
|
+
|
|
81814
|
+
### \u6AA2\u67E5\u5931\u6557
|
|
81815
|
+
1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
|
|
81816
|
+
2. \u4FEE\u6B63\u554F\u984C
|
|
81817
|
+
3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
|
|
81818
|
+
|
|
81819
|
+
### \u8DF3\u904E\u6AA2\u67E5
|
|
81820
|
+
\u53EA\u6709\u5728\u7DCA\u6025\u60C5\u6CC1\u4E0B\u624D\u4F7F\u7528\uFF1A
|
|
81821
|
+
\`\`\`bash
|
|
81822
|
+
git commit --no-verify
|
|
81823
|
+
\`\`\`
|
|
81824
|
+
|
|
81825
|
+
### \u79FB\u9664 Pre-commit Hook
|
|
81826
|
+
\`\`\`bash
|
|
81827
|
+
rm .git/hooks/pre-commit
|
|
81828
|
+
\`\`\`
|
|
81829
|
+
`;
|
|
81830
|
+
}
|
|
81421
81831
|
log(message) {
|
|
81422
81832
|
if (this.config.verbose) {
|
|
81423
81833
|
console.log(message);
|
|
@@ -81588,6 +81998,11 @@ class CleanArchitectureGenerator extends BaseGenerator {
|
|
|
81588
81998
|
type: "directory",
|
|
81589
81999
|
name: "Providers",
|
|
81590
82000
|
children: [
|
|
82001
|
+
{
|
|
82002
|
+
type: "file",
|
|
82003
|
+
name: "index.ts",
|
|
82004
|
+
content: this.generateProvidersIndex()
|
|
82005
|
+
},
|
|
81591
82006
|
{
|
|
81592
82007
|
type: "file",
|
|
81593
82008
|
name: "AppServiceProvider.ts",
|
|
@@ -81597,6 +82012,16 @@ class CleanArchitectureGenerator extends BaseGenerator {
|
|
|
81597
82012
|
type: "file",
|
|
81598
82013
|
name: "RepositoryServiceProvider.ts",
|
|
81599
82014
|
content: this.generateRepositoryServiceProvider()
|
|
82015
|
+
},
|
|
82016
|
+
{
|
|
82017
|
+
type: "file",
|
|
82018
|
+
name: "MiddlewareProvider.ts",
|
|
82019
|
+
content: this.generateMiddlewareProvider()
|
|
82020
|
+
},
|
|
82021
|
+
{
|
|
82022
|
+
type: "file",
|
|
82023
|
+
name: "RouteProvider.ts",
|
|
82024
|
+
content: this.generateRouteProvider()
|
|
81600
82025
|
}
|
|
81601
82026
|
]
|
|
81602
82027
|
}
|
|
@@ -82080,6 +82505,65 @@ export class RepositoryServiceProvider extends ServiceProvider {
|
|
|
82080
82505
|
container.singleton('mailService', () => new MailService())
|
|
82081
82506
|
}
|
|
82082
82507
|
}
|
|
82508
|
+
`;
|
|
82509
|
+
}
|
|
82510
|
+
generateProvidersIndex() {
|
|
82511
|
+
return `/**
|
|
82512
|
+
* Application Service Providers
|
|
82513
|
+
*/
|
|
82514
|
+
|
|
82515
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
82516
|
+
export { RepositoryServiceProvider } from './RepositoryServiceProvider'
|
|
82517
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
82518
|
+
export { RouteProvider } from './RouteProvider'
|
|
82519
|
+
`;
|
|
82520
|
+
}
|
|
82521
|
+
generateMiddlewareProvider() {
|
|
82522
|
+
return `/**
|
|
82523
|
+
* Middleware Service Provider
|
|
82524
|
+
*/
|
|
82525
|
+
|
|
82526
|
+
import {
|
|
82527
|
+
ServiceProvider,
|
|
82528
|
+
type Container,
|
|
82529
|
+
type PlanetCore,
|
|
82530
|
+
bodySizeLimit,
|
|
82531
|
+
securityHeaders,
|
|
82532
|
+
} from '@gravito/core'
|
|
82533
|
+
|
|
82534
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
82535
|
+
register(_container: Container): void {}
|
|
82536
|
+
|
|
82537
|
+
boot(core: PlanetCore): void {
|
|
82538
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
82539
|
+
|
|
82540
|
+
core.adapter.use('*', securityHeaders({
|
|
82541
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
82542
|
+
}))
|
|
82543
|
+
|
|
82544
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
82545
|
+
|
|
82546
|
+
core.logger.info('\uD83D\uDEE1\uFE0F Middleware registered')
|
|
82547
|
+
}
|
|
82548
|
+
}
|
|
82549
|
+
`;
|
|
82550
|
+
}
|
|
82551
|
+
generateRouteProvider() {
|
|
82552
|
+
return `/**
|
|
82553
|
+
* Route Service Provider
|
|
82554
|
+
*/
|
|
82555
|
+
|
|
82556
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
82557
|
+
import { registerApiRoutes } from '../../Interface/Http/Routes/api'
|
|
82558
|
+
|
|
82559
|
+
export class RouteProvider extends ServiceProvider {
|
|
82560
|
+
register(_container: Container): void {}
|
|
82561
|
+
|
|
82562
|
+
boot(core: PlanetCore): void {
|
|
82563
|
+
registerApiRoutes(core.router)
|
|
82564
|
+
core.logger.info('\uD83D\uDEE4\uFE0F Routes registered')
|
|
82565
|
+
}
|
|
82566
|
+
}
|
|
82083
82567
|
`;
|
|
82084
82568
|
}
|
|
82085
82569
|
generateUserController() {
|
|
@@ -82161,64 +82645,55 @@ export class UserPresenter {
|
|
|
82161
82645
|
}
|
|
82162
82646
|
`;
|
|
82163
82647
|
}
|
|
82164
|
-
generateBootstrap(
|
|
82648
|
+
generateBootstrap(_context) {
|
|
82165
82649
|
return `/**
|
|
82166
82650
|
* Application Bootstrap
|
|
82651
|
+
*
|
|
82652
|
+
* The entry point for your Clean Architecture application.
|
|
82653
|
+
* Uses the ServiceProvider pattern for modular initialization.
|
|
82654
|
+
*
|
|
82655
|
+
* Lifecycle:
|
|
82656
|
+
* 1. Configure: Load app config and orbits
|
|
82657
|
+
* 2. Boot: Initialize PlanetCore
|
|
82658
|
+
* 3. Register Providers: Bind services to container
|
|
82659
|
+
* 4. Bootstrap: Boot all providers
|
|
82167
82660
|
*/
|
|
82168
82661
|
|
|
82169
|
-
import {
|
|
82662
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
82170
82663
|
import { OrbitAtlas } from '@gravito/atlas'
|
|
82171
|
-
import
|
|
82172
|
-
import {
|
|
82173
|
-
|
|
82174
|
-
|
|
82175
|
-
|
|
82176
|
-
|
|
82177
|
-
|
|
82178
|
-
|
|
82179
|
-
|
|
82180
|
-
|
|
82181
|
-
|
|
82182
|
-
|
|
82183
|
-
|
|
82184
|
-
"default-src 'self'",
|
|
82185
|
-
"script-src 'self' 'unsafe-inline'",
|
|
82186
|
-
"style-src 'self' 'unsafe-inline'",
|
|
82187
|
-
"img-src 'self' data:",
|
|
82188
|
-
"object-src 'none'",
|
|
82189
|
-
"base-uri 'self'",
|
|
82190
|
-
"frame-ancestors 'none'",
|
|
82191
|
-
].join('; ')
|
|
82192
|
-
const cspValue = process.env.APP_CSP
|
|
82193
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
82194
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
82195
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
82196
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
82197
|
-
|
|
82198
|
-
core.adapter.use(
|
|
82199
|
-
'*',
|
|
82200
|
-
securityHeaders({
|
|
82201
|
-
contentSecurityPolicy: csp,
|
|
82202
|
-
hsts:
|
|
82203
|
-
process.env.NODE_ENV === 'production'
|
|
82204
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
82205
|
-
: false,
|
|
82664
|
+
import appConfig from '../config/app'
|
|
82665
|
+
import {
|
|
82666
|
+
AppServiceProvider,
|
|
82667
|
+
RepositoryServiceProvider,
|
|
82668
|
+
MiddlewareProvider,
|
|
82669
|
+
RouteProvider,
|
|
82670
|
+
} from './Infrastructure/Providers'
|
|
82671
|
+
|
|
82672
|
+
export async function bootstrap() {
|
|
82673
|
+
// 1. Configure
|
|
82674
|
+
const config = defineConfig({
|
|
82675
|
+
config: appConfig,
|
|
82676
|
+
orbits: [new OrbitAtlas()],
|
|
82206
82677
|
})
|
|
82207
|
-
)
|
|
82208
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
82209
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
82210
|
-
}
|
|
82211
82678
|
|
|
82212
|
-
|
|
82679
|
+
// 2. Boot Core
|
|
82680
|
+
const core = await PlanetCore.boot(config)
|
|
82681
|
+
core.registerGlobalErrorHandlers()
|
|
82213
82682
|
|
|
82214
|
-
|
|
82215
|
-
core.register(new
|
|
82683
|
+
// 3. Register Providers
|
|
82684
|
+
core.register(new RepositoryServiceProvider())
|
|
82685
|
+
core.register(new AppServiceProvider())
|
|
82686
|
+
core.register(new MiddlewareProvider())
|
|
82687
|
+
core.register(new RouteProvider())
|
|
82216
82688
|
|
|
82217
|
-
|
|
82689
|
+
// 4. Bootstrap All Providers
|
|
82690
|
+
await core.bootstrap()
|
|
82218
82691
|
|
|
82219
|
-
|
|
82220
|
-
|
|
82692
|
+
return core
|
|
82693
|
+
}
|
|
82221
82694
|
|
|
82695
|
+
// Application Entry Point
|
|
82696
|
+
const core = await bootstrap()
|
|
82222
82697
|
export default core.liftoff()
|
|
82223
82698
|
`;
|
|
82224
82699
|
}
|
|
@@ -82230,6 +82705,15 @@ export default core.liftoff()
|
|
|
82230
82705
|
This project follows **Clean Architecture** (by Robert C. Martin).
|
|
82231
82706
|
The key principle is the **Dependency Rule**: dependencies point inward.
|
|
82232
82707
|
|
|
82708
|
+
## Service Providers
|
|
82709
|
+
|
|
82710
|
+
Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
82711
|
+
|
|
82712
|
+
### Provider Lifecycle
|
|
82713
|
+
|
|
82714
|
+
1. **register()**: Bind services to the container (sync or async).
|
|
82715
|
+
2. **boot()**: Called after ALL providers have registered. Safe to use other services.
|
|
82716
|
+
|
|
82233
82717
|
## Layer Structure
|
|
82234
82718
|
|
|
82235
82719
|
\`\`\`
|
|
@@ -82301,6 +82785,10 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
82301
82785
|
start: "bun run dist/bootstrap.js",
|
|
82302
82786
|
test: "bun test",
|
|
82303
82787
|
typecheck: "tsc --noEmit",
|
|
82788
|
+
check: "bun run typecheck && bun run test",
|
|
82789
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
82790
|
+
validate: "bun run check && bun run check:deps",
|
|
82791
|
+
precommit: "bun run validate",
|
|
82304
82792
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
82305
82793
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
82306
82794
|
},
|
|
@@ -82605,63 +83093,46 @@ class DddGenerator extends BaseGenerator {
|
|
|
82605
83093
|
]
|
|
82606
83094
|
};
|
|
82607
83095
|
}
|
|
82608
|
-
generateBootstrapApp(
|
|
83096
|
+
generateBootstrapApp(_context) {
|
|
82609
83097
|
return `/**
|
|
82610
83098
|
* Application Bootstrap
|
|
82611
83099
|
*
|
|
82612
|
-
* Central configuration and initialization
|
|
83100
|
+
* Central configuration and initialization using the ServiceProvider pattern.
|
|
83101
|
+
*
|
|
83102
|
+
* Lifecycle:
|
|
83103
|
+
* 1. Configure: Load app config and orbits
|
|
83104
|
+
* 2. Boot: Initialize PlanetCore
|
|
83105
|
+
* 3. Register Providers: Bind services to container
|
|
83106
|
+
* 4. Bootstrap: Boot all providers
|
|
82613
83107
|
*/
|
|
82614
83108
|
|
|
82615
|
-
import {
|
|
83109
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
83110
|
+
import { OrbitAtlas } from '@gravito/atlas'
|
|
83111
|
+
import appConfig from '../../config/app'
|
|
82616
83112
|
import { registerProviders } from './providers'
|
|
82617
83113
|
import { registerRoutes } from './routes'
|
|
82618
83114
|
|
|
82619
83115
|
export async function createApp(): Promise<PlanetCore> {
|
|
82620
|
-
|
|
82621
|
-
|
|
82622
|
-
|
|
82623
|
-
|
|
82624
|
-
|
|
83116
|
+
// 1. Configure
|
|
83117
|
+
const config = defineConfig({
|
|
83118
|
+
config: appConfig,
|
|
83119
|
+
orbits: [new OrbitAtlas()],
|
|
83120
|
+
})
|
|
82625
83121
|
|
|
82626
|
-
|
|
82627
|
-
|
|
82628
|
-
|
|
82629
|
-
"style-src 'self' 'unsafe-inline'",
|
|
82630
|
-
"img-src 'self' data:",
|
|
82631
|
-
"object-src 'none'",
|
|
82632
|
-
"base-uri 'self'",
|
|
82633
|
-
"frame-ancestors 'none'",
|
|
82634
|
-
].join('; ')
|
|
82635
|
-
const cspValue = process.env.APP_CSP
|
|
82636
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
82637
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
82638
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
82639
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
82640
|
-
|
|
82641
|
-
core.adapter.use(
|
|
82642
|
-
'*',
|
|
82643
|
-
securityHeaders({
|
|
82644
|
-
contentSecurityPolicy: csp,
|
|
82645
|
-
hsts:
|
|
82646
|
-
process.env.NODE_ENV === 'production'
|
|
82647
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
82648
|
-
: false,
|
|
82649
|
-
})
|
|
82650
|
-
)
|
|
82651
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
82652
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
82653
|
-
}
|
|
83122
|
+
// 2. Boot Core
|
|
83123
|
+
const core = await PlanetCore.boot(config)
|
|
83124
|
+
core.registerGlobalErrorHandlers()
|
|
82654
83125
|
|
|
82655
|
-
|
|
82656
|
-
|
|
83126
|
+
// 3. Register Providers
|
|
83127
|
+
await registerProviders(core)
|
|
82657
83128
|
|
|
82658
|
-
|
|
82659
|
-
|
|
83129
|
+
// 4. Bootstrap All Providers
|
|
83130
|
+
await core.bootstrap()
|
|
82660
83131
|
|
|
82661
|
-
|
|
82662
|
-
|
|
83132
|
+
// Register routes after bootstrap
|
|
83133
|
+
registerRoutes(core.router)
|
|
82663
83134
|
|
|
82664
|
-
|
|
83135
|
+
return core
|
|
82665
83136
|
}
|
|
82666
83137
|
`;
|
|
82667
83138
|
}
|
|
@@ -82669,19 +83140,48 @@ export async function createApp(): Promise<PlanetCore> {
|
|
|
82669
83140
|
return `/**
|
|
82670
83141
|
* Service Providers Registry
|
|
82671
83142
|
*
|
|
82672
|
-
* Register all
|
|
83143
|
+
* Register all service providers here.
|
|
83144
|
+
* Include both global and module-specific providers.
|
|
82673
83145
|
*/
|
|
82674
83146
|
|
|
82675
|
-
import
|
|
83147
|
+
import {
|
|
83148
|
+
ServiceProvider,
|
|
83149
|
+
type Container,
|
|
83150
|
+
type PlanetCore,
|
|
83151
|
+
bodySizeLimit,
|
|
83152
|
+
securityHeaders,
|
|
83153
|
+
} from '@gravito/core'
|
|
82676
83154
|
import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
|
|
82677
83155
|
import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
|
|
82678
83156
|
|
|
83157
|
+
/**
|
|
83158
|
+
* Middleware Provider - Global middleware registration
|
|
83159
|
+
*/
|
|
83160
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
83161
|
+
register(_container: Container): void {}
|
|
83162
|
+
|
|
83163
|
+
boot(core: PlanetCore): void {
|
|
83164
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
83165
|
+
|
|
83166
|
+
core.adapter.use('*', securityHeaders({
|
|
83167
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
83168
|
+
}))
|
|
83169
|
+
|
|
83170
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
83171
|
+
|
|
83172
|
+
core.logger.info('\uD83D\uDEE1\uFE0F Global middleware registered')
|
|
83173
|
+
}
|
|
83174
|
+
}
|
|
83175
|
+
|
|
82679
83176
|
export async function registerProviders(core: PlanetCore): Promise<void> {
|
|
82680
|
-
|
|
82681
|
-
|
|
82682
|
-
|
|
83177
|
+
// Global Providers
|
|
83178
|
+
core.register(new MiddlewareProvider())
|
|
83179
|
+
|
|
83180
|
+
// Module Providers
|
|
83181
|
+
core.register(new OrderingServiceProvider())
|
|
83182
|
+
core.register(new CatalogServiceProvider())
|
|
82683
83183
|
|
|
82684
|
-
|
|
83184
|
+
// Add more providers as needed
|
|
82685
83185
|
}
|
|
82686
83186
|
`;
|
|
82687
83187
|
}
|
|
@@ -82792,6 +83292,10 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
82792
83292
|
start: "bun run dist/main.js",
|
|
82793
83293
|
test: "bun test",
|
|
82794
83294
|
typecheck: "tsc --noEmit",
|
|
83295
|
+
check: "bun run typecheck && bun run test",
|
|
83296
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
83297
|
+
validate: "bun run check && bun run check:deps",
|
|
83298
|
+
precommit: "bun run validate",
|
|
82795
83299
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
82796
83300
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
82797
83301
|
},
|
|
@@ -83223,6 +83727,16 @@ export function report(error: unknown): void {
|
|
|
83223
83727
|
|
|
83224
83728
|
This project follows **Domain-Driven Design (DDD)** with strategic and tactical patterns.
|
|
83225
83729
|
|
|
83730
|
+
## Service Providers
|
|
83731
|
+
|
|
83732
|
+
Service providers are the central place to configure your application and modules. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
83733
|
+
|
|
83734
|
+
### Internal Bootstrapping
|
|
83735
|
+
|
|
83736
|
+
1. **Bootstrap/app.ts**: Orchestrates the 4-step lifecycle (Configure, Boot, Register, Bootstrap).
|
|
83737
|
+
2. **Bootstrap/providers.ts**: Central registry for all global and module-specific providers.
|
|
83738
|
+
3. **Infrastructure/Providers/[Module]ServiceProvider.ts**: Module-specific service registration.
|
|
83739
|
+
|
|
83226
83740
|
## Bounded Contexts
|
|
83227
83741
|
|
|
83228
83742
|
\`\`\`
|
|
@@ -83359,6 +83873,11 @@ class EnterpriseMvcGenerator extends BaseGenerator {
|
|
|
83359
83873
|
type: "directory",
|
|
83360
83874
|
name: "Providers",
|
|
83361
83875
|
children: [
|
|
83876
|
+
{
|
|
83877
|
+
type: "file",
|
|
83878
|
+
name: "index.ts",
|
|
83879
|
+
content: this.generateProvidersIndex()
|
|
83880
|
+
},
|
|
83362
83881
|
{
|
|
83363
83882
|
type: "file",
|
|
83364
83883
|
name: "AppServiceProvider.ts",
|
|
@@ -83366,8 +83885,18 @@ class EnterpriseMvcGenerator extends BaseGenerator {
|
|
|
83366
83885
|
},
|
|
83367
83886
|
{
|
|
83368
83887
|
type: "file",
|
|
83369
|
-
name: "
|
|
83370
|
-
content: this.
|
|
83888
|
+
name: "DatabaseProvider.ts",
|
|
83889
|
+
content: this.generateDatabaseProvider()
|
|
83890
|
+
},
|
|
83891
|
+
{
|
|
83892
|
+
type: "file",
|
|
83893
|
+
name: "MiddlewareProvider.ts",
|
|
83894
|
+
content: this.generateMiddlewareProvider()
|
|
83895
|
+
},
|
|
83896
|
+
{
|
|
83897
|
+
type: "file",
|
|
83898
|
+
name: "RouteProvider.ts",
|
|
83899
|
+
content: this.generateRouteProvider()
|
|
83371
83900
|
}
|
|
83372
83901
|
]
|
|
83373
83902
|
},
|
|
@@ -83828,6 +84357,132 @@ export class RouteServiceProvider extends ServiceProvider {
|
|
|
83828
84357
|
registerRoutes(core.router)
|
|
83829
84358
|
}
|
|
83830
84359
|
}
|
|
84360
|
+
`;
|
|
84361
|
+
}
|
|
84362
|
+
generateProvidersIndex() {
|
|
84363
|
+
return `/**
|
|
84364
|
+
* Application Service Providers
|
|
84365
|
+
*
|
|
84366
|
+
* Export all providers for easy importing in bootstrap.
|
|
84367
|
+
* Providers are registered in the order they are listed.
|
|
84368
|
+
*/
|
|
84369
|
+
|
|
84370
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
84371
|
+
export { DatabaseProvider } from './DatabaseProvider'
|
|
84372
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
84373
|
+
export { RouteProvider } from './RouteProvider'
|
|
84374
|
+
`;
|
|
84375
|
+
}
|
|
84376
|
+
generateDatabaseProvider() {
|
|
84377
|
+
return `/**
|
|
84378
|
+
* Database Service Provider
|
|
84379
|
+
*
|
|
84380
|
+
* Handles database initialization and migrations.
|
|
84381
|
+
*
|
|
84382
|
+
* Lifecycle:
|
|
84383
|
+
* - register(): Bind database config to container
|
|
84384
|
+
* - boot(): Run migrations and seeders
|
|
84385
|
+
*/
|
|
84386
|
+
|
|
84387
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
84388
|
+
import databaseConfig from '../../config/database'
|
|
84389
|
+
|
|
84390
|
+
export class DatabaseProvider extends ServiceProvider {
|
|
84391
|
+
/**
|
|
84392
|
+
* Register database configuration.
|
|
84393
|
+
*/
|
|
84394
|
+
register(_container: Container): void {
|
|
84395
|
+
this.mergeConfig(this.core!.config, 'database', databaseConfig)
|
|
84396
|
+
}
|
|
84397
|
+
|
|
84398
|
+
/**
|
|
84399
|
+
* Initialize database connections.
|
|
84400
|
+
*/
|
|
84401
|
+
async boot(core: PlanetCore): Promise<void> {
|
|
84402
|
+
// Database initialization will be handled by Atlas orbit
|
|
84403
|
+
core.logger.info('\uD83D\uDCE6 Database provider booted')
|
|
84404
|
+
}
|
|
84405
|
+
}
|
|
84406
|
+
`;
|
|
84407
|
+
}
|
|
84408
|
+
generateMiddlewareProvider() {
|
|
84409
|
+
return `/**
|
|
84410
|
+
* Middleware Service Provider
|
|
84411
|
+
*
|
|
84412
|
+
* Registers global middleware stack.
|
|
84413
|
+
* Provides a centralized location for middleware configuration.
|
|
84414
|
+
*
|
|
84415
|
+
* Lifecycle:
|
|
84416
|
+
* - register(): N/A (no container bindings)
|
|
84417
|
+
* - boot(): Register global middleware
|
|
84418
|
+
*/
|
|
84419
|
+
|
|
84420
|
+
import {
|
|
84421
|
+
ServiceProvider,
|
|
84422
|
+
type Container,
|
|
84423
|
+
type PlanetCore,
|
|
84424
|
+
bodySizeLimit,
|
|
84425
|
+
securityHeaders,
|
|
84426
|
+
} from '@gravito/core'
|
|
84427
|
+
|
|
84428
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
84429
|
+
/**
|
|
84430
|
+
* No container bindings needed.
|
|
84431
|
+
*/
|
|
84432
|
+
register(_container: Container): void {
|
|
84433
|
+
// Middleware doesn't require container bindings
|
|
84434
|
+
}
|
|
84435
|
+
|
|
84436
|
+
/**
|
|
84437
|
+
* Register global middleware stack.
|
|
84438
|
+
*/
|
|
84439
|
+
boot(core: PlanetCore): void {
|
|
84440
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
84441
|
+
|
|
84442
|
+
// Security Headers
|
|
84443
|
+
core.adapter.use('*', securityHeaders({
|
|
84444
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
84445
|
+
}))
|
|
84446
|
+
|
|
84447
|
+
// Body Parser Limits
|
|
84448
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024)) // 10MB limit
|
|
84449
|
+
|
|
84450
|
+
core.logger.info('\uD83D\uDEE1\uFE0F Middleware registered')
|
|
84451
|
+
}
|
|
84452
|
+
}
|
|
84453
|
+
`;
|
|
84454
|
+
}
|
|
84455
|
+
generateRouteProvider() {
|
|
84456
|
+
return `/**
|
|
84457
|
+
* Route Service Provider
|
|
84458
|
+
*
|
|
84459
|
+
* Registers application routes.
|
|
84460
|
+
* Routes are registered in the boot phase after all services are available.
|
|
84461
|
+
*
|
|
84462
|
+
* Lifecycle:
|
|
84463
|
+
* - register(): N/A
|
|
84464
|
+
* - boot(): Register routes
|
|
84465
|
+
*/
|
|
84466
|
+
|
|
84467
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
84468
|
+
import { registerRoutes } from '../routes'
|
|
84469
|
+
|
|
84470
|
+
export class RouteProvider extends ServiceProvider {
|
|
84471
|
+
/**
|
|
84472
|
+
* No container bindings needed.
|
|
84473
|
+
*/
|
|
84474
|
+
register(_container: Container): void {
|
|
84475
|
+
// Routes don't require container bindings
|
|
84476
|
+
}
|
|
84477
|
+
|
|
84478
|
+
/**
|
|
84479
|
+
* Register application routes.
|
|
84480
|
+
*/
|
|
84481
|
+
boot(core: PlanetCore): void {
|
|
84482
|
+
registerRoutes(core.router)
|
|
84483
|
+
core.logger.info('\uD83D\uDEE4\uFE0F Routes registered')
|
|
84484
|
+
}
|
|
84485
|
+
}
|
|
83831
84486
|
`;
|
|
83832
84487
|
}
|
|
83833
84488
|
generateExceptionHandler() {
|
|
@@ -83877,74 +84532,75 @@ export const dontReport: string[] = [
|
|
|
83877
84532
|
generateBootstrap(context) {
|
|
83878
84533
|
const spectrumImport = context.withSpectrum ? `import { SpectrumOrbit } from '@gravito/spectrum'
|
|
83879
84534
|
` : "";
|
|
83880
|
-
const spectrumOrbit = context.withSpectrum ?
|
|
83881
|
-
// Enable Debug Dashboard
|
|
83882
|
-
if (process.env.APP_DEBUG === 'true') {
|
|
83883
|
-
await core.orbit(new SpectrumOrbit())
|
|
83884
|
-
}
|
|
83885
|
-
` : "";
|
|
84535
|
+
const spectrumOrbit = context.withSpectrum ? " new SpectrumOrbit()," : "";
|
|
83886
84536
|
return `/**
|
|
83887
84537
|
* Application Bootstrap
|
|
83888
84538
|
*
|
|
83889
|
-
*
|
|
83890
|
-
*
|
|
84539
|
+
* The entry point for your Gravito application.
|
|
84540
|
+
* Uses the ServiceProvider pattern for modular, maintainable initialization.
|
|
84541
|
+
*
|
|
84542
|
+
* Lifecycle:
|
|
84543
|
+
* 1. Configure: Load app config and orbits
|
|
84544
|
+
* 2. Boot: Initialize PlanetCore
|
|
84545
|
+
* 3. Register Providers: Bind services to container
|
|
84546
|
+
* 4. Bootstrap: Boot all providers
|
|
84547
|
+
*
|
|
84548
|
+
* @module bootstrap
|
|
83891
84549
|
*/
|
|
83892
84550
|
|
|
83893
|
-
import {
|
|
84551
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
83894
84552
|
import { OrbitAtlas } from '@gravito/atlas'
|
|
83895
|
-
import
|
|
83896
|
-
${spectrumImport}import {
|
|
83897
|
-
|
|
83898
|
-
|
|
83899
|
-
|
|
83900
|
-
|
|
83901
|
-
|
|
83902
|
-
// Create application core
|
|
83903
|
-
const core = new PlanetCore({
|
|
83904
|
-
config: {
|
|
83905
|
-
APP_NAME: process.env.APP_NAME ?? '${context.name}',
|
|
83906
|
-
database: databaseConfig,
|
|
83907
|
-
},
|
|
83908
|
-
})
|
|
83909
|
-
const defaultCsp = [
|
|
83910
|
-
"default-src 'self'",
|
|
83911
|
-
"script-src 'self' 'unsafe-inline'",
|
|
83912
|
-
"style-src 'self' 'unsafe-inline'",
|
|
83913
|
-
"img-src 'self' data:",
|
|
83914
|
-
"object-src 'none'",
|
|
83915
|
-
"base-uri 'self'",
|
|
83916
|
-
"frame-ancestors 'none'",
|
|
83917
|
-
].join('; ')
|
|
83918
|
-
const cspValue = process.env.APP_CSP
|
|
83919
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
83920
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
83921
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
83922
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
83923
|
-
|
|
83924
|
-
core.adapter.use(
|
|
83925
|
-
'*',
|
|
83926
|
-
securityHeaders({
|
|
83927
|
-
contentSecurityPolicy: csp,
|
|
83928
|
-
hsts:
|
|
83929
|
-
process.env.NODE_ENV === 'production'
|
|
83930
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
83931
|
-
: false,
|
|
83932
|
-
})
|
|
83933
|
-
)
|
|
83934
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
83935
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
83936
|
-
}
|
|
84553
|
+
import appConfig from '../config/app'
|
|
84554
|
+
${spectrumImport}import {
|
|
84555
|
+
AppServiceProvider,
|
|
84556
|
+
DatabaseProvider,
|
|
84557
|
+
MiddlewareProvider,
|
|
84558
|
+
RouteProvider,
|
|
84559
|
+
} from './Providers'
|
|
83937
84560
|
|
|
83938
|
-
|
|
84561
|
+
/**
|
|
84562
|
+
* Bootstrap the application with service providers.
|
|
84563
|
+
*
|
|
84564
|
+
* @returns The booted PlanetCore instance
|
|
84565
|
+
*/
|
|
84566
|
+
export async function bootstrap() {
|
|
84567
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84568
|
+
// 1. Configure
|
|
84569
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84570
|
+
const config = defineConfig({
|
|
84571
|
+
config: appConfig,
|
|
84572
|
+
orbits: [
|
|
84573
|
+
new OrbitAtlas(),
|
|
83939
84574
|
${spectrumOrbit}
|
|
83940
|
-
|
|
83941
|
-
|
|
83942
|
-
core.register(new RouteServiceProvider())
|
|
84575
|
+
],
|
|
84576
|
+
})
|
|
83943
84577
|
|
|
83944
|
-
//
|
|
83945
|
-
|
|
84578
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84579
|
+
// 2. Boot Core
|
|
84580
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84581
|
+
const core = await PlanetCore.boot(config)
|
|
84582
|
+
core.registerGlobalErrorHandlers()
|
|
84583
|
+
|
|
84584
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84585
|
+
// 3. Register Providers
|
|
84586
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84587
|
+
core.register(new AppServiceProvider())
|
|
84588
|
+
core.register(new DatabaseProvider())
|
|
84589
|
+
core.register(new MiddlewareProvider())
|
|
84590
|
+
core.register(new RouteProvider())
|
|
84591
|
+
|
|
84592
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84593
|
+
// 4. Bootstrap All Providers
|
|
84594
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84595
|
+
await core.bootstrap()
|
|
84596
|
+
|
|
84597
|
+
return core
|
|
84598
|
+
}
|
|
83946
84599
|
|
|
83947
|
-
//
|
|
84600
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84601
|
+
// Application Entry Point
|
|
84602
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
84603
|
+
const core = await bootstrap()
|
|
83948
84604
|
export default core.liftoff()
|
|
83949
84605
|
`;
|
|
83950
84606
|
}
|
|
@@ -84350,7 +85006,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
84350
85006
|
scripts: {
|
|
84351
85007
|
build: "tsup src/index.ts --format cjs,esm --dts",
|
|
84352
85008
|
test: "bun test",
|
|
84353
|
-
typecheck: "tsc --noEmit"
|
|
85009
|
+
typecheck: "tsc --noEmit",
|
|
85010
|
+
check: "bun run typecheck && bun run test",
|
|
85011
|
+
validate: "bun run check"
|
|
84354
85012
|
},
|
|
84355
85013
|
dependencies: {
|
|
84356
85014
|
"@gravito/core": depVersion,
|
|
@@ -84638,10 +85296,25 @@ class ActionDomainGenerator extends BaseGenerator {
|
|
|
84638
85296
|
type: "directory",
|
|
84639
85297
|
name: "providers",
|
|
84640
85298
|
children: [
|
|
85299
|
+
{
|
|
85300
|
+
type: "file",
|
|
85301
|
+
name: "index.ts",
|
|
85302
|
+
content: this.generateProvidersIndex()
|
|
85303
|
+
},
|
|
84641
85304
|
{
|
|
84642
85305
|
type: "file",
|
|
84643
85306
|
name: "AppServiceProvider.ts",
|
|
84644
85307
|
content: this.generateAppServiceProvider(context)
|
|
85308
|
+
},
|
|
85309
|
+
{
|
|
85310
|
+
type: "file",
|
|
85311
|
+
name: "MiddlewareProvider.ts",
|
|
85312
|
+
content: this.generateMiddlewareProvider()
|
|
85313
|
+
},
|
|
85314
|
+
{
|
|
85315
|
+
type: "file",
|
|
85316
|
+
name: "RouteProvider.ts",
|
|
85317
|
+
content: this.generateRouteProvider()
|
|
84645
85318
|
}
|
|
84646
85319
|
]
|
|
84647
85320
|
},
|
|
@@ -84801,7 +85474,7 @@ export function registerApiRoutes(router: Router) {
|
|
|
84801
85474
|
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
84802
85475
|
|
|
84803
85476
|
export class AppServiceProvider extends ServiceProvider {
|
|
84804
|
-
register(
|
|
85477
|
+
register(_container: Container): void {
|
|
84805
85478
|
// Register global services here
|
|
84806
85479
|
}
|
|
84807
85480
|
|
|
@@ -84811,82 +85484,139 @@ export class AppServiceProvider extends ServiceProvider {
|
|
|
84811
85484
|
}
|
|
84812
85485
|
`;
|
|
84813
85486
|
}
|
|
84814
|
-
|
|
85487
|
+
generateProvidersIndex() {
|
|
84815
85488
|
return `/**
|
|
84816
|
-
* Application
|
|
85489
|
+
* Application Service Providers
|
|
84817
85490
|
*/
|
|
84818
85491
|
|
|
84819
|
-
|
|
84820
|
-
|
|
84821
|
-
|
|
84822
|
-
|
|
84823
|
-
|
|
84824
|
-
|
|
84825
|
-
|
|
84826
|
-
|
|
84827
|
-
|
|
84828
|
-
APP_NAME: '${context.name}',
|
|
84829
|
-
database: databaseConfig
|
|
84830
|
-
},
|
|
84831
|
-
})
|
|
85492
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
85493
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
85494
|
+
export { RouteProvider } from './RouteProvider'
|
|
85495
|
+
`;
|
|
85496
|
+
}
|
|
85497
|
+
generateMiddlewareProvider() {
|
|
85498
|
+
return `/**
|
|
85499
|
+
* Middleware Service Provider
|
|
85500
|
+
*/
|
|
84832
85501
|
|
|
84833
|
-
|
|
84834
|
-
|
|
84835
|
-
|
|
85502
|
+
import {
|
|
85503
|
+
ServiceProvider,
|
|
85504
|
+
type Container,
|
|
85505
|
+
type PlanetCore,
|
|
85506
|
+
bodySizeLimit,
|
|
85507
|
+
securityHeaders,
|
|
85508
|
+
} from '@gravito/core'
|
|
84836
85509
|
|
|
84837
|
-
|
|
84838
|
-
|
|
85510
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
85511
|
+
register(_container: Container): void {}
|
|
84839
85512
|
|
|
84840
|
-
|
|
84841
|
-
core.
|
|
85513
|
+
boot(core: PlanetCore): void {
|
|
85514
|
+
core.adapter.use('*', securityHeaders())
|
|
85515
|
+
core.adapter.use('*', bodySizeLimit(1024 * 1024))
|
|
85516
|
+
core.logger.info('\uD83D\uDEE1\uFE0F Middleware registered')
|
|
85517
|
+
}
|
|
85518
|
+
}
|
|
85519
|
+
`;
|
|
85520
|
+
}
|
|
85521
|
+
generateRouteProvider() {
|
|
85522
|
+
return `/**
|
|
85523
|
+
* Route Service Provider
|
|
85524
|
+
*/
|
|
84842
85525
|
|
|
84843
|
-
|
|
84844
|
-
|
|
85526
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
85527
|
+
import { registerApiRoutes } from '../routes/api'
|
|
85528
|
+
|
|
85529
|
+
export class RouteProvider extends ServiceProvider {
|
|
85530
|
+
register(_container: Container): void {}
|
|
84845
85531
|
|
|
84846
|
-
|
|
84847
|
-
registerApiRoutes(core.router)
|
|
85532
|
+
boot(core: PlanetCore): void {
|
|
85533
|
+
registerApiRoutes(core.router)
|
|
85534
|
+
core.logger.info('\uD83D\uDEE4\uFE0F Routes registered')
|
|
85535
|
+
}
|
|
85536
|
+
}
|
|
85537
|
+
`;
|
|
85538
|
+
}
|
|
85539
|
+
generateBootstrap(_context) {
|
|
85540
|
+
return `/**
|
|
85541
|
+
* Application Bootstrap
|
|
85542
|
+
*
|
|
85543
|
+
* Uses the ServiceProvider pattern for modular initialization.
|
|
85544
|
+
*/
|
|
85545
|
+
|
|
85546
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
85547
|
+
import { OrbitAtlas } from '@gravito/atlas'
|
|
85548
|
+
import appConfig from '../config/app'
|
|
85549
|
+
import {
|
|
85550
|
+
AppServiceProvider,
|
|
85551
|
+
MiddlewareProvider,
|
|
85552
|
+
RouteProvider,
|
|
85553
|
+
} from './providers'
|
|
85554
|
+
|
|
85555
|
+
export async function bootstrap() {
|
|
85556
|
+
const config = defineConfig({
|
|
85557
|
+
config: appConfig,
|
|
85558
|
+
orbits: [new OrbitAtlas()],
|
|
85559
|
+
})
|
|
84848
85560
|
|
|
84849
|
-
|
|
85561
|
+
const core = await PlanetCore.boot(config)
|
|
85562
|
+
core.registerGlobalErrorHandlers()
|
|
85563
|
+
|
|
85564
|
+
core.register(new AppServiceProvider())
|
|
85565
|
+
core.register(new MiddlewareProvider())
|
|
85566
|
+
core.register(new RouteProvider())
|
|
85567
|
+
|
|
85568
|
+
await core.bootstrap()
|
|
85569
|
+
|
|
85570
|
+
return core
|
|
85571
|
+
}
|
|
85572
|
+
|
|
85573
|
+
const core = await bootstrap()
|
|
84850
85574
|
export default core.liftoff()
|
|
84851
85575
|
`;
|
|
84852
85576
|
}
|
|
84853
85577
|
generateArchitectureDoc(context) {
|
|
84854
|
-
return
|
|
84855
|
-
|
|
84856
|
-
|
|
84857
|
-
|
|
84858
|
-
|
|
84859
|
-
|
|
84860
|
-
|
|
84861
|
-
|
|
84862
|
-
|
|
84863
|
-
|
|
84864
|
-
|
|
84865
|
-
|
|
84866
|
-
|
|
84867
|
-
|
|
84868
|
-
|
|
84869
|
-
|
|
84870
|
-
|
|
84871
|
-
|
|
84872
|
-
|
|
84873
|
-
|
|
84874
|
-
|
|
84875
|
-
|
|
84876
|
-
|
|
84877
|
-
|
|
84878
|
-
|
|
84879
|
-
|
|
84880
|
-
|
|
84881
|
-
|
|
84882
|
-
|
|
84883
|
-
|
|
84884
|
-
|
|
84885
|
-
|
|
84886
|
-
|
|
84887
|
-
|
|
84888
|
-
|
|
84889
|
-
|
|
85578
|
+
return `# ${context.name} - Action Domain Architecture
|
|
85579
|
+
|
|
85580
|
+
## Overview
|
|
85581
|
+
|
|
85582
|
+
This project uses the **Action Domain** pattern, designed for high-clarity API implementations.
|
|
85583
|
+
|
|
85584
|
+
## Service Providers
|
|
85585
|
+
|
|
85586
|
+
Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
85587
|
+
|
|
85588
|
+
## Directory Structure
|
|
85589
|
+
|
|
85590
|
+
\`\`\`
|
|
85591
|
+
src/
|
|
85592
|
+
\u251C\u2500\u2500 actions/ # Single Responsibility Business Logic
|
|
85593
|
+
\u2502 \u251C\u2500\u2500 Action.ts # Base Action class
|
|
85594
|
+
\u2502 \u2514\u2500\u2500 [Domain]/ # Domain-specific actions
|
|
85595
|
+
\u251C\u2500\u2500 controllers/ # HTTP Request Handlers
|
|
85596
|
+
\u2502 \u2514\u2500\u2500 api/v1/ # API Controllers
|
|
85597
|
+
\u251C\u2500\u2500 types/ # TypeScript Definitions
|
|
85598
|
+
\u251C\u2500\u2500 repositories/ # Data Access Layer
|
|
85599
|
+
\u251C\u2500\u2500 routes/ # Route Definitions
|
|
85600
|
+
\u251C\u2500\u2500 providers/ # Service Providers
|
|
85601
|
+
\u2514\u2500\u2500 config/ # Configuration
|
|
85602
|
+
\`\`\`
|
|
85603
|
+
|
|
85604
|
+
## Core Concepts
|
|
85605
|
+
|
|
85606
|
+
### Actions
|
|
85607
|
+
Every business operation is an "Action". An action:
|
|
85608
|
+
- Does ONE thing.
|
|
85609
|
+
- Takes specific input.
|
|
85610
|
+
- Returns specific output.
|
|
85611
|
+
- Is framework-agnostic (ideally).
|
|
85612
|
+
|
|
85613
|
+
### Controllers
|
|
85614
|
+
Controllers are thin. They:
|
|
85615
|
+
1. Parse the request.
|
|
85616
|
+
2. Instantiate/Call the Action.
|
|
85617
|
+
3. Return the response.
|
|
85618
|
+
|
|
85619
|
+
Created with \u2764\uFE0F using Gravito Framework
|
|
84890
85620
|
`;
|
|
84891
85621
|
}
|
|
84892
85622
|
generatePackageJson(context) {
|
|
@@ -84899,7 +85629,11 @@ export default core.liftoff()
|
|
|
84899
85629
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
84900
85630
|
start: "bun run dist/bootstrap.js",
|
|
84901
85631
|
test: "bun test",
|
|
84902
|
-
typecheck: "tsc --noEmit"
|
|
85632
|
+
typecheck: "tsc --noEmit",
|
|
85633
|
+
check: "bun run typecheck && bun run test",
|
|
85634
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
85635
|
+
validate: "bun run check && bun run check:deps",
|
|
85636
|
+
precommit: "bun run validate"
|
|
84903
85637
|
},
|
|
84904
85638
|
dependencies: {
|
|
84905
85639
|
"@gravito/core": "workspace:*",
|
|
@@ -85096,7 +85830,6 @@ var package_default = {
|
|
|
85096
85830
|
// ../core/src/adapters/PhotonAdapter.ts
|
|
85097
85831
|
class PhotonRequestWrapper {
|
|
85098
85832
|
photonCtx;
|
|
85099
|
-
_cachedJson = null;
|
|
85100
85833
|
constructor(photonCtx) {
|
|
85101
85834
|
this.photonCtx = photonCtx;
|
|
85102
85835
|
}
|
|
@@ -88259,6 +88992,11 @@ async function initCommand(options = {}) {
|
|
|
88259
88992
|
value: "ddd",
|
|
88260
88993
|
label: "\uD83C\uDFDB\uFE0F Domain-Driven Design (DDD)",
|
|
88261
88994
|
hint: "\u5B8C\u6574\u7684 DDD \u67B6\u69CB\uFF0C\u5305\u542B Modules\u3001Bounded Contexts"
|
|
88995
|
+
},
|
|
88996
|
+
{
|
|
88997
|
+
value: "action-domain",
|
|
88998
|
+
label: "\u26A1 Action-Domain-Responder (ADR)",
|
|
88999
|
+
hint: "Web \u7279\u5316\u7684\u8A2D\u8A08\u6A21\u5F0F\uFF0C\u95DC\u6CE8 Actions \u8207 Domain \u908F\u8F2F\u7684\u5206\u96E2"
|
|
88262
89000
|
}
|
|
88263
89001
|
]
|
|
88264
89002
|
});
|
|
@@ -88367,7 +89105,8 @@ async function initCommand(options = {}) {
|
|
|
88367
89105
|
"enterprise-mvc": "\uD83D\uDCE6 Enterprise MVC",
|
|
88368
89106
|
clean: "\uD83E\uDDC5 Clean Architecture",
|
|
88369
89107
|
ddd: "\uD83C\uDFDB\uFE0F Domain-Driven Design",
|
|
88370
|
-
satellite: "\uD83D\uDEF0\uFE0F Satellite Service"
|
|
89108
|
+
satellite: "\uD83D\uDEF0\uFE0F Satellite Service",
|
|
89109
|
+
"action-domain": "\u26A1 Action-Domain-Responder"
|
|
88371
89110
|
};
|
|
88372
89111
|
note2(`\u5C08\u6848\u540D\u7A31: ${pc3.cyan(projectName)}
|
|
88373
89112
|
\u67B6\u69CB\u6A21\u5F0F: ${pc3.green(archLabels[architecture])}
|
|
@@ -89922,7 +90661,7 @@ cli.command("schedule:list", "List scheduled tasks").option("--entry <file>", "E
|
|
|
89922
90661
|
});
|
|
89923
90662
|
cli.command("create [name]", "Create a new Gravito project").option("--template <template>", "Template to use (basic, inertia-react)").option("--profile <profile>", "Profile preset (core, scale, enterprise)", { default: "core" }).option("--with <features>", "Feature add-ons (comma-separated, e.g. redis,queue)", {
|
|
89924
90663
|
default: ""
|
|
89925
|
-
}).option("--recommend", "Auto-detect profile based on environment").action(async (name, options) => {
|
|
90664
|
+
}).option("--recommend", "Auto-detect profile based on environment").option("--framework <framework>", "Frontend framework (react, vue) for static-site template").action(async (name, options) => {
|
|
89926
90665
|
console.clear();
|
|
89927
90666
|
intro2(pc11.bgBlack(pc11.white(" \uD83C\uDF0C Gravito CLI ")));
|
|
89928
90667
|
if (options.recommend) {
|
|
@@ -90016,26 +90755,30 @@ Confidence: ${detection.confidence}`, "Environment Detection");
|
|
|
90016
90755
|
}
|
|
90017
90756
|
let framework = null;
|
|
90018
90757
|
if (project.template === "static-site") {
|
|
90019
|
-
|
|
90020
|
-
|
|
90021
|
-
|
|
90022
|
-
|
|
90023
|
-
|
|
90024
|
-
|
|
90025
|
-
|
|
90026
|
-
|
|
90027
|
-
|
|
90028
|
-
|
|
90029
|
-
|
|
90030
|
-
|
|
90031
|
-
|
|
90032
|
-
|
|
90033
|
-
|
|
90034
|
-
|
|
90035
|
-
|
|
90036
|
-
|
|
90758
|
+
if (options.framework) {
|
|
90759
|
+
framework = options.framework;
|
|
90760
|
+
} else {
|
|
90761
|
+
const frameworkResult = await select2({
|
|
90762
|
+
message: "Choose your frontend framework:",
|
|
90763
|
+
options: [
|
|
90764
|
+
{
|
|
90765
|
+
value: "react",
|
|
90766
|
+
label: "\u269B\uFE0F React",
|
|
90767
|
+
hint: "Recommended for most projects"
|
|
90768
|
+
},
|
|
90769
|
+
{
|
|
90770
|
+
value: "vue",
|
|
90771
|
+
label: "\uD83D\uDFE2 Vue 3",
|
|
90772
|
+
hint: "Composition API with TypeScript"
|
|
90773
|
+
}
|
|
90774
|
+
]
|
|
90775
|
+
});
|
|
90776
|
+
if (isCancel2(frameworkResult)) {
|
|
90777
|
+
cancel2("Operation cancelled.");
|
|
90778
|
+
process.exit(0);
|
|
90779
|
+
}
|
|
90780
|
+
framework = frameworkResult;
|
|
90037
90781
|
}
|
|
90038
|
-
framework = frameworkResult;
|
|
90039
90782
|
}
|
|
90040
90783
|
const s = spinner5();
|
|
90041
90784
|
s.start("Scaffolding your universe...");
|