@gravito/scaffold 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +979 -187
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +979 -187
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -346,6 +346,7 @@ var BaseGenerator = class {
|
|
|
346
346
|
"ARCHITECTURE.md",
|
|
347
347
|
this.generateArchitectureDoc(context)
|
|
348
348
|
);
|
|
349
|
+
await this.generateCheckScripts(context);
|
|
349
350
|
}
|
|
350
351
|
/**
|
|
351
352
|
* Apply profile-specific overlays
|
|
@@ -433,6 +434,10 @@ var BaseGenerator = class {
|
|
|
433
434
|
start: "bun run dist/bootstrap.js",
|
|
434
435
|
test: "bun test",
|
|
435
436
|
typecheck: "tsc --noEmit",
|
|
437
|
+
check: "bun run typecheck && bun run test",
|
|
438
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
439
|
+
validate: "bun run check && bun run check:deps",
|
|
440
|
+
precommit: "bun run validate",
|
|
436
441
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
437
442
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
438
443
|
},
|
|
@@ -685,6 +690,430 @@ coverage/
|
|
|
685
690
|
};
|
|
686
691
|
return JSON.stringify(config, null, 2);
|
|
687
692
|
}
|
|
693
|
+
/**
|
|
694
|
+
* Generate check scripts for project validation.
|
|
695
|
+
*/
|
|
696
|
+
async generateCheckScripts(context) {
|
|
697
|
+
const scriptsDir = path2.resolve(context.targetDir, "scripts");
|
|
698
|
+
await fs2.mkdir(scriptsDir, { recursive: true });
|
|
699
|
+
await this.writeFile(
|
|
700
|
+
scriptsDir,
|
|
701
|
+
"check-dependencies.ts",
|
|
702
|
+
this.generateCheckDependenciesScript()
|
|
703
|
+
);
|
|
704
|
+
await this.writeFile(scriptsDir, "check.sh", this.generateCheckShellScript());
|
|
705
|
+
await this.writeFile(scriptsDir, "pre-commit.sh", this.generatePreCommitScript());
|
|
706
|
+
await this.writeFile(context.targetDir, "CHECK_SYSTEM.md", this.generateCheckSystemDoc(context));
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Generate check-dependencies.ts script content.
|
|
710
|
+
*/
|
|
711
|
+
generateCheckDependenciesScript() {
|
|
712
|
+
return `/**
|
|
713
|
+
* \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5\u8173\u672C
|
|
714
|
+
*
|
|
715
|
+
* \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
|
|
716
|
+
* \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
717
|
+
*/
|
|
718
|
+
|
|
719
|
+
import { readFileSync } from 'fs'
|
|
720
|
+
import { join } from 'path'
|
|
721
|
+
|
|
722
|
+
interface PackageJson {
|
|
723
|
+
dependencies?: Record<string, string>
|
|
724
|
+
devDependencies?: Record<string, string>
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
interface PackageInfo {
|
|
728
|
+
name: string
|
|
729
|
+
current: string
|
|
730
|
+
latest: string
|
|
731
|
+
outdated: boolean
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const colors = {
|
|
735
|
+
reset: '\\x1b[0m',
|
|
736
|
+
green: '\\x1b[32m',
|
|
737
|
+
yellow: '\\x1b[33m',
|
|
738
|
+
red: '\\x1b[31m',
|
|
739
|
+
blue: '\\x1b[36m',
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function log(message: string, color: keyof typeof colors = 'reset') {
|
|
743
|
+
console.log(\`\${colors[color]}\${message}\${colors.reset}\`)
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
async function getLatestVersion(packageName: string): Promise<string | null> {
|
|
747
|
+
try {
|
|
748
|
+
const response = await fetch(\`https://registry.npmjs.org/\${packageName}/latest\`)
|
|
749
|
+
if (!response.ok) return null
|
|
750
|
+
const data = await response.json()
|
|
751
|
+
return data.version
|
|
752
|
+
} catch {
|
|
753
|
+
return null
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function parseVersion(version: string): string {
|
|
758
|
+
// \u79FB\u9664 ^, ~, >= \u7B49\u524D\u7DB4
|
|
759
|
+
return version.replace(/^[\\^~>=<]/, '')
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
async function checkPackage(
|
|
763
|
+
name: string,
|
|
764
|
+
currentVersion: string
|
|
765
|
+
): Promise<PackageInfo | null> {
|
|
766
|
+
// \u8DF3\u904E\u672C\u5730\u9023\u7D50\u7684\u5957\u4EF6
|
|
767
|
+
if (currentVersion.startsWith('link:') || currentVersion.startsWith('workspace:')) {
|
|
768
|
+
return null
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const current = parseVersion(currentVersion)
|
|
772
|
+
const latest = await getLatestVersion(name)
|
|
773
|
+
|
|
774
|
+
if (!latest) {
|
|
775
|
+
return null
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return {
|
|
779
|
+
name,
|
|
780
|
+
current,
|
|
781
|
+
latest,
|
|
782
|
+
outdated: current !== latest,
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
async function main() {
|
|
787
|
+
log('\\n=== \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5 ===\\n', 'blue')
|
|
788
|
+
|
|
789
|
+
const packageJsonPath = join(process.cwd(), 'package.json')
|
|
790
|
+
const packageJson: PackageJson = JSON.parse(
|
|
791
|
+
readFileSync(packageJsonPath, 'utf-8')
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
const allDependencies = {
|
|
795
|
+
...packageJson.dependencies,
|
|
796
|
+
...packageJson.devDependencies,
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
log(\`\u6AA2\u67E5 \${Object.keys(allDependencies).length} \u500B\u5957\u4EF6...\\n\`, 'yellow')
|
|
800
|
+
|
|
801
|
+
const results: PackageInfo[] = []
|
|
802
|
+
const outdated: PackageInfo[] = []
|
|
803
|
+
const upToDate: PackageInfo[] = []
|
|
804
|
+
|
|
805
|
+
// \u6AA2\u67E5\u6240\u6709\u5957\u4EF6
|
|
806
|
+
for (const [name, version] of Object.entries(allDependencies)) {
|
|
807
|
+
const info = await checkPackage(name, version)
|
|
808
|
+
if (info) {
|
|
809
|
+
results.push(info)
|
|
810
|
+
if (info.outdated) {
|
|
811
|
+
outdated.push(info)
|
|
812
|
+
} else {
|
|
813
|
+
upToDate.push(info)
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// \u986F\u793A\u7D50\u679C
|
|
819
|
+
if (upToDate.length > 0) {
|
|
820
|
+
log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
|
|
821
|
+
upToDate.forEach((pkg) => {
|
|
822
|
+
log(\` \${pkg.name}: \${pkg.current}\`, 'green')
|
|
823
|
+
})
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (outdated.length > 0) {
|
|
827
|
+
log(\`\\n\u26A0 \u9700\u8981\u66F4\u65B0 (\${outdated.length}):\`, 'yellow')
|
|
828
|
+
outdated.forEach((pkg) => {
|
|
829
|
+
log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
|
|
830
|
+
})
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// \u7E3D\u7D50
|
|
834
|
+
log('\\n=== \u6AA2\u67E5\u7D50\u679C ===', 'blue')
|
|
835
|
+
log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
|
|
836
|
+
log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
|
|
837
|
+
log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
|
|
838
|
+
|
|
839
|
+
// \u5982\u679C\u6709\u9700\u8981\u66F4\u65B0\u7684\u5957\u4EF6\uFF0C\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u78BC
|
|
840
|
+
if (outdated.length > 0) {
|
|
841
|
+
log('\\n\u5EFA\u8B70\u57F7\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0\u5957\u4EF6\uFF1A', 'yellow')
|
|
842
|
+
log(' bun update', 'yellow')
|
|
843
|
+
process.exit(1)
|
|
844
|
+
} else {
|
|
845
|
+
log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
|
|
846
|
+
process.exit(0)
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
main().catch((error) => {
|
|
851
|
+
log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
|
|
852
|
+
process.exit(1)
|
|
853
|
+
})
|
|
854
|
+
`;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Generate check.sh script content.
|
|
858
|
+
*/
|
|
859
|
+
generateCheckShellScript() {
|
|
860
|
+
return `#!/bin/bash
|
|
861
|
+
|
|
862
|
+
# \u5C08\u6848\u6AA2\u67E5\u8173\u672C
|
|
863
|
+
# \u57F7\u884C\u6240\u6709\u5FC5\u8981\u7684\u6AA2\u67E5\uFF1A\u985E\u578B\u6AA2\u67E5\u3001\u6E2C\u8A66\u3001\u4F9D\u8CF4\u6AA2\u67E5\u7B49
|
|
864
|
+
|
|
865
|
+
set -e
|
|
866
|
+
|
|
867
|
+
# \u984F\u8272\u5B9A\u7FA9
|
|
868
|
+
GREEN='\\033[0;32m'
|
|
869
|
+
YELLOW='\\033[1;33m'
|
|
870
|
+
RED='\\033[0;31m'
|
|
871
|
+
BLUE='\\033[0;34m'
|
|
872
|
+
NC='\\033[0m' # No Color
|
|
873
|
+
|
|
874
|
+
echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
|
|
875
|
+
|
|
876
|
+
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
877
|
+
if [ ! -f "package.json" ]; then
|
|
878
|
+
echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
|
|
879
|
+
exit 1
|
|
880
|
+
fi
|
|
881
|
+
|
|
882
|
+
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
883
|
+
if ! command -v bun &> /dev/null; then
|
|
884
|
+
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
885
|
+
exit 1
|
|
886
|
+
fi
|
|
887
|
+
|
|
888
|
+
# 1. \u985E\u578B\u6AA2\u67E5
|
|
889
|
+
echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
890
|
+
if bun run typecheck; then
|
|
891
|
+
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
892
|
+
else
|
|
893
|
+
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
894
|
+
exit 1
|
|
895
|
+
fi
|
|
896
|
+
|
|
897
|
+
# 2. \u57F7\u884C\u6E2C\u8A66
|
|
898
|
+
echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
899
|
+
if bun test; then
|
|
900
|
+
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
901
|
+
else
|
|
902
|
+
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
903
|
+
exit 1
|
|
904
|
+
fi
|
|
905
|
+
|
|
906
|
+
# 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
|
|
907
|
+
echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
|
|
908
|
+
if bun run check:deps; then
|
|
909
|
+
echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
|
|
910
|
+
else
|
|
911
|
+
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"
|
|
912
|
+
fi
|
|
913
|
+
|
|
914
|
+
echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
|
|
915
|
+
`;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Generate pre-commit.sh script content.
|
|
919
|
+
*/
|
|
920
|
+
generatePreCommitScript() {
|
|
921
|
+
return `#!/bin/bash
|
|
922
|
+
|
|
923
|
+
# Pre-commit Hook
|
|
924
|
+
# \u5728 git commit \u524D\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5
|
|
925
|
+
#
|
|
926
|
+
# \u5B89\u88DD\u65B9\u5F0F\uFF1A
|
|
927
|
+
# ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
|
|
928
|
+
# \u6216
|
|
929
|
+
# cp scripts/pre-commit.sh .git/hooks/pre-commit
|
|
930
|
+
# chmod +x .git/hooks/pre-commit
|
|
931
|
+
|
|
932
|
+
set -e
|
|
933
|
+
|
|
934
|
+
# \u984F\u8272\u5B9A\u7FA9
|
|
935
|
+
GREEN='\\033[0;32m'
|
|
936
|
+
YELLOW='\\033[1;33m'
|
|
937
|
+
RED='\\033[0;31m'
|
|
938
|
+
BLUE='\\033[0;34m'
|
|
939
|
+
NC='\\033[0m' # No Color
|
|
940
|
+
|
|
941
|
+
echo -e "\${BLUE}=== Pre-commit \u6AA2\u67E5 ===\${NC}\\n"
|
|
942
|
+
|
|
943
|
+
# \u5207\u63DB\u5230\u5C08\u6848\u6839\u76EE\u9304
|
|
944
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
945
|
+
|
|
946
|
+
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
947
|
+
if [ ! -f "package.json" ]; then
|
|
948
|
+
echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
|
|
949
|
+
exit 1
|
|
950
|
+
fi
|
|
951
|
+
|
|
952
|
+
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
953
|
+
if ! command -v bun &> /dev/null; then
|
|
954
|
+
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
955
|
+
exit 1
|
|
956
|
+
fi
|
|
957
|
+
|
|
958
|
+
# 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
|
|
959
|
+
echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
960
|
+
if bun run typecheck; then
|
|
961
|
+
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
962
|
+
else
|
|
963
|
+
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
964
|
+
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
965
|
+
exit 1
|
|
966
|
+
fi
|
|
967
|
+
|
|
968
|
+
# 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
|
|
969
|
+
echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
970
|
+
if bun test; then
|
|
971
|
+
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
972
|
+
else
|
|
973
|
+
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
974
|
+
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
975
|
+
exit 1
|
|
976
|
+
fi
|
|
977
|
+
|
|
978
|
+
echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
|
|
979
|
+
`;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Generate CHECK_SYSTEM.md documentation.
|
|
983
|
+
*/
|
|
984
|
+
generateCheckSystemDoc(context) {
|
|
985
|
+
return `# \u5C08\u6848\u6AA2\u67E5\u7CFB\u7D71
|
|
986
|
+
|
|
987
|
+
\u672C\u5C08\u6848\u5DF2\u5EFA\u7ACB\u5B8C\u6574\u7684\u672C\u5730\u6AA2\u67E5\u6A5F\u5236\uFF0C\u7121\u9700\u4F9D\u8CF4 GitHub CI\u3002
|
|
988
|
+
|
|
989
|
+
## \u5FEB\u901F\u958B\u59CB
|
|
990
|
+
|
|
991
|
+
### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
|
|
992
|
+
\`\`\`bash
|
|
993
|
+
bun run validate
|
|
994
|
+
\`\`\`
|
|
995
|
+
|
|
996
|
+
### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
|
|
997
|
+
\`\`\`bash
|
|
998
|
+
# \u985E\u578B\u6AA2\u67E5
|
|
999
|
+
bun run typecheck
|
|
1000
|
+
|
|
1001
|
+
# \u6E2C\u8A66
|
|
1002
|
+
bun run test
|
|
1003
|
+
|
|
1004
|
+
# \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
1005
|
+
bun run check:deps
|
|
1006
|
+
\`\`\`
|
|
1007
|
+
|
|
1008
|
+
## \u53EF\u7528\u547D\u4EE4
|
|
1009
|
+
|
|
1010
|
+
### Package.json \u8173\u672C
|
|
1011
|
+
|
|
1012
|
+
| \u547D\u4EE4 | \u8AAA\u660E |
|
|
1013
|
+
|------|------|
|
|
1014
|
+
| \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
|
|
1015
|
+
| \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
|
|
1016
|
+
| \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
|
|
1017
|
+
| \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
|
|
1018
|
+
| \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
|
|
1019
|
+
| \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
|
|
1020
|
+
|
|
1021
|
+
### Shell \u8173\u672C
|
|
1022
|
+
|
|
1023
|
+
| \u8173\u672C | \u8AAA\u660E |
|
|
1024
|
+
|------|------|
|
|
1025
|
+
| \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
|
|
1026
|
+
| \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
|
|
1027
|
+
|
|
1028
|
+
## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
1029
|
+
|
|
1030
|
+
\u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
|
|
1031
|
+
|
|
1032
|
+
\`\`\`bash
|
|
1033
|
+
# \u5B89\u88DD pre-commit hook
|
|
1034
|
+
ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
|
|
1035
|
+
|
|
1036
|
+
# \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
|
|
1037
|
+
cp scripts/pre-commit.sh .git/hooks/pre-commit
|
|
1038
|
+
chmod +x .git/hooks/pre-commit
|
|
1039
|
+
\`\`\`
|
|
1040
|
+
|
|
1041
|
+
**\u529F\u80FD\uFF1A**
|
|
1042
|
+
- \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
|
|
1043
|
+
- \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
|
|
1044
|
+
- \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
|
|
1045
|
+
|
|
1046
|
+
**\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
|
|
1047
|
+
\`\`\`bash
|
|
1048
|
+
git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
|
|
1049
|
+
\`\`\`
|
|
1050
|
+
|
|
1051
|
+
## \u6AA2\u67E5\u9805\u76EE
|
|
1052
|
+
|
|
1053
|
+
### 1. \u985E\u578B\u6AA2\u67E5
|
|
1054
|
+
- \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
|
|
1055
|
+
- \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
|
|
1056
|
+
|
|
1057
|
+
### 2. \u6E2C\u8A66
|
|
1058
|
+
- \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
|
|
1059
|
+
- \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
|
|
1060
|
+
|
|
1061
|
+
### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
|
|
1062
|
+
- \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
|
|
1063
|
+
- \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
1064
|
+
- \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
|
|
1065
|
+
|
|
1066
|
+
## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
|
|
1067
|
+
|
|
1068
|
+
### \u958B\u767C\u6642
|
|
1069
|
+
1. \u958B\u767C\u529F\u80FD
|
|
1070
|
+
2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
|
|
1071
|
+
3. \u4FEE\u6B63\u554F\u984C
|
|
1072
|
+
4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
|
|
1073
|
+
|
|
1074
|
+
### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
1075
|
+
1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
|
|
1076
|
+
2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
|
|
1077
|
+
3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
|
|
1078
|
+
4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
|
|
1079
|
+
|
|
1080
|
+
## \u6A94\u6848\u7D50\u69CB
|
|
1081
|
+
|
|
1082
|
+
\`\`\`
|
|
1083
|
+
${context.nameKebabCase}/
|
|
1084
|
+
\u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
|
|
1085
|
+
\u251C\u2500\u2500 scripts/
|
|
1086
|
+
\u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
|
|
1087
|
+
\u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
1088
|
+
\u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
|
|
1089
|
+
\u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
|
|
1090
|
+
\`\`\`
|
|
1091
|
+
|
|
1092
|
+
## \u6CE8\u610F\u4E8B\u9805
|
|
1093
|
+
|
|
1094
|
+
1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
|
|
1095
|
+
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
|
|
1096
|
+
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
|
|
1097
|
+
|
|
1098
|
+
## \u6545\u969C\u6392\u9664
|
|
1099
|
+
|
|
1100
|
+
### \u6AA2\u67E5\u5931\u6557
|
|
1101
|
+
1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
|
|
1102
|
+
2. \u4FEE\u6B63\u554F\u984C
|
|
1103
|
+
3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
|
|
1104
|
+
|
|
1105
|
+
### \u8DF3\u904E\u6AA2\u67E5
|
|
1106
|
+
\u53EA\u6709\u5728\u7DCA\u6025\u60C5\u6CC1\u4E0B\u624D\u4F7F\u7528\uFF1A
|
|
1107
|
+
\`\`\`bash
|
|
1108
|
+
git commit --no-verify
|
|
1109
|
+
\`\`\`
|
|
1110
|
+
|
|
1111
|
+
### \u79FB\u9664 Pre-commit Hook
|
|
1112
|
+
\`\`\`bash
|
|
1113
|
+
rm .git/hooks/pre-commit
|
|
1114
|
+
\`\`\`
|
|
1115
|
+
`;
|
|
1116
|
+
}
|
|
688
1117
|
/**
|
|
689
1118
|
* Log a message if verbose mode is enabled.
|
|
690
1119
|
*/
|
|
@@ -865,6 +1294,11 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
|
|
|
865
1294
|
type: "directory",
|
|
866
1295
|
name: "Providers",
|
|
867
1296
|
children: [
|
|
1297
|
+
{
|
|
1298
|
+
type: "file",
|
|
1299
|
+
name: "index.ts",
|
|
1300
|
+
content: this.generateProvidersIndex()
|
|
1301
|
+
},
|
|
868
1302
|
{
|
|
869
1303
|
type: "file",
|
|
870
1304
|
name: "AppServiceProvider.ts",
|
|
@@ -874,6 +1308,16 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
|
|
|
874
1308
|
type: "file",
|
|
875
1309
|
name: "RepositoryServiceProvider.ts",
|
|
876
1310
|
content: this.generateRepositoryServiceProvider()
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
type: "file",
|
|
1314
|
+
name: "MiddlewareProvider.ts",
|
|
1315
|
+
content: this.generateMiddlewareProvider()
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
type: "file",
|
|
1319
|
+
name: "RouteProvider.ts",
|
|
1320
|
+
content: this.generateRouteProvider()
|
|
877
1321
|
}
|
|
878
1322
|
]
|
|
879
1323
|
}
|
|
@@ -1367,6 +1811,65 @@ export class RepositoryServiceProvider extends ServiceProvider {
|
|
|
1367
1811
|
container.singleton('mailService', () => new MailService())
|
|
1368
1812
|
}
|
|
1369
1813
|
}
|
|
1814
|
+
`;
|
|
1815
|
+
}
|
|
1816
|
+
generateProvidersIndex() {
|
|
1817
|
+
return `/**
|
|
1818
|
+
* Application Service Providers
|
|
1819
|
+
*/
|
|
1820
|
+
|
|
1821
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
1822
|
+
export { RepositoryServiceProvider } from './RepositoryServiceProvider'
|
|
1823
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
1824
|
+
export { RouteProvider } from './RouteProvider'
|
|
1825
|
+
`;
|
|
1826
|
+
}
|
|
1827
|
+
generateMiddlewareProvider() {
|
|
1828
|
+
return `/**
|
|
1829
|
+
* Middleware Service Provider
|
|
1830
|
+
*/
|
|
1831
|
+
|
|
1832
|
+
import {
|
|
1833
|
+
ServiceProvider,
|
|
1834
|
+
type Container,
|
|
1835
|
+
type PlanetCore,
|
|
1836
|
+
bodySizeLimit,
|
|
1837
|
+
securityHeaders,
|
|
1838
|
+
} from '@gravito/core'
|
|
1839
|
+
|
|
1840
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
1841
|
+
register(_container: Container): void {}
|
|
1842
|
+
|
|
1843
|
+
boot(core: PlanetCore): void {
|
|
1844
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
1845
|
+
|
|
1846
|
+
core.adapter.use('*', securityHeaders({
|
|
1847
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
1848
|
+
}))
|
|
1849
|
+
|
|
1850
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
1851
|
+
|
|
1852
|
+
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
`;
|
|
1856
|
+
}
|
|
1857
|
+
generateRouteProvider() {
|
|
1858
|
+
return `/**
|
|
1859
|
+
* Route Service Provider
|
|
1860
|
+
*/
|
|
1861
|
+
|
|
1862
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
1863
|
+
import { registerApiRoutes } from '../../Interface/Http/Routes/api'
|
|
1864
|
+
|
|
1865
|
+
export class RouteProvider extends ServiceProvider {
|
|
1866
|
+
register(_container: Container): void {}
|
|
1867
|
+
|
|
1868
|
+
boot(core: PlanetCore): void {
|
|
1869
|
+
registerApiRoutes(core.router)
|
|
1870
|
+
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1370
1873
|
`;
|
|
1371
1874
|
}
|
|
1372
1875
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -1451,64 +1954,55 @@ export class UserPresenter {
|
|
|
1451
1954
|
}
|
|
1452
1955
|
`;
|
|
1453
1956
|
}
|
|
1454
|
-
generateBootstrap(
|
|
1957
|
+
generateBootstrap(_context) {
|
|
1455
1958
|
return `/**
|
|
1456
1959
|
* Application Bootstrap
|
|
1960
|
+
*
|
|
1961
|
+
* The entry point for your Clean Architecture application.
|
|
1962
|
+
* Uses the ServiceProvider pattern for modular initialization.
|
|
1963
|
+
*
|
|
1964
|
+
* Lifecycle:
|
|
1965
|
+
* 1. Configure: Load app config and orbits
|
|
1966
|
+
* 2. Boot: Initialize PlanetCore
|
|
1967
|
+
* 3. Register Providers: Bind services to container
|
|
1968
|
+
* 4. Bootstrap: Boot all providers
|
|
1457
1969
|
*/
|
|
1458
1970
|
|
|
1459
|
-
import {
|
|
1971
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
1460
1972
|
import { OrbitAtlas } from '@gravito/atlas'
|
|
1461
|
-
import
|
|
1462
|
-
import {
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
"default-src 'self'",
|
|
1475
|
-
"script-src 'self' 'unsafe-inline'",
|
|
1476
|
-
"style-src 'self' 'unsafe-inline'",
|
|
1477
|
-
"img-src 'self' data:",
|
|
1478
|
-
"object-src 'none'",
|
|
1479
|
-
"base-uri 'self'",
|
|
1480
|
-
"frame-ancestors 'none'",
|
|
1481
|
-
].join('; ')
|
|
1482
|
-
const cspValue = process.env.APP_CSP
|
|
1483
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
1484
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
1485
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
1486
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
1487
|
-
|
|
1488
|
-
core.adapter.use(
|
|
1489
|
-
'*',
|
|
1490
|
-
securityHeaders({
|
|
1491
|
-
contentSecurityPolicy: csp,
|
|
1492
|
-
hsts:
|
|
1493
|
-
process.env.NODE_ENV === 'production'
|
|
1494
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
1495
|
-
: false,
|
|
1973
|
+
import appConfig from '../config/app'
|
|
1974
|
+
import {
|
|
1975
|
+
AppServiceProvider,
|
|
1976
|
+
RepositoryServiceProvider,
|
|
1977
|
+
MiddlewareProvider,
|
|
1978
|
+
RouteProvider,
|
|
1979
|
+
} from './Infrastructure/Providers'
|
|
1980
|
+
|
|
1981
|
+
export async function bootstrap() {
|
|
1982
|
+
// 1. Configure
|
|
1983
|
+
const config = defineConfig({
|
|
1984
|
+
config: appConfig,
|
|
1985
|
+
orbits: [new OrbitAtlas()],
|
|
1496
1986
|
})
|
|
1497
|
-
)
|
|
1498
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
1499
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
1500
|
-
}
|
|
1501
1987
|
|
|
1502
|
-
|
|
1988
|
+
// 2. Boot Core
|
|
1989
|
+
const core = await PlanetCore.boot(config)
|
|
1990
|
+
core.registerGlobalErrorHandlers()
|
|
1503
1991
|
|
|
1504
|
-
|
|
1505
|
-
core.register(new
|
|
1992
|
+
// 3. Register Providers
|
|
1993
|
+
core.register(new RepositoryServiceProvider())
|
|
1994
|
+
core.register(new AppServiceProvider())
|
|
1995
|
+
core.register(new MiddlewareProvider())
|
|
1996
|
+
core.register(new RouteProvider())
|
|
1506
1997
|
|
|
1507
|
-
|
|
1998
|
+
// 4. Bootstrap All Providers
|
|
1999
|
+
await core.bootstrap()
|
|
1508
2000
|
|
|
1509
|
-
|
|
1510
|
-
|
|
2001
|
+
return core
|
|
2002
|
+
}
|
|
1511
2003
|
|
|
2004
|
+
// Application Entry Point
|
|
2005
|
+
const core = await bootstrap()
|
|
1512
2006
|
export default core.liftoff()
|
|
1513
2007
|
`;
|
|
1514
2008
|
}
|
|
@@ -1520,6 +2014,15 @@ export default core.liftoff()
|
|
|
1520
2014
|
This project follows **Clean Architecture** (by Robert C. Martin).
|
|
1521
2015
|
The key principle is the **Dependency Rule**: dependencies point inward.
|
|
1522
2016
|
|
|
2017
|
+
## Service Providers
|
|
2018
|
+
|
|
2019
|
+
Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
2020
|
+
|
|
2021
|
+
### Provider Lifecycle
|
|
2022
|
+
|
|
2023
|
+
1. **register()**: Bind services to the container (sync or async).
|
|
2024
|
+
2. **boot()**: Called after ALL providers have registered. Safe to use other services.
|
|
2025
|
+
|
|
1523
2026
|
## Layer Structure
|
|
1524
2027
|
|
|
1525
2028
|
\`\`\`
|
|
@@ -1591,6 +2094,10 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
1591
2094
|
start: "bun run dist/bootstrap.js",
|
|
1592
2095
|
test: "bun test",
|
|
1593
2096
|
typecheck: "tsc --noEmit",
|
|
2097
|
+
check: "bun run typecheck && bun run test",
|
|
2098
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2099
|
+
validate: "bun run check && bun run check:deps",
|
|
2100
|
+
precommit: "bun run validate",
|
|
1594
2101
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
1595
2102
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
1596
2103
|
},
|
|
@@ -1914,63 +2421,46 @@ var DddGenerator = class extends BaseGenerator {
|
|
|
1914
2421
|
// ─────────────────────────────────────────────────────────────
|
|
1915
2422
|
// Bootstrap File Generators
|
|
1916
2423
|
// ─────────────────────────────────────────────────────────────
|
|
1917
|
-
generateBootstrapApp(
|
|
2424
|
+
generateBootstrapApp(_context) {
|
|
1918
2425
|
return `/**
|
|
1919
2426
|
* Application Bootstrap
|
|
1920
2427
|
*
|
|
1921
|
-
* Central configuration and initialization
|
|
2428
|
+
* Central configuration and initialization using the ServiceProvider pattern.
|
|
2429
|
+
*
|
|
2430
|
+
* Lifecycle:
|
|
2431
|
+
* 1. Configure: Load app config and orbits
|
|
2432
|
+
* 2. Boot: Initialize PlanetCore
|
|
2433
|
+
* 3. Register Providers: Bind services to container
|
|
2434
|
+
* 4. Bootstrap: Boot all providers
|
|
1922
2435
|
*/
|
|
1923
2436
|
|
|
1924
|
-
import {
|
|
2437
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
2438
|
+
import { OrbitAtlas } from '@gravito/atlas'
|
|
2439
|
+
import appConfig from '../../config/app'
|
|
1925
2440
|
import { registerProviders } from './providers'
|
|
1926
2441
|
import { registerRoutes } from './routes'
|
|
1927
2442
|
|
|
1928
2443
|
export async function createApp(): Promise<PlanetCore> {
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
2444
|
+
// 1. Configure
|
|
2445
|
+
const config = defineConfig({
|
|
2446
|
+
config: appConfig,
|
|
2447
|
+
orbits: [new OrbitAtlas()],
|
|
2448
|
+
})
|
|
1934
2449
|
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
"style-src 'self' 'unsafe-inline'",
|
|
1939
|
-
"img-src 'self' data:",
|
|
1940
|
-
"object-src 'none'",
|
|
1941
|
-
"base-uri 'self'",
|
|
1942
|
-
"frame-ancestors 'none'",
|
|
1943
|
-
].join('; ')
|
|
1944
|
-
const cspValue = process.env.APP_CSP
|
|
1945
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
1946
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
1947
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
1948
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
1949
|
-
|
|
1950
|
-
core.adapter.use(
|
|
1951
|
-
'*',
|
|
1952
|
-
securityHeaders({
|
|
1953
|
-
contentSecurityPolicy: csp,
|
|
1954
|
-
hsts:
|
|
1955
|
-
process.env.NODE_ENV === 'production'
|
|
1956
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
1957
|
-
: false,
|
|
1958
|
-
})
|
|
1959
|
-
)
|
|
1960
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
1961
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
1962
|
-
}
|
|
2450
|
+
// 2. Boot Core
|
|
2451
|
+
const core = await PlanetCore.boot(config)
|
|
2452
|
+
core.registerGlobalErrorHandlers()
|
|
1963
2453
|
|
|
1964
|
-
|
|
1965
|
-
|
|
2454
|
+
// 3. Register Providers
|
|
2455
|
+
await registerProviders(core)
|
|
1966
2456
|
|
|
1967
|
-
|
|
1968
|
-
|
|
2457
|
+
// 4. Bootstrap All Providers
|
|
2458
|
+
await core.bootstrap()
|
|
1969
2459
|
|
|
1970
|
-
|
|
1971
|
-
|
|
2460
|
+
// Register routes after bootstrap
|
|
2461
|
+
registerRoutes(core.router)
|
|
1972
2462
|
|
|
1973
|
-
|
|
2463
|
+
return core
|
|
1974
2464
|
}
|
|
1975
2465
|
`;
|
|
1976
2466
|
}
|
|
@@ -1978,19 +2468,48 @@ export async function createApp(): Promise<PlanetCore> {
|
|
|
1978
2468
|
return `/**
|
|
1979
2469
|
* Service Providers Registry
|
|
1980
2470
|
*
|
|
1981
|
-
* Register all
|
|
2471
|
+
* Register all service providers here.
|
|
2472
|
+
* Include both global and module-specific providers.
|
|
1982
2473
|
*/
|
|
1983
2474
|
|
|
1984
|
-
import
|
|
2475
|
+
import {
|
|
2476
|
+
ServiceProvider,
|
|
2477
|
+
type Container,
|
|
2478
|
+
type PlanetCore,
|
|
2479
|
+
bodySizeLimit,
|
|
2480
|
+
securityHeaders,
|
|
2481
|
+
} from '@gravito/core'
|
|
1985
2482
|
import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
|
|
1986
2483
|
import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
|
|
1987
2484
|
|
|
2485
|
+
/**
|
|
2486
|
+
* Middleware Provider - Global middleware registration
|
|
2487
|
+
*/
|
|
2488
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
2489
|
+
register(_container: Container): void {}
|
|
2490
|
+
|
|
2491
|
+
boot(core: PlanetCore): void {
|
|
2492
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
2493
|
+
|
|
2494
|
+
core.adapter.use('*', securityHeaders({
|
|
2495
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
2496
|
+
}))
|
|
2497
|
+
|
|
2498
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
2499
|
+
|
|
2500
|
+
core.logger.info('\u{1F6E1}\uFE0F Global middleware registered')
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
|
|
1988
2504
|
export async function registerProviders(core: PlanetCore): Promise<void> {
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2505
|
+
// Global Providers
|
|
2506
|
+
core.register(new MiddlewareProvider())
|
|
2507
|
+
|
|
2508
|
+
// Module Providers
|
|
2509
|
+
core.register(new OrderingServiceProvider())
|
|
2510
|
+
core.register(new CatalogServiceProvider())
|
|
1992
2511
|
|
|
1993
|
-
|
|
2512
|
+
// Add more providers as needed
|
|
1994
2513
|
}
|
|
1995
2514
|
`;
|
|
1996
2515
|
}
|
|
@@ -2104,6 +2623,10 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
2104
2623
|
start: "bun run dist/main.js",
|
|
2105
2624
|
test: "bun test",
|
|
2106
2625
|
typecheck: "tsc --noEmit",
|
|
2626
|
+
check: "bun run typecheck && bun run test",
|
|
2627
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2628
|
+
validate: "bun run check && bun run check:deps",
|
|
2629
|
+
precommit: "bun run validate",
|
|
2107
2630
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
2108
2631
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
2109
2632
|
},
|
|
@@ -2544,6 +3067,16 @@ export function report(error: unknown): void {
|
|
|
2544
3067
|
|
|
2545
3068
|
This project follows **Domain-Driven Design (DDD)** with strategic and tactical patterns.
|
|
2546
3069
|
|
|
3070
|
+
## Service Providers
|
|
3071
|
+
|
|
3072
|
+
Service providers are the central place to configure your application and modules. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
3073
|
+
|
|
3074
|
+
### Internal Bootstrapping
|
|
3075
|
+
|
|
3076
|
+
1. **Bootstrap/app.ts**: Orchestrates the 4-step lifecycle (Configure, Boot, Register, Bootstrap).
|
|
3077
|
+
2. **Bootstrap/providers.ts**: Central registry for all global and module-specific providers.
|
|
3078
|
+
3. **Infrastructure/Providers/[Module]ServiceProvider.ts**: Module-specific service registration.
|
|
3079
|
+
|
|
2547
3080
|
## Bounded Contexts
|
|
2548
3081
|
|
|
2549
3082
|
\`\`\`
|
|
@@ -2681,6 +3214,11 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
|
|
|
2681
3214
|
type: "directory",
|
|
2682
3215
|
name: "Providers",
|
|
2683
3216
|
children: [
|
|
3217
|
+
{
|
|
3218
|
+
type: "file",
|
|
3219
|
+
name: "index.ts",
|
|
3220
|
+
content: this.generateProvidersIndex()
|
|
3221
|
+
},
|
|
2684
3222
|
{
|
|
2685
3223
|
type: "file",
|
|
2686
3224
|
name: "AppServiceProvider.ts",
|
|
@@ -2688,8 +3226,18 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
|
|
|
2688
3226
|
},
|
|
2689
3227
|
{
|
|
2690
3228
|
type: "file",
|
|
2691
|
-
name: "
|
|
2692
|
-
content: this.
|
|
3229
|
+
name: "DatabaseProvider.ts",
|
|
3230
|
+
content: this.generateDatabaseProvider()
|
|
3231
|
+
},
|
|
3232
|
+
{
|
|
3233
|
+
type: "file",
|
|
3234
|
+
name: "MiddlewareProvider.ts",
|
|
3235
|
+
content: this.generateMiddlewareProvider()
|
|
3236
|
+
},
|
|
3237
|
+
{
|
|
3238
|
+
type: "file",
|
|
3239
|
+
name: "RouteProvider.ts",
|
|
3240
|
+
content: this.generateRouteProvider()
|
|
2693
3241
|
}
|
|
2694
3242
|
]
|
|
2695
3243
|
},
|
|
@@ -3156,6 +3704,135 @@ export class RouteServiceProvider extends ServiceProvider {
|
|
|
3156
3704
|
registerRoutes(core.router)
|
|
3157
3705
|
}
|
|
3158
3706
|
}
|
|
3707
|
+
`;
|
|
3708
|
+
}
|
|
3709
|
+
// ─────────────────────────────────────────────────────────────
|
|
3710
|
+
// Modern Provider Generators (ServiceProvider Pattern)
|
|
3711
|
+
// ─────────────────────────────────────────────────────────────
|
|
3712
|
+
generateProvidersIndex() {
|
|
3713
|
+
return `/**
|
|
3714
|
+
* Application Service Providers
|
|
3715
|
+
*
|
|
3716
|
+
* Export all providers for easy importing in bootstrap.
|
|
3717
|
+
* Providers are registered in the order they are listed.
|
|
3718
|
+
*/
|
|
3719
|
+
|
|
3720
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
3721
|
+
export { DatabaseProvider } from './DatabaseProvider'
|
|
3722
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
3723
|
+
export { RouteProvider } from './RouteProvider'
|
|
3724
|
+
`;
|
|
3725
|
+
}
|
|
3726
|
+
generateDatabaseProvider() {
|
|
3727
|
+
return `/**
|
|
3728
|
+
* Database Service Provider
|
|
3729
|
+
*
|
|
3730
|
+
* Handles database initialization and migrations.
|
|
3731
|
+
*
|
|
3732
|
+
* Lifecycle:
|
|
3733
|
+
* - register(): Bind database config to container
|
|
3734
|
+
* - boot(): Run migrations and seeders
|
|
3735
|
+
*/
|
|
3736
|
+
|
|
3737
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
3738
|
+
import databaseConfig from '../../config/database'
|
|
3739
|
+
|
|
3740
|
+
export class DatabaseProvider extends ServiceProvider {
|
|
3741
|
+
/**
|
|
3742
|
+
* Register database configuration.
|
|
3743
|
+
*/
|
|
3744
|
+
register(_container: Container): void {
|
|
3745
|
+
this.mergeConfig(this.core!.config, 'database', databaseConfig)
|
|
3746
|
+
}
|
|
3747
|
+
|
|
3748
|
+
/**
|
|
3749
|
+
* Initialize database connections.
|
|
3750
|
+
*/
|
|
3751
|
+
async boot(core: PlanetCore): Promise<void> {
|
|
3752
|
+
// Database initialization will be handled by Atlas orbit
|
|
3753
|
+
core.logger.info('\u{1F4E6} Database provider booted')
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
`;
|
|
3757
|
+
}
|
|
3758
|
+
generateMiddlewareProvider() {
|
|
3759
|
+
return `/**
|
|
3760
|
+
* Middleware Service Provider
|
|
3761
|
+
*
|
|
3762
|
+
* Registers global middleware stack.
|
|
3763
|
+
* Provides a centralized location for middleware configuration.
|
|
3764
|
+
*
|
|
3765
|
+
* Lifecycle:
|
|
3766
|
+
* - register(): N/A (no container bindings)
|
|
3767
|
+
* - boot(): Register global middleware
|
|
3768
|
+
*/
|
|
3769
|
+
|
|
3770
|
+
import {
|
|
3771
|
+
ServiceProvider,
|
|
3772
|
+
type Container,
|
|
3773
|
+
type PlanetCore,
|
|
3774
|
+
bodySizeLimit,
|
|
3775
|
+
securityHeaders,
|
|
3776
|
+
} from '@gravito/core'
|
|
3777
|
+
|
|
3778
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
3779
|
+
/**
|
|
3780
|
+
* No container bindings needed.
|
|
3781
|
+
*/
|
|
3782
|
+
register(_container: Container): void {
|
|
3783
|
+
// Middleware doesn't require container bindings
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
/**
|
|
3787
|
+
* Register global middleware stack.
|
|
3788
|
+
*/
|
|
3789
|
+
boot(core: PlanetCore): void {
|
|
3790
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
3791
|
+
|
|
3792
|
+
// Security Headers
|
|
3793
|
+
core.adapter.use('*', securityHeaders({
|
|
3794
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
3795
|
+
}))
|
|
3796
|
+
|
|
3797
|
+
// Body Parser Limits
|
|
3798
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024)) // 10MB limit
|
|
3799
|
+
|
|
3800
|
+
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
`;
|
|
3804
|
+
}
|
|
3805
|
+
generateRouteProvider() {
|
|
3806
|
+
return `/**
|
|
3807
|
+
* Route Service Provider
|
|
3808
|
+
*
|
|
3809
|
+
* Registers application routes.
|
|
3810
|
+
* Routes are registered in the boot phase after all services are available.
|
|
3811
|
+
*
|
|
3812
|
+
* Lifecycle:
|
|
3813
|
+
* - register(): N/A
|
|
3814
|
+
* - boot(): Register routes
|
|
3815
|
+
*/
|
|
3816
|
+
|
|
3817
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
3818
|
+
import { registerRoutes } from '../routes'
|
|
3819
|
+
|
|
3820
|
+
export class RouteProvider extends ServiceProvider {
|
|
3821
|
+
/**
|
|
3822
|
+
* No container bindings needed.
|
|
3823
|
+
*/
|
|
3824
|
+
register(_container: Container): void {
|
|
3825
|
+
// Routes don't require container bindings
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3828
|
+
/**
|
|
3829
|
+
* Register application routes.
|
|
3830
|
+
*/
|
|
3831
|
+
boot(core: PlanetCore): void {
|
|
3832
|
+
registerRoutes(core.router)
|
|
3833
|
+
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3159
3836
|
`;
|
|
3160
3837
|
}
|
|
3161
3838
|
generateExceptionHandler() {
|
|
@@ -3204,74 +3881,75 @@ export const dontReport: string[] = [
|
|
|
3204
3881
|
}
|
|
3205
3882
|
generateBootstrap(context) {
|
|
3206
3883
|
const spectrumImport = context.withSpectrum ? "import { SpectrumOrbit } from '@gravito/spectrum'\n" : "";
|
|
3207
|
-
const spectrumOrbit = context.withSpectrum ?
|
|
3208
|
-
// Enable Debug Dashboard
|
|
3209
|
-
if (process.env.APP_DEBUG === 'true') {
|
|
3210
|
-
await core.orbit(new SpectrumOrbit())
|
|
3211
|
-
}
|
|
3212
|
-
` : "";
|
|
3884
|
+
const spectrumOrbit = context.withSpectrum ? " new SpectrumOrbit()," : "";
|
|
3213
3885
|
return `/**
|
|
3214
3886
|
* Application Bootstrap
|
|
3215
3887
|
*
|
|
3216
|
-
*
|
|
3217
|
-
*
|
|
3888
|
+
* The entry point for your Gravito application.
|
|
3889
|
+
* Uses the ServiceProvider pattern for modular, maintainable initialization.
|
|
3890
|
+
*
|
|
3891
|
+
* Lifecycle:
|
|
3892
|
+
* 1. Configure: Load app config and orbits
|
|
3893
|
+
* 2. Boot: Initialize PlanetCore
|
|
3894
|
+
* 3. Register Providers: Bind services to container
|
|
3895
|
+
* 4. Bootstrap: Boot all providers
|
|
3896
|
+
*
|
|
3897
|
+
* @module bootstrap
|
|
3218
3898
|
*/
|
|
3219
3899
|
|
|
3220
|
-
import {
|
|
3900
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
3221
3901
|
import { OrbitAtlas } from '@gravito/atlas'
|
|
3222
|
-
import
|
|
3223
|
-
${spectrumImport}import {
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
// Create application core
|
|
3230
|
-
const core = new PlanetCore({
|
|
3231
|
-
config: {
|
|
3232
|
-
APP_NAME: process.env.APP_NAME ?? '${context.name}',
|
|
3233
|
-
database: databaseConfig,
|
|
3234
|
-
},
|
|
3235
|
-
})
|
|
3236
|
-
const defaultCsp = [
|
|
3237
|
-
"default-src 'self'",
|
|
3238
|
-
"script-src 'self' 'unsafe-inline'",
|
|
3239
|
-
"style-src 'self' 'unsafe-inline'",
|
|
3240
|
-
"img-src 'self' data:",
|
|
3241
|
-
"object-src 'none'",
|
|
3242
|
-
"base-uri 'self'",
|
|
3243
|
-
"frame-ancestors 'none'",
|
|
3244
|
-
].join('; ')
|
|
3245
|
-
const cspValue = process.env.APP_CSP
|
|
3246
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
3247
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
3248
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
3249
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
3250
|
-
|
|
3251
|
-
core.adapter.use(
|
|
3252
|
-
'*',
|
|
3253
|
-
securityHeaders({
|
|
3254
|
-
contentSecurityPolicy: csp,
|
|
3255
|
-
hsts:
|
|
3256
|
-
process.env.NODE_ENV === 'production'
|
|
3257
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
3258
|
-
: false,
|
|
3259
|
-
})
|
|
3260
|
-
)
|
|
3261
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
3262
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
3263
|
-
}
|
|
3902
|
+
import appConfig from '../config/app'
|
|
3903
|
+
${spectrumImport}import {
|
|
3904
|
+
AppServiceProvider,
|
|
3905
|
+
DatabaseProvider,
|
|
3906
|
+
MiddlewareProvider,
|
|
3907
|
+
RouteProvider,
|
|
3908
|
+
} from './Providers'
|
|
3264
3909
|
|
|
3265
|
-
|
|
3910
|
+
/**
|
|
3911
|
+
* Bootstrap the application with service providers.
|
|
3912
|
+
*
|
|
3913
|
+
* @returns The booted PlanetCore instance
|
|
3914
|
+
*/
|
|
3915
|
+
export async function bootstrap() {
|
|
3916
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3917
|
+
// 1. Configure
|
|
3918
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3919
|
+
const config = defineConfig({
|
|
3920
|
+
config: appConfig,
|
|
3921
|
+
orbits: [
|
|
3922
|
+
new OrbitAtlas(),
|
|
3266
3923
|
${spectrumOrbit}
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
core.register(new RouteServiceProvider())
|
|
3924
|
+
],
|
|
3925
|
+
})
|
|
3270
3926
|
|
|
3271
|
-
//
|
|
3272
|
-
|
|
3927
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3928
|
+
// 2. Boot Core
|
|
3929
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3930
|
+
const core = await PlanetCore.boot(config)
|
|
3931
|
+
core.registerGlobalErrorHandlers()
|
|
3932
|
+
|
|
3933
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3934
|
+
// 3. Register Providers
|
|
3935
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3936
|
+
core.register(new AppServiceProvider())
|
|
3937
|
+
core.register(new DatabaseProvider())
|
|
3938
|
+
core.register(new MiddlewareProvider())
|
|
3939
|
+
core.register(new RouteProvider())
|
|
3940
|
+
|
|
3941
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3942
|
+
// 4. Bootstrap All Providers
|
|
3943
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3944
|
+
await core.bootstrap()
|
|
3945
|
+
|
|
3946
|
+
return core
|
|
3947
|
+
}
|
|
3273
3948
|
|
|
3274
|
-
//
|
|
3949
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3950
|
+
// Application Entry Point
|
|
3951
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3952
|
+
const core = await bootstrap()
|
|
3275
3953
|
export default core.liftoff()
|
|
3276
3954
|
`;
|
|
3277
3955
|
}
|
|
@@ -3698,7 +4376,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
3698
4376
|
scripts: {
|
|
3699
4377
|
build: "tsup src/index.ts --format cjs,esm --dts",
|
|
3700
4378
|
test: "bun test",
|
|
3701
|
-
typecheck: "tsc --noEmit"
|
|
4379
|
+
typecheck: "tsc --noEmit",
|
|
4380
|
+
check: "bun run typecheck && bun run test",
|
|
4381
|
+
validate: "bun run check"
|
|
3702
4382
|
},
|
|
3703
4383
|
dependencies: {
|
|
3704
4384
|
"@gravito/core": depVersion,
|
|
@@ -3996,10 +4676,25 @@ var ActionDomainGenerator = class extends BaseGenerator {
|
|
|
3996
4676
|
type: "directory",
|
|
3997
4677
|
name: "providers",
|
|
3998
4678
|
children: [
|
|
4679
|
+
{
|
|
4680
|
+
type: "file",
|
|
4681
|
+
name: "index.ts",
|
|
4682
|
+
content: this.generateProvidersIndex()
|
|
4683
|
+
},
|
|
3999
4684
|
{
|
|
4000
4685
|
type: "file",
|
|
4001
4686
|
name: "AppServiceProvider.ts",
|
|
4002
4687
|
content: this.generateAppServiceProvider(context)
|
|
4688
|
+
},
|
|
4689
|
+
{
|
|
4690
|
+
type: "file",
|
|
4691
|
+
name: "MiddlewareProvider.ts",
|
|
4692
|
+
content: this.generateMiddlewareProvider()
|
|
4693
|
+
},
|
|
4694
|
+
{
|
|
4695
|
+
type: "file",
|
|
4696
|
+
name: "RouteProvider.ts",
|
|
4697
|
+
content: this.generateRouteProvider()
|
|
4003
4698
|
}
|
|
4004
4699
|
]
|
|
4005
4700
|
},
|
|
@@ -4177,7 +4872,7 @@ export function registerApiRoutes(router: Router) {
|
|
|
4177
4872
|
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
4178
4873
|
|
|
4179
4874
|
export class AppServiceProvider extends ServiceProvider {
|
|
4180
|
-
register(
|
|
4875
|
+
register(_container: Container): void {
|
|
4181
4876
|
// Register global services here
|
|
4182
4877
|
}
|
|
4183
4878
|
|
|
@@ -4187,47 +4882,140 @@ export class AppServiceProvider extends ServiceProvider {
|
|
|
4187
4882
|
}
|
|
4188
4883
|
`;
|
|
4189
4884
|
}
|
|
4190
|
-
|
|
4885
|
+
generateProvidersIndex() {
|
|
4191
4886
|
return `/**
|
|
4192
|
-
* Application
|
|
4887
|
+
* Application Service Providers
|
|
4193
4888
|
*/
|
|
4194
4889
|
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
APP_NAME: '${context.name}',
|
|
4205
|
-
database: databaseConfig
|
|
4206
|
-
},
|
|
4207
|
-
})
|
|
4890
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
4891
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
4892
|
+
export { RouteProvider } from './RouteProvider'
|
|
4893
|
+
`;
|
|
4894
|
+
}
|
|
4895
|
+
generateMiddlewareProvider() {
|
|
4896
|
+
return `/**
|
|
4897
|
+
* Middleware Service Provider
|
|
4898
|
+
*/
|
|
4208
4899
|
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4900
|
+
import {
|
|
4901
|
+
ServiceProvider,
|
|
4902
|
+
type Container,
|
|
4903
|
+
type PlanetCore,
|
|
4904
|
+
bodySizeLimit,
|
|
4905
|
+
securityHeaders,
|
|
4906
|
+
} from '@gravito/core'
|
|
4212
4907
|
|
|
4213
|
-
|
|
4214
|
-
|
|
4908
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
4909
|
+
register(_container: Container): void {}
|
|
4215
4910
|
|
|
4216
|
-
|
|
4217
|
-
core.
|
|
4911
|
+
boot(core: PlanetCore): void {
|
|
4912
|
+
core.adapter.use('*', securityHeaders())
|
|
4913
|
+
core.adapter.use('*', bodySizeLimit(1024 * 1024))
|
|
4914
|
+
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
4915
|
+
}
|
|
4916
|
+
}
|
|
4917
|
+
`;
|
|
4918
|
+
}
|
|
4919
|
+
generateRouteProvider() {
|
|
4920
|
+
return `/**
|
|
4921
|
+
* Route Service Provider
|
|
4922
|
+
*/
|
|
4923
|
+
|
|
4924
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
4925
|
+
import { registerApiRoutes } from '../routes/api'
|
|
4926
|
+
|
|
4927
|
+
export class RouteProvider extends ServiceProvider {
|
|
4928
|
+
register(_container: Container): void {}
|
|
4929
|
+
|
|
4930
|
+
boot(core: PlanetCore): void {
|
|
4931
|
+
registerApiRoutes(core.router)
|
|
4932
|
+
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
`;
|
|
4936
|
+
}
|
|
4937
|
+
generateBootstrap(_context) {
|
|
4938
|
+
return `/**
|
|
4939
|
+
* Application Bootstrap
|
|
4940
|
+
*
|
|
4941
|
+
* Uses the ServiceProvider pattern for modular initialization.
|
|
4942
|
+
*/
|
|
4943
|
+
|
|
4944
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
4945
|
+
import { OrbitAtlas } from '@gravito/atlas'
|
|
4946
|
+
import appConfig from '../config/app'
|
|
4947
|
+
import {
|
|
4948
|
+
AppServiceProvider,
|
|
4949
|
+
MiddlewareProvider,
|
|
4950
|
+
RouteProvider,
|
|
4951
|
+
} from './providers'
|
|
4952
|
+
|
|
4953
|
+
export async function bootstrap() {
|
|
4954
|
+
const config = defineConfig({
|
|
4955
|
+
config: appConfig,
|
|
4956
|
+
orbits: [new OrbitAtlas()],
|
|
4957
|
+
})
|
|
4958
|
+
|
|
4959
|
+
const core = await PlanetCore.boot(config)
|
|
4960
|
+
core.registerGlobalErrorHandlers()
|
|
4218
4961
|
|
|
4219
|
-
|
|
4220
|
-
|
|
4962
|
+
core.register(new AppServiceProvider())
|
|
4963
|
+
core.register(new MiddlewareProvider())
|
|
4964
|
+
core.register(new RouteProvider())
|
|
4221
4965
|
|
|
4222
|
-
|
|
4223
|
-
registerApiRoutes(core.router)
|
|
4966
|
+
await core.bootstrap()
|
|
4224
4967
|
|
|
4225
|
-
|
|
4968
|
+
return core
|
|
4969
|
+
}
|
|
4970
|
+
|
|
4971
|
+
const core = await bootstrap()
|
|
4226
4972
|
export default core.liftoff()
|
|
4227
4973
|
`;
|
|
4228
4974
|
}
|
|
4229
4975
|
generateArchitectureDoc(context) {
|
|
4230
|
-
return
|
|
4976
|
+
return `# ${context.name} - Action Domain Architecture
|
|
4977
|
+
|
|
4978
|
+
## Overview
|
|
4979
|
+
|
|
4980
|
+
This project uses the **Action Domain** pattern, designed for high-clarity API implementations.
|
|
4981
|
+
|
|
4982
|
+
## Service Providers
|
|
4983
|
+
|
|
4984
|
+
Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
4985
|
+
|
|
4986
|
+
## Directory Structure
|
|
4987
|
+
|
|
4988
|
+
\`\`\`
|
|
4989
|
+
src/
|
|
4990
|
+
\u251C\u2500\u2500 actions/ # Single Responsibility Business Logic
|
|
4991
|
+
\u2502 \u251C\u2500\u2500 Action.ts # Base Action class
|
|
4992
|
+
\u2502 \u2514\u2500\u2500 [Domain]/ # Domain-specific actions
|
|
4993
|
+
\u251C\u2500\u2500 controllers/ # HTTP Request Handlers
|
|
4994
|
+
\u2502 \u2514\u2500\u2500 api/v1/ # API Controllers
|
|
4995
|
+
\u251C\u2500\u2500 types/ # TypeScript Definitions
|
|
4996
|
+
\u251C\u2500\u2500 repositories/ # Data Access Layer
|
|
4997
|
+
\u251C\u2500\u2500 routes/ # Route Definitions
|
|
4998
|
+
\u251C\u2500\u2500 providers/ # Service Providers
|
|
4999
|
+
\u2514\u2500\u2500 config/ # Configuration
|
|
5000
|
+
\`\`\`
|
|
5001
|
+
|
|
5002
|
+
## Core Concepts
|
|
5003
|
+
|
|
5004
|
+
### Actions
|
|
5005
|
+
Every business operation is an "Action". An action:
|
|
5006
|
+
- Does ONE thing.
|
|
5007
|
+
- Takes specific input.
|
|
5008
|
+
- Returns specific output.
|
|
5009
|
+
- Is framework-agnostic (ideally).
|
|
5010
|
+
|
|
5011
|
+
### Controllers
|
|
5012
|
+
Controllers are thin. They:
|
|
5013
|
+
1. Parse the request.
|
|
5014
|
+
2. Instantiate/Call the Action.
|
|
5015
|
+
3. Return the response.
|
|
5016
|
+
|
|
5017
|
+
Created with \u2764\uFE0F using Gravito Framework
|
|
5018
|
+
`;
|
|
4231
5019
|
}
|
|
4232
5020
|
generatePackageJson(context) {
|
|
4233
5021
|
const pkg = {
|
|
@@ -4239,7 +5027,11 @@ export default core.liftoff()
|
|
|
4239
5027
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
4240
5028
|
start: "bun run dist/bootstrap.js",
|
|
4241
5029
|
test: "bun test",
|
|
4242
|
-
typecheck: "tsc --noEmit"
|
|
5030
|
+
typecheck: "tsc --noEmit",
|
|
5031
|
+
check: "bun run typecheck && bun run test",
|
|
5032
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
5033
|
+
validate: "bun run check && bun run check:deps",
|
|
5034
|
+
precommit: "bun run validate"
|
|
4243
5035
|
},
|
|
4244
5036
|
dependencies: {
|
|
4245
5037
|
"@gravito/core": "workspace:*",
|