@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.cjs
CHANGED
|
@@ -392,6 +392,7 @@ var BaseGenerator = class {
|
|
|
392
392
|
"ARCHITECTURE.md",
|
|
393
393
|
this.generateArchitectureDoc(context)
|
|
394
394
|
);
|
|
395
|
+
await this.generateCheckScripts(context);
|
|
395
396
|
}
|
|
396
397
|
/**
|
|
397
398
|
* Apply profile-specific overlays
|
|
@@ -479,6 +480,10 @@ var BaseGenerator = class {
|
|
|
479
480
|
start: "bun run dist/bootstrap.js",
|
|
480
481
|
test: "bun test",
|
|
481
482
|
typecheck: "tsc --noEmit",
|
|
483
|
+
check: "bun run typecheck && bun run test",
|
|
484
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
485
|
+
validate: "bun run check && bun run check:deps",
|
|
486
|
+
precommit: "bun run validate",
|
|
482
487
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
483
488
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
484
489
|
},
|
|
@@ -731,6 +736,430 @@ coverage/
|
|
|
731
736
|
};
|
|
732
737
|
return JSON.stringify(config, null, 2);
|
|
733
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* Generate check scripts for project validation.
|
|
741
|
+
*/
|
|
742
|
+
async generateCheckScripts(context) {
|
|
743
|
+
const scriptsDir = import_node_path2.default.resolve(context.targetDir, "scripts");
|
|
744
|
+
await import_promises2.default.mkdir(scriptsDir, { recursive: true });
|
|
745
|
+
await this.writeFile(
|
|
746
|
+
scriptsDir,
|
|
747
|
+
"check-dependencies.ts",
|
|
748
|
+
this.generateCheckDependenciesScript()
|
|
749
|
+
);
|
|
750
|
+
await this.writeFile(scriptsDir, "check.sh", this.generateCheckShellScript());
|
|
751
|
+
await this.writeFile(scriptsDir, "pre-commit.sh", this.generatePreCommitScript());
|
|
752
|
+
await this.writeFile(context.targetDir, "CHECK_SYSTEM.md", this.generateCheckSystemDoc(context));
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Generate check-dependencies.ts script content.
|
|
756
|
+
*/
|
|
757
|
+
generateCheckDependenciesScript() {
|
|
758
|
+
return `/**
|
|
759
|
+
* \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5\u8173\u672C
|
|
760
|
+
*
|
|
761
|
+
* \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
|
|
762
|
+
* \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
763
|
+
*/
|
|
764
|
+
|
|
765
|
+
import { readFileSync } from 'fs'
|
|
766
|
+
import { join } from 'path'
|
|
767
|
+
|
|
768
|
+
interface PackageJson {
|
|
769
|
+
dependencies?: Record<string, string>
|
|
770
|
+
devDependencies?: Record<string, string>
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
interface PackageInfo {
|
|
774
|
+
name: string
|
|
775
|
+
current: string
|
|
776
|
+
latest: string
|
|
777
|
+
outdated: boolean
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const colors = {
|
|
781
|
+
reset: '\\x1b[0m',
|
|
782
|
+
green: '\\x1b[32m',
|
|
783
|
+
yellow: '\\x1b[33m',
|
|
784
|
+
red: '\\x1b[31m',
|
|
785
|
+
blue: '\\x1b[36m',
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function log(message: string, color: keyof typeof colors = 'reset') {
|
|
789
|
+
console.log(\`\${colors[color]}\${message}\${colors.reset}\`)
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
async function getLatestVersion(packageName: string): Promise<string | null> {
|
|
793
|
+
try {
|
|
794
|
+
const response = await fetch(\`https://registry.npmjs.org/\${packageName}/latest\`)
|
|
795
|
+
if (!response.ok) return null
|
|
796
|
+
const data = await response.json()
|
|
797
|
+
return data.version
|
|
798
|
+
} catch {
|
|
799
|
+
return null
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function parseVersion(version: string): string {
|
|
804
|
+
// \u79FB\u9664 ^, ~, >= \u7B49\u524D\u7DB4
|
|
805
|
+
return version.replace(/^[\\^~>=<]/, '')
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
async function checkPackage(
|
|
809
|
+
name: string,
|
|
810
|
+
currentVersion: string
|
|
811
|
+
): Promise<PackageInfo | null> {
|
|
812
|
+
// \u8DF3\u904E\u672C\u5730\u9023\u7D50\u7684\u5957\u4EF6
|
|
813
|
+
if (currentVersion.startsWith('link:') || currentVersion.startsWith('workspace:')) {
|
|
814
|
+
return null
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const current = parseVersion(currentVersion)
|
|
818
|
+
const latest = await getLatestVersion(name)
|
|
819
|
+
|
|
820
|
+
if (!latest) {
|
|
821
|
+
return null
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
return {
|
|
825
|
+
name,
|
|
826
|
+
current,
|
|
827
|
+
latest,
|
|
828
|
+
outdated: current !== latest,
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async function main() {
|
|
833
|
+
log('\\n=== \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5 ===\\n', 'blue')
|
|
834
|
+
|
|
835
|
+
const packageJsonPath = join(process.cwd(), 'package.json')
|
|
836
|
+
const packageJson: PackageJson = JSON.parse(
|
|
837
|
+
readFileSync(packageJsonPath, 'utf-8')
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
const allDependencies = {
|
|
841
|
+
...packageJson.dependencies,
|
|
842
|
+
...packageJson.devDependencies,
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
log(\`\u6AA2\u67E5 \${Object.keys(allDependencies).length} \u500B\u5957\u4EF6...\\n\`, 'yellow')
|
|
846
|
+
|
|
847
|
+
const results: PackageInfo[] = []
|
|
848
|
+
const outdated: PackageInfo[] = []
|
|
849
|
+
const upToDate: PackageInfo[] = []
|
|
850
|
+
|
|
851
|
+
// \u6AA2\u67E5\u6240\u6709\u5957\u4EF6
|
|
852
|
+
for (const [name, version] of Object.entries(allDependencies)) {
|
|
853
|
+
const info = await checkPackage(name, version)
|
|
854
|
+
if (info) {
|
|
855
|
+
results.push(info)
|
|
856
|
+
if (info.outdated) {
|
|
857
|
+
outdated.push(info)
|
|
858
|
+
} else {
|
|
859
|
+
upToDate.push(info)
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// \u986F\u793A\u7D50\u679C
|
|
865
|
+
if (upToDate.length > 0) {
|
|
866
|
+
log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
|
|
867
|
+
upToDate.forEach((pkg) => {
|
|
868
|
+
log(\` \${pkg.name}: \${pkg.current}\`, 'green')
|
|
869
|
+
})
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (outdated.length > 0) {
|
|
873
|
+
log(\`\\n\u26A0 \u9700\u8981\u66F4\u65B0 (\${outdated.length}):\`, 'yellow')
|
|
874
|
+
outdated.forEach((pkg) => {
|
|
875
|
+
log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
|
|
876
|
+
})
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// \u7E3D\u7D50
|
|
880
|
+
log('\\n=== \u6AA2\u67E5\u7D50\u679C ===', 'blue')
|
|
881
|
+
log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
|
|
882
|
+
log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
|
|
883
|
+
log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
|
|
884
|
+
|
|
885
|
+
// \u5982\u679C\u6709\u9700\u8981\u66F4\u65B0\u7684\u5957\u4EF6\uFF0C\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u78BC
|
|
886
|
+
if (outdated.length > 0) {
|
|
887
|
+
log('\\n\u5EFA\u8B70\u57F7\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0\u5957\u4EF6\uFF1A', 'yellow')
|
|
888
|
+
log(' bun update', 'yellow')
|
|
889
|
+
process.exit(1)
|
|
890
|
+
} else {
|
|
891
|
+
log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
|
|
892
|
+
process.exit(0)
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
main().catch((error) => {
|
|
897
|
+
log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
|
|
898
|
+
process.exit(1)
|
|
899
|
+
})
|
|
900
|
+
`;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Generate check.sh script content.
|
|
904
|
+
*/
|
|
905
|
+
generateCheckShellScript() {
|
|
906
|
+
return `#!/bin/bash
|
|
907
|
+
|
|
908
|
+
# \u5C08\u6848\u6AA2\u67E5\u8173\u672C
|
|
909
|
+
# \u57F7\u884C\u6240\u6709\u5FC5\u8981\u7684\u6AA2\u67E5\uFF1A\u985E\u578B\u6AA2\u67E5\u3001\u6E2C\u8A66\u3001\u4F9D\u8CF4\u6AA2\u67E5\u7B49
|
|
910
|
+
|
|
911
|
+
set -e
|
|
912
|
+
|
|
913
|
+
# \u984F\u8272\u5B9A\u7FA9
|
|
914
|
+
GREEN='\\033[0;32m'
|
|
915
|
+
YELLOW='\\033[1;33m'
|
|
916
|
+
RED='\\033[0;31m'
|
|
917
|
+
BLUE='\\033[0;34m'
|
|
918
|
+
NC='\\033[0m' # No Color
|
|
919
|
+
|
|
920
|
+
echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
|
|
921
|
+
|
|
922
|
+
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
923
|
+
if [ ! -f "package.json" ]; then
|
|
924
|
+
echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
|
|
925
|
+
exit 1
|
|
926
|
+
fi
|
|
927
|
+
|
|
928
|
+
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
929
|
+
if ! command -v bun &> /dev/null; then
|
|
930
|
+
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
931
|
+
exit 1
|
|
932
|
+
fi
|
|
933
|
+
|
|
934
|
+
# 1. \u985E\u578B\u6AA2\u67E5
|
|
935
|
+
echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
936
|
+
if bun run typecheck; then
|
|
937
|
+
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
938
|
+
else
|
|
939
|
+
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
940
|
+
exit 1
|
|
941
|
+
fi
|
|
942
|
+
|
|
943
|
+
# 2. \u57F7\u884C\u6E2C\u8A66
|
|
944
|
+
echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
945
|
+
if bun test; then
|
|
946
|
+
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
947
|
+
else
|
|
948
|
+
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
949
|
+
exit 1
|
|
950
|
+
fi
|
|
951
|
+
|
|
952
|
+
# 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
|
|
953
|
+
echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
|
|
954
|
+
if bun run check:deps; then
|
|
955
|
+
echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
|
|
956
|
+
else
|
|
957
|
+
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"
|
|
958
|
+
fi
|
|
959
|
+
|
|
960
|
+
echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
|
|
961
|
+
`;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Generate pre-commit.sh script content.
|
|
965
|
+
*/
|
|
966
|
+
generatePreCommitScript() {
|
|
967
|
+
return `#!/bin/bash
|
|
968
|
+
|
|
969
|
+
# Pre-commit Hook
|
|
970
|
+
# \u5728 git commit \u524D\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5
|
|
971
|
+
#
|
|
972
|
+
# \u5B89\u88DD\u65B9\u5F0F\uFF1A
|
|
973
|
+
# ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
|
|
974
|
+
# \u6216
|
|
975
|
+
# cp scripts/pre-commit.sh .git/hooks/pre-commit
|
|
976
|
+
# chmod +x .git/hooks/pre-commit
|
|
977
|
+
|
|
978
|
+
set -e
|
|
979
|
+
|
|
980
|
+
# \u984F\u8272\u5B9A\u7FA9
|
|
981
|
+
GREEN='\\033[0;32m'
|
|
982
|
+
YELLOW='\\033[1;33m'
|
|
983
|
+
RED='\\033[0;31m'
|
|
984
|
+
BLUE='\\033[0;34m'
|
|
985
|
+
NC='\\033[0m' # No Color
|
|
986
|
+
|
|
987
|
+
echo -e "\${BLUE}=== Pre-commit \u6AA2\u67E5 ===\${NC}\\n"
|
|
988
|
+
|
|
989
|
+
# \u5207\u63DB\u5230\u5C08\u6848\u6839\u76EE\u9304
|
|
990
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
991
|
+
|
|
992
|
+
# \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
|
|
993
|
+
if [ ! -f "package.json" ]; then
|
|
994
|
+
echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
|
|
995
|
+
exit 1
|
|
996
|
+
fi
|
|
997
|
+
|
|
998
|
+
# \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
|
|
999
|
+
if ! command -v bun &> /dev/null; then
|
|
1000
|
+
echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
|
|
1001
|
+
exit 1
|
|
1002
|
+
fi
|
|
1003
|
+
|
|
1004
|
+
# 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
|
|
1005
|
+
echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
|
|
1006
|
+
if bun run typecheck; then
|
|
1007
|
+
echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
|
|
1008
|
+
else
|
|
1009
|
+
echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
|
|
1010
|
+
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
1011
|
+
exit 1
|
|
1012
|
+
fi
|
|
1013
|
+
|
|
1014
|
+
# 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
|
|
1015
|
+
echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
|
|
1016
|
+
if bun test; then
|
|
1017
|
+
echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
|
|
1018
|
+
else
|
|
1019
|
+
echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
|
|
1020
|
+
echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
|
|
1021
|
+
exit 1
|
|
1022
|
+
fi
|
|
1023
|
+
|
|
1024
|
+
echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
|
|
1025
|
+
`;
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Generate CHECK_SYSTEM.md documentation.
|
|
1029
|
+
*/
|
|
1030
|
+
generateCheckSystemDoc(context) {
|
|
1031
|
+
return `# \u5C08\u6848\u6AA2\u67E5\u7CFB\u7D71
|
|
1032
|
+
|
|
1033
|
+
\u672C\u5C08\u6848\u5DF2\u5EFA\u7ACB\u5B8C\u6574\u7684\u672C\u5730\u6AA2\u67E5\u6A5F\u5236\uFF0C\u7121\u9700\u4F9D\u8CF4 GitHub CI\u3002
|
|
1034
|
+
|
|
1035
|
+
## \u5FEB\u901F\u958B\u59CB
|
|
1036
|
+
|
|
1037
|
+
### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
|
|
1038
|
+
\`\`\`bash
|
|
1039
|
+
bun run validate
|
|
1040
|
+
\`\`\`
|
|
1041
|
+
|
|
1042
|
+
### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
|
|
1043
|
+
\`\`\`bash
|
|
1044
|
+
# \u985E\u578B\u6AA2\u67E5
|
|
1045
|
+
bun run typecheck
|
|
1046
|
+
|
|
1047
|
+
# \u6E2C\u8A66
|
|
1048
|
+
bun run test
|
|
1049
|
+
|
|
1050
|
+
# \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
1051
|
+
bun run check:deps
|
|
1052
|
+
\`\`\`
|
|
1053
|
+
|
|
1054
|
+
## \u53EF\u7528\u547D\u4EE4
|
|
1055
|
+
|
|
1056
|
+
### Package.json \u8173\u672C
|
|
1057
|
+
|
|
1058
|
+
| \u547D\u4EE4 | \u8AAA\u660E |
|
|
1059
|
+
|------|------|
|
|
1060
|
+
| \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
|
|
1061
|
+
| \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
|
|
1062
|
+
| \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
|
|
1063
|
+
| \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
|
|
1064
|
+
| \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
|
|
1065
|
+
| \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
|
|
1066
|
+
|
|
1067
|
+
### Shell \u8173\u672C
|
|
1068
|
+
|
|
1069
|
+
| \u8173\u672C | \u8AAA\u660E |
|
|
1070
|
+
|------|------|
|
|
1071
|
+
| \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
|
|
1072
|
+
| \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
|
|
1073
|
+
|
|
1074
|
+
## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
1075
|
+
|
|
1076
|
+
\u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
|
|
1077
|
+
|
|
1078
|
+
\`\`\`bash
|
|
1079
|
+
# \u5B89\u88DD pre-commit hook
|
|
1080
|
+
ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
|
|
1081
|
+
|
|
1082
|
+
# \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
|
|
1083
|
+
cp scripts/pre-commit.sh .git/hooks/pre-commit
|
|
1084
|
+
chmod +x .git/hooks/pre-commit
|
|
1085
|
+
\`\`\`
|
|
1086
|
+
|
|
1087
|
+
**\u529F\u80FD\uFF1A**
|
|
1088
|
+
- \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
|
|
1089
|
+
- \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
|
|
1090
|
+
- \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
|
|
1091
|
+
|
|
1092
|
+
**\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
|
|
1093
|
+
\`\`\`bash
|
|
1094
|
+
git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
|
|
1095
|
+
\`\`\`
|
|
1096
|
+
|
|
1097
|
+
## \u6AA2\u67E5\u9805\u76EE
|
|
1098
|
+
|
|
1099
|
+
### 1. \u985E\u578B\u6AA2\u67E5
|
|
1100
|
+
- \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
|
|
1101
|
+
- \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
|
|
1102
|
+
|
|
1103
|
+
### 2. \u6E2C\u8A66
|
|
1104
|
+
- \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
|
|
1105
|
+
- \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
|
|
1106
|
+
|
|
1107
|
+
### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
|
|
1108
|
+
- \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
|
|
1109
|
+
- \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
|
|
1110
|
+
- \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
|
|
1111
|
+
|
|
1112
|
+
## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
|
|
1113
|
+
|
|
1114
|
+
### \u958B\u767C\u6642
|
|
1115
|
+
1. \u958B\u767C\u529F\u80FD
|
|
1116
|
+
2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
|
|
1117
|
+
3. \u4FEE\u6B63\u554F\u984C
|
|
1118
|
+
4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
|
|
1119
|
+
|
|
1120
|
+
### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
|
|
1121
|
+
1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
|
|
1122
|
+
2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
|
|
1123
|
+
3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
|
|
1124
|
+
4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
|
|
1125
|
+
|
|
1126
|
+
## \u6A94\u6848\u7D50\u69CB
|
|
1127
|
+
|
|
1128
|
+
\`\`\`
|
|
1129
|
+
${context.nameKebabCase}/
|
|
1130
|
+
\u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
|
|
1131
|
+
\u251C\u2500\u2500 scripts/
|
|
1132
|
+
\u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
|
|
1133
|
+
\u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
|
|
1134
|
+
\u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
|
|
1135
|
+
\u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
|
|
1136
|
+
\`\`\`
|
|
1137
|
+
|
|
1138
|
+
## \u6CE8\u610F\u4E8B\u9805
|
|
1139
|
+
|
|
1140
|
+
1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
|
|
1141
|
+
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
|
|
1142
|
+
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
|
|
1143
|
+
|
|
1144
|
+
## \u6545\u969C\u6392\u9664
|
|
1145
|
+
|
|
1146
|
+
### \u6AA2\u67E5\u5931\u6557
|
|
1147
|
+
1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
|
|
1148
|
+
2. \u4FEE\u6B63\u554F\u984C
|
|
1149
|
+
3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
|
|
1150
|
+
|
|
1151
|
+
### \u8DF3\u904E\u6AA2\u67E5
|
|
1152
|
+
\u53EA\u6709\u5728\u7DCA\u6025\u60C5\u6CC1\u4E0B\u624D\u4F7F\u7528\uFF1A
|
|
1153
|
+
\`\`\`bash
|
|
1154
|
+
git commit --no-verify
|
|
1155
|
+
\`\`\`
|
|
1156
|
+
|
|
1157
|
+
### \u79FB\u9664 Pre-commit Hook
|
|
1158
|
+
\`\`\`bash
|
|
1159
|
+
rm .git/hooks/pre-commit
|
|
1160
|
+
\`\`\`
|
|
1161
|
+
`;
|
|
1162
|
+
}
|
|
734
1163
|
/**
|
|
735
1164
|
* Log a message if verbose mode is enabled.
|
|
736
1165
|
*/
|
|
@@ -911,6 +1340,11 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
|
|
|
911
1340
|
type: "directory",
|
|
912
1341
|
name: "Providers",
|
|
913
1342
|
children: [
|
|
1343
|
+
{
|
|
1344
|
+
type: "file",
|
|
1345
|
+
name: "index.ts",
|
|
1346
|
+
content: this.generateProvidersIndex()
|
|
1347
|
+
},
|
|
914
1348
|
{
|
|
915
1349
|
type: "file",
|
|
916
1350
|
name: "AppServiceProvider.ts",
|
|
@@ -920,6 +1354,16 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
|
|
|
920
1354
|
type: "file",
|
|
921
1355
|
name: "RepositoryServiceProvider.ts",
|
|
922
1356
|
content: this.generateRepositoryServiceProvider()
|
|
1357
|
+
},
|
|
1358
|
+
{
|
|
1359
|
+
type: "file",
|
|
1360
|
+
name: "MiddlewareProvider.ts",
|
|
1361
|
+
content: this.generateMiddlewareProvider()
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
type: "file",
|
|
1365
|
+
name: "RouteProvider.ts",
|
|
1366
|
+
content: this.generateRouteProvider()
|
|
923
1367
|
}
|
|
924
1368
|
]
|
|
925
1369
|
}
|
|
@@ -1413,6 +1857,65 @@ export class RepositoryServiceProvider extends ServiceProvider {
|
|
|
1413
1857
|
container.singleton('mailService', () => new MailService())
|
|
1414
1858
|
}
|
|
1415
1859
|
}
|
|
1860
|
+
`;
|
|
1861
|
+
}
|
|
1862
|
+
generateProvidersIndex() {
|
|
1863
|
+
return `/**
|
|
1864
|
+
* Application Service Providers
|
|
1865
|
+
*/
|
|
1866
|
+
|
|
1867
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
1868
|
+
export { RepositoryServiceProvider } from './RepositoryServiceProvider'
|
|
1869
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
1870
|
+
export { RouteProvider } from './RouteProvider'
|
|
1871
|
+
`;
|
|
1872
|
+
}
|
|
1873
|
+
generateMiddlewareProvider() {
|
|
1874
|
+
return `/**
|
|
1875
|
+
* Middleware Service Provider
|
|
1876
|
+
*/
|
|
1877
|
+
|
|
1878
|
+
import {
|
|
1879
|
+
ServiceProvider,
|
|
1880
|
+
type Container,
|
|
1881
|
+
type PlanetCore,
|
|
1882
|
+
bodySizeLimit,
|
|
1883
|
+
securityHeaders,
|
|
1884
|
+
} from '@gravito/core'
|
|
1885
|
+
|
|
1886
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
1887
|
+
register(_container: Container): void {}
|
|
1888
|
+
|
|
1889
|
+
boot(core: PlanetCore): void {
|
|
1890
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
1891
|
+
|
|
1892
|
+
core.adapter.use('*', securityHeaders({
|
|
1893
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
1894
|
+
}))
|
|
1895
|
+
|
|
1896
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
1897
|
+
|
|
1898
|
+
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
`;
|
|
1902
|
+
}
|
|
1903
|
+
generateRouteProvider() {
|
|
1904
|
+
return `/**
|
|
1905
|
+
* Route Service Provider
|
|
1906
|
+
*/
|
|
1907
|
+
|
|
1908
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
1909
|
+
import { registerApiRoutes } from '../../Interface/Http/Routes/api'
|
|
1910
|
+
|
|
1911
|
+
export class RouteProvider extends ServiceProvider {
|
|
1912
|
+
register(_container: Container): void {}
|
|
1913
|
+
|
|
1914
|
+
boot(core: PlanetCore): void {
|
|
1915
|
+
registerApiRoutes(core.router)
|
|
1916
|
+
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1416
1919
|
`;
|
|
1417
1920
|
}
|
|
1418
1921
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -1497,64 +2000,55 @@ export class UserPresenter {
|
|
|
1497
2000
|
}
|
|
1498
2001
|
`;
|
|
1499
2002
|
}
|
|
1500
|
-
generateBootstrap(
|
|
2003
|
+
generateBootstrap(_context) {
|
|
1501
2004
|
return `/**
|
|
1502
2005
|
* Application Bootstrap
|
|
2006
|
+
*
|
|
2007
|
+
* The entry point for your Clean Architecture application.
|
|
2008
|
+
* Uses the ServiceProvider pattern for modular initialization.
|
|
2009
|
+
*
|
|
2010
|
+
* Lifecycle:
|
|
2011
|
+
* 1. Configure: Load app config and orbits
|
|
2012
|
+
* 2. Boot: Initialize PlanetCore
|
|
2013
|
+
* 3. Register Providers: Bind services to container
|
|
2014
|
+
* 4. Bootstrap: Boot all providers
|
|
1503
2015
|
*/
|
|
1504
2016
|
|
|
1505
|
-
import {
|
|
2017
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
1506
2018
|
import { OrbitAtlas } from '@gravito/atlas'
|
|
1507
|
-
import
|
|
1508
|
-
import {
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
"default-src 'self'",
|
|
1521
|
-
"script-src 'self' 'unsafe-inline'",
|
|
1522
|
-
"style-src 'self' 'unsafe-inline'",
|
|
1523
|
-
"img-src 'self' data:",
|
|
1524
|
-
"object-src 'none'",
|
|
1525
|
-
"base-uri 'self'",
|
|
1526
|
-
"frame-ancestors 'none'",
|
|
1527
|
-
].join('; ')
|
|
1528
|
-
const cspValue = process.env.APP_CSP
|
|
1529
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
1530
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
1531
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
1532
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
1533
|
-
|
|
1534
|
-
core.adapter.use(
|
|
1535
|
-
'*',
|
|
1536
|
-
securityHeaders({
|
|
1537
|
-
contentSecurityPolicy: csp,
|
|
1538
|
-
hsts:
|
|
1539
|
-
process.env.NODE_ENV === 'production'
|
|
1540
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
1541
|
-
: false,
|
|
2019
|
+
import appConfig from '../config/app'
|
|
2020
|
+
import {
|
|
2021
|
+
AppServiceProvider,
|
|
2022
|
+
RepositoryServiceProvider,
|
|
2023
|
+
MiddlewareProvider,
|
|
2024
|
+
RouteProvider,
|
|
2025
|
+
} from './Infrastructure/Providers'
|
|
2026
|
+
|
|
2027
|
+
export async function bootstrap() {
|
|
2028
|
+
// 1. Configure
|
|
2029
|
+
const config = defineConfig({
|
|
2030
|
+
config: appConfig,
|
|
2031
|
+
orbits: [new OrbitAtlas()],
|
|
1542
2032
|
})
|
|
1543
|
-
)
|
|
1544
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
1545
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
1546
|
-
}
|
|
1547
2033
|
|
|
1548
|
-
|
|
2034
|
+
// 2. Boot Core
|
|
2035
|
+
const core = await PlanetCore.boot(config)
|
|
2036
|
+
core.registerGlobalErrorHandlers()
|
|
1549
2037
|
|
|
1550
|
-
|
|
1551
|
-
core.register(new
|
|
2038
|
+
// 3. Register Providers
|
|
2039
|
+
core.register(new RepositoryServiceProvider())
|
|
2040
|
+
core.register(new AppServiceProvider())
|
|
2041
|
+
core.register(new MiddlewareProvider())
|
|
2042
|
+
core.register(new RouteProvider())
|
|
1552
2043
|
|
|
1553
|
-
|
|
2044
|
+
// 4. Bootstrap All Providers
|
|
2045
|
+
await core.bootstrap()
|
|
1554
2046
|
|
|
1555
|
-
|
|
1556
|
-
|
|
2047
|
+
return core
|
|
2048
|
+
}
|
|
1557
2049
|
|
|
2050
|
+
// Application Entry Point
|
|
2051
|
+
const core = await bootstrap()
|
|
1558
2052
|
export default core.liftoff()
|
|
1559
2053
|
`;
|
|
1560
2054
|
}
|
|
@@ -1566,6 +2060,15 @@ export default core.liftoff()
|
|
|
1566
2060
|
This project follows **Clean Architecture** (by Robert C. Martin).
|
|
1567
2061
|
The key principle is the **Dependency Rule**: dependencies point inward.
|
|
1568
2062
|
|
|
2063
|
+
## Service Providers
|
|
2064
|
+
|
|
2065
|
+
Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
2066
|
+
|
|
2067
|
+
### Provider Lifecycle
|
|
2068
|
+
|
|
2069
|
+
1. **register()**: Bind services to the container (sync or async).
|
|
2070
|
+
2. **boot()**: Called after ALL providers have registered. Safe to use other services.
|
|
2071
|
+
|
|
1569
2072
|
## Layer Structure
|
|
1570
2073
|
|
|
1571
2074
|
\`\`\`
|
|
@@ -1637,6 +2140,10 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
1637
2140
|
start: "bun run dist/bootstrap.js",
|
|
1638
2141
|
test: "bun test",
|
|
1639
2142
|
typecheck: "tsc --noEmit",
|
|
2143
|
+
check: "bun run typecheck && bun run test",
|
|
2144
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2145
|
+
validate: "bun run check && bun run check:deps",
|
|
2146
|
+
precommit: "bun run validate",
|
|
1640
2147
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
1641
2148
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
1642
2149
|
},
|
|
@@ -1960,63 +2467,46 @@ var DddGenerator = class extends BaseGenerator {
|
|
|
1960
2467
|
// ─────────────────────────────────────────────────────────────
|
|
1961
2468
|
// Bootstrap File Generators
|
|
1962
2469
|
// ─────────────────────────────────────────────────────────────
|
|
1963
|
-
generateBootstrapApp(
|
|
2470
|
+
generateBootstrapApp(_context) {
|
|
1964
2471
|
return `/**
|
|
1965
2472
|
* Application Bootstrap
|
|
1966
2473
|
*
|
|
1967
|
-
* Central configuration and initialization
|
|
2474
|
+
* Central configuration and initialization using the ServiceProvider pattern.
|
|
2475
|
+
*
|
|
2476
|
+
* Lifecycle:
|
|
2477
|
+
* 1. Configure: Load app config and orbits
|
|
2478
|
+
* 2. Boot: Initialize PlanetCore
|
|
2479
|
+
* 3. Register Providers: Bind services to container
|
|
2480
|
+
* 4. Bootstrap: Boot all providers
|
|
1968
2481
|
*/
|
|
1969
2482
|
|
|
1970
|
-
import {
|
|
2483
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
2484
|
+
import { OrbitAtlas } from '@gravito/atlas'
|
|
2485
|
+
import appConfig from '../../config/app'
|
|
1971
2486
|
import { registerProviders } from './providers'
|
|
1972
2487
|
import { registerRoutes } from './routes'
|
|
1973
2488
|
|
|
1974
2489
|
export async function createApp(): Promise<PlanetCore> {
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
2490
|
+
// 1. Configure
|
|
2491
|
+
const config = defineConfig({
|
|
2492
|
+
config: appConfig,
|
|
2493
|
+
orbits: [new OrbitAtlas()],
|
|
2494
|
+
})
|
|
1980
2495
|
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
"style-src 'self' 'unsafe-inline'",
|
|
1985
|
-
"img-src 'self' data:",
|
|
1986
|
-
"object-src 'none'",
|
|
1987
|
-
"base-uri 'self'",
|
|
1988
|
-
"frame-ancestors 'none'",
|
|
1989
|
-
].join('; ')
|
|
1990
|
-
const cspValue = process.env.APP_CSP
|
|
1991
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
1992
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
1993
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
1994
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
1995
|
-
|
|
1996
|
-
core.adapter.use(
|
|
1997
|
-
'*',
|
|
1998
|
-
securityHeaders({
|
|
1999
|
-
contentSecurityPolicy: csp,
|
|
2000
|
-
hsts:
|
|
2001
|
-
process.env.NODE_ENV === 'production'
|
|
2002
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
2003
|
-
: false,
|
|
2004
|
-
})
|
|
2005
|
-
)
|
|
2006
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
2007
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
2008
|
-
}
|
|
2496
|
+
// 2. Boot Core
|
|
2497
|
+
const core = await PlanetCore.boot(config)
|
|
2498
|
+
core.registerGlobalErrorHandlers()
|
|
2009
2499
|
|
|
2010
|
-
|
|
2011
|
-
|
|
2500
|
+
// 3. Register Providers
|
|
2501
|
+
await registerProviders(core)
|
|
2012
2502
|
|
|
2013
|
-
|
|
2014
|
-
|
|
2503
|
+
// 4. Bootstrap All Providers
|
|
2504
|
+
await core.bootstrap()
|
|
2015
2505
|
|
|
2016
|
-
|
|
2017
|
-
|
|
2506
|
+
// Register routes after bootstrap
|
|
2507
|
+
registerRoutes(core.router)
|
|
2018
2508
|
|
|
2019
|
-
|
|
2509
|
+
return core
|
|
2020
2510
|
}
|
|
2021
2511
|
`;
|
|
2022
2512
|
}
|
|
@@ -2024,19 +2514,48 @@ export async function createApp(): Promise<PlanetCore> {
|
|
|
2024
2514
|
return `/**
|
|
2025
2515
|
* Service Providers Registry
|
|
2026
2516
|
*
|
|
2027
|
-
* Register all
|
|
2517
|
+
* Register all service providers here.
|
|
2518
|
+
* Include both global and module-specific providers.
|
|
2028
2519
|
*/
|
|
2029
2520
|
|
|
2030
|
-
import
|
|
2521
|
+
import {
|
|
2522
|
+
ServiceProvider,
|
|
2523
|
+
type Container,
|
|
2524
|
+
type PlanetCore,
|
|
2525
|
+
bodySizeLimit,
|
|
2526
|
+
securityHeaders,
|
|
2527
|
+
} from '@gravito/core'
|
|
2031
2528
|
import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
|
|
2032
2529
|
import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
|
|
2033
2530
|
|
|
2531
|
+
/**
|
|
2532
|
+
* Middleware Provider - Global middleware registration
|
|
2533
|
+
*/
|
|
2534
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
2535
|
+
register(_container: Container): void {}
|
|
2536
|
+
|
|
2537
|
+
boot(core: PlanetCore): void {
|
|
2538
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
2539
|
+
|
|
2540
|
+
core.adapter.use('*', securityHeaders({
|
|
2541
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
2542
|
+
}))
|
|
2543
|
+
|
|
2544
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
|
|
2545
|
+
|
|
2546
|
+
core.logger.info('\u{1F6E1}\uFE0F Global middleware registered')
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2034
2550
|
export async function registerProviders(core: PlanetCore): Promise<void> {
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2551
|
+
// Global Providers
|
|
2552
|
+
core.register(new MiddlewareProvider())
|
|
2553
|
+
|
|
2554
|
+
// Module Providers
|
|
2555
|
+
core.register(new OrderingServiceProvider())
|
|
2556
|
+
core.register(new CatalogServiceProvider())
|
|
2038
2557
|
|
|
2039
|
-
|
|
2558
|
+
// Add more providers as needed
|
|
2040
2559
|
}
|
|
2041
2560
|
`;
|
|
2042
2561
|
}
|
|
@@ -2150,6 +2669,10 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
2150
2669
|
start: "bun run dist/main.js",
|
|
2151
2670
|
test: "bun test",
|
|
2152
2671
|
typecheck: "tsc --noEmit",
|
|
2672
|
+
check: "bun run typecheck && bun run test",
|
|
2673
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
2674
|
+
validate: "bun run check && bun run check:deps",
|
|
2675
|
+
precommit: "bun run validate",
|
|
2153
2676
|
"docker:build": `docker build -t ${context.nameKebabCase} .`,
|
|
2154
2677
|
"docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
|
|
2155
2678
|
},
|
|
@@ -2590,6 +3113,16 @@ export function report(error: unknown): void {
|
|
|
2590
3113
|
|
|
2591
3114
|
This project follows **Domain-Driven Design (DDD)** with strategic and tactical patterns.
|
|
2592
3115
|
|
|
3116
|
+
## Service Providers
|
|
3117
|
+
|
|
3118
|
+
Service providers are the central place to configure your application and modules. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
3119
|
+
|
|
3120
|
+
### Internal Bootstrapping
|
|
3121
|
+
|
|
3122
|
+
1. **Bootstrap/app.ts**: Orchestrates the 4-step lifecycle (Configure, Boot, Register, Bootstrap).
|
|
3123
|
+
2. **Bootstrap/providers.ts**: Central registry for all global and module-specific providers.
|
|
3124
|
+
3. **Infrastructure/Providers/[Module]ServiceProvider.ts**: Module-specific service registration.
|
|
3125
|
+
|
|
2593
3126
|
## Bounded Contexts
|
|
2594
3127
|
|
|
2595
3128
|
\`\`\`
|
|
@@ -2727,6 +3260,11 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
|
|
|
2727
3260
|
type: "directory",
|
|
2728
3261
|
name: "Providers",
|
|
2729
3262
|
children: [
|
|
3263
|
+
{
|
|
3264
|
+
type: "file",
|
|
3265
|
+
name: "index.ts",
|
|
3266
|
+
content: this.generateProvidersIndex()
|
|
3267
|
+
},
|
|
2730
3268
|
{
|
|
2731
3269
|
type: "file",
|
|
2732
3270
|
name: "AppServiceProvider.ts",
|
|
@@ -2734,8 +3272,18 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
|
|
|
2734
3272
|
},
|
|
2735
3273
|
{
|
|
2736
3274
|
type: "file",
|
|
2737
|
-
name: "
|
|
2738
|
-
content: this.
|
|
3275
|
+
name: "DatabaseProvider.ts",
|
|
3276
|
+
content: this.generateDatabaseProvider()
|
|
3277
|
+
},
|
|
3278
|
+
{
|
|
3279
|
+
type: "file",
|
|
3280
|
+
name: "MiddlewareProvider.ts",
|
|
3281
|
+
content: this.generateMiddlewareProvider()
|
|
3282
|
+
},
|
|
3283
|
+
{
|
|
3284
|
+
type: "file",
|
|
3285
|
+
name: "RouteProvider.ts",
|
|
3286
|
+
content: this.generateRouteProvider()
|
|
2739
3287
|
}
|
|
2740
3288
|
]
|
|
2741
3289
|
},
|
|
@@ -3202,6 +3750,135 @@ export class RouteServiceProvider extends ServiceProvider {
|
|
|
3202
3750
|
registerRoutes(core.router)
|
|
3203
3751
|
}
|
|
3204
3752
|
}
|
|
3753
|
+
`;
|
|
3754
|
+
}
|
|
3755
|
+
// ─────────────────────────────────────────────────────────────
|
|
3756
|
+
// Modern Provider Generators (ServiceProvider Pattern)
|
|
3757
|
+
// ─────────────────────────────────────────────────────────────
|
|
3758
|
+
generateProvidersIndex() {
|
|
3759
|
+
return `/**
|
|
3760
|
+
* Application Service Providers
|
|
3761
|
+
*
|
|
3762
|
+
* Export all providers for easy importing in bootstrap.
|
|
3763
|
+
* Providers are registered in the order they are listed.
|
|
3764
|
+
*/
|
|
3765
|
+
|
|
3766
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
3767
|
+
export { DatabaseProvider } from './DatabaseProvider'
|
|
3768
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
3769
|
+
export { RouteProvider } from './RouteProvider'
|
|
3770
|
+
`;
|
|
3771
|
+
}
|
|
3772
|
+
generateDatabaseProvider() {
|
|
3773
|
+
return `/**
|
|
3774
|
+
* Database Service Provider
|
|
3775
|
+
*
|
|
3776
|
+
* Handles database initialization and migrations.
|
|
3777
|
+
*
|
|
3778
|
+
* Lifecycle:
|
|
3779
|
+
* - register(): Bind database config to container
|
|
3780
|
+
* - boot(): Run migrations and seeders
|
|
3781
|
+
*/
|
|
3782
|
+
|
|
3783
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
3784
|
+
import databaseConfig from '../../config/database'
|
|
3785
|
+
|
|
3786
|
+
export class DatabaseProvider extends ServiceProvider {
|
|
3787
|
+
/**
|
|
3788
|
+
* Register database configuration.
|
|
3789
|
+
*/
|
|
3790
|
+
register(_container: Container): void {
|
|
3791
|
+
this.mergeConfig(this.core!.config, 'database', databaseConfig)
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3794
|
+
/**
|
|
3795
|
+
* Initialize database connections.
|
|
3796
|
+
*/
|
|
3797
|
+
async boot(core: PlanetCore): Promise<void> {
|
|
3798
|
+
// Database initialization will be handled by Atlas orbit
|
|
3799
|
+
core.logger.info('\u{1F4E6} Database provider booted')
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
`;
|
|
3803
|
+
}
|
|
3804
|
+
generateMiddlewareProvider() {
|
|
3805
|
+
return `/**
|
|
3806
|
+
* Middleware Service Provider
|
|
3807
|
+
*
|
|
3808
|
+
* Registers global middleware stack.
|
|
3809
|
+
* Provides a centralized location for middleware configuration.
|
|
3810
|
+
*
|
|
3811
|
+
* Lifecycle:
|
|
3812
|
+
* - register(): N/A (no container bindings)
|
|
3813
|
+
* - boot(): Register global middleware
|
|
3814
|
+
*/
|
|
3815
|
+
|
|
3816
|
+
import {
|
|
3817
|
+
ServiceProvider,
|
|
3818
|
+
type Container,
|
|
3819
|
+
type PlanetCore,
|
|
3820
|
+
bodySizeLimit,
|
|
3821
|
+
securityHeaders,
|
|
3822
|
+
} from '@gravito/core'
|
|
3823
|
+
|
|
3824
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
3825
|
+
/**
|
|
3826
|
+
* No container bindings needed.
|
|
3827
|
+
*/
|
|
3828
|
+
register(_container: Container): void {
|
|
3829
|
+
// Middleware doesn't require container bindings
|
|
3830
|
+
}
|
|
3831
|
+
|
|
3832
|
+
/**
|
|
3833
|
+
* Register global middleware stack.
|
|
3834
|
+
*/
|
|
3835
|
+
boot(core: PlanetCore): void {
|
|
3836
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
3837
|
+
|
|
3838
|
+
// Security Headers
|
|
3839
|
+
core.adapter.use('*', securityHeaders({
|
|
3840
|
+
contentSecurityPolicy: isDev ? false : undefined,
|
|
3841
|
+
}))
|
|
3842
|
+
|
|
3843
|
+
// Body Parser Limits
|
|
3844
|
+
core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024)) // 10MB limit
|
|
3845
|
+
|
|
3846
|
+
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
`;
|
|
3850
|
+
}
|
|
3851
|
+
generateRouteProvider() {
|
|
3852
|
+
return `/**
|
|
3853
|
+
* Route Service Provider
|
|
3854
|
+
*
|
|
3855
|
+
* Registers application routes.
|
|
3856
|
+
* Routes are registered in the boot phase after all services are available.
|
|
3857
|
+
*
|
|
3858
|
+
* Lifecycle:
|
|
3859
|
+
* - register(): N/A
|
|
3860
|
+
* - boot(): Register routes
|
|
3861
|
+
*/
|
|
3862
|
+
|
|
3863
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
3864
|
+
import { registerRoutes } from '../routes'
|
|
3865
|
+
|
|
3866
|
+
export class RouteProvider extends ServiceProvider {
|
|
3867
|
+
/**
|
|
3868
|
+
* No container bindings needed.
|
|
3869
|
+
*/
|
|
3870
|
+
register(_container: Container): void {
|
|
3871
|
+
// Routes don't require container bindings
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
/**
|
|
3875
|
+
* Register application routes.
|
|
3876
|
+
*/
|
|
3877
|
+
boot(core: PlanetCore): void {
|
|
3878
|
+
registerRoutes(core.router)
|
|
3879
|
+
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3205
3882
|
`;
|
|
3206
3883
|
}
|
|
3207
3884
|
generateExceptionHandler() {
|
|
@@ -3250,74 +3927,75 @@ export const dontReport: string[] = [
|
|
|
3250
3927
|
}
|
|
3251
3928
|
generateBootstrap(context) {
|
|
3252
3929
|
const spectrumImport = context.withSpectrum ? "import { SpectrumOrbit } from '@gravito/spectrum'\n" : "";
|
|
3253
|
-
const spectrumOrbit = context.withSpectrum ?
|
|
3254
|
-
// Enable Debug Dashboard
|
|
3255
|
-
if (process.env.APP_DEBUG === 'true') {
|
|
3256
|
-
await core.orbit(new SpectrumOrbit())
|
|
3257
|
-
}
|
|
3258
|
-
` : "";
|
|
3930
|
+
const spectrumOrbit = context.withSpectrum ? " new SpectrumOrbit()," : "";
|
|
3259
3931
|
return `/**
|
|
3260
3932
|
* Application Bootstrap
|
|
3261
3933
|
*
|
|
3262
|
-
*
|
|
3263
|
-
*
|
|
3934
|
+
* The entry point for your Gravito application.
|
|
3935
|
+
* Uses the ServiceProvider pattern for modular, maintainable initialization.
|
|
3936
|
+
*
|
|
3937
|
+
* Lifecycle:
|
|
3938
|
+
* 1. Configure: Load app config and orbits
|
|
3939
|
+
* 2. Boot: Initialize PlanetCore
|
|
3940
|
+
* 3. Register Providers: Bind services to container
|
|
3941
|
+
* 4. Bootstrap: Boot all providers
|
|
3942
|
+
*
|
|
3943
|
+
* @module bootstrap
|
|
3264
3944
|
*/
|
|
3265
3945
|
|
|
3266
|
-
import {
|
|
3946
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
3267
3947
|
import { OrbitAtlas } from '@gravito/atlas'
|
|
3268
|
-
import
|
|
3269
|
-
${spectrumImport}import {
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
// Create application core
|
|
3276
|
-
const core = new PlanetCore({
|
|
3277
|
-
config: {
|
|
3278
|
-
APP_NAME: process.env.APP_NAME ?? '${context.name}',
|
|
3279
|
-
database: databaseConfig,
|
|
3280
|
-
},
|
|
3281
|
-
})
|
|
3282
|
-
const defaultCsp = [
|
|
3283
|
-
"default-src 'self'",
|
|
3284
|
-
"script-src 'self' 'unsafe-inline'",
|
|
3285
|
-
"style-src 'self' 'unsafe-inline'",
|
|
3286
|
-
"img-src 'self' data:",
|
|
3287
|
-
"object-src 'none'",
|
|
3288
|
-
"base-uri 'self'",
|
|
3289
|
-
"frame-ancestors 'none'",
|
|
3290
|
-
].join('; ')
|
|
3291
|
-
const cspValue = process.env.APP_CSP
|
|
3292
|
-
const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
|
|
3293
|
-
const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
|
|
3294
|
-
const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
|
|
3295
|
-
const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
|
|
3296
|
-
|
|
3297
|
-
core.adapter.use(
|
|
3298
|
-
'*',
|
|
3299
|
-
securityHeaders({
|
|
3300
|
-
contentSecurityPolicy: csp,
|
|
3301
|
-
hsts:
|
|
3302
|
-
process.env.NODE_ENV === 'production'
|
|
3303
|
-
? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
|
|
3304
|
-
: false,
|
|
3305
|
-
})
|
|
3306
|
-
)
|
|
3307
|
-
if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
|
|
3308
|
-
core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
|
|
3309
|
-
}
|
|
3948
|
+
import appConfig from '../config/app'
|
|
3949
|
+
${spectrumImport}import {
|
|
3950
|
+
AppServiceProvider,
|
|
3951
|
+
DatabaseProvider,
|
|
3952
|
+
MiddlewareProvider,
|
|
3953
|
+
RouteProvider,
|
|
3954
|
+
} from './Providers'
|
|
3310
3955
|
|
|
3311
|
-
|
|
3956
|
+
/**
|
|
3957
|
+
* Bootstrap the application with service providers.
|
|
3958
|
+
*
|
|
3959
|
+
* @returns The booted PlanetCore instance
|
|
3960
|
+
*/
|
|
3961
|
+
export async function bootstrap() {
|
|
3962
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3963
|
+
// 1. Configure
|
|
3964
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3965
|
+
const config = defineConfig({
|
|
3966
|
+
config: appConfig,
|
|
3967
|
+
orbits: [
|
|
3968
|
+
new OrbitAtlas(),
|
|
3312
3969
|
${spectrumOrbit}
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
core.register(new RouteServiceProvider())
|
|
3970
|
+
],
|
|
3971
|
+
})
|
|
3316
3972
|
|
|
3317
|
-
//
|
|
3318
|
-
|
|
3973
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3974
|
+
// 2. Boot Core
|
|
3975
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3976
|
+
const core = await PlanetCore.boot(config)
|
|
3977
|
+
core.registerGlobalErrorHandlers()
|
|
3978
|
+
|
|
3979
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3980
|
+
// 3. Register Providers
|
|
3981
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3982
|
+
core.register(new AppServiceProvider())
|
|
3983
|
+
core.register(new DatabaseProvider())
|
|
3984
|
+
core.register(new MiddlewareProvider())
|
|
3985
|
+
core.register(new RouteProvider())
|
|
3986
|
+
|
|
3987
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3988
|
+
// 4. Bootstrap All Providers
|
|
3989
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3990
|
+
await core.bootstrap()
|
|
3991
|
+
|
|
3992
|
+
return core
|
|
3993
|
+
}
|
|
3319
3994
|
|
|
3320
|
-
//
|
|
3995
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3996
|
+
// Application Entry Point
|
|
3997
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3998
|
+
const core = await bootstrap()
|
|
3321
3999
|
export default core.liftoff()
|
|
3322
4000
|
`;
|
|
3323
4001
|
}
|
|
@@ -3744,7 +4422,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
3744
4422
|
scripts: {
|
|
3745
4423
|
build: "tsup src/index.ts --format cjs,esm --dts",
|
|
3746
4424
|
test: "bun test",
|
|
3747
|
-
typecheck: "tsc --noEmit"
|
|
4425
|
+
typecheck: "tsc --noEmit",
|
|
4426
|
+
check: "bun run typecheck && bun run test",
|
|
4427
|
+
validate: "bun run check"
|
|
3748
4428
|
},
|
|
3749
4429
|
dependencies: {
|
|
3750
4430
|
"@gravito/core": depVersion,
|
|
@@ -4042,10 +4722,25 @@ var ActionDomainGenerator = class extends BaseGenerator {
|
|
|
4042
4722
|
type: "directory",
|
|
4043
4723
|
name: "providers",
|
|
4044
4724
|
children: [
|
|
4725
|
+
{
|
|
4726
|
+
type: "file",
|
|
4727
|
+
name: "index.ts",
|
|
4728
|
+
content: this.generateProvidersIndex()
|
|
4729
|
+
},
|
|
4045
4730
|
{
|
|
4046
4731
|
type: "file",
|
|
4047
4732
|
name: "AppServiceProvider.ts",
|
|
4048
4733
|
content: this.generateAppServiceProvider(context)
|
|
4734
|
+
},
|
|
4735
|
+
{
|
|
4736
|
+
type: "file",
|
|
4737
|
+
name: "MiddlewareProvider.ts",
|
|
4738
|
+
content: this.generateMiddlewareProvider()
|
|
4739
|
+
},
|
|
4740
|
+
{
|
|
4741
|
+
type: "file",
|
|
4742
|
+
name: "RouteProvider.ts",
|
|
4743
|
+
content: this.generateRouteProvider()
|
|
4049
4744
|
}
|
|
4050
4745
|
]
|
|
4051
4746
|
},
|
|
@@ -4223,7 +4918,7 @@ export function registerApiRoutes(router: Router) {
|
|
|
4223
4918
|
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
4224
4919
|
|
|
4225
4920
|
export class AppServiceProvider extends ServiceProvider {
|
|
4226
|
-
register(
|
|
4921
|
+
register(_container: Container): void {
|
|
4227
4922
|
// Register global services here
|
|
4228
4923
|
}
|
|
4229
4924
|
|
|
@@ -4233,47 +4928,140 @@ export class AppServiceProvider extends ServiceProvider {
|
|
|
4233
4928
|
}
|
|
4234
4929
|
`;
|
|
4235
4930
|
}
|
|
4236
|
-
|
|
4931
|
+
generateProvidersIndex() {
|
|
4237
4932
|
return `/**
|
|
4238
|
-
* Application
|
|
4933
|
+
* Application Service Providers
|
|
4239
4934
|
*/
|
|
4240
4935
|
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
APP_NAME: '${context.name}',
|
|
4251
|
-
database: databaseConfig
|
|
4252
|
-
},
|
|
4253
|
-
})
|
|
4936
|
+
export { AppServiceProvider } from './AppServiceProvider'
|
|
4937
|
+
export { MiddlewareProvider } from './MiddlewareProvider'
|
|
4938
|
+
export { RouteProvider } from './RouteProvider'
|
|
4939
|
+
`;
|
|
4940
|
+
}
|
|
4941
|
+
generateMiddlewareProvider() {
|
|
4942
|
+
return `/**
|
|
4943
|
+
* Middleware Service Provider
|
|
4944
|
+
*/
|
|
4254
4945
|
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4946
|
+
import {
|
|
4947
|
+
ServiceProvider,
|
|
4948
|
+
type Container,
|
|
4949
|
+
type PlanetCore,
|
|
4950
|
+
bodySizeLimit,
|
|
4951
|
+
securityHeaders,
|
|
4952
|
+
} from '@gravito/core'
|
|
4258
4953
|
|
|
4259
|
-
|
|
4260
|
-
|
|
4954
|
+
export class MiddlewareProvider extends ServiceProvider {
|
|
4955
|
+
register(_container: Container): void {}
|
|
4261
4956
|
|
|
4262
|
-
|
|
4263
|
-
core.
|
|
4957
|
+
boot(core: PlanetCore): void {
|
|
4958
|
+
core.adapter.use('*', securityHeaders())
|
|
4959
|
+
core.adapter.use('*', bodySizeLimit(1024 * 1024))
|
|
4960
|
+
core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
`;
|
|
4964
|
+
}
|
|
4965
|
+
generateRouteProvider() {
|
|
4966
|
+
return `/**
|
|
4967
|
+
* Route Service Provider
|
|
4968
|
+
*/
|
|
4969
|
+
|
|
4970
|
+
import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
|
|
4971
|
+
import { registerApiRoutes } from '../routes/api'
|
|
4972
|
+
|
|
4973
|
+
export class RouteProvider extends ServiceProvider {
|
|
4974
|
+
register(_container: Container): void {}
|
|
4975
|
+
|
|
4976
|
+
boot(core: PlanetCore): void {
|
|
4977
|
+
registerApiRoutes(core.router)
|
|
4978
|
+
core.logger.info('\u{1F6E4}\uFE0F Routes registered')
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
`;
|
|
4982
|
+
}
|
|
4983
|
+
generateBootstrap(_context) {
|
|
4984
|
+
return `/**
|
|
4985
|
+
* Application Bootstrap
|
|
4986
|
+
*
|
|
4987
|
+
* Uses the ServiceProvider pattern for modular initialization.
|
|
4988
|
+
*/
|
|
4989
|
+
|
|
4990
|
+
import { defineConfig, PlanetCore } from '@gravito/core'
|
|
4991
|
+
import { OrbitAtlas } from '@gravito/atlas'
|
|
4992
|
+
import appConfig from '../config/app'
|
|
4993
|
+
import {
|
|
4994
|
+
AppServiceProvider,
|
|
4995
|
+
MiddlewareProvider,
|
|
4996
|
+
RouteProvider,
|
|
4997
|
+
} from './providers'
|
|
4998
|
+
|
|
4999
|
+
export async function bootstrap() {
|
|
5000
|
+
const config = defineConfig({
|
|
5001
|
+
config: appConfig,
|
|
5002
|
+
orbits: [new OrbitAtlas()],
|
|
5003
|
+
})
|
|
5004
|
+
|
|
5005
|
+
const core = await PlanetCore.boot(config)
|
|
5006
|
+
core.registerGlobalErrorHandlers()
|
|
4264
5007
|
|
|
4265
|
-
|
|
4266
|
-
|
|
5008
|
+
core.register(new AppServiceProvider())
|
|
5009
|
+
core.register(new MiddlewareProvider())
|
|
5010
|
+
core.register(new RouteProvider())
|
|
4267
5011
|
|
|
4268
|
-
|
|
4269
|
-
registerApiRoutes(core.router)
|
|
5012
|
+
await core.bootstrap()
|
|
4270
5013
|
|
|
4271
|
-
|
|
5014
|
+
return core
|
|
5015
|
+
}
|
|
5016
|
+
|
|
5017
|
+
const core = await bootstrap()
|
|
4272
5018
|
export default core.liftoff()
|
|
4273
5019
|
`;
|
|
4274
5020
|
}
|
|
4275
5021
|
generateArchitectureDoc(context) {
|
|
4276
|
-
return
|
|
5022
|
+
return `# ${context.name} - Action Domain Architecture
|
|
5023
|
+
|
|
5024
|
+
## Overview
|
|
5025
|
+
|
|
5026
|
+
This project uses the **Action Domain** pattern, designed for high-clarity API implementations.
|
|
5027
|
+
|
|
5028
|
+
## Service Providers
|
|
5029
|
+
|
|
5030
|
+
Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
|
|
5031
|
+
|
|
5032
|
+
## Directory Structure
|
|
5033
|
+
|
|
5034
|
+
\`\`\`
|
|
5035
|
+
src/
|
|
5036
|
+
\u251C\u2500\u2500 actions/ # Single Responsibility Business Logic
|
|
5037
|
+
\u2502 \u251C\u2500\u2500 Action.ts # Base Action class
|
|
5038
|
+
\u2502 \u2514\u2500\u2500 [Domain]/ # Domain-specific actions
|
|
5039
|
+
\u251C\u2500\u2500 controllers/ # HTTP Request Handlers
|
|
5040
|
+
\u2502 \u2514\u2500\u2500 api/v1/ # API Controllers
|
|
5041
|
+
\u251C\u2500\u2500 types/ # TypeScript Definitions
|
|
5042
|
+
\u251C\u2500\u2500 repositories/ # Data Access Layer
|
|
5043
|
+
\u251C\u2500\u2500 routes/ # Route Definitions
|
|
5044
|
+
\u251C\u2500\u2500 providers/ # Service Providers
|
|
5045
|
+
\u2514\u2500\u2500 config/ # Configuration
|
|
5046
|
+
\`\`\`
|
|
5047
|
+
|
|
5048
|
+
## Core Concepts
|
|
5049
|
+
|
|
5050
|
+
### Actions
|
|
5051
|
+
Every business operation is an "Action". An action:
|
|
5052
|
+
- Does ONE thing.
|
|
5053
|
+
- Takes specific input.
|
|
5054
|
+
- Returns specific output.
|
|
5055
|
+
- Is framework-agnostic (ideally).
|
|
5056
|
+
|
|
5057
|
+
### Controllers
|
|
5058
|
+
Controllers are thin. They:
|
|
5059
|
+
1. Parse the request.
|
|
5060
|
+
2. Instantiate/Call the Action.
|
|
5061
|
+
3. Return the response.
|
|
5062
|
+
|
|
5063
|
+
Created with \u2764\uFE0F using Gravito Framework
|
|
5064
|
+
`;
|
|
4277
5065
|
}
|
|
4278
5066
|
generatePackageJson(context) {
|
|
4279
5067
|
const pkg = {
|
|
@@ -4285,7 +5073,11 @@ export default core.liftoff()
|
|
|
4285
5073
|
build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
|
|
4286
5074
|
start: "bun run dist/bootstrap.js",
|
|
4287
5075
|
test: "bun test",
|
|
4288
|
-
typecheck: "tsc --noEmit"
|
|
5076
|
+
typecheck: "tsc --noEmit",
|
|
5077
|
+
check: "bun run typecheck && bun run test",
|
|
5078
|
+
"check:deps": "bun run scripts/check-dependencies.ts",
|
|
5079
|
+
validate: "bun run check && bun run check:deps",
|
|
5080
|
+
precommit: "bun run validate"
|
|
4289
5081
|
},
|
|
4290
5082
|
dependencies: {
|
|
4291
5083
|
"@gravito/core": "workspace:*",
|