@gravito/scaffold 1.0.0 → 2.0.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.
Files changed (84) hide show
  1. package/dist/index.cjs +1119 -187
  2. package/dist/index.d.cts +32 -1
  3. package/dist/index.d.ts +32 -1
  4. package/dist/index.js +1119 -187
  5. package/package.json +1 -1
  6. package/templates/skills/SKILLS_USAGE.md +54 -0
  7. package/templates/skills/adr-scaffold/SKILL.md +67 -0
  8. package/templates/skills/adr-scaffold/assets/.keep +0 -0
  9. package/templates/skills/adr-scaffold/references/.keep +0 -0
  10. package/templates/skills/adr-scaffold/references/adr-rules.md +29 -0
  11. package/templates/skills/adr-scaffold/scripts/.keep +0 -0
  12. package/templates/skills/architecture-refiner/SKILL.md +28 -0
  13. package/templates/skills/architecture-refiner/assets/.keep +0 -0
  14. package/templates/skills/architecture-refiner/references/.keep +0 -0
  15. package/templates/skills/architecture-refiner/scripts/.keep +0 -0
  16. package/templates/skills/atlas-expert/SKILL.md +34 -0
  17. package/templates/skills/atlas-expert/assets/.keep +0 -0
  18. package/templates/skills/atlas-expert/references/.keep +0 -0
  19. package/templates/skills/atlas-expert/references/decorators.md +24 -0
  20. package/templates/skills/atlas-expert/scripts/.keep +0 -0
  21. package/templates/skills/clean-architect/SKILL.md +63 -0
  22. package/templates/skills/clean-architect/assets/.keep +0 -0
  23. package/templates/skills/clean-architect/references/.keep +0 -0
  24. package/templates/skills/clean-architect/scripts/.keep +0 -0
  25. package/templates/skills/cms-engine/SKILL.md +54 -0
  26. package/templates/skills/cms-engine/assets/.keep +0 -0
  27. package/templates/skills/cms-engine/references/.keep +0 -0
  28. package/templates/skills/cms-engine/scripts/.keep +0 -0
  29. package/templates/skills/commerce-blueprint/SKILL.md +53 -0
  30. package/templates/skills/commerce-blueprint/assets/.keep +0 -0
  31. package/templates/skills/commerce-blueprint/references/.keep +0 -0
  32. package/templates/skills/commerce-blueprint/scripts/.keep +0 -0
  33. package/templates/skills/ddd-domain-expert/SKILL.md +71 -0
  34. package/templates/skills/ddd-domain-expert/assets/.keep +0 -0
  35. package/templates/skills/ddd-domain-expert/references/.keep +0 -0
  36. package/templates/skills/ddd-domain-expert/scripts/.keep +0 -0
  37. package/templates/skills/fortify-security/SKILL.md +28 -0
  38. package/templates/skills/fortify-security/assets/.keep +0 -0
  39. package/templates/skills/fortify-security/references/.keep +0 -0
  40. package/templates/skills/fortify-security/scripts/.keep +0 -0
  41. package/templates/skills/freeze-static/SKILL.md +55 -0
  42. package/templates/skills/freeze-static/assets/.keep +0 -0
  43. package/templates/skills/freeze-static/references/.keep +0 -0
  44. package/templates/skills/freeze-static/scripts/.keep +0 -0
  45. package/templates/skills/identity-hub/SKILL.md +51 -0
  46. package/templates/skills/identity-hub/assets/.keep +0 -0
  47. package/templates/skills/identity-hub/references/.keep +0 -0
  48. package/templates/skills/identity-hub/scripts/.keep +0 -0
  49. package/templates/skills/localization-linguist/SKILL.md +28 -0
  50. package/templates/skills/localization-linguist/assets/.keep +0 -0
  51. package/templates/skills/localization-linguist/references/.keep +0 -0
  52. package/templates/skills/localization-linguist/scripts/.keep +0 -0
  53. package/templates/skills/migration-master/SKILL.md +28 -0
  54. package/templates/skills/migration-master/assets/.keep +0 -0
  55. package/templates/skills/migration-master/references/.keep +0 -0
  56. package/templates/skills/migration-master/scripts/.keep +0 -0
  57. package/templates/skills/mvc-master/SKILL.md +88 -0
  58. package/templates/skills/mvc-master/assets/.keep +0 -0
  59. package/templates/skills/mvc-master/references/.keep +0 -0
  60. package/templates/skills/mvc-master/scripts/.keep +0 -0
  61. package/templates/skills/ops-commander/SKILL.md +28 -0
  62. package/templates/skills/ops-commander/assets/.keep +0 -0
  63. package/templates/skills/ops-commander/references/.keep +0 -0
  64. package/templates/skills/ops-commander/scripts/.keep +0 -0
  65. package/templates/skills/performance-tuner/SKILL.md +28 -0
  66. package/templates/skills/performance-tuner/assets/.keep +0 -0
  67. package/templates/skills/performance-tuner/references/.keep +0 -0
  68. package/templates/skills/performance-tuner/scripts/.keep +0 -0
  69. package/templates/skills/quasar-queue/SKILL.md +28 -0
  70. package/templates/skills/quasar-queue/assets/.keep +0 -0
  71. package/templates/skills/quasar-queue/references/.keep +0 -0
  72. package/templates/skills/quasar-queue/scripts/.keep +0 -0
  73. package/templates/skills/satellites-pilot/SKILL.md +27 -0
  74. package/templates/skills/satellites-pilot/assets/.keep +0 -0
  75. package/templates/skills/satellites-pilot/references/.keep +0 -0
  76. package/templates/skills/satellites-pilot/scripts/.keep +0 -0
  77. package/templates/skills/test-guardian/SKILL.md +28 -0
  78. package/templates/skills/test-guardian/assets/.keep +0 -0
  79. package/templates/skills/test-guardian/references/.keep +0 -0
  80. package/templates/skills/test-guardian/scripts/.keep +0 -0
  81. package/templates/skills/zenith-ui/SKILL.md +29 -0
  82. package/templates/skills/zenith-ui/assets/.keep +0 -0
  83. package/templates/skills/zenith-ui/references/.keep +0 -0
  84. package/templates/skills/zenith-ui/scripts/.keep +0 -0
package/dist/index.cjs CHANGED
@@ -392,6 +392,31 @@ var BaseGenerator = class {
392
392
  "ARCHITECTURE.md",
393
393
  this.generateArchitectureDoc(context)
394
394
  );
395
+ await this.generateCheckScripts(context);
396
+ await this.generateSkills(context);
397
+ }
398
+ /**
399
+ * Copy AI Skills to the project
400
+ */
401
+ async generateSkills(context) {
402
+ const skillsDir = import_node_path2.default.resolve(this.config.templatesDir, "skills");
403
+ const targetSkillsDir = import_node_path2.default.join(".skills");
404
+ try {
405
+ await import_promises2.default.access(skillsDir);
406
+ } catch {
407
+ return;
408
+ }
409
+ const files = await walk(skillsDir);
410
+ for (const filePath of files) {
411
+ const relativePath = import_node_path2.default.relative(skillsDir, filePath);
412
+ const targetPath = import_node_path2.default.join(targetSkillsDir, relativePath);
413
+ let content = await import_promises2.default.readFile(filePath, "utf-8");
414
+ try {
415
+ content = this.stubGenerator.render(content, context);
416
+ } catch {
417
+ }
418
+ await this.writeFile(context.targetDir, targetPath, content);
419
+ }
395
420
  }
396
421
  /**
397
422
  * Apply profile-specific overlays
@@ -479,6 +504,10 @@ var BaseGenerator = class {
479
504
  start: "bun run dist/bootstrap.js",
480
505
  test: "bun test",
481
506
  typecheck: "tsc --noEmit",
507
+ check: "bun run typecheck && bun run test",
508
+ "check:deps": "bun run scripts/check-dependencies.ts",
509
+ validate: "bun run check && bun run check:deps",
510
+ precommit: "bun run validate",
482
511
  "docker:build": `docker build -t ${context.nameKebabCase} .`,
483
512
  "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
484
513
  },
@@ -731,6 +760,430 @@ coverage/
731
760
  };
732
761
  return JSON.stringify(config, null, 2);
733
762
  }
763
+ /**
764
+ * Generate check scripts for project validation.
765
+ */
766
+ async generateCheckScripts(context) {
767
+ const scriptsDir = import_node_path2.default.resolve(context.targetDir, "scripts");
768
+ await import_promises2.default.mkdir(scriptsDir, { recursive: true });
769
+ await this.writeFile(
770
+ scriptsDir,
771
+ "check-dependencies.ts",
772
+ this.generateCheckDependenciesScript()
773
+ );
774
+ await this.writeFile(scriptsDir, "check.sh", this.generateCheckShellScript());
775
+ await this.writeFile(scriptsDir, "pre-commit.sh", this.generatePreCommitScript());
776
+ await this.writeFile(context.targetDir, "CHECK_SYSTEM.md", this.generateCheckSystemDoc(context));
777
+ }
778
+ /**
779
+ * Generate check-dependencies.ts script content.
780
+ */
781
+ generateCheckDependenciesScript() {
782
+ return `/**
783
+ * \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5\u8173\u672C
784
+ *
785
+ * \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
786
+ * \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
787
+ */
788
+
789
+ import { readFileSync } from 'fs'
790
+ import { join } from 'path'
791
+
792
+ interface PackageJson {
793
+ dependencies?: Record<string, string>
794
+ devDependencies?: Record<string, string>
795
+ }
796
+
797
+ interface PackageInfo {
798
+ name: string
799
+ current: string
800
+ latest: string
801
+ outdated: boolean
802
+ }
803
+
804
+ const colors = {
805
+ reset: '\\x1b[0m',
806
+ green: '\\x1b[32m',
807
+ yellow: '\\x1b[33m',
808
+ red: '\\x1b[31m',
809
+ blue: '\\x1b[36m',
810
+ }
811
+
812
+ function log(message: string, color: keyof typeof colors = 'reset') {
813
+ console.log(\`\${colors[color]}\${message}\${colors.reset}\`)
814
+ }
815
+
816
+ async function getLatestVersion(packageName: string): Promise<string | null> {
817
+ try {
818
+ const response = await fetch(\`https://registry.npmjs.org/\${packageName}/latest\`)
819
+ if (!response.ok) return null
820
+ const data = await response.json()
821
+ return data.version
822
+ } catch {
823
+ return null
824
+ }
825
+ }
826
+
827
+ function parseVersion(version: string): string {
828
+ // \u79FB\u9664 ^, ~, >= \u7B49\u524D\u7DB4
829
+ return version.replace(/^[\\^~>=<]/, '')
830
+ }
831
+
832
+ async function checkPackage(
833
+ name: string,
834
+ currentVersion: string
835
+ ): Promise<PackageInfo | null> {
836
+ // \u8DF3\u904E\u672C\u5730\u9023\u7D50\u7684\u5957\u4EF6
837
+ if (currentVersion.startsWith('link:') || currentVersion.startsWith('workspace:')) {
838
+ return null
839
+ }
840
+
841
+ const current = parseVersion(currentVersion)
842
+ const latest = await getLatestVersion(name)
843
+
844
+ if (!latest) {
845
+ return null
846
+ }
847
+
848
+ return {
849
+ name,
850
+ current,
851
+ latest,
852
+ outdated: current !== latest,
853
+ }
854
+ }
855
+
856
+ async function main() {
857
+ log('\\n=== \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5 ===\\n', 'blue')
858
+
859
+ const packageJsonPath = join(process.cwd(), 'package.json')
860
+ const packageJson: PackageJson = JSON.parse(
861
+ readFileSync(packageJsonPath, 'utf-8')
862
+ )
863
+
864
+ const allDependencies = {
865
+ ...packageJson.dependencies,
866
+ ...packageJson.devDependencies,
867
+ }
868
+
869
+ log(\`\u6AA2\u67E5 \${Object.keys(allDependencies).length} \u500B\u5957\u4EF6...\\n\`, 'yellow')
870
+
871
+ const results: PackageInfo[] = []
872
+ const outdated: PackageInfo[] = []
873
+ const upToDate: PackageInfo[] = []
874
+
875
+ // \u6AA2\u67E5\u6240\u6709\u5957\u4EF6
876
+ for (const [name, version] of Object.entries(allDependencies)) {
877
+ const info = await checkPackage(name, version)
878
+ if (info) {
879
+ results.push(info)
880
+ if (info.outdated) {
881
+ outdated.push(info)
882
+ } else {
883
+ upToDate.push(info)
884
+ }
885
+ }
886
+ }
887
+
888
+ // \u986F\u793A\u7D50\u679C
889
+ if (upToDate.length > 0) {
890
+ log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
891
+ upToDate.forEach((pkg) => {
892
+ log(\` \${pkg.name}: \${pkg.current}\`, 'green')
893
+ })
894
+ }
895
+
896
+ if (outdated.length > 0) {
897
+ log(\`\\n\u26A0 \u9700\u8981\u66F4\u65B0 (\${outdated.length}):\`, 'yellow')
898
+ outdated.forEach((pkg) => {
899
+ log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
900
+ })
901
+ }
902
+
903
+ // \u7E3D\u7D50
904
+ log('\\n=== \u6AA2\u67E5\u7D50\u679C ===', 'blue')
905
+ log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
906
+ log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
907
+ log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
908
+
909
+ // \u5982\u679C\u6709\u9700\u8981\u66F4\u65B0\u7684\u5957\u4EF6\uFF0C\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u78BC
910
+ if (outdated.length > 0) {
911
+ log('\\n\u5EFA\u8B70\u57F7\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0\u5957\u4EF6\uFF1A', 'yellow')
912
+ log(' bun update', 'yellow')
913
+ process.exit(1)
914
+ } else {
915
+ log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
916
+ process.exit(0)
917
+ }
918
+ }
919
+
920
+ main().catch((error) => {
921
+ log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
922
+ process.exit(1)
923
+ })
924
+ `;
925
+ }
926
+ /**
927
+ * Generate check.sh script content.
928
+ */
929
+ generateCheckShellScript() {
930
+ return `#!/bin/bash
931
+
932
+ # \u5C08\u6848\u6AA2\u67E5\u8173\u672C
933
+ # \u57F7\u884C\u6240\u6709\u5FC5\u8981\u7684\u6AA2\u67E5\uFF1A\u985E\u578B\u6AA2\u67E5\u3001\u6E2C\u8A66\u3001\u4F9D\u8CF4\u6AA2\u67E5\u7B49
934
+
935
+ set -e
936
+
937
+ # \u984F\u8272\u5B9A\u7FA9
938
+ GREEN='\\033[0;32m'
939
+ YELLOW='\\033[1;33m'
940
+ RED='\\033[0;31m'
941
+ BLUE='\\033[0;34m'
942
+ NC='\\033[0m' # No Color
943
+
944
+ echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
945
+
946
+ # \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
947
+ if [ ! -f "package.json" ]; then
948
+ echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
949
+ exit 1
950
+ fi
951
+
952
+ # \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
953
+ if ! command -v bun &> /dev/null; then
954
+ echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
955
+ exit 1
956
+ fi
957
+
958
+ # 1. \u985E\u578B\u6AA2\u67E5
959
+ echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
960
+ if bun run typecheck; then
961
+ echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
962
+ else
963
+ echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
964
+ exit 1
965
+ fi
966
+
967
+ # 2. \u57F7\u884C\u6E2C\u8A66
968
+ echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
969
+ if bun test; then
970
+ echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
971
+ else
972
+ echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
973
+ exit 1
974
+ fi
975
+
976
+ # 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
977
+ echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
978
+ if bun run check:deps; then
979
+ echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
980
+ else
981
+ echo -e "\${YELLOW}\u26A0 \u4F9D\u8CF4\u6AA2\u67E5\u6709\u8B66\u544A\uFF08\u67D0\u4E9B\u5957\u4EF6\u53EF\u80FD\u9700\u8981\u66F4\u65B0\uFF09\${NC}\\n"
982
+ fi
983
+
984
+ echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
985
+ `;
986
+ }
987
+ /**
988
+ * Generate pre-commit.sh script content.
989
+ */
990
+ generatePreCommitScript() {
991
+ return `#!/bin/bash
992
+
993
+ # Pre-commit Hook
994
+ # \u5728 git commit \u524D\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5
995
+ #
996
+ # \u5B89\u88DD\u65B9\u5F0F\uFF1A
997
+ # ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
998
+ # \u6216
999
+ # cp scripts/pre-commit.sh .git/hooks/pre-commit
1000
+ # chmod +x .git/hooks/pre-commit
1001
+
1002
+ set -e
1003
+
1004
+ # \u984F\u8272\u5B9A\u7FA9
1005
+ GREEN='\\033[0;32m'
1006
+ YELLOW='\\033[1;33m'
1007
+ RED='\\033[0;31m'
1008
+ BLUE='\\033[0;34m'
1009
+ NC='\\033[0m' # No Color
1010
+
1011
+ echo -e "\${BLUE}=== Pre-commit \u6AA2\u67E5 ===\${NC}\\n"
1012
+
1013
+ # \u5207\u63DB\u5230\u5C08\u6848\u6839\u76EE\u9304
1014
+ cd "$(git rev-parse --show-toplevel)"
1015
+
1016
+ # \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
1017
+ if [ ! -f "package.json" ]; then
1018
+ echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
1019
+ exit 1
1020
+ fi
1021
+
1022
+ # \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
1023
+ if ! command -v bun &> /dev/null; then
1024
+ echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
1025
+ exit 1
1026
+ fi
1027
+
1028
+ # 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
1029
+ echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
1030
+ if bun run typecheck; then
1031
+ echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
1032
+ else
1033
+ echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
1034
+ echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
1035
+ exit 1
1036
+ fi
1037
+
1038
+ # 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
1039
+ echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
1040
+ if bun test; then
1041
+ echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
1042
+ else
1043
+ echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
1044
+ echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
1045
+ exit 1
1046
+ fi
1047
+
1048
+ echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
1049
+ `;
1050
+ }
1051
+ /**
1052
+ * Generate CHECK_SYSTEM.md documentation.
1053
+ */
1054
+ generateCheckSystemDoc(context) {
1055
+ return `# \u5C08\u6848\u6AA2\u67E5\u7CFB\u7D71
1056
+
1057
+ \u672C\u5C08\u6848\u5DF2\u5EFA\u7ACB\u5B8C\u6574\u7684\u672C\u5730\u6AA2\u67E5\u6A5F\u5236\uFF0C\u7121\u9700\u4F9D\u8CF4 GitHub CI\u3002
1058
+
1059
+ ## \u5FEB\u901F\u958B\u59CB
1060
+
1061
+ ### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
1062
+ \`\`\`bash
1063
+ bun run validate
1064
+ \`\`\`
1065
+
1066
+ ### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
1067
+ \`\`\`bash
1068
+ # \u985E\u578B\u6AA2\u67E5
1069
+ bun run typecheck
1070
+
1071
+ # \u6E2C\u8A66
1072
+ bun run test
1073
+
1074
+ # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
1075
+ bun run check:deps
1076
+ \`\`\`
1077
+
1078
+ ## \u53EF\u7528\u547D\u4EE4
1079
+
1080
+ ### Package.json \u8173\u672C
1081
+
1082
+ | \u547D\u4EE4 | \u8AAA\u660E |
1083
+ |------|------|
1084
+ | \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
1085
+ | \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
1086
+ | \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
1087
+ | \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
1088
+ | \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
1089
+ | \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
1090
+
1091
+ ### Shell \u8173\u672C
1092
+
1093
+ | \u8173\u672C | \u8AAA\u660E |
1094
+ |------|------|
1095
+ | \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
1096
+ | \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
1097
+
1098
+ ## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
1099
+
1100
+ \u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
1101
+
1102
+ \`\`\`bash
1103
+ # \u5B89\u88DD pre-commit hook
1104
+ ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
1105
+
1106
+ # \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
1107
+ cp scripts/pre-commit.sh .git/hooks/pre-commit
1108
+ chmod +x .git/hooks/pre-commit
1109
+ \`\`\`
1110
+
1111
+ **\u529F\u80FD\uFF1A**
1112
+ - \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
1113
+ - \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
1114
+ - \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
1115
+
1116
+ **\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
1117
+ \`\`\`bash
1118
+ git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
1119
+ \`\`\`
1120
+
1121
+ ## \u6AA2\u67E5\u9805\u76EE
1122
+
1123
+ ### 1. \u985E\u578B\u6AA2\u67E5
1124
+ - \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
1125
+ - \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
1126
+
1127
+ ### 2. \u6E2C\u8A66
1128
+ - \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
1129
+ - \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
1130
+
1131
+ ### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
1132
+ - \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
1133
+ - \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
1134
+ - \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
1135
+
1136
+ ## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
1137
+
1138
+ ### \u958B\u767C\u6642
1139
+ 1. \u958B\u767C\u529F\u80FD
1140
+ 2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
1141
+ 3. \u4FEE\u6B63\u554F\u984C
1142
+ 4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
1143
+
1144
+ ### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
1145
+ 1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
1146
+ 2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
1147
+ 3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
1148
+ 4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
1149
+
1150
+ ## \u6A94\u6848\u7D50\u69CB
1151
+
1152
+ \`\`\`
1153
+ ${context.nameKebabCase}/
1154
+ \u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
1155
+ \u251C\u2500\u2500 scripts/
1156
+ \u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
1157
+ \u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
1158
+ \u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
1159
+ \u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
1160
+ \`\`\`
1161
+
1162
+ ## \u6CE8\u610F\u4E8B\u9805
1163
+
1164
+ 1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
1165
+ 2. **\u6E2C\u8A66\u6642\u9593**\uFF1A\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\uFF0C\u53EF\u4EE5\u7DE8\u8F2F \`pre-commit.sh\` \u8A3B\u89E3\u6389\u6E2C\u8A66\u90E8\u5206
1166
+ 3. **\u985E\u578B\u932F\u8AA4**\uFF1A\u5C08\u6848\u4E2D\u53EF\u80FD\u9084\u6709\u4E00\u4E9B\u65E2\u6709\u7684\u985E\u578B\u932F\u8AA4\uFF0C\u5EFA\u8B70\u9010\u6B65\u4FEE\u6B63
1167
+
1168
+ ## \u6545\u969C\u6392\u9664
1169
+
1170
+ ### \u6AA2\u67E5\u5931\u6557
1171
+ 1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
1172
+ 2. \u4FEE\u6B63\u554F\u984C
1173
+ 3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
1174
+
1175
+ ### \u8DF3\u904E\u6AA2\u67E5
1176
+ \u53EA\u6709\u5728\u7DCA\u6025\u60C5\u6CC1\u4E0B\u624D\u4F7F\u7528\uFF1A
1177
+ \`\`\`bash
1178
+ git commit --no-verify
1179
+ \`\`\`
1180
+
1181
+ ### \u79FB\u9664 Pre-commit Hook
1182
+ \`\`\`bash
1183
+ rm .git/hooks/pre-commit
1184
+ \`\`\`
1185
+ `;
1186
+ }
734
1187
  /**
735
1188
  * Log a message if verbose mode is enabled.
736
1189
  */
@@ -911,6 +1364,11 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
911
1364
  type: "directory",
912
1365
  name: "Providers",
913
1366
  children: [
1367
+ {
1368
+ type: "file",
1369
+ name: "index.ts",
1370
+ content: this.generateProvidersIndex()
1371
+ },
914
1372
  {
915
1373
  type: "file",
916
1374
  name: "AppServiceProvider.ts",
@@ -920,6 +1378,16 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
920
1378
  type: "file",
921
1379
  name: "RepositoryServiceProvider.ts",
922
1380
  content: this.generateRepositoryServiceProvider()
1381
+ },
1382
+ {
1383
+ type: "file",
1384
+ name: "MiddlewareProvider.ts",
1385
+ content: this.generateMiddlewareProvider()
1386
+ },
1387
+ {
1388
+ type: "file",
1389
+ name: "RouteProvider.ts",
1390
+ content: this.generateRouteProvider()
923
1391
  }
924
1392
  ]
925
1393
  }
@@ -1413,6 +1881,65 @@ export class RepositoryServiceProvider extends ServiceProvider {
1413
1881
  container.singleton('mailService', () => new MailService())
1414
1882
  }
1415
1883
  }
1884
+ `;
1885
+ }
1886
+ generateProvidersIndex() {
1887
+ return `/**
1888
+ * Application Service Providers
1889
+ */
1890
+
1891
+ export { AppServiceProvider } from './AppServiceProvider'
1892
+ export { RepositoryServiceProvider } from './RepositoryServiceProvider'
1893
+ export { MiddlewareProvider } from './MiddlewareProvider'
1894
+ export { RouteProvider } from './RouteProvider'
1895
+ `;
1896
+ }
1897
+ generateMiddlewareProvider() {
1898
+ return `/**
1899
+ * Middleware Service Provider
1900
+ */
1901
+
1902
+ import {
1903
+ ServiceProvider,
1904
+ type Container,
1905
+ type PlanetCore,
1906
+ bodySizeLimit,
1907
+ securityHeaders,
1908
+ } from '@gravito/core'
1909
+
1910
+ export class MiddlewareProvider extends ServiceProvider {
1911
+ register(_container: Container): void {}
1912
+
1913
+ boot(core: PlanetCore): void {
1914
+ const isDev = process.env.NODE_ENV !== 'production'
1915
+
1916
+ core.adapter.use('*', securityHeaders({
1917
+ contentSecurityPolicy: isDev ? false : undefined,
1918
+ }))
1919
+
1920
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
1921
+
1922
+ core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
1923
+ }
1924
+ }
1925
+ `;
1926
+ }
1927
+ generateRouteProvider() {
1928
+ return `/**
1929
+ * Route Service Provider
1930
+ */
1931
+
1932
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1933
+ import { registerApiRoutes } from '../../Interface/Http/Routes/api'
1934
+
1935
+ export class RouteProvider extends ServiceProvider {
1936
+ register(_container: Container): void {}
1937
+
1938
+ boot(core: PlanetCore): void {
1939
+ registerApiRoutes(core.router)
1940
+ core.logger.info('\u{1F6E4}\uFE0F Routes registered')
1941
+ }
1942
+ }
1416
1943
  `;
1417
1944
  }
1418
1945
  // ─────────────────────────────────────────────────────────────
@@ -1497,64 +2024,55 @@ export class UserPresenter {
1497
2024
  }
1498
2025
  `;
1499
2026
  }
1500
- generateBootstrap(context) {
2027
+ generateBootstrap(_context) {
1501
2028
  return `/**
1502
2029
  * Application Bootstrap
2030
+ *
2031
+ * The entry point for your Clean Architecture application.
2032
+ * Uses the ServiceProvider pattern for modular initialization.
2033
+ *
2034
+ * Lifecycle:
2035
+ * 1. Configure: Load app config and orbits
2036
+ * 2. Boot: Initialize PlanetCore
2037
+ * 3. Register Providers: Bind services to container
2038
+ * 4. Bootstrap: Boot all providers
1503
2039
  */
1504
2040
 
1505
- import { bodySizeLimit, PlanetCore, securityHeaders } from '@gravito/core'
2041
+ import { defineConfig, PlanetCore } from '@gravito/core'
1506
2042
  import { OrbitAtlas } from '@gravito/atlas'
1507
- import databaseConfig from '../config/database'
1508
- import { AppServiceProvider } from './Infrastructure/Providers/AppServiceProvider'
1509
- import { RepositoryServiceProvider } from './Infrastructure/Providers/RepositoryServiceProvider'
1510
- import { registerApiRoutes } from './Interface/Http/Routes/api'
1511
-
1512
- const core = new PlanetCore({
1513
- config: {
1514
- APP_NAME: '${context.name}',
1515
- database: databaseConfig,
1516
- },
1517
- })
1518
-
1519
- const defaultCsp = [
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,
2043
+ import appConfig from '../config/app'
2044
+ import {
2045
+ AppServiceProvider,
2046
+ RepositoryServiceProvider,
2047
+ MiddlewareProvider,
2048
+ RouteProvider,
2049
+ } from './Infrastructure/Providers'
2050
+
2051
+ export async function bootstrap() {
2052
+ // 1. Configure
2053
+ const config = defineConfig({
2054
+ config: appConfig,
2055
+ orbits: [new OrbitAtlas()],
1542
2056
  })
1543
- )
1544
- if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
1545
- core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
1546
- }
1547
2057
 
1548
- await core.orbit(new OrbitAtlas())
2058
+ // 2. Boot Core
2059
+ const core = await PlanetCore.boot(config)
2060
+ core.registerGlobalErrorHandlers()
1549
2061
 
1550
- core.register(new RepositoryServiceProvider())
1551
- core.register(new AppServiceProvider())
2062
+ // 3. Register Providers
2063
+ core.register(new RepositoryServiceProvider())
2064
+ core.register(new AppServiceProvider())
2065
+ core.register(new MiddlewareProvider())
2066
+ core.register(new RouteProvider())
1552
2067
 
1553
- await core.bootstrap()
2068
+ // 4. Bootstrap All Providers
2069
+ await core.bootstrap()
1554
2070
 
1555
- // Register routes
1556
- registerApiRoutes(core.router)
2071
+ return core
2072
+ }
1557
2073
 
2074
+ // Application Entry Point
2075
+ const core = await bootstrap()
1558
2076
  export default core.liftoff()
1559
2077
  `;
1560
2078
  }
@@ -1566,6 +2084,15 @@ export default core.liftoff()
1566
2084
  This project follows **Clean Architecture** (by Robert C. Martin).
1567
2085
  The key principle is the **Dependency Rule**: dependencies point inward.
1568
2086
 
2087
+ ## Service Providers
2088
+
2089
+ Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
2090
+
2091
+ ### Provider Lifecycle
2092
+
2093
+ 1. **register()**: Bind services to the container (sync or async).
2094
+ 2. **boot()**: Called after ALL providers have registered. Safe to use other services.
2095
+
1569
2096
  ## Layer Structure
1570
2097
 
1571
2098
  \`\`\`
@@ -1637,6 +2164,10 @@ Created with \u2764\uFE0F using Gravito Framework
1637
2164
  start: "bun run dist/bootstrap.js",
1638
2165
  test: "bun test",
1639
2166
  typecheck: "tsc --noEmit",
2167
+ check: "bun run typecheck && bun run test",
2168
+ "check:deps": "bun run scripts/check-dependencies.ts",
2169
+ validate: "bun run check && bun run check:deps",
2170
+ precommit: "bun run validate",
1640
2171
  "docker:build": `docker build -t ${context.nameKebabCase} .`,
1641
2172
  "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
1642
2173
  },
@@ -1960,63 +2491,46 @@ var DddGenerator = class extends BaseGenerator {
1960
2491
  // ─────────────────────────────────────────────────────────────
1961
2492
  // Bootstrap File Generators
1962
2493
  // ─────────────────────────────────────────────────────────────
1963
- generateBootstrapApp(context) {
2494
+ generateBootstrapApp(_context) {
1964
2495
  return `/**
1965
2496
  * Application Bootstrap
1966
2497
  *
1967
- * Central configuration and initialization of the application.
2498
+ * Central configuration and initialization using the ServiceProvider pattern.
2499
+ *
2500
+ * Lifecycle:
2501
+ * 1. Configure: Load app config and orbits
2502
+ * 2. Boot: Initialize PlanetCore
2503
+ * 3. Register Providers: Bind services to container
2504
+ * 4. Bootstrap: Boot all providers
1968
2505
  */
1969
2506
 
1970
- import { bodySizeLimit, PlanetCore, securityHeaders } from '@gravito/core'
2507
+ import { defineConfig, PlanetCore } from '@gravito/core'
2508
+ import { OrbitAtlas } from '@gravito/atlas'
2509
+ import appConfig from '../../config/app'
1971
2510
  import { registerProviders } from './providers'
1972
2511
  import { registerRoutes } from './routes'
1973
2512
 
1974
2513
  export async function createApp(): Promise<PlanetCore> {
1975
- const core = new PlanetCore({
1976
- config: {
1977
- APP_NAME: '${context.name}',
1978
- },
1979
- })
2514
+ // 1. Configure
2515
+ const config = defineConfig({
2516
+ config: appConfig,
2517
+ orbits: [new OrbitAtlas()],
2518
+ })
1980
2519
 
1981
- const defaultCsp = [
1982
- "default-src 'self'",
1983
- "script-src 'self' 'unsafe-inline'",
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
- }
2520
+ // 2. Boot Core
2521
+ const core = await PlanetCore.boot(config)
2522
+ core.registerGlobalErrorHandlers()
2009
2523
 
2010
- // Register all service providers
2011
- await registerProviders(core)
2524
+ // 3. Register Providers
2525
+ await registerProviders(core)
2012
2526
 
2013
- // Bootstrap the application
2014
- await core.bootstrap()
2527
+ // 4. Bootstrap All Providers
2528
+ await core.bootstrap()
2015
2529
 
2016
- // Register routes
2017
- registerRoutes(core.router)
2530
+ // Register routes after bootstrap
2531
+ registerRoutes(core.router)
2018
2532
 
2019
- return core
2533
+ return core
2020
2534
  }
2021
2535
  `;
2022
2536
  }
@@ -2024,19 +2538,48 @@ export async function createApp(): Promise<PlanetCore> {
2024
2538
  return `/**
2025
2539
  * Service Providers Registry
2026
2540
  *
2027
- * Register all module service providers here.
2541
+ * Register all service providers here.
2542
+ * Include both global and module-specific providers.
2028
2543
  */
2029
2544
 
2030
- import type { PlanetCore } from '@gravito/core'
2545
+ import {
2546
+ ServiceProvider,
2547
+ type Container,
2548
+ type PlanetCore,
2549
+ bodySizeLimit,
2550
+ securityHeaders,
2551
+ } from '@gravito/core'
2031
2552
  import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
2032
2553
  import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
2033
2554
 
2555
+ /**
2556
+ * Middleware Provider - Global middleware registration
2557
+ */
2558
+ export class MiddlewareProvider extends ServiceProvider {
2559
+ register(_container: Container): void {}
2560
+
2561
+ boot(core: PlanetCore): void {
2562
+ const isDev = process.env.NODE_ENV !== 'production'
2563
+
2564
+ core.adapter.use('*', securityHeaders({
2565
+ contentSecurityPolicy: isDev ? false : undefined,
2566
+ }))
2567
+
2568
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
2569
+
2570
+ core.logger.info('\u{1F6E1}\uFE0F Global middleware registered')
2571
+ }
2572
+ }
2573
+
2034
2574
  export async function registerProviders(core: PlanetCore): Promise<void> {
2035
- // Register module providers
2036
- core.register(new OrderingServiceProvider())
2037
- core.register(new CatalogServiceProvider())
2575
+ // Global Providers
2576
+ core.register(new MiddlewareProvider())
2038
2577
 
2039
- // Add more providers as needed
2578
+ // Module Providers
2579
+ core.register(new OrderingServiceProvider())
2580
+ core.register(new CatalogServiceProvider())
2581
+
2582
+ // Add more providers as needed
2040
2583
  }
2041
2584
  `;
2042
2585
  }
@@ -2150,6 +2693,10 @@ export class ${name}ServiceProvider extends ServiceProvider {
2150
2693
  start: "bun run dist/main.js",
2151
2694
  test: "bun test",
2152
2695
  typecheck: "tsc --noEmit",
2696
+ check: "bun run typecheck && bun run test",
2697
+ "check:deps": "bun run scripts/check-dependencies.ts",
2698
+ validate: "bun run check && bun run check:deps",
2699
+ precommit: "bun run validate",
2153
2700
  "docker:build": `docker build -t ${context.nameKebabCase} .`,
2154
2701
  "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
2155
2702
  },
@@ -2590,6 +3137,16 @@ export function report(error: unknown): void {
2590
3137
 
2591
3138
  This project follows **Domain-Driven Design (DDD)** with strategic and tactical patterns.
2592
3139
 
3140
+ ## Service Providers
3141
+
3142
+ Service providers are the central place to configure your application and modules. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
3143
+
3144
+ ### Internal Bootstrapping
3145
+
3146
+ 1. **Bootstrap/app.ts**: Orchestrates the 4-step lifecycle (Configure, Boot, Register, Bootstrap).
3147
+ 2. **Bootstrap/providers.ts**: Central registry for all global and module-specific providers.
3148
+ 3. **Infrastructure/Providers/[Module]ServiceProvider.ts**: Module-specific service registration.
3149
+
2593
3150
  ## Bounded Contexts
2594
3151
 
2595
3152
  \`\`\`
@@ -2727,6 +3284,11 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
2727
3284
  type: "directory",
2728
3285
  name: "Providers",
2729
3286
  children: [
3287
+ {
3288
+ type: "file",
3289
+ name: "index.ts",
3290
+ content: this.generateProvidersIndex()
3291
+ },
2730
3292
  {
2731
3293
  type: "file",
2732
3294
  name: "AppServiceProvider.ts",
@@ -2734,8 +3296,18 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
2734
3296
  },
2735
3297
  {
2736
3298
  type: "file",
2737
- name: "RouteServiceProvider.ts",
2738
- content: this.generateRouteServiceProvider(context)
3299
+ name: "DatabaseProvider.ts",
3300
+ content: this.generateDatabaseProvider()
3301
+ },
3302
+ {
3303
+ type: "file",
3304
+ name: "MiddlewareProvider.ts",
3305
+ content: this.generateMiddlewareProvider()
3306
+ },
3307
+ {
3308
+ type: "file",
3309
+ name: "RouteProvider.ts",
3310
+ content: this.generateRouteProvider()
2739
3311
  }
2740
3312
  ]
2741
3313
  },
@@ -3202,6 +3774,135 @@ export class RouteServiceProvider extends ServiceProvider {
3202
3774
  registerRoutes(core.router)
3203
3775
  }
3204
3776
  }
3777
+ `;
3778
+ }
3779
+ // ─────────────────────────────────────────────────────────────
3780
+ // Modern Provider Generators (ServiceProvider Pattern)
3781
+ // ─────────────────────────────────────────────────────────────
3782
+ generateProvidersIndex() {
3783
+ return `/**
3784
+ * Application Service Providers
3785
+ *
3786
+ * Export all providers for easy importing in bootstrap.
3787
+ * Providers are registered in the order they are listed.
3788
+ */
3789
+
3790
+ export { AppServiceProvider } from './AppServiceProvider'
3791
+ export { DatabaseProvider } from './DatabaseProvider'
3792
+ export { MiddlewareProvider } from './MiddlewareProvider'
3793
+ export { RouteProvider } from './RouteProvider'
3794
+ `;
3795
+ }
3796
+ generateDatabaseProvider() {
3797
+ return `/**
3798
+ * Database Service Provider
3799
+ *
3800
+ * Handles database initialization and migrations.
3801
+ *
3802
+ * Lifecycle:
3803
+ * - register(): Bind database config to container
3804
+ * - boot(): Run migrations and seeders
3805
+ */
3806
+
3807
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
3808
+ import databaseConfig from '../../config/database'
3809
+
3810
+ export class DatabaseProvider extends ServiceProvider {
3811
+ /**
3812
+ * Register database configuration.
3813
+ */
3814
+ register(_container: Container): void {
3815
+ this.mergeConfig(this.core!.config, 'database', databaseConfig)
3816
+ }
3817
+
3818
+ /**
3819
+ * Initialize database connections.
3820
+ */
3821
+ async boot(core: PlanetCore): Promise<void> {
3822
+ // Database initialization will be handled by Atlas orbit
3823
+ core.logger.info('\u{1F4E6} Database provider booted')
3824
+ }
3825
+ }
3826
+ `;
3827
+ }
3828
+ generateMiddlewareProvider() {
3829
+ return `/**
3830
+ * Middleware Service Provider
3831
+ *
3832
+ * Registers global middleware stack.
3833
+ * Provides a centralized location for middleware configuration.
3834
+ *
3835
+ * Lifecycle:
3836
+ * - register(): N/A (no container bindings)
3837
+ * - boot(): Register global middleware
3838
+ */
3839
+
3840
+ import {
3841
+ ServiceProvider,
3842
+ type Container,
3843
+ type PlanetCore,
3844
+ bodySizeLimit,
3845
+ securityHeaders,
3846
+ } from '@gravito/core'
3847
+
3848
+ export class MiddlewareProvider extends ServiceProvider {
3849
+ /**
3850
+ * No container bindings needed.
3851
+ */
3852
+ register(_container: Container): void {
3853
+ // Middleware doesn't require container bindings
3854
+ }
3855
+
3856
+ /**
3857
+ * Register global middleware stack.
3858
+ */
3859
+ boot(core: PlanetCore): void {
3860
+ const isDev = process.env.NODE_ENV !== 'production'
3861
+
3862
+ // Security Headers
3863
+ core.adapter.use('*', securityHeaders({
3864
+ contentSecurityPolicy: isDev ? false : undefined,
3865
+ }))
3866
+
3867
+ // Body Parser Limits
3868
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024)) // 10MB limit
3869
+
3870
+ core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
3871
+ }
3872
+ }
3873
+ `;
3874
+ }
3875
+ generateRouteProvider() {
3876
+ return `/**
3877
+ * Route Service Provider
3878
+ *
3879
+ * Registers application routes.
3880
+ * Routes are registered in the boot phase after all services are available.
3881
+ *
3882
+ * Lifecycle:
3883
+ * - register(): N/A
3884
+ * - boot(): Register routes
3885
+ */
3886
+
3887
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
3888
+ import { registerRoutes } from '../routes'
3889
+
3890
+ export class RouteProvider extends ServiceProvider {
3891
+ /**
3892
+ * No container bindings needed.
3893
+ */
3894
+ register(_container: Container): void {
3895
+ // Routes don't require container bindings
3896
+ }
3897
+
3898
+ /**
3899
+ * Register application routes.
3900
+ */
3901
+ boot(core: PlanetCore): void {
3902
+ registerRoutes(core.router)
3903
+ core.logger.info('\u{1F6E4}\uFE0F Routes registered')
3904
+ }
3905
+ }
3205
3906
  `;
3206
3907
  }
3207
3908
  generateExceptionHandler() {
@@ -3250,74 +3951,75 @@ export const dontReport: string[] = [
3250
3951
  }
3251
3952
  generateBootstrap(context) {
3252
3953
  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
- ` : "";
3954
+ const spectrumOrbit = context.withSpectrum ? " new SpectrumOrbit()," : "";
3259
3955
  return `/**
3260
3956
  * Application Bootstrap
3261
3957
  *
3262
- * This is the entry point for your application.
3263
- * It initializes the core and registers all providers.
3958
+ * The entry point for your Gravito application.
3959
+ * Uses the ServiceProvider pattern for modular, maintainable initialization.
3960
+ *
3961
+ * Lifecycle:
3962
+ * 1. Configure: Load app config and orbits
3963
+ * 2. Boot: Initialize PlanetCore
3964
+ * 3. Register Providers: Bind services to container
3965
+ * 4. Bootstrap: Boot all providers
3966
+ *
3967
+ * @module bootstrap
3264
3968
  */
3265
3969
 
3266
- import { bodySizeLimit, PlanetCore, securityHeaders } from '@gravito/core'
3970
+ import { defineConfig, PlanetCore } from '@gravito/core'
3267
3971
  import { OrbitAtlas } from '@gravito/atlas'
3268
- import databaseConfig from '../config/database'
3269
- ${spectrumImport}import { AppServiceProvider } from './Providers/AppServiceProvider'
3270
- import { RouteServiceProvider } from './Providers/RouteServiceProvider'
3271
-
3272
- // Load environment variables
3273
- // Bun automatically loads .env
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
- }
3972
+ import appConfig from '../config/app'
3973
+ ${spectrumImport}import {
3974
+ AppServiceProvider,
3975
+ DatabaseProvider,
3976
+ MiddlewareProvider,
3977
+ RouteProvider,
3978
+ } from './Providers'
3310
3979
 
3311
- await core.orbit(new OrbitAtlas())
3980
+ /**
3981
+ * Bootstrap the application with service providers.
3982
+ *
3983
+ * @returns The booted PlanetCore instance
3984
+ */
3985
+ export async function bootstrap() {
3986
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3987
+ // 1. Configure
3988
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3989
+ const config = defineConfig({
3990
+ config: appConfig,
3991
+ orbits: [
3992
+ new OrbitAtlas(),
3312
3993
  ${spectrumOrbit}
3313
- // Register service providers
3314
- core.register(new AppServiceProvider())
3315
- core.register(new RouteServiceProvider())
3994
+ ],
3995
+ })
3316
3996
 
3317
- // Bootstrap the application
3318
- await core.bootstrap()
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
+ // 2. Boot Core
3999
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4000
+ const core = await PlanetCore.boot(config)
4001
+ core.registerGlobalErrorHandlers()
4002
+
4003
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4004
+ // 3. Register Providers
4005
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4006
+ core.register(new AppServiceProvider())
4007
+ core.register(new DatabaseProvider())
4008
+ core.register(new MiddlewareProvider())
4009
+ core.register(new RouteProvider())
4010
+
4011
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4012
+ // 4. Bootstrap All Providers
4013
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4014
+ await core.bootstrap()
4015
+
4016
+ return core
4017
+ }
3319
4018
 
3320
- // Export for Bun.serve()
4019
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4020
+ // Application Entry Point
4021
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4022
+ const core = await bootstrap()
3321
4023
  export default core.liftoff()
3322
4024
  `;
3323
4025
  }
@@ -3744,7 +4446,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
3744
4446
  scripts: {
3745
4447
  build: "tsup src/index.ts --format cjs,esm --dts",
3746
4448
  test: "bun test",
3747
- typecheck: "tsc --noEmit"
4449
+ typecheck: "tsc --noEmit",
4450
+ check: "bun run typecheck && bun run test",
4451
+ validate: "bun run check"
3748
4452
  },
3749
4453
  dependencies: {
3750
4454
  "@gravito/core": depVersion,
@@ -4042,10 +4746,25 @@ var ActionDomainGenerator = class extends BaseGenerator {
4042
4746
  type: "directory",
4043
4747
  name: "providers",
4044
4748
  children: [
4749
+ {
4750
+ type: "file",
4751
+ name: "index.ts",
4752
+ content: this.generateProvidersIndex()
4753
+ },
4045
4754
  {
4046
4755
  type: "file",
4047
4756
  name: "AppServiceProvider.ts",
4048
4757
  content: this.generateAppServiceProvider(context)
4758
+ },
4759
+ {
4760
+ type: "file",
4761
+ name: "MiddlewareProvider.ts",
4762
+ content: this.generateMiddlewareProvider()
4763
+ },
4764
+ {
4765
+ type: "file",
4766
+ name: "RouteProvider.ts",
4767
+ content: this.generateRouteProvider()
4049
4768
  }
4050
4769
  ]
4051
4770
  },
@@ -4223,7 +4942,7 @@ export function registerApiRoutes(router: Router) {
4223
4942
  import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
4224
4943
 
4225
4944
  export class AppServiceProvider extends ServiceProvider {
4226
- register(container: Container): void {
4945
+ register(_container: Container): void {
4227
4946
  // Register global services here
4228
4947
  }
4229
4948
 
@@ -4233,47 +4952,140 @@ export class AppServiceProvider extends ServiceProvider {
4233
4952
  }
4234
4953
  `;
4235
4954
  }
4236
- generateBootstrap(context) {
4955
+ generateProvidersIndex() {
4237
4956
  return `/**
4238
- * Application Entry Point
4957
+ * Application Service Providers
4239
4958
  */
4240
4959
 
4241
- import { PlanetCore, securityHeaders, bodySizeLimit } from '@gravito/core'
4242
- import { OrbitAtlas } from '@gravito/atlas'
4243
- import databaseConfig from '../config/database'
4244
- import { AppServiceProvider } from './providers/AppServiceProvider'
4245
- import { registerApiRoutes } from './routes/api'
4246
-
4247
- // Initialize Core
4248
- const core = new PlanetCore({
4249
- config: {
4250
- APP_NAME: '${context.name}',
4251
- database: databaseConfig
4252
- },
4253
- })
4960
+ export { AppServiceProvider } from './AppServiceProvider'
4961
+ export { MiddlewareProvider } from './MiddlewareProvider'
4962
+ export { RouteProvider } from './RouteProvider'
4963
+ `;
4964
+ }
4965
+ generateMiddlewareProvider() {
4966
+ return `/**
4967
+ * Middleware Service Provider
4968
+ */
4254
4969
 
4255
- // Middleware
4256
- core.adapter.use('*', securityHeaders())
4257
- core.adapter.use('*', bodySizeLimit(1024 * 1024)) // 1MB
4970
+ import {
4971
+ ServiceProvider,
4972
+ type Container,
4973
+ type PlanetCore,
4974
+ bodySizeLimit,
4975
+ securityHeaders,
4976
+ } from '@gravito/core'
4258
4977
 
4259
- // Install Orbits
4260
- await core.orbit(new OrbitAtlas())
4978
+ export class MiddlewareProvider extends ServiceProvider {
4979
+ register(_container: Container): void {}
4261
4980
 
4262
- // Service Providers
4263
- core.register(new AppServiceProvider())
4981
+ boot(core: PlanetCore): void {
4982
+ core.adapter.use('*', securityHeaders())
4983
+ core.adapter.use('*', bodySizeLimit(1024 * 1024))
4984
+ core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
4985
+ }
4986
+ }
4987
+ `;
4988
+ }
4989
+ generateRouteProvider() {
4990
+ return `/**
4991
+ * Route Service Provider
4992
+ */
4264
4993
 
4265
- // Bootstrap
4266
- await core.bootstrap()
4994
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
4995
+ import { registerApiRoutes } from '../routes/api'
4267
4996
 
4268
- // Routes
4269
- registerApiRoutes(core.router)
4997
+ export class RouteProvider extends ServiceProvider {
4998
+ register(_container: Container): void {}
4270
4999
 
4271
- // Liftoff
5000
+ boot(core: PlanetCore): void {
5001
+ registerApiRoutes(core.router)
5002
+ core.logger.info('\u{1F6E4}\uFE0F Routes registered')
5003
+ }
5004
+ }
5005
+ `;
5006
+ }
5007
+ generateBootstrap(_context) {
5008
+ return `/**
5009
+ * Application Bootstrap
5010
+ *
5011
+ * Uses the ServiceProvider pattern for modular initialization.
5012
+ */
5013
+
5014
+ import { defineConfig, PlanetCore } from '@gravito/core'
5015
+ import { OrbitAtlas } from '@gravito/atlas'
5016
+ import appConfig from '../config/app'
5017
+ import {
5018
+ AppServiceProvider,
5019
+ MiddlewareProvider,
5020
+ RouteProvider,
5021
+ } from './providers'
5022
+
5023
+ export async function bootstrap() {
5024
+ const config = defineConfig({
5025
+ config: appConfig,
5026
+ orbits: [new OrbitAtlas()],
5027
+ })
5028
+
5029
+ const core = await PlanetCore.boot(config)
5030
+ core.registerGlobalErrorHandlers()
5031
+
5032
+ core.register(new AppServiceProvider())
5033
+ core.register(new MiddlewareProvider())
5034
+ core.register(new RouteProvider())
5035
+
5036
+ await core.bootstrap()
5037
+
5038
+ return core
5039
+ }
5040
+
5041
+ const core = await bootstrap()
4272
5042
  export default core.liftoff()
4273
5043
  `;
4274
5044
  }
4275
5045
  generateArchitectureDoc(context) {
4276
- return "# " + context.name + ' - Action Domain Architecture\n\n## Overview\n\nThis project uses the **Action Domain** pattern, designed for high-clarity API implementations.\n\n## Directory Structure\n\n```\nsrc/\n\u251C\u2500\u2500 actions/ # Single Responsibility Business Logic\n\u2502 \u251C\u2500\u2500 Action.ts # Base Action class\n\u2502 \u2514\u2500\u2500 [Domain]/ # Domain-specific actions\n\u251C\u2500\u2500 controllers/ # HTTP Request Handlers\n\u2502 \u2514\u2500\u2500 api/v1/ # API Controllers\n\u251C\u2500\u2500 types/ # TypeScript Definitions\n\u2502 \u251C\u2500\u2500 requests/ # Request Payloads\n\u2502 \u2514\u2500\u2500 responses/ # Response Structures\n\u251C\u2500\u2500 repositories/ # Data Access Layer\n\u251C\u2500\u2500 routes/ # Route Definitions\n\u2514\u2500\u2500 config/ # Configuration\n```\n\n## Core Concepts\n\n### Actions\nEvery business operation is an "Action". An action:\n- Does ONE thing.\n- Takes specific input.\n- Returns specific output.\n- Is framework-agnostic (ideally).\n\n### Controllers\nControllers are thin. They:\n1. Parse the request.\n2. Instantiate/Call the Action.\n3. Return the response.\n\nCreated with \u2764\uFE0F using Gravito Framework\n';
5046
+ return `# ${context.name} - Action Domain Architecture
5047
+
5048
+ ## Overview
5049
+
5050
+ This project uses the **Action Domain** pattern, designed for high-clarity API implementations.
5051
+
5052
+ ## Service Providers
5053
+
5054
+ Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
5055
+
5056
+ ## Directory Structure
5057
+
5058
+ \`\`\`
5059
+ src/
5060
+ \u251C\u2500\u2500 actions/ # Single Responsibility Business Logic
5061
+ \u2502 \u251C\u2500\u2500 Action.ts # Base Action class
5062
+ \u2502 \u2514\u2500\u2500 [Domain]/ # Domain-specific actions
5063
+ \u251C\u2500\u2500 controllers/ # HTTP Request Handlers
5064
+ \u2502 \u2514\u2500\u2500 api/v1/ # API Controllers
5065
+ \u251C\u2500\u2500 types/ # TypeScript Definitions
5066
+ \u251C\u2500\u2500 repositories/ # Data Access Layer
5067
+ \u251C\u2500\u2500 routes/ # Route Definitions
5068
+ \u251C\u2500\u2500 providers/ # Service Providers
5069
+ \u2514\u2500\u2500 config/ # Configuration
5070
+ \`\`\`
5071
+
5072
+ ## Core Concepts
5073
+
5074
+ ### Actions
5075
+ Every business operation is an "Action". An action:
5076
+ - Does ONE thing.
5077
+ - Takes specific input.
5078
+ - Returns specific output.
5079
+ - Is framework-agnostic (ideally).
5080
+
5081
+ ### Controllers
5082
+ Controllers are thin. They:
5083
+ 1. Parse the request.
5084
+ 2. Instantiate/Call the Action.
5085
+ 3. Return the response.
5086
+
5087
+ Created with \u2764\uFE0F using Gravito Framework
5088
+ `;
4277
5089
  }
4278
5090
  generatePackageJson(context) {
4279
5091
  const pkg = {
@@ -4285,7 +5097,11 @@ export default core.liftoff()
4285
5097
  build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
4286
5098
  start: "bun run dist/bootstrap.js",
4287
5099
  test: "bun test",
4288
- typecheck: "tsc --noEmit"
5100
+ typecheck: "tsc --noEmit",
5101
+ check: "bun run typecheck && bun run test",
5102
+ "check:deps": "bun run scripts/check-dependencies.ts",
5103
+ validate: "bun run check && bun run check:deps",
5104
+ precommit: "bun run validate"
4289
5105
  },
4290
5106
  dependencies: {
4291
5107
  "@gravito/core": "workspace:*",
@@ -4302,6 +5118,115 @@ export default core.liftoff()
4302
5118
  }
4303
5119
  };
4304
5120
 
5121
+ // src/generators/StandaloneEngineGenerator.ts
5122
+ var StandaloneEngineGenerator = class extends BaseGenerator {
5123
+ get architectureType() {
5124
+ return "standalone-engine";
5125
+ }
5126
+ get displayName() {
5127
+ return "Standalone Engine";
5128
+ }
5129
+ get description() {
5130
+ return "High-performance pure Gravito Engine (minimal)";
5131
+ }
5132
+ getDirectoryStructure(context) {
5133
+ return [
5134
+ {
5135
+ type: "directory",
5136
+ name: "src",
5137
+ children: [
5138
+ {
5139
+ type: "file",
5140
+ name: "index.ts",
5141
+ content: this.getIndexContent()
5142
+ }
5143
+ ]
5144
+ },
5145
+ {
5146
+ type: "file",
5147
+ name: "README.md",
5148
+ content: this.getReadmeContent(context)
5149
+ }
5150
+ ];
5151
+ }
5152
+ async generateCommonFiles(context) {
5153
+ await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
5154
+ await this.writeFile(context.targetDir, "tsconfig.json", this.generateTsConfig());
5155
+ await this.writeFile(context.targetDir, ".gitignore", this.generateGitignore());
5156
+ }
5157
+ generatePackageJson(context) {
5158
+ const pkg = {
5159
+ name: context.nameKebabCase,
5160
+ version: "0.1.0",
5161
+ type: "module",
5162
+ scripts: {
5163
+ dev: "bun run --watch src/index.ts",
5164
+ build: "bun build ./src/index.ts --outdir ./dist --target bun",
5165
+ start: "bun run dist/index.js",
5166
+ test: "bun test"
5167
+ },
5168
+ dependencies: {
5169
+ "@gravito/core": "^1.1.0"
5170
+ },
5171
+ devDependencies: {
5172
+ "bun-types": "latest",
5173
+ typescript: "^5.0.0"
5174
+ }
5175
+ };
5176
+ return JSON.stringify(pkg, null, 2);
5177
+ }
5178
+ generateArchitectureDoc(_context) {
5179
+ return "";
5180
+ }
5181
+ getIndexContent() {
5182
+ return `import { Gravito } from '@gravito/core/engine'
5183
+
5184
+ const app = new Gravito()
5185
+
5186
+ // Basic Route
5187
+ app.get('/', (c) => c.text('Hello Gravito Engine!'))
5188
+
5189
+ // JSON Response
5190
+ app.get('/json', (c) => c.json({ message: 'High Performance' }))
5191
+
5192
+ // Path Parameters
5193
+ app.get('/user/:name', (c) => {
5194
+ const name = c.req.param('name')
5195
+ return c.text(\`Hello \${name}\`)
5196
+ })
5197
+
5198
+ export default app
5199
+ `;
5200
+ }
5201
+ getReadmeContent(context) {
5202
+ return `# ${context.name}
5203
+
5204
+ A high-performance web application powered by Gravito Engine.
5205
+
5206
+ ## Getting Started
5207
+
5208
+ ### Install Dependencies
5209
+
5210
+ \`\`\`bash
5211
+ bun install
5212
+ \`\`\`
5213
+
5214
+ ### Run Development Server
5215
+
5216
+ \`\`\`bash
5217
+ bun run dev
5218
+ \`\`\`
5219
+
5220
+ ### Production Build
5221
+
5222
+ \`\`\`bash
5223
+ bun run build
5224
+ bun start
5225
+ \`\`\`
5226
+ `;
5227
+ }
5228
+ };
5229
+
4305
5230
  // src/Scaffold.ts
4306
5231
  var Scaffold = class {
4307
5232
  templatesDir;
@@ -4335,6 +5260,11 @@ var Scaffold = class {
4335
5260
  name: "Action Domain",
4336
5261
  description: "Single Action Controllers pattern for high-clarity APIs"
4337
5262
  },
5263
+ {
5264
+ type: "standalone-engine",
5265
+ name: "Standalone Engine",
5266
+ description: "High-performance pure Gravito Engine (minimal)"
5267
+ },
4338
5268
  {
4339
5269
  type: "satellite",
4340
5270
  name: "Gravito Satellite",
@@ -4410,6 +5340,8 @@ var Scaffold = class {
4410
5340
  return new ActionDomainGenerator(config);
4411
5341
  case "satellite":
4412
5342
  return new SatelliteGenerator(config);
5343
+ case "standalone-engine":
5344
+ return new StandaloneEngineGenerator(config);
4413
5345
  default:
4414
5346
  throw new Error(`Unknown architecture type: ${type}`);
4415
5347
  }