@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.js CHANGED
@@ -346,6 +346,31 @@ var BaseGenerator = class {
346
346
  "ARCHITECTURE.md",
347
347
  this.generateArchitectureDoc(context)
348
348
  );
349
+ await this.generateCheckScripts(context);
350
+ await this.generateSkills(context);
351
+ }
352
+ /**
353
+ * Copy AI Skills to the project
354
+ */
355
+ async generateSkills(context) {
356
+ const skillsDir = path2.resolve(this.config.templatesDir, "skills");
357
+ const targetSkillsDir = path2.join(".skills");
358
+ try {
359
+ await fs2.access(skillsDir);
360
+ } catch {
361
+ return;
362
+ }
363
+ const files = await walk(skillsDir);
364
+ for (const filePath of files) {
365
+ const relativePath = path2.relative(skillsDir, filePath);
366
+ const targetPath = path2.join(targetSkillsDir, relativePath);
367
+ let content = await fs2.readFile(filePath, "utf-8");
368
+ try {
369
+ content = this.stubGenerator.render(content, context);
370
+ } catch {
371
+ }
372
+ await this.writeFile(context.targetDir, targetPath, content);
373
+ }
349
374
  }
350
375
  /**
351
376
  * Apply profile-specific overlays
@@ -433,6 +458,10 @@ var BaseGenerator = class {
433
458
  start: "bun run dist/bootstrap.js",
434
459
  test: "bun test",
435
460
  typecheck: "tsc --noEmit",
461
+ check: "bun run typecheck && bun run test",
462
+ "check:deps": "bun run scripts/check-dependencies.ts",
463
+ validate: "bun run check && bun run check:deps",
464
+ precommit: "bun run validate",
436
465
  "docker:build": `docker build -t ${context.nameKebabCase} .`,
437
466
  "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
438
467
  },
@@ -685,6 +714,430 @@ coverage/
685
714
  };
686
715
  return JSON.stringify(config, null, 2);
687
716
  }
717
+ /**
718
+ * Generate check scripts for project validation.
719
+ */
720
+ async generateCheckScripts(context) {
721
+ const scriptsDir = path2.resolve(context.targetDir, "scripts");
722
+ await fs2.mkdir(scriptsDir, { recursive: true });
723
+ await this.writeFile(
724
+ scriptsDir,
725
+ "check-dependencies.ts",
726
+ this.generateCheckDependenciesScript()
727
+ );
728
+ await this.writeFile(scriptsDir, "check.sh", this.generateCheckShellScript());
729
+ await this.writeFile(scriptsDir, "pre-commit.sh", this.generatePreCommitScript());
730
+ await this.writeFile(context.targetDir, "CHECK_SYSTEM.md", this.generateCheckSystemDoc(context));
731
+ }
732
+ /**
733
+ * Generate check-dependencies.ts script content.
734
+ */
735
+ generateCheckDependenciesScript() {
736
+ return `/**
737
+ * \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5\u8173\u672C
738
+ *
739
+ * \u6AA2\u67E5 package.json \u4E2D\u7684\u5957\u4EF6\u662F\u5426\u70BA\u6700\u65B0\u7A69\u5B9A\u7248\u672C
740
+ * \u4E26\u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
741
+ */
742
+
743
+ import { readFileSync } from 'fs'
744
+ import { join } from 'path'
745
+
746
+ interface PackageJson {
747
+ dependencies?: Record<string, string>
748
+ devDependencies?: Record<string, string>
749
+ }
750
+
751
+ interface PackageInfo {
752
+ name: string
753
+ current: string
754
+ latest: string
755
+ outdated: boolean
756
+ }
757
+
758
+ const colors = {
759
+ reset: '\\x1b[0m',
760
+ green: '\\x1b[32m',
761
+ yellow: '\\x1b[33m',
762
+ red: '\\x1b[31m',
763
+ blue: '\\x1b[36m',
764
+ }
765
+
766
+ function log(message: string, color: keyof typeof colors = 'reset') {
767
+ console.log(\`\${colors[color]}\${message}\${colors.reset}\`)
768
+ }
769
+
770
+ async function getLatestVersion(packageName: string): Promise<string | null> {
771
+ try {
772
+ const response = await fetch(\`https://registry.npmjs.org/\${packageName}/latest\`)
773
+ if (!response.ok) return null
774
+ const data = await response.json()
775
+ return data.version
776
+ } catch {
777
+ return null
778
+ }
779
+ }
780
+
781
+ function parseVersion(version: string): string {
782
+ // \u79FB\u9664 ^, ~, >= \u7B49\u524D\u7DB4
783
+ return version.replace(/^[\\^~>=<]/, '')
784
+ }
785
+
786
+ async function checkPackage(
787
+ name: string,
788
+ currentVersion: string
789
+ ): Promise<PackageInfo | null> {
790
+ // \u8DF3\u904E\u672C\u5730\u9023\u7D50\u7684\u5957\u4EF6
791
+ if (currentVersion.startsWith('link:') || currentVersion.startsWith('workspace:')) {
792
+ return null
793
+ }
794
+
795
+ const current = parseVersion(currentVersion)
796
+ const latest = await getLatestVersion(name)
797
+
798
+ if (!latest) {
799
+ return null
800
+ }
801
+
802
+ return {
803
+ name,
804
+ current,
805
+ latest,
806
+ outdated: current !== latest,
807
+ }
808
+ }
809
+
810
+ async function main() {
811
+ log('\\n=== \u76F8\u4F9D\u5957\u4EF6\u7248\u672C\u6AA2\u67E5 ===\\n', 'blue')
812
+
813
+ const packageJsonPath = join(process.cwd(), 'package.json')
814
+ const packageJson: PackageJson = JSON.parse(
815
+ readFileSync(packageJsonPath, 'utf-8')
816
+ )
817
+
818
+ const allDependencies = {
819
+ ...packageJson.dependencies,
820
+ ...packageJson.devDependencies,
821
+ }
822
+
823
+ log(\`\u6AA2\u67E5 \${Object.keys(allDependencies).length} \u500B\u5957\u4EF6...\\n\`, 'yellow')
824
+
825
+ const results: PackageInfo[] = []
826
+ const outdated: PackageInfo[] = []
827
+ const upToDate: PackageInfo[] = []
828
+
829
+ // \u6AA2\u67E5\u6240\u6709\u5957\u4EF6
830
+ for (const [name, version] of Object.entries(allDependencies)) {
831
+ const info = await checkPackage(name, version)
832
+ if (info) {
833
+ results.push(info)
834
+ if (info.outdated) {
835
+ outdated.push(info)
836
+ } else {
837
+ upToDate.push(info)
838
+ }
839
+ }
840
+ }
841
+
842
+ // \u986F\u793A\u7D50\u679C
843
+ if (upToDate.length > 0) {
844
+ log(\`\\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C (\${upToDate.length}):\`, 'green')
845
+ upToDate.forEach((pkg) => {
846
+ log(\` \${pkg.name}: \${pkg.current}\`, 'green')
847
+ })
848
+ }
849
+
850
+ if (outdated.length > 0) {
851
+ log(\`\\n\u26A0 \u9700\u8981\u66F4\u65B0 (\${outdated.length}):\`, 'yellow')
852
+ outdated.forEach((pkg) => {
853
+ log(\` \${pkg.name}: \${pkg.current} \u2192 \${pkg.latest}\`, 'yellow')
854
+ })
855
+ }
856
+
857
+ // \u7E3D\u7D50
858
+ log('\\n=== \u6AA2\u67E5\u7D50\u679C ===', 'blue')
859
+ log(\`\u7E3D\u8A08: \${results.length} \u500B\u5957\u4EF6\`, 'blue')
860
+ log(\`\u6700\u65B0: \${upToDate.length} \u500B\`, 'green')
861
+ log(\`\u9700\u66F4\u65B0: \${outdated.length} \u500B\`, outdated.length > 0 ? 'yellow' : 'green')
862
+
863
+ // \u5982\u679C\u6709\u9700\u8981\u66F4\u65B0\u7684\u5957\u4EF6\uFF0C\u8FD4\u56DE\u975E\u96F6\u9000\u51FA\u78BC
864
+ if (outdated.length > 0) {
865
+ log('\\n\u5EFA\u8B70\u57F7\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0\u5957\u4EF6\uFF1A', 'yellow')
866
+ log(' bun update', 'yellow')
867
+ process.exit(1)
868
+ } else {
869
+ log('\\n\u2713 \u6240\u6709\u5957\u4EF6\u90FD\u662F\u6700\u65B0\u7248\u672C\uFF01', 'green')
870
+ process.exit(0)
871
+ }
872
+ }
873
+
874
+ main().catch((error) => {
875
+ log(\`\\n\u932F\u8AA4: \${error.message}\`, 'red')
876
+ process.exit(1)
877
+ })
878
+ `;
879
+ }
880
+ /**
881
+ * Generate check.sh script content.
882
+ */
883
+ generateCheckShellScript() {
884
+ return `#!/bin/bash
885
+
886
+ # \u5C08\u6848\u6AA2\u67E5\u8173\u672C
887
+ # \u57F7\u884C\u6240\u6709\u5FC5\u8981\u7684\u6AA2\u67E5\uFF1A\u985E\u578B\u6AA2\u67E5\u3001\u6E2C\u8A66\u3001\u4F9D\u8CF4\u6AA2\u67E5\u7B49
888
+
889
+ set -e
890
+
891
+ # \u984F\u8272\u5B9A\u7FA9
892
+ GREEN='\\033[0;32m'
893
+ YELLOW='\\033[1;33m'
894
+ RED='\\033[0;31m'
895
+ BLUE='\\033[0;34m'
896
+ NC='\\033[0m' # No Color
897
+
898
+ echo -e "\${BLUE}=== \u5C08\u6848\u6AA2\u67E5 ===\${NC}\\n"
899
+
900
+ # \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
901
+ if [ ! -f "package.json" ]; then
902
+ echo -e "\${RED}\u932F\u8AA4: \u8ACB\u5728\u5C08\u6848\u6839\u76EE\u9304\u57F7\u884C\u6B64\u8173\u672C\${NC}"
903
+ exit 1
904
+ fi
905
+
906
+ # \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
907
+ if ! command -v bun &> /dev/null; then
908
+ echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
909
+ exit 1
910
+ fi
911
+
912
+ # 1. \u985E\u578B\u6AA2\u67E5
913
+ echo -e "\${YELLOW}[1/3] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
914
+ if bun run typecheck; then
915
+ echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
916
+ else
917
+ echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
918
+ exit 1
919
+ fi
920
+
921
+ # 2. \u57F7\u884C\u6E2C\u8A66
922
+ echo -e "\${YELLOW}[2/3] \u57F7\u884C\u6E2C\u8A66...\${NC}"
923
+ if bun test; then
924
+ echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
925
+ else
926
+ echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
927
+ exit 1
928
+ fi
929
+
930
+ # 3. \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C\uFF08\u53EF\u9078\uFF0C\u56E0\u70BA\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA\uFF09
931
+ echo -e "\${YELLOW}[3/3] \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C...\${NC}"
932
+ if bun run check:deps; then
933
+ echo -e "\${GREEN}\u2713 \u4F9D\u8CF4\u6AA2\u67E5\u5B8C\u6210\${NC}\\n"
934
+ else
935
+ echo -e "\${YELLOW}\u26A0 \u4F9D\u8CF4\u6AA2\u67E5\u6709\u8B66\u544A\uFF08\u67D0\u4E9B\u5957\u4EF6\u53EF\u80FD\u9700\u8981\u66F4\u65B0\uFF09\${NC}\\n"
936
+ fi
937
+
938
+ echo -e "\${GREEN}=== \u6240\u6709\u6AA2\u67E5\u5B8C\u6210 ===\${NC}"
939
+ `;
940
+ }
941
+ /**
942
+ * Generate pre-commit.sh script content.
943
+ */
944
+ generatePreCommitScript() {
945
+ return `#!/bin/bash
946
+
947
+ # Pre-commit Hook
948
+ # \u5728 git commit \u524D\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5
949
+ #
950
+ # \u5B89\u88DD\u65B9\u5F0F\uFF1A
951
+ # ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
952
+ # \u6216
953
+ # cp scripts/pre-commit.sh .git/hooks/pre-commit
954
+ # chmod +x .git/hooks/pre-commit
955
+
956
+ set -e
957
+
958
+ # \u984F\u8272\u5B9A\u7FA9
959
+ GREEN='\\033[0;32m'
960
+ YELLOW='\\033[1;33m'
961
+ RED='\\033[0;31m'
962
+ BLUE='\\033[0;34m'
963
+ NC='\\033[0m' # No Color
964
+
965
+ echo -e "\${BLUE}=== Pre-commit \u6AA2\u67E5 ===\${NC}\\n"
966
+
967
+ # \u5207\u63DB\u5230\u5C08\u6848\u6839\u76EE\u9304
968
+ cd "$(git rev-parse --show-toplevel)"
969
+
970
+ # \u6AA2\u67E5\u662F\u5426\u5728\u6B63\u78BA\u7684\u76EE\u9304
971
+ if [ ! -f "package.json" ]; then
972
+ echo -e "\${RED}\u932F\u8AA4: \u627E\u4E0D\u5230 package.json\${NC}"
973
+ exit 1
974
+ fi
975
+
976
+ # \u6AA2\u67E5 Bun \u662F\u5426\u5B89\u88DD
977
+ if ! command -v bun &> /dev/null; then
978
+ echo -e "\${RED}\u932F\u8AA4: \u672A\u627E\u5230 bun\uFF0C\u8ACB\u5148\u5B89\u88DD Bun\${NC}"
979
+ exit 1
980
+ fi
981
+
982
+ # 1. \u985E\u578B\u6AA2\u67E5\uFF08\u5FEB\u901F\u6AA2\u67E5\uFF09
983
+ echo -e "\${YELLOW}[1/2] \u57F7\u884C\u985E\u578B\u6AA2\u67E5...\${NC}"
984
+ if bun run typecheck; then
985
+ echo -e "\${GREEN}\u2713 \u985E\u578B\u6AA2\u67E5\u901A\u904E\${NC}\\n"
986
+ else
987
+ echo -e "\${RED}\u2717 \u985E\u578B\u6AA2\u67E5\u5931\u6557\${NC}"
988
+ echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u985E\u578B\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
989
+ exit 1
990
+ fi
991
+
992
+ # 2. \u57F7\u884C\u6E2C\u8A66\uFF08\u53EF\u9078\uFF0C\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\u53EF\u4EE5\u8A3B\u89E3\u6389\uFF09
993
+ echo -e "\${YELLOW}[2/2] \u57F7\u884C\u6E2C\u8A66...\${NC}"
994
+ if bun test; then
995
+ echo -e "\${GREEN}\u2713 \u6E2C\u8A66\u901A\u904E\${NC}\\n"
996
+ else
997
+ echo -e "\${RED}\u2717 \u6E2C\u8A66\u5931\u6557\${NC}"
998
+ echo -e "\${YELLOW}\u63D0\u793A: \u8ACB\u4FEE\u6B63\u6E2C\u8A66\u932F\u8AA4\u5F8C\u518D\u63D0\u4EA4\${NC}"
999
+ exit 1
1000
+ fi
1001
+
1002
+ echo -e "\${GREEN}=== Pre-commit \u6AA2\u67E5\u901A\u904E ===\${NC}\\n"
1003
+ `;
1004
+ }
1005
+ /**
1006
+ * Generate CHECK_SYSTEM.md documentation.
1007
+ */
1008
+ generateCheckSystemDoc(context) {
1009
+ return `# \u5C08\u6848\u6AA2\u67E5\u7CFB\u7D71
1010
+
1011
+ \u672C\u5C08\u6848\u5DF2\u5EFA\u7ACB\u5B8C\u6574\u7684\u672C\u5730\u6AA2\u67E5\u6A5F\u5236\uFF0C\u7121\u9700\u4F9D\u8CF4 GitHub CI\u3002
1012
+
1013
+ ## \u5FEB\u901F\u958B\u59CB
1014
+
1015
+ ### \u57F7\u884C\u5B8C\u6574\u6AA2\u67E5
1016
+ \`\`\`bash
1017
+ bun run validate
1018
+ \`\`\`
1019
+
1020
+ ### \u57F7\u884C\u55AE\u9805\u6AA2\u67E5
1021
+ \`\`\`bash
1022
+ # \u985E\u578B\u6AA2\u67E5
1023
+ bun run typecheck
1024
+
1025
+ # \u6E2C\u8A66
1026
+ bun run test
1027
+
1028
+ # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
1029
+ bun run check:deps
1030
+ \`\`\`
1031
+
1032
+ ## \u53EF\u7528\u547D\u4EE4
1033
+
1034
+ ### Package.json \u8173\u672C
1035
+
1036
+ | \u547D\u4EE4 | \u8AAA\u660E |
1037
+ |------|------|
1038
+ | \`bun run typecheck\` | TypeScript \u985E\u578B\u6AA2\u67E5 |
1039
+ | \`bun run test\` | \u57F7\u884C\u6240\u6709\u6E2C\u8A66 |
1040
+ | \`bun run check\` | \u985E\u578B\u6AA2\u67E5 + \u6E2C\u8A66 |
1041
+ | \`bun run check:deps\` | \u6AA2\u67E5\u4F9D\u8CF4\u7248\u672C |
1042
+ | \`bun run validate\` | \u5B8C\u6574\u9A57\u8B49\uFF08\u985E\u578B + \u6E2C\u8A66 + \u4F9D\u8CF4\uFF09 |
1043
+ | \`bun run precommit\` | \u7B49\u540C\u65BC \`validate\` |
1044
+
1045
+ ### Shell \u8173\u672C
1046
+
1047
+ | \u8173\u672C | \u8AAA\u660E |
1048
+ |------|------|
1049
+ | \`./scripts/check.sh\` | \u5B8C\u6574\u5C08\u6848\u6AA2\u67E5\uFF08Shell \u7248\u672C\uFF09 |
1050
+ | \`./scripts/pre-commit.sh\` | Pre-commit hook \u8173\u672C |
1051
+
1052
+ ## Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
1053
+
1054
+ \u5B89\u88DD pre-commit hook \u5F8C\uFF0C\u6BCF\u6B21 \`git commit\` \u524D\u6703\u81EA\u52D5\u57F7\u884C\u6AA2\u67E5\uFF1A
1055
+
1056
+ \`\`\`bash
1057
+ # \u5B89\u88DD pre-commit hook
1058
+ ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
1059
+
1060
+ # \u6216\u4F7F\u7528\u8907\u88FD\u65B9\u5F0F
1061
+ cp scripts/pre-commit.sh .git/hooks/pre-commit
1062
+ chmod +x .git/hooks/pre-commit
1063
+ \`\`\`
1064
+
1065
+ **\u529F\u80FD\uFF1A**
1066
+ - \u2705 \u81EA\u52D5\u57F7\u884C\u985E\u578B\u6AA2\u67E5
1067
+ - \u2705 \u81EA\u52D5\u57F7\u884C\u6E2C\u8A66
1068
+ - \u274C \u6AA2\u67E5\u5931\u6557\u6642\u963B\u6B62\u63D0\u4EA4
1069
+
1070
+ **\u8DF3\u904E\u6AA2\u67E5\uFF08\u4E0D\u63A8\u85A6\uFF09\uFF1A**
1071
+ \`\`\`bash
1072
+ git commit --no-verify -m "\u7DCA\u6025\u4FEE\u5FA9"
1073
+ \`\`\`
1074
+
1075
+ ## \u6AA2\u67E5\u9805\u76EE
1076
+
1077
+ ### 1. \u985E\u578B\u6AA2\u67E5
1078
+ - \u4F7F\u7528 \`tsc --noEmit\` \u6AA2\u67E5 TypeScript \u985E\u578B
1079
+ - \u78BA\u4FDD\u6C92\u6709\u985E\u578B\u932F\u8AA4
1080
+
1081
+ ### 2. \u6E2C\u8A66
1082
+ - \u57F7\u884C\u6240\u6709\u55AE\u5143\u6E2C\u8A66\u548C\u6574\u5408\u6E2C\u8A66
1083
+ - \u78BA\u4FDD\u6E2C\u8A66\u901A\u904E
1084
+
1085
+ ### 3. \u4F9D\u8CF4\u6AA2\u67E5\uFF08\u53EF\u9078\uFF09
1086
+ - \u6AA2\u67E5\u5957\u4EF6\u7248\u672C\u662F\u5426\u70BA\u6700\u65B0
1087
+ - \u63D0\u4F9B\u66F4\u65B0\u5EFA\u8B70
1088
+ - \u9700\u8981\u7DB2\u8DEF\u9023\u7DDA
1089
+
1090
+ ## \u5DE5\u4F5C\u6D41\u7A0B\u5EFA\u8B70
1091
+
1092
+ ### \u958B\u767C\u6642
1093
+ 1. \u958B\u767C\u529F\u80FD
1094
+ 2. \u63D0\u4EA4\u524D\u57F7\u884C \`bun run validate\`
1095
+ 3. \u4FEE\u6B63\u554F\u984C
1096
+ 4. \u63D0\u4EA4\u7A0B\u5F0F\u78BC
1097
+
1098
+ ### \u4F7F\u7528 Pre-commit Hook\uFF08\u63A8\u85A6\uFF09
1099
+ 1. \u5B89\u88DD pre-commit hook\uFF08\u53EA\u9700\u4E00\u6B21\uFF09
1100
+ 2. \u6B63\u5E38\u958B\u767C\u548C\u63D0\u4EA4
1101
+ 3. \u6AA2\u67E5\u6703\u81EA\u52D5\u57F7\u884C
1102
+ 4. \u5982\u6709\u554F\u984C\uFF0C\u4FEE\u6B63\u5F8C\u91CD\u65B0\u63D0\u4EA4
1103
+
1104
+ ## \u6A94\u6848\u7D50\u69CB
1105
+
1106
+ \`\`\`
1107
+ ${context.nameKebabCase}/
1108
+ \u251C\u2500\u2500 package.json # \u6AA2\u67E5\u8173\u672C\u5B9A\u7FA9
1109
+ \u251C\u2500\u2500 scripts/
1110
+ \u2502 \u251C\u2500\u2500 check.sh # \u5B8C\u6574\u6AA2\u67E5\u8173\u672C\uFF08Shell\uFF09
1111
+ \u2502 \u251C\u2500\u2500 check-dependencies.ts # \u4F9D\u8CF4\u7248\u672C\u6AA2\u67E5
1112
+ \u2502 \u2514\u2500\u2500 pre-commit.sh # Pre-commit hook
1113
+ \u2514\u2500\u2500 CHECK_SYSTEM.md # \u672C\u6587\u4EF6
1114
+ \`\`\`
1115
+
1116
+ ## \u6CE8\u610F\u4E8B\u9805
1117
+
1118
+ 1. **\u4F9D\u8CF4\u6AA2\u67E5\u9700\u8981\u7DB2\u8DEF\u9023\u7DDA**\uFF1A\`check:deps\` \u9700\u8981\u9023\u63A5\u5230 npm registry
1119
+ 2. **\u6E2C\u8A66\u6642\u9593**\uFF1A\u5982\u679C\u6E2C\u8A66\u6642\u9593\u8F03\u9577\uFF0C\u53EF\u4EE5\u7DE8\u8F2F \`pre-commit.sh\` \u8A3B\u89E3\u6389\u6E2C\u8A66\u90E8\u5206
1120
+ 3. **\u985E\u578B\u932F\u8AA4**\uFF1A\u5C08\u6848\u4E2D\u53EF\u80FD\u9084\u6709\u4E00\u4E9B\u65E2\u6709\u7684\u985E\u578B\u932F\u8AA4\uFF0C\u5EFA\u8B70\u9010\u6B65\u4FEE\u6B63
1121
+
1122
+ ## \u6545\u969C\u6392\u9664
1123
+
1124
+ ### \u6AA2\u67E5\u5931\u6557
1125
+ 1. \u67E5\u770B\u932F\u8AA4\u8A0A\u606F
1126
+ 2. \u4FEE\u6B63\u554F\u984C
1127
+ 3. \u91CD\u65B0\u57F7\u884C\u6AA2\u67E5
1128
+
1129
+ ### \u8DF3\u904E\u6AA2\u67E5
1130
+ \u53EA\u6709\u5728\u7DCA\u6025\u60C5\u6CC1\u4E0B\u624D\u4F7F\u7528\uFF1A
1131
+ \`\`\`bash
1132
+ git commit --no-verify
1133
+ \`\`\`
1134
+
1135
+ ### \u79FB\u9664 Pre-commit Hook
1136
+ \`\`\`bash
1137
+ rm .git/hooks/pre-commit
1138
+ \`\`\`
1139
+ `;
1140
+ }
688
1141
  /**
689
1142
  * Log a message if verbose mode is enabled.
690
1143
  */
@@ -865,6 +1318,11 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
865
1318
  type: "directory",
866
1319
  name: "Providers",
867
1320
  children: [
1321
+ {
1322
+ type: "file",
1323
+ name: "index.ts",
1324
+ content: this.generateProvidersIndex()
1325
+ },
868
1326
  {
869
1327
  type: "file",
870
1328
  name: "AppServiceProvider.ts",
@@ -874,6 +1332,16 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
874
1332
  type: "file",
875
1333
  name: "RepositoryServiceProvider.ts",
876
1334
  content: this.generateRepositoryServiceProvider()
1335
+ },
1336
+ {
1337
+ type: "file",
1338
+ name: "MiddlewareProvider.ts",
1339
+ content: this.generateMiddlewareProvider()
1340
+ },
1341
+ {
1342
+ type: "file",
1343
+ name: "RouteProvider.ts",
1344
+ content: this.generateRouteProvider()
877
1345
  }
878
1346
  ]
879
1347
  }
@@ -1367,6 +1835,65 @@ export class RepositoryServiceProvider extends ServiceProvider {
1367
1835
  container.singleton('mailService', () => new MailService())
1368
1836
  }
1369
1837
  }
1838
+ `;
1839
+ }
1840
+ generateProvidersIndex() {
1841
+ return `/**
1842
+ * Application Service Providers
1843
+ */
1844
+
1845
+ export { AppServiceProvider } from './AppServiceProvider'
1846
+ export { RepositoryServiceProvider } from './RepositoryServiceProvider'
1847
+ export { MiddlewareProvider } from './MiddlewareProvider'
1848
+ export { RouteProvider } from './RouteProvider'
1849
+ `;
1850
+ }
1851
+ generateMiddlewareProvider() {
1852
+ return `/**
1853
+ * Middleware Service Provider
1854
+ */
1855
+
1856
+ import {
1857
+ ServiceProvider,
1858
+ type Container,
1859
+ type PlanetCore,
1860
+ bodySizeLimit,
1861
+ securityHeaders,
1862
+ } from '@gravito/core'
1863
+
1864
+ export class MiddlewareProvider extends ServiceProvider {
1865
+ register(_container: Container): void {}
1866
+
1867
+ boot(core: PlanetCore): void {
1868
+ const isDev = process.env.NODE_ENV !== 'production'
1869
+
1870
+ core.adapter.use('*', securityHeaders({
1871
+ contentSecurityPolicy: isDev ? false : undefined,
1872
+ }))
1873
+
1874
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
1875
+
1876
+ core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
1877
+ }
1878
+ }
1879
+ `;
1880
+ }
1881
+ generateRouteProvider() {
1882
+ return `/**
1883
+ * Route Service Provider
1884
+ */
1885
+
1886
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
1887
+ import { registerApiRoutes } from '../../Interface/Http/Routes/api'
1888
+
1889
+ export class RouteProvider extends ServiceProvider {
1890
+ register(_container: Container): void {}
1891
+
1892
+ boot(core: PlanetCore): void {
1893
+ registerApiRoutes(core.router)
1894
+ core.logger.info('\u{1F6E4}\uFE0F Routes registered')
1895
+ }
1896
+ }
1370
1897
  `;
1371
1898
  }
1372
1899
  // ─────────────────────────────────────────────────────────────
@@ -1451,64 +1978,55 @@ export class UserPresenter {
1451
1978
  }
1452
1979
  `;
1453
1980
  }
1454
- generateBootstrap(context) {
1981
+ generateBootstrap(_context) {
1455
1982
  return `/**
1456
1983
  * Application Bootstrap
1984
+ *
1985
+ * The entry point for your Clean Architecture application.
1986
+ * Uses the ServiceProvider pattern for modular initialization.
1987
+ *
1988
+ * Lifecycle:
1989
+ * 1. Configure: Load app config and orbits
1990
+ * 2. Boot: Initialize PlanetCore
1991
+ * 3. Register Providers: Bind services to container
1992
+ * 4. Bootstrap: Boot all providers
1457
1993
  */
1458
1994
 
1459
- import { bodySizeLimit, PlanetCore, securityHeaders } from '@gravito/core'
1995
+ import { defineConfig, PlanetCore } from '@gravito/core'
1460
1996
  import { OrbitAtlas } from '@gravito/atlas'
1461
- import databaseConfig from '../config/database'
1462
- import { AppServiceProvider } from './Infrastructure/Providers/AppServiceProvider'
1463
- import { RepositoryServiceProvider } from './Infrastructure/Providers/RepositoryServiceProvider'
1464
- import { registerApiRoutes } from './Interface/Http/Routes/api'
1465
-
1466
- const core = new PlanetCore({
1467
- config: {
1468
- APP_NAME: '${context.name}',
1469
- database: databaseConfig,
1470
- },
1471
- })
1472
-
1473
- const defaultCsp = [
1474
- "default-src 'self'",
1475
- "script-src 'self' 'unsafe-inline'",
1476
- "style-src 'self' 'unsafe-inline'",
1477
- "img-src 'self' data:",
1478
- "object-src 'none'",
1479
- "base-uri 'self'",
1480
- "frame-ancestors 'none'",
1481
- ].join('; ')
1482
- const cspValue = process.env.APP_CSP
1483
- const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
1484
- const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
1485
- const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
1486
- const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
1487
-
1488
- core.adapter.use(
1489
- '*',
1490
- securityHeaders({
1491
- contentSecurityPolicy: csp,
1492
- hsts:
1493
- process.env.NODE_ENV === 'production'
1494
- ? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
1495
- : false,
1997
+ import appConfig from '../config/app'
1998
+ import {
1999
+ AppServiceProvider,
2000
+ RepositoryServiceProvider,
2001
+ MiddlewareProvider,
2002
+ RouteProvider,
2003
+ } from './Infrastructure/Providers'
2004
+
2005
+ export async function bootstrap() {
2006
+ // 1. Configure
2007
+ const config = defineConfig({
2008
+ config: appConfig,
2009
+ orbits: [new OrbitAtlas()],
1496
2010
  })
1497
- )
1498
- if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
1499
- core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
1500
- }
1501
2011
 
1502
- await core.orbit(new OrbitAtlas())
2012
+ // 2. Boot Core
2013
+ const core = await PlanetCore.boot(config)
2014
+ core.registerGlobalErrorHandlers()
1503
2015
 
1504
- core.register(new RepositoryServiceProvider())
1505
- core.register(new AppServiceProvider())
2016
+ // 3. Register Providers
2017
+ core.register(new RepositoryServiceProvider())
2018
+ core.register(new AppServiceProvider())
2019
+ core.register(new MiddlewareProvider())
2020
+ core.register(new RouteProvider())
1506
2021
 
1507
- await core.bootstrap()
2022
+ // 4. Bootstrap All Providers
2023
+ await core.bootstrap()
1508
2024
 
1509
- // Register routes
1510
- registerApiRoutes(core.router)
2025
+ return core
2026
+ }
1511
2027
 
2028
+ // Application Entry Point
2029
+ const core = await bootstrap()
1512
2030
  export default core.liftoff()
1513
2031
  `;
1514
2032
  }
@@ -1520,6 +2038,15 @@ export default core.liftoff()
1520
2038
  This project follows **Clean Architecture** (by Robert C. Martin).
1521
2039
  The key principle is the **Dependency Rule**: dependencies point inward.
1522
2040
 
2041
+ ## Service Providers
2042
+
2043
+ Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
2044
+
2045
+ ### Provider Lifecycle
2046
+
2047
+ 1. **register()**: Bind services to the container (sync or async).
2048
+ 2. **boot()**: Called after ALL providers have registered. Safe to use other services.
2049
+
1523
2050
  ## Layer Structure
1524
2051
 
1525
2052
  \`\`\`
@@ -1591,6 +2118,10 @@ Created with \u2764\uFE0F using Gravito Framework
1591
2118
  start: "bun run dist/bootstrap.js",
1592
2119
  test: "bun test",
1593
2120
  typecheck: "tsc --noEmit",
2121
+ check: "bun run typecheck && bun run test",
2122
+ "check:deps": "bun run scripts/check-dependencies.ts",
2123
+ validate: "bun run check && bun run check:deps",
2124
+ precommit: "bun run validate",
1594
2125
  "docker:build": `docker build -t ${context.nameKebabCase} .`,
1595
2126
  "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
1596
2127
  },
@@ -1914,63 +2445,46 @@ var DddGenerator = class extends BaseGenerator {
1914
2445
  // ─────────────────────────────────────────────────────────────
1915
2446
  // Bootstrap File Generators
1916
2447
  // ─────────────────────────────────────────────────────────────
1917
- generateBootstrapApp(context) {
2448
+ generateBootstrapApp(_context) {
1918
2449
  return `/**
1919
2450
  * Application Bootstrap
1920
2451
  *
1921
- * Central configuration and initialization of the application.
2452
+ * Central configuration and initialization using the ServiceProvider pattern.
2453
+ *
2454
+ * Lifecycle:
2455
+ * 1. Configure: Load app config and orbits
2456
+ * 2. Boot: Initialize PlanetCore
2457
+ * 3. Register Providers: Bind services to container
2458
+ * 4. Bootstrap: Boot all providers
1922
2459
  */
1923
2460
 
1924
- import { bodySizeLimit, PlanetCore, securityHeaders } from '@gravito/core'
2461
+ import { defineConfig, PlanetCore } from '@gravito/core'
2462
+ import { OrbitAtlas } from '@gravito/atlas'
2463
+ import appConfig from '../../config/app'
1925
2464
  import { registerProviders } from './providers'
1926
2465
  import { registerRoutes } from './routes'
1927
2466
 
1928
2467
  export async function createApp(): Promise<PlanetCore> {
1929
- const core = new PlanetCore({
1930
- config: {
1931
- APP_NAME: '${context.name}',
1932
- },
1933
- })
2468
+ // 1. Configure
2469
+ const config = defineConfig({
2470
+ config: appConfig,
2471
+ orbits: [new OrbitAtlas()],
2472
+ })
1934
2473
 
1935
- const defaultCsp = [
1936
- "default-src 'self'",
1937
- "script-src 'self' 'unsafe-inline'",
1938
- "style-src 'self' 'unsafe-inline'",
1939
- "img-src 'self' data:",
1940
- "object-src 'none'",
1941
- "base-uri 'self'",
1942
- "frame-ancestors 'none'",
1943
- ].join('; ')
1944
- const cspValue = process.env.APP_CSP
1945
- const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
1946
- const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
1947
- const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
1948
- const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
1949
-
1950
- core.adapter.use(
1951
- '*',
1952
- securityHeaders({
1953
- contentSecurityPolicy: csp,
1954
- hsts:
1955
- process.env.NODE_ENV === 'production'
1956
- ? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
1957
- : false,
1958
- })
1959
- )
1960
- if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
1961
- core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
1962
- }
2474
+ // 2. Boot Core
2475
+ const core = await PlanetCore.boot(config)
2476
+ core.registerGlobalErrorHandlers()
1963
2477
 
1964
- // Register all service providers
1965
- await registerProviders(core)
2478
+ // 3. Register Providers
2479
+ await registerProviders(core)
1966
2480
 
1967
- // Bootstrap the application
1968
- await core.bootstrap()
2481
+ // 4. Bootstrap All Providers
2482
+ await core.bootstrap()
1969
2483
 
1970
- // Register routes
1971
- registerRoutes(core.router)
2484
+ // Register routes after bootstrap
2485
+ registerRoutes(core.router)
1972
2486
 
1973
- return core
2487
+ return core
1974
2488
  }
1975
2489
  `;
1976
2490
  }
@@ -1978,19 +2492,48 @@ export async function createApp(): Promise<PlanetCore> {
1978
2492
  return `/**
1979
2493
  * Service Providers Registry
1980
2494
  *
1981
- * Register all module service providers here.
2495
+ * Register all service providers here.
2496
+ * Include both global and module-specific providers.
1982
2497
  */
1983
2498
 
1984
- import type { PlanetCore } from '@gravito/core'
2499
+ import {
2500
+ ServiceProvider,
2501
+ type Container,
2502
+ type PlanetCore,
2503
+ bodySizeLimit,
2504
+ securityHeaders,
2505
+ } from '@gravito/core'
1985
2506
  import { OrderingServiceProvider } from '../Modules/Ordering/Infrastructure/Providers/OrderingServiceProvider'
1986
2507
  import { CatalogServiceProvider } from '../Modules/Catalog/Infrastructure/Providers/CatalogServiceProvider'
1987
2508
 
2509
+ /**
2510
+ * Middleware Provider - Global middleware registration
2511
+ */
2512
+ export class MiddlewareProvider extends ServiceProvider {
2513
+ register(_container: Container): void {}
2514
+
2515
+ boot(core: PlanetCore): void {
2516
+ const isDev = process.env.NODE_ENV !== 'production'
2517
+
2518
+ core.adapter.use('*', securityHeaders({
2519
+ contentSecurityPolicy: isDev ? false : undefined,
2520
+ }))
2521
+
2522
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024))
2523
+
2524
+ core.logger.info('\u{1F6E1}\uFE0F Global middleware registered')
2525
+ }
2526
+ }
2527
+
1988
2528
  export async function registerProviders(core: PlanetCore): Promise<void> {
1989
- // Register module providers
1990
- core.register(new OrderingServiceProvider())
1991
- core.register(new CatalogServiceProvider())
2529
+ // Global Providers
2530
+ core.register(new MiddlewareProvider())
1992
2531
 
1993
- // Add more providers as needed
2532
+ // Module Providers
2533
+ core.register(new OrderingServiceProvider())
2534
+ core.register(new CatalogServiceProvider())
2535
+
2536
+ // Add more providers as needed
1994
2537
  }
1995
2538
  `;
1996
2539
  }
@@ -2104,6 +2647,10 @@ export class ${name}ServiceProvider extends ServiceProvider {
2104
2647
  start: "bun run dist/main.js",
2105
2648
  test: "bun test",
2106
2649
  typecheck: "tsc --noEmit",
2650
+ check: "bun run typecheck && bun run test",
2651
+ "check:deps": "bun run scripts/check-dependencies.ts",
2652
+ validate: "bun run check && bun run check:deps",
2653
+ precommit: "bun run validate",
2107
2654
  "docker:build": `docker build -t ${context.nameKebabCase} .`,
2108
2655
  "docker:run": `docker run -it -p 3000:3000 ${context.nameKebabCase}`
2109
2656
  },
@@ -2544,6 +3091,16 @@ export function report(error: unknown): void {
2544
3091
 
2545
3092
  This project follows **Domain-Driven Design (DDD)** with strategic and tactical patterns.
2546
3093
 
3094
+ ## Service Providers
3095
+
3096
+ Service providers are the central place to configure your application and modules. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
3097
+
3098
+ ### Internal Bootstrapping
3099
+
3100
+ 1. **Bootstrap/app.ts**: Orchestrates the 4-step lifecycle (Configure, Boot, Register, Bootstrap).
3101
+ 2. **Bootstrap/providers.ts**: Central registry for all global and module-specific providers.
3102
+ 3. **Infrastructure/Providers/[Module]ServiceProvider.ts**: Module-specific service registration.
3103
+
2547
3104
  ## Bounded Contexts
2548
3105
 
2549
3106
  \`\`\`
@@ -2681,6 +3238,11 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
2681
3238
  type: "directory",
2682
3239
  name: "Providers",
2683
3240
  children: [
3241
+ {
3242
+ type: "file",
3243
+ name: "index.ts",
3244
+ content: this.generateProvidersIndex()
3245
+ },
2684
3246
  {
2685
3247
  type: "file",
2686
3248
  name: "AppServiceProvider.ts",
@@ -2688,8 +3250,18 @@ var EnterpriseMvcGenerator = class extends BaseGenerator {
2688
3250
  },
2689
3251
  {
2690
3252
  type: "file",
2691
- name: "RouteServiceProvider.ts",
2692
- content: this.generateRouteServiceProvider(context)
3253
+ name: "DatabaseProvider.ts",
3254
+ content: this.generateDatabaseProvider()
3255
+ },
3256
+ {
3257
+ type: "file",
3258
+ name: "MiddlewareProvider.ts",
3259
+ content: this.generateMiddlewareProvider()
3260
+ },
3261
+ {
3262
+ type: "file",
3263
+ name: "RouteProvider.ts",
3264
+ content: this.generateRouteProvider()
2693
3265
  }
2694
3266
  ]
2695
3267
  },
@@ -3156,6 +3728,135 @@ export class RouteServiceProvider extends ServiceProvider {
3156
3728
  registerRoutes(core.router)
3157
3729
  }
3158
3730
  }
3731
+ `;
3732
+ }
3733
+ // ─────────────────────────────────────────────────────────────
3734
+ // Modern Provider Generators (ServiceProvider Pattern)
3735
+ // ─────────────────────────────────────────────────────────────
3736
+ generateProvidersIndex() {
3737
+ return `/**
3738
+ * Application Service Providers
3739
+ *
3740
+ * Export all providers for easy importing in bootstrap.
3741
+ * Providers are registered in the order they are listed.
3742
+ */
3743
+
3744
+ export { AppServiceProvider } from './AppServiceProvider'
3745
+ export { DatabaseProvider } from './DatabaseProvider'
3746
+ export { MiddlewareProvider } from './MiddlewareProvider'
3747
+ export { RouteProvider } from './RouteProvider'
3748
+ `;
3749
+ }
3750
+ generateDatabaseProvider() {
3751
+ return `/**
3752
+ * Database Service Provider
3753
+ *
3754
+ * Handles database initialization and migrations.
3755
+ *
3756
+ * Lifecycle:
3757
+ * - register(): Bind database config to container
3758
+ * - boot(): Run migrations and seeders
3759
+ */
3760
+
3761
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
3762
+ import databaseConfig from '../../config/database'
3763
+
3764
+ export class DatabaseProvider extends ServiceProvider {
3765
+ /**
3766
+ * Register database configuration.
3767
+ */
3768
+ register(_container: Container): void {
3769
+ this.mergeConfig(this.core!.config, 'database', databaseConfig)
3770
+ }
3771
+
3772
+ /**
3773
+ * Initialize database connections.
3774
+ */
3775
+ async boot(core: PlanetCore): Promise<void> {
3776
+ // Database initialization will be handled by Atlas orbit
3777
+ core.logger.info('\u{1F4E6} Database provider booted')
3778
+ }
3779
+ }
3780
+ `;
3781
+ }
3782
+ generateMiddlewareProvider() {
3783
+ return `/**
3784
+ * Middleware Service Provider
3785
+ *
3786
+ * Registers global middleware stack.
3787
+ * Provides a centralized location for middleware configuration.
3788
+ *
3789
+ * Lifecycle:
3790
+ * - register(): N/A (no container bindings)
3791
+ * - boot(): Register global middleware
3792
+ */
3793
+
3794
+ import {
3795
+ ServiceProvider,
3796
+ type Container,
3797
+ type PlanetCore,
3798
+ bodySizeLimit,
3799
+ securityHeaders,
3800
+ } from '@gravito/core'
3801
+
3802
+ export class MiddlewareProvider extends ServiceProvider {
3803
+ /**
3804
+ * No container bindings needed.
3805
+ */
3806
+ register(_container: Container): void {
3807
+ // Middleware doesn't require container bindings
3808
+ }
3809
+
3810
+ /**
3811
+ * Register global middleware stack.
3812
+ */
3813
+ boot(core: PlanetCore): void {
3814
+ const isDev = process.env.NODE_ENV !== 'production'
3815
+
3816
+ // Security Headers
3817
+ core.adapter.use('*', securityHeaders({
3818
+ contentSecurityPolicy: isDev ? false : undefined,
3819
+ }))
3820
+
3821
+ // Body Parser Limits
3822
+ core.adapter.use('*', bodySizeLimit(10 * 1024 * 1024)) // 10MB limit
3823
+
3824
+ core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
3825
+ }
3826
+ }
3827
+ `;
3828
+ }
3829
+ generateRouteProvider() {
3830
+ return `/**
3831
+ * Route Service Provider
3832
+ *
3833
+ * Registers application routes.
3834
+ * Routes are registered in the boot phase after all services are available.
3835
+ *
3836
+ * Lifecycle:
3837
+ * - register(): N/A
3838
+ * - boot(): Register routes
3839
+ */
3840
+
3841
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
3842
+ import { registerRoutes } from '../routes'
3843
+
3844
+ export class RouteProvider extends ServiceProvider {
3845
+ /**
3846
+ * No container bindings needed.
3847
+ */
3848
+ register(_container: Container): void {
3849
+ // Routes don't require container bindings
3850
+ }
3851
+
3852
+ /**
3853
+ * Register application routes.
3854
+ */
3855
+ boot(core: PlanetCore): void {
3856
+ registerRoutes(core.router)
3857
+ core.logger.info('\u{1F6E4}\uFE0F Routes registered')
3858
+ }
3859
+ }
3159
3860
  `;
3160
3861
  }
3161
3862
  generateExceptionHandler() {
@@ -3204,74 +3905,75 @@ export const dontReport: string[] = [
3204
3905
  }
3205
3906
  generateBootstrap(context) {
3206
3907
  const spectrumImport = context.withSpectrum ? "import { SpectrumOrbit } from '@gravito/spectrum'\n" : "";
3207
- const spectrumOrbit = context.withSpectrum ? `
3208
- // Enable Debug Dashboard
3209
- if (process.env.APP_DEBUG === 'true') {
3210
- await core.orbit(new SpectrumOrbit())
3211
- }
3212
- ` : "";
3908
+ const spectrumOrbit = context.withSpectrum ? " new SpectrumOrbit()," : "";
3213
3909
  return `/**
3214
3910
  * Application Bootstrap
3215
3911
  *
3216
- * This is the entry point for your application.
3217
- * It initializes the core and registers all providers.
3912
+ * The entry point for your Gravito application.
3913
+ * Uses the ServiceProvider pattern for modular, maintainable initialization.
3914
+ *
3915
+ * Lifecycle:
3916
+ * 1. Configure: Load app config and orbits
3917
+ * 2. Boot: Initialize PlanetCore
3918
+ * 3. Register Providers: Bind services to container
3919
+ * 4. Bootstrap: Boot all providers
3920
+ *
3921
+ * @module bootstrap
3218
3922
  */
3219
3923
 
3220
- import { bodySizeLimit, PlanetCore, securityHeaders } from '@gravito/core'
3924
+ import { defineConfig, PlanetCore } from '@gravito/core'
3221
3925
  import { OrbitAtlas } from '@gravito/atlas'
3222
- import databaseConfig from '../config/database'
3223
- ${spectrumImport}import { AppServiceProvider } from './Providers/AppServiceProvider'
3224
- import { RouteServiceProvider } from './Providers/RouteServiceProvider'
3225
-
3226
- // Load environment variables
3227
- // Bun automatically loads .env
3228
-
3229
- // Create application core
3230
- const core = new PlanetCore({
3231
- config: {
3232
- APP_NAME: process.env.APP_NAME ?? '${context.name}',
3233
- database: databaseConfig,
3234
- },
3235
- })
3236
- const defaultCsp = [
3237
- "default-src 'self'",
3238
- "script-src 'self' 'unsafe-inline'",
3239
- "style-src 'self' 'unsafe-inline'",
3240
- "img-src 'self' data:",
3241
- "object-src 'none'",
3242
- "base-uri 'self'",
3243
- "frame-ancestors 'none'",
3244
- ].join('; ')
3245
- const cspValue = process.env.APP_CSP
3246
- const csp = cspValue === 'false' ? false : (cspValue ?? defaultCsp)
3247
- const hstsMaxAge = Number.parseInt(process.env.APP_HSTS_MAX_AGE ?? '15552000', 10)
3248
- const bodyLimit = Number.parseInt(process.env.APP_BODY_LIMIT ?? '1048576', 10)
3249
- const requireLength = process.env.APP_BODY_REQUIRE_LENGTH === 'true'
3250
-
3251
- core.adapter.use(
3252
- '*',
3253
- securityHeaders({
3254
- contentSecurityPolicy: csp,
3255
- hsts:
3256
- process.env.NODE_ENV === 'production'
3257
- ? { maxAge: Number.isNaN(hstsMaxAge) ? 15552000 : hstsMaxAge, includeSubDomains: true }
3258
- : false,
3259
- })
3260
- )
3261
- if (!Number.isNaN(bodyLimit) && bodyLimit > 0) {
3262
- core.adapter.use('*', bodySizeLimit(bodyLimit, { requireContentLength: requireLength }))
3263
- }
3926
+ import appConfig from '../config/app'
3927
+ ${spectrumImport}import {
3928
+ AppServiceProvider,
3929
+ DatabaseProvider,
3930
+ MiddlewareProvider,
3931
+ RouteProvider,
3932
+ } from './Providers'
3264
3933
 
3265
- await core.orbit(new OrbitAtlas())
3934
+ /**
3935
+ * Bootstrap the application with service providers.
3936
+ *
3937
+ * @returns The booted PlanetCore instance
3938
+ */
3939
+ export async function bootstrap() {
3940
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3941
+ // 1. Configure
3942
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3943
+ const config = defineConfig({
3944
+ config: appConfig,
3945
+ orbits: [
3946
+ new OrbitAtlas(),
3266
3947
  ${spectrumOrbit}
3267
- // Register service providers
3268
- core.register(new AppServiceProvider())
3269
- core.register(new RouteServiceProvider())
3948
+ ],
3949
+ })
3270
3950
 
3271
- // Bootstrap the application
3272
- await core.bootstrap()
3951
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3952
+ // 2. Boot Core
3953
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3954
+ const core = await PlanetCore.boot(config)
3955
+ core.registerGlobalErrorHandlers()
3956
+
3957
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3958
+ // 3. Register Providers
3959
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3960
+ core.register(new AppServiceProvider())
3961
+ core.register(new DatabaseProvider())
3962
+ core.register(new MiddlewareProvider())
3963
+ core.register(new RouteProvider())
3964
+
3965
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3966
+ // 4. Bootstrap All Providers
3967
+ // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
3968
+ await core.bootstrap()
3969
+
3970
+ return core
3971
+ }
3273
3972
 
3274
- // Export for Bun.serve()
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
+ // Application Entry Point
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 bootstrap()
3275
3977
  export default core.liftoff()
3276
3978
  `;
3277
3979
  }
@@ -3698,7 +4400,9 @@ export class ${name}ServiceProvider extends ServiceProvider {
3698
4400
  scripts: {
3699
4401
  build: "tsup src/index.ts --format cjs,esm --dts",
3700
4402
  test: "bun test",
3701
- typecheck: "tsc --noEmit"
4403
+ typecheck: "tsc --noEmit",
4404
+ check: "bun run typecheck && bun run test",
4405
+ validate: "bun run check"
3702
4406
  },
3703
4407
  dependencies: {
3704
4408
  "@gravito/core": depVersion,
@@ -3996,10 +4700,25 @@ var ActionDomainGenerator = class extends BaseGenerator {
3996
4700
  type: "directory",
3997
4701
  name: "providers",
3998
4702
  children: [
4703
+ {
4704
+ type: "file",
4705
+ name: "index.ts",
4706
+ content: this.generateProvidersIndex()
4707
+ },
3999
4708
  {
4000
4709
  type: "file",
4001
4710
  name: "AppServiceProvider.ts",
4002
4711
  content: this.generateAppServiceProvider(context)
4712
+ },
4713
+ {
4714
+ type: "file",
4715
+ name: "MiddlewareProvider.ts",
4716
+ content: this.generateMiddlewareProvider()
4717
+ },
4718
+ {
4719
+ type: "file",
4720
+ name: "RouteProvider.ts",
4721
+ content: this.generateRouteProvider()
4003
4722
  }
4004
4723
  ]
4005
4724
  },
@@ -4177,7 +4896,7 @@ export function registerApiRoutes(router: Router) {
4177
4896
  import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
4178
4897
 
4179
4898
  export class AppServiceProvider extends ServiceProvider {
4180
- register(container: Container): void {
4899
+ register(_container: Container): void {
4181
4900
  // Register global services here
4182
4901
  }
4183
4902
 
@@ -4187,47 +4906,140 @@ export class AppServiceProvider extends ServiceProvider {
4187
4906
  }
4188
4907
  `;
4189
4908
  }
4190
- generateBootstrap(context) {
4909
+ generateProvidersIndex() {
4191
4910
  return `/**
4192
- * Application Entry Point
4911
+ * Application Service Providers
4193
4912
  */
4194
4913
 
4195
- import { PlanetCore, securityHeaders, bodySizeLimit } from '@gravito/core'
4196
- import { OrbitAtlas } from '@gravito/atlas'
4197
- import databaseConfig from '../config/database'
4198
- import { AppServiceProvider } from './providers/AppServiceProvider'
4199
- import { registerApiRoutes } from './routes/api'
4200
-
4201
- // Initialize Core
4202
- const core = new PlanetCore({
4203
- config: {
4204
- APP_NAME: '${context.name}',
4205
- database: databaseConfig
4206
- },
4207
- })
4914
+ export { AppServiceProvider } from './AppServiceProvider'
4915
+ export { MiddlewareProvider } from './MiddlewareProvider'
4916
+ export { RouteProvider } from './RouteProvider'
4917
+ `;
4918
+ }
4919
+ generateMiddlewareProvider() {
4920
+ return `/**
4921
+ * Middleware Service Provider
4922
+ */
4208
4923
 
4209
- // Middleware
4210
- core.adapter.use('*', securityHeaders())
4211
- core.adapter.use('*', bodySizeLimit(1024 * 1024)) // 1MB
4924
+ import {
4925
+ ServiceProvider,
4926
+ type Container,
4927
+ type PlanetCore,
4928
+ bodySizeLimit,
4929
+ securityHeaders,
4930
+ } from '@gravito/core'
4212
4931
 
4213
- // Install Orbits
4214
- await core.orbit(new OrbitAtlas())
4932
+ export class MiddlewareProvider extends ServiceProvider {
4933
+ register(_container: Container): void {}
4215
4934
 
4216
- // Service Providers
4217
- core.register(new AppServiceProvider())
4935
+ boot(core: PlanetCore): void {
4936
+ core.adapter.use('*', securityHeaders())
4937
+ core.adapter.use('*', bodySizeLimit(1024 * 1024))
4938
+ core.logger.info('\u{1F6E1}\uFE0F Middleware registered')
4939
+ }
4940
+ }
4941
+ `;
4942
+ }
4943
+ generateRouteProvider() {
4944
+ return `/**
4945
+ * Route Service Provider
4946
+ */
4218
4947
 
4219
- // Bootstrap
4220
- await core.bootstrap()
4948
+ import { ServiceProvider, type Container, type PlanetCore } from '@gravito/core'
4949
+ import { registerApiRoutes } from '../routes/api'
4221
4950
 
4222
- // Routes
4223
- registerApiRoutes(core.router)
4951
+ export class RouteProvider extends ServiceProvider {
4952
+ register(_container: Container): void {}
4224
4953
 
4225
- // Liftoff
4954
+ boot(core: PlanetCore): void {
4955
+ registerApiRoutes(core.router)
4956
+ core.logger.info('\u{1F6E4}\uFE0F Routes registered')
4957
+ }
4958
+ }
4959
+ `;
4960
+ }
4961
+ generateBootstrap(_context) {
4962
+ return `/**
4963
+ * Application Bootstrap
4964
+ *
4965
+ * Uses the ServiceProvider pattern for modular initialization.
4966
+ */
4967
+
4968
+ import { defineConfig, PlanetCore } from '@gravito/core'
4969
+ import { OrbitAtlas } from '@gravito/atlas'
4970
+ import appConfig from '../config/app'
4971
+ import {
4972
+ AppServiceProvider,
4973
+ MiddlewareProvider,
4974
+ RouteProvider,
4975
+ } from './providers'
4976
+
4977
+ export async function bootstrap() {
4978
+ const config = defineConfig({
4979
+ config: appConfig,
4980
+ orbits: [new OrbitAtlas()],
4981
+ })
4982
+
4983
+ const core = await PlanetCore.boot(config)
4984
+ core.registerGlobalErrorHandlers()
4985
+
4986
+ core.register(new AppServiceProvider())
4987
+ core.register(new MiddlewareProvider())
4988
+ core.register(new RouteProvider())
4989
+
4990
+ await core.bootstrap()
4991
+
4992
+ return core
4993
+ }
4994
+
4995
+ const core = await bootstrap()
4226
4996
  export default core.liftoff()
4227
4997
  `;
4228
4998
  }
4229
4999
  generateArchitectureDoc(context) {
4230
- 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';
5000
+ return `# ${context.name} - Action Domain Architecture
5001
+
5002
+ ## Overview
5003
+
5004
+ This project uses the **Action Domain** pattern, designed for high-clarity API implementations.
5005
+
5006
+ ## Service Providers
5007
+
5008
+ Service providers are the central place to configure your application. They follow the ServiceProvider pattern with \`register()\` and \`boot()\` lifecycle methods.
5009
+
5010
+ ## Directory Structure
5011
+
5012
+ \`\`\`
5013
+ src/
5014
+ \u251C\u2500\u2500 actions/ # Single Responsibility Business Logic
5015
+ \u2502 \u251C\u2500\u2500 Action.ts # Base Action class
5016
+ \u2502 \u2514\u2500\u2500 [Domain]/ # Domain-specific actions
5017
+ \u251C\u2500\u2500 controllers/ # HTTP Request Handlers
5018
+ \u2502 \u2514\u2500\u2500 api/v1/ # API Controllers
5019
+ \u251C\u2500\u2500 types/ # TypeScript Definitions
5020
+ \u251C\u2500\u2500 repositories/ # Data Access Layer
5021
+ \u251C\u2500\u2500 routes/ # Route Definitions
5022
+ \u251C\u2500\u2500 providers/ # Service Providers
5023
+ \u2514\u2500\u2500 config/ # Configuration
5024
+ \`\`\`
5025
+
5026
+ ## Core Concepts
5027
+
5028
+ ### Actions
5029
+ Every business operation is an "Action". An action:
5030
+ - Does ONE thing.
5031
+ - Takes specific input.
5032
+ - Returns specific output.
5033
+ - Is framework-agnostic (ideally).
5034
+
5035
+ ### Controllers
5036
+ Controllers are thin. They:
5037
+ 1. Parse the request.
5038
+ 2. Instantiate/Call the Action.
5039
+ 3. Return the response.
5040
+
5041
+ Created with \u2764\uFE0F using Gravito Framework
5042
+ `;
4231
5043
  }
4232
5044
  generatePackageJson(context) {
4233
5045
  const pkg = {
@@ -4239,7 +5051,11 @@ export default core.liftoff()
4239
5051
  build: "bun build ./src/bootstrap.ts --outdir ./dist --target bun",
4240
5052
  start: "bun run dist/bootstrap.js",
4241
5053
  test: "bun test",
4242
- typecheck: "tsc --noEmit"
5054
+ typecheck: "tsc --noEmit",
5055
+ check: "bun run typecheck && bun run test",
5056
+ "check:deps": "bun run scripts/check-dependencies.ts",
5057
+ validate: "bun run check && bun run check:deps",
5058
+ precommit: "bun run validate"
4243
5059
  },
4244
5060
  dependencies: {
4245
5061
  "@gravito/core": "workspace:*",
@@ -4256,6 +5072,115 @@ export default core.liftoff()
4256
5072
  }
4257
5073
  };
4258
5074
 
5075
+ // src/generators/StandaloneEngineGenerator.ts
5076
+ var StandaloneEngineGenerator = class extends BaseGenerator {
5077
+ get architectureType() {
5078
+ return "standalone-engine";
5079
+ }
5080
+ get displayName() {
5081
+ return "Standalone Engine";
5082
+ }
5083
+ get description() {
5084
+ return "High-performance pure Gravito Engine (minimal)";
5085
+ }
5086
+ getDirectoryStructure(context) {
5087
+ return [
5088
+ {
5089
+ type: "directory",
5090
+ name: "src",
5091
+ children: [
5092
+ {
5093
+ type: "file",
5094
+ name: "index.ts",
5095
+ content: this.getIndexContent()
5096
+ }
5097
+ ]
5098
+ },
5099
+ {
5100
+ type: "file",
5101
+ name: "README.md",
5102
+ content: this.getReadmeContent(context)
5103
+ }
5104
+ ];
5105
+ }
5106
+ async generateCommonFiles(context) {
5107
+ await this.writeFile(context.targetDir, "package.json", this.generatePackageJson(context));
5108
+ await this.writeFile(context.targetDir, "tsconfig.json", this.generateTsConfig());
5109
+ await this.writeFile(context.targetDir, ".gitignore", this.generateGitignore());
5110
+ }
5111
+ generatePackageJson(context) {
5112
+ const pkg = {
5113
+ name: context.nameKebabCase,
5114
+ version: "0.1.0",
5115
+ type: "module",
5116
+ scripts: {
5117
+ dev: "bun run --watch src/index.ts",
5118
+ build: "bun build ./src/index.ts --outdir ./dist --target bun",
5119
+ start: "bun run dist/index.js",
5120
+ test: "bun test"
5121
+ },
5122
+ dependencies: {
5123
+ "@gravito/core": "^1.1.0"
5124
+ },
5125
+ devDependencies: {
5126
+ "bun-types": "latest",
5127
+ typescript: "^5.0.0"
5128
+ }
5129
+ };
5130
+ return JSON.stringify(pkg, null, 2);
5131
+ }
5132
+ generateArchitectureDoc(_context) {
5133
+ return "";
5134
+ }
5135
+ getIndexContent() {
5136
+ return `import { Gravito } from '@gravito/core/engine'
5137
+
5138
+ const app = new Gravito()
5139
+
5140
+ // Basic Route
5141
+ app.get('/', (c) => c.text('Hello Gravito Engine!'))
5142
+
5143
+ // JSON Response
5144
+ app.get('/json', (c) => c.json({ message: 'High Performance' }))
5145
+
5146
+ // Path Parameters
5147
+ app.get('/user/:name', (c) => {
5148
+ const name = c.req.param('name')
5149
+ return c.text(\`Hello \${name}\`)
5150
+ })
5151
+
5152
+ export default app
5153
+ `;
5154
+ }
5155
+ getReadmeContent(context) {
5156
+ return `# ${context.name}
5157
+
5158
+ A high-performance web application powered by Gravito Engine.
5159
+
5160
+ ## Getting Started
5161
+
5162
+ ### Install Dependencies
5163
+
5164
+ \`\`\`bash
5165
+ bun install
5166
+ \`\`\`
5167
+
5168
+ ### Run Development Server
5169
+
5170
+ \`\`\`bash
5171
+ bun run dev
5172
+ \`\`\`
5173
+
5174
+ ### Production Build
5175
+
5176
+ \`\`\`bash
5177
+ bun run build
5178
+ bun start
5179
+ \`\`\`
5180
+ `;
5181
+ }
5182
+ };
5183
+
4259
5184
  // src/Scaffold.ts
4260
5185
  var Scaffold = class {
4261
5186
  templatesDir;
@@ -4289,6 +5214,11 @@ var Scaffold = class {
4289
5214
  name: "Action Domain",
4290
5215
  description: "Single Action Controllers pattern for high-clarity APIs"
4291
5216
  },
5217
+ {
5218
+ type: "standalone-engine",
5219
+ name: "Standalone Engine",
5220
+ description: "High-performance pure Gravito Engine (minimal)"
5221
+ },
4292
5222
  {
4293
5223
  type: "satellite",
4294
5224
  name: "Gravito Satellite",
@@ -4364,6 +5294,8 @@ var Scaffold = class {
4364
5294
  return new ActionDomainGenerator(config);
4365
5295
  case "satellite":
4366
5296
  return new SatelliteGenerator(config);
5297
+ case "standalone-engine":
5298
+ return new StandaloneEngineGenerator(config);
4367
5299
  default:
4368
5300
  throw new Error(`Unknown architecture type: ${type}`);
4369
5301
  }