deploy-bbc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CLAUDE.md +329 -0
  2. package/README.md +0 -0
  3. package/cli/README.md +0 -0
  4. package/cli/package.json +43 -0
  5. package/cli/src/cli/index.ts +449 -0
  6. package/cli/src/helpers/create-project.ts +66 -0
  7. package/cli/src/helpers/generate-docker-compose.ts +133 -0
  8. package/cli/src/helpers/generate-dockerfile.ts +45 -0
  9. package/cli/src/helpers/init-git.ts +33 -0
  10. package/cli/src/helpers/install-dependencies.ts +28 -0
  11. package/cli/src/helpers/log-next-steps.ts +84 -0
  12. package/cli/src/helpers/scaffold-project.ts +62 -0
  13. package/cli/src/index.ts +18 -0
  14. package/cli/src/installers/ai.ts +123 -0
  15. package/cli/src/installers/auth.ts +132 -0
  16. package/cli/src/installers/base.ts +16 -0
  17. package/cli/src/installers/cloud.ts +127 -0
  18. package/cli/src/installers/database.ts +212 -0
  19. package/cli/src/installers/docs.ts +93 -0
  20. package/cli/src/installers/email.ts +119 -0
  21. package/cli/src/installers/env-variables.ts +27 -0
  22. package/cli/src/installers/index.ts +145 -0
  23. package/cli/src/installers/observability.ts +103 -0
  24. package/cli/src/installers/queue.ts +103 -0
  25. package/cli/src/installers/ratelimit.ts +98 -0
  26. package/cli/src/installers/realtime.ts +79 -0
  27. package/cli/src/installers/testing.ts +88 -0
  28. package/cli/src/installers/validation.ts +85 -0
  29. package/cli/src/templates/base/.env.example +3 -0
  30. package/cli/src/templates/base/README.md +31 -0
  31. package/cli/src/templates/base/package.json +20 -0
  32. package/cli/src/templates/base/src/config/index.ts +8 -0
  33. package/cli/src/templates/base/src/index.ts +26 -0
  34. package/cli/src/templates/base/src/middleware/error.ts +13 -0
  35. package/cli/src/templates/base/src/middleware/logger.ts +8 -0
  36. package/cli/src/templates/base/src/routes/index.ts +12 -0
  37. package/cli/src/templates/base/src/types/index.ts +2 -0
  38. package/cli/src/templates/base/src/utils/env.ts +5 -0
  39. package/cli/src/templates/base/tsconfig.json +20 -0
  40. package/cli/src/templates/base-bun-native/.env.example +3 -0
  41. package/cli/src/templates/base-bun-native/README.md +31 -0
  42. package/cli/src/templates/base-bun-native/package.json +19 -0
  43. package/cli/src/templates/base-bun-native/src/config/index.ts +8 -0
  44. package/cli/src/templates/base-bun-native/src/index.ts +50 -0
  45. package/cli/src/templates/base-bun-native/src/middleware/error.ts +20 -0
  46. package/cli/src/templates/base-bun-native/src/middleware/logger.ts +6 -0
  47. package/cli/src/templates/base-bun-native/src/routes/index.ts +21 -0
  48. package/cli/src/templates/base-bun-native/src/types/index.ts +2 -0
  49. package/cli/src/templates/base-bun-native/src/utils/env.ts +5 -0
  50. package/cli/src/templates/base-bun-native/tsconfig.json +20 -0
  51. package/cli/src/templates/base-express/.env.example +3 -0
  52. package/cli/src/templates/base-express/README.md +31 -0
  53. package/cli/src/templates/base-express/package.json +21 -0
  54. package/cli/src/templates/base-express/src/config/index.ts +8 -0
  55. package/cli/src/templates/base-express/src/index.ts +27 -0
  56. package/cli/src/templates/base-express/src/middleware/error.ts +15 -0
  57. package/cli/src/templates/base-express/src/middleware/logger.ts +12 -0
  58. package/cli/src/templates/base-express/src/routes/index.ts +12 -0
  59. package/cli/src/templates/base-express/src/types/index.ts +2 -0
  60. package/cli/src/templates/base-express/src/utils/env.ts +5 -0
  61. package/cli/src/templates/base-express/tsconfig.json +20 -0
  62. package/cli/src/templates/extras/ai/anthropic/src/routes/ai/claude.ts +0 -0
  63. package/cli/src/templates/extras/ai/anthropic/src/services/ai/anthropic.ts +0 -0
  64. package/cli/src/templates/extras/ai/gemini/src/services/ai/gemini.ts +0 -0
  65. package/cli/src/templates/extras/ai/openai/src/routes/ai/chat.ts +0 -0
  66. package/cli/src/templates/extras/ai/openai/src/services/ai/openai.ts +0 -0
  67. package/cli/src/templates/extras/ai/vercel-ai/src/routes/ai/generate.ts +0 -0
  68. package/cli/src/templates/extras/ai/vercel-ai/src/routes/ai/stream.ts +0 -0
  69. package/cli/src/templates/extras/ai/vercel-ai/src/services/ai/index.ts +0 -0
  70. package/cli/src/templates/extras/auth/jwt/src/middleware/auth.ts +0 -0
  71. package/cli/src/templates/extras/auth/jwt/src/routes/auth.ts +0 -0
  72. package/cli/src/templates/extras/auth/jwt/src/utils/jwt.ts +0 -0
  73. package/cli/src/templates/extras/auth/oauth/src/config/oauth.ts +0 -0
  74. package/cli/src/templates/extras/auth/oauth/src/routes/auth.ts +0 -0
  75. package/cli/src/templates/extras/auth/session/src/config/session.ts +0 -0
  76. package/cli/src/templates/extras/auth/session/src/middleware/session.ts +0 -0
  77. package/cli/src/templates/extras/cloud/aws/src/services/aws/s3.ts +0 -0
  78. package/cli/src/templates/extras/cloud/aws/src/services/aws/ses.ts +0 -0
  79. package/cli/src/templates/extras/cloud/azure/src/services/azure/blob.ts +0 -0
  80. package/cli/src/templates/extras/cloud/cloudflare-r2/src/services/cloudflare/r2.ts +0 -0
  81. package/cli/src/templates/extras/cloud/gcp/src/services/gcp/storage.ts +0 -0
  82. package/cli/src/templates/extras/database/mongodb/src/db/index.ts +0 -0
  83. package/cli/src/templates/extras/database/mongodb/src/db/models/user.model.ts +0 -0
  84. package/cli/src/templates/extras/database/mysql/drizzle.config.ts +0 -0
  85. package/cli/src/templates/extras/database/postgres/src/db/index.ts +0 -0
  86. package/cli/src/templates/extras/database/redis/src/db/redis.ts +0 -0
  87. package/cli/src/templates/extras/docs/scalar/src/openapi/index.ts +0 -0
  88. package/cli/src/templates/extras/docs/scalar/src/routes/docs.ts +0 -0
  89. package/cli/src/templates/extras/docs/swagger/src/openapi/index.ts +0 -0
  90. package/cli/src/templates/extras/docs/swagger/src/routes/docs.ts +0 -0
  91. package/cli/src/templates/extras/email/nodemailer/src/services/email/nodemailer.ts +0 -0
  92. package/cli/src/templates/extras/email/resend/src/services/email/resend.ts +0 -0
  93. package/cli/src/templates/extras/email/resend/src/templates/email/welcome.ts +0 -0
  94. package/cli/src/templates/extras/email/sendgrid/src/services/email/sendgrid.ts +0 -0
  95. package/cli/src/templates/extras/observability/logtail/src/config/logger.ts +0 -0
  96. package/cli/src/templates/extras/observability/sentry/src/config/sentry.ts +0 -0
  97. package/cli/src/templates/extras/observability/sentry/src/middleware/sentry.ts +0 -0
  98. package/cli/src/templates/extras/queue/bullmq/src/queue/index.ts +0 -0
  99. package/cli/src/templates/extras/queue/bullmq/src/queue/jobs/email.job.ts +0 -0
  100. package/cli/src/templates/extras/queue/bullmq/src/queue/processors/email.processor.ts +0 -0
  101. package/cli/src/templates/extras/queue/bullmq/src/routes/queue.ts +0 -0
  102. package/cli/src/templates/extras/queue/inngest/src/inngest/client.ts +0 -0
  103. package/cli/src/templates/extras/queue/inngest/src/inngest/functions/email.ts +0 -0
  104. package/cli/src/templates/extras/queue/inngest/src/routes/inngest.ts +0 -0
  105. package/cli/src/templates/extras/realtime/socketio/src/socket/handlers.ts +0 -0
  106. package/cli/src/templates/extras/realtime/socketio/src/socket/index.ts +0 -0
  107. package/cli/src/templates/extras/realtime/sse/src/routes/sse.ts +0 -0
  108. package/cli/src/templates/extras/testing/vitest/src/__tests__/example.test.ts +0 -0
  109. package/cli/src/templates/extras/testing/vitest/src/__tests__/setup.ts +0 -0
  110. package/cli/src/templates/extras/testing/vitest/vitest.config.ts +0 -0
  111. package/cli/src/templates/extras/validation/yup/src/middleware/index.ts +1 -0
  112. package/cli/src/templates/extras/validation/yup/src/middleware/validate.ts +83 -0
  113. package/cli/src/templates/extras/validation/yup/src/routes/users.ts +132 -0
  114. package/cli/src/templates/extras/validation/zod/src/middleware/index.ts +1 -0
  115. package/cli/src/templates/extras/validation/zod/src/middleware/validate.ts +80 -0
  116. package/cli/src/templates/extras/validation/zod/src/routes/users.ts +128 -0
  117. package/cli/src/types/index.ts +126 -0
  118. package/cli/src/utils/add-package-dependency.ts +56 -0
  119. package/cli/src/utils/dependency-version-map.ts +85 -0
  120. package/cli/src/utils/logger.ts +19 -0
  121. package/cli/src/utils/parse-name-and-path.ts +55 -0
  122. package/cli/src/utils/render-title.ts +11 -0
  123. package/cli/tsconfig.json +35 -0
  124. package/package.json +20 -0
  125. package/prettier.config.mjs +0 -0
  126. package/test-cli.sh +56 -0
  127. package/tsconfig.json +15 -0
@@ -0,0 +1,33 @@
1
+ import { execa } from "execa";
2
+ import chalk from "chalk";
3
+
4
+ /**
5
+ * Initializes a Git repository and creates an initial commit.
6
+ * Does not throw on failure - git is optional.
7
+ *
8
+ * @param projectDir - Absolute path to the project directory
9
+ */
10
+ export async function init_git(projectDir: string): Promise<void> {
11
+ try {
12
+ // Initialize git repository
13
+ await execa("git", ["init"], { cwd: projectDir });
14
+
15
+ // Add all files
16
+ await execa("git", ["add", "."], { cwd: projectDir });
17
+
18
+ // Create initial commit
19
+ await execa(
20
+ "git",
21
+ ["commit", "-m", "Initial commit from deploy-bbc"],
22
+ { cwd: projectDir }
23
+ );
24
+
25
+ console.log(chalk.green("✓") + " Git repository initialized");
26
+ } catch (error) {
27
+ // Git initialization is optional, so just warn on failure
28
+ console.log(
29
+ chalk.yellow("⚠") +
30
+ " Git initialization failed (this is optional and can be done manually)"
31
+ );
32
+ }
33
+ }
@@ -0,0 +1,28 @@
1
+ import { execa } from "execa";
2
+ import ora from "ora";
3
+
4
+ /**
5
+ * Installs project dependencies using Bun package manager.
6
+ * Runs `bun install` in the project directory.
7
+ *
8
+ * @param projectDir - Absolute path to the project directory
9
+ * @throws Error if installation fails
10
+ */
11
+ export async function install_dependencies(projectDir: string): Promise<void> {
12
+ const spinner = ora("Installing dependencies...").start();
13
+
14
+ try {
15
+ await execa("bun", ["install"], {
16
+ cwd: projectDir,
17
+ stdio: "inherit",
18
+ });
19
+
20
+ spinner.succeed("Dependencies installed successfully");
21
+ } catch (error) {
22
+ spinner.fail("Failed to install dependencies");
23
+ console.error("\nYou can install dependencies manually by running:");
24
+ console.error(` cd ${projectDir}`);
25
+ console.error(` bun install\n`);
26
+ throw error;
27
+ }
28
+ }
@@ -0,0 +1,84 @@
1
+ import chalk from "chalk";
2
+ import { type InstallerOptions } from "../types/index.js";
3
+ import { type CliResults } from "../types/index.js";
4
+ import { AvailablePackages } from "../types/index.js";
5
+
6
+ /**
7
+ * Displays next steps for the user after project creation.
8
+ * Shows commands for navigation, Docker setup, migrations, and running the dev server.
9
+ *
10
+ * @param options - Installer options with project configuration
11
+ * @param cliResults - CLI results with selected packages and flags
12
+ */
13
+ export function log_next_steps(
14
+ options: InstallerOptions,
15
+ _cliResults: CliResults
16
+ ): void {
17
+ const { appName, packages } = options;
18
+
19
+ console.log("\n" + chalk.bold.green("✨ Project created successfully!"));
20
+ console.log("\n" + chalk.bold("Next steps:"));
21
+ console.log();
22
+
23
+ // Step 1: Navigate to project
24
+ console.log(chalk.cyan("1.") + " Navigate to your project:");
25
+ console.log(` ${chalk.gray("cd")} ${appName}`);
26
+ console.log();
27
+
28
+ // Step 2: Environment variables
29
+ console.log(chalk.cyan("2.") + " Set up environment variables:");
30
+ console.log(` ${chalk.gray("cp")} .env.example .env`);
31
+ console.log(` ${chalk.gray("# Edit .env with your configuration")}`);
32
+ console.log();
33
+
34
+ // Step 3: Docker (if database or redis selected)
35
+ const hasDocker =
36
+ packages.includes(AvailablePackages.postgres) ||
37
+ packages.includes(AvailablePackages.mysql) ||
38
+ packages.includes(AvailablePackages.mongodb) ||
39
+ packages.includes(AvailablePackages.redis);
40
+
41
+ if (hasDocker) {
42
+ console.log(chalk.cyan("3.") + " Start Docker services:");
43
+ console.log(` ${chalk.gray("docker-compose up -d")}`);
44
+ console.log();
45
+ }
46
+
47
+ // Step 4: Database migrations (if postgres or mysql)
48
+ const needsMigration =
49
+ packages.includes(AvailablePackages.postgres) ||
50
+ packages.includes(AvailablePackages.mysql);
51
+
52
+ if (needsMigration) {
53
+ const stepNum = hasDocker ? "4" : "3";
54
+ console.log(chalk.cyan(`${stepNum}.`) + " Run database migrations:");
55
+ console.log(` ${chalk.gray("bun run db:migrate")}`);
56
+ console.log();
57
+ }
58
+
59
+ // Step 5: Start development server
60
+ const lastStepNum = needsMigration
61
+ ? hasDocker
62
+ ? "5"
63
+ : "4"
64
+ : hasDocker
65
+ ? "4"
66
+ : "3";
67
+ console.log(chalk.cyan(`${lastStepNum}.`) + " Start the development server:");
68
+ console.log(` ${chalk.gray("bun run dev")}`);
69
+ console.log();
70
+
71
+ // Additional info for docs
72
+ if (
73
+ packages.includes(AvailablePackages.swagger) ||
74
+ packages.includes(AvailablePackages.scalar)
75
+ ) {
76
+ console.log(
77
+ chalk.bold("📚 API Documentation:") + " http://localhost:3000/docs"
78
+ );
79
+ console.log();
80
+ }
81
+
82
+ console.log(chalk.gray("Happy coding! 🚀"));
83
+ console.log();
84
+ }
@@ -0,0 +1,62 @@
1
+ import path from "path";
2
+ import fs from "fs-extra";
3
+ import ora from "ora";
4
+ import { fileURLToPath } from "url";
5
+ import { type InstallerOptions } from "../types/index.js";
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ /**
11
+ * Scaffolds the base project structure by copying template files.
12
+ * Creates the project directory and copies all files from templates/base/
13
+ *
14
+ * @param options - Installer options containing projectDir and other config
15
+ * @throws Error if project directory already exists and is not empty
16
+ */
17
+ export async function scaffold_project(
18
+ options: InstallerOptions
19
+ ): Promise<void> {
20
+ const { projectDir, appName, framework } = options;
21
+ const spinner = ora("Scaffolding project...").start();
22
+
23
+ try {
24
+ // Check if directory exists and is not empty
25
+ if (await fs.pathExists(projectDir)) {
26
+ const files = await fs.readdir(projectDir);
27
+ if (files.length > 0) {
28
+ spinner.fail(`Directory ${projectDir} already exists and is not empty`);
29
+ throw new Error(
30
+ `Cannot create project: directory "${appName}" is not empty`
31
+ );
32
+ }
33
+ }
34
+
35
+ // Ensure project directory exists
36
+ await fs.ensureDir(projectDir);
37
+
38
+ // Select base template based on framework
39
+ const templateDirName = framework === "hono"
40
+ ? "base"
41
+ : framework === "express"
42
+ ? "base-express"
43
+ : "base-bun-native";
44
+
45
+ const templateDir = path.resolve(__dirname, `../templates/${templateDirName}`);
46
+ await fs.copy(templateDir, projectDir, {
47
+ overwrite: false,
48
+ errorOnExist: false,
49
+ });
50
+
51
+ // Update package.json with the actual project name
52
+ const packageJsonPath = path.join(projectDir, "package.json");
53
+ const packageJson = await fs.readJson(packageJsonPath);
54
+ packageJson.name = appName;
55
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
56
+
57
+ spinner.succeed("Project scaffolded successfully");
58
+ } catch (error) {
59
+ spinner.fail("Failed to scaffold project");
60
+ throw error;
61
+ }
62
+ }
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { run_cli } from "./cli/index.js";
4
+ import { create_project } from "./helpers/create-project.js";
5
+ import { logger } from "./utils/logger.js";
6
+
7
+ async function main() {
8
+ try {
9
+ const cliResults = await run_cli();
10
+ await create_project(cliResults);
11
+ } catch (error) {
12
+ logger.error("\n❌ An error occurred:");
13
+ logger.error(error);
14
+ process.exit(1);
15
+ }
16
+ }
17
+
18
+ main();
@@ -0,0 +1,123 @@
1
+ import path from "path";
2
+ import fs from "fs-extra";
3
+ import { fileURLToPath } from "url";
4
+ import { type InstallerOptions, AvailablePackages } from "../types/index.js";
5
+ import { add_package_dependency } from "../utils/add-package-dependency.js";
6
+ import { DEPENDENCY_VERSION_MAP } from "../utils/dependency-version-map.js";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ /**
12
+ * AI installer - handles OpenAI, Anthropic, Gemini, and Vercel AI SDK.
13
+ *
14
+ * @param options - Installer options with selected packages
15
+ */
16
+ export async function ai_installer(options: InstallerOptions): Promise<void> {
17
+ const { projectDir, packages } = options;
18
+
19
+ for (const pkg of packages) {
20
+ switch (pkg) {
21
+ case AvailablePackages.openai:
22
+ await install_openai(projectDir);
23
+ break;
24
+ case AvailablePackages.anthropic:
25
+ await install_anthropic(projectDir);
26
+ break;
27
+ case AvailablePackages.gemini:
28
+ await install_gemini(projectDir);
29
+ break;
30
+ case AvailablePackages.vercelAI:
31
+ await install_vercel_ai(projectDir);
32
+ break;
33
+ }
34
+ }
35
+ }
36
+
37
+ async function install_openai(projectDir: string): Promise<void> {
38
+ const templateDir = path.resolve(__dirname, "../templates/extras/ai/openai");
39
+ await copy_template_files(templateDir, projectDir);
40
+
41
+ await add_package_dependency(projectDir, {
42
+ openai: DEPENDENCY_VERSION_MAP["openai"],
43
+ });
44
+
45
+ await append_env_example(
46
+ projectDir,
47
+ "\n# OpenAI Configuration\nOPENAI_API_KEY=your-openai-api-key\n"
48
+ );
49
+ }
50
+
51
+ async function install_anthropic(projectDir: string): Promise<void> {
52
+ const templateDir = path.resolve(
53
+ __dirname,
54
+ "../templates/extras/ai/anthropic"
55
+ );
56
+ await copy_template_files(templateDir, projectDir);
57
+
58
+ await add_package_dependency(projectDir, {
59
+ "@anthropic-ai/sdk": DEPENDENCY_VERSION_MAP["@anthropic-ai/sdk"],
60
+ });
61
+
62
+ await append_env_example(
63
+ projectDir,
64
+ "\n# Anthropic Configuration\nANTHROPIC_API_KEY=your-anthropic-api-key\n"
65
+ );
66
+ }
67
+
68
+ async function install_gemini(projectDir: string): Promise<void> {
69
+ const templateDir = path.resolve(__dirname, "../templates/extras/ai/gemini");
70
+ await copy_template_files(templateDir, projectDir);
71
+
72
+ await add_package_dependency(projectDir, {
73
+ "@google/generative-ai": DEPENDENCY_VERSION_MAP["@google/generative-ai"],
74
+ });
75
+
76
+ await append_env_example(
77
+ projectDir,
78
+ "\n# Google Gemini Configuration\nGEMINI_API_KEY=your-gemini-api-key\n"
79
+ );
80
+ }
81
+
82
+ async function install_vercel_ai(projectDir: string): Promise<void> {
83
+ const templateDir = path.resolve(
84
+ __dirname,
85
+ "../templates/extras/ai/vercel-ai"
86
+ );
87
+ await copy_template_files(templateDir, projectDir);
88
+
89
+ await add_package_dependency(projectDir, {
90
+ ai: DEPENDENCY_VERSION_MAP["ai"],
91
+ });
92
+
93
+ await append_env_example(
94
+ projectDir,
95
+ "\n# Vercel AI SDK Configuration\n# Add API keys for the providers you want to use with Vercel AI\n"
96
+ );
97
+ }
98
+
99
+ async function copy_template_files(
100
+ templateDir: string,
101
+ projectDir: string
102
+ ): Promise<void> {
103
+ if (!(await fs.pathExists(templateDir))) {
104
+ return;
105
+ }
106
+
107
+ const srcDir = path.join(templateDir, "src");
108
+ if (await fs.pathExists(srcDir)) {
109
+ const targetSrcDir = path.join(projectDir, "src");
110
+ await fs.copy(srcDir, targetSrcDir, {
111
+ overwrite: false,
112
+ errorOnExist: false,
113
+ });
114
+ }
115
+ }
116
+
117
+ async function append_env_example(
118
+ projectDir: string,
119
+ content: string
120
+ ): Promise<void> {
121
+ const envPath = path.join(projectDir, ".env.example");
122
+ await fs.appendFile(envPath, content);
123
+ }
@@ -0,0 +1,132 @@
1
+ import path from "path";
2
+ import fs from "fs-extra";
3
+ import { fileURLToPath } from "url";
4
+ import { type InstallerOptions, AvailablePackages } from "../types/index.js";
5
+ import { add_package_dependency } from "../utils/add-package-dependency.js";
6
+ import { DEPENDENCY_VERSION_MAP } from "../utils/dependency-version-map.js";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ /**
12
+ * Auth installer - handles JWT, OAuth, and session authentication.
13
+ *
14
+ * @param options - Installer options with selected packages
15
+ */
16
+ export async function auth_installer(
17
+ options: InstallerOptions
18
+ ): Promise<void> {
19
+ const { projectDir, packages } = options;
20
+
21
+ for (const pkg of packages) {
22
+ switch (pkg) {
23
+ case AvailablePackages.jwt:
24
+ await install_jwt(projectDir);
25
+ break;
26
+ case AvailablePackages.oauth:
27
+ await install_oauth(projectDir);
28
+ break;
29
+ case AvailablePackages.session:
30
+ await install_session(projectDir);
31
+ break;
32
+ }
33
+ }
34
+ }
35
+
36
+ async function install_jwt(projectDir: string): Promise<void> {
37
+ const templateDir = path.resolve(__dirname, "../templates/extras/auth/jwt");
38
+ await copy_template_files(templateDir, projectDir);
39
+
40
+ await add_package_dependency(projectDir, {
41
+ jsonwebtoken: DEPENDENCY_VERSION_MAP["jsonwebtoken"],
42
+ bcryptjs: DEPENDENCY_VERSION_MAP["bcryptjs"],
43
+ });
44
+
45
+ await add_package_dependency(
46
+ projectDir,
47
+ {},
48
+ {
49
+ "@types/jsonwebtoken": DEPENDENCY_VERSION_MAP["@types/jsonwebtoken"],
50
+ "@types/bcryptjs": DEPENDENCY_VERSION_MAP["@types/bcryptjs"],
51
+ }
52
+ );
53
+
54
+ await append_env_example(
55
+ projectDir,
56
+ "\n# JWT Authentication\nJWT_SECRET=your-super-secret-jwt-key-change-this\nJWT_EXPIRES_IN=7d\n"
57
+ );
58
+ }
59
+
60
+ async function install_oauth(projectDir: string): Promise<void> {
61
+ const templateDir = path.resolve(__dirname, "../templates/extras/auth/oauth");
62
+ await copy_template_files(templateDir, projectDir);
63
+
64
+ await add_package_dependency(projectDir, {
65
+ passport: DEPENDENCY_VERSION_MAP["passport"],
66
+ "passport-oauth2": DEPENDENCY_VERSION_MAP["passport-oauth2"],
67
+ });
68
+
69
+ await add_package_dependency(
70
+ projectDir,
71
+ {},
72
+ {
73
+ "@types/passport": DEPENDENCY_VERSION_MAP["@types/passport"],
74
+ }
75
+ );
76
+
77
+ await append_env_example(
78
+ projectDir,
79
+ "\n# OAuth Configuration\nOAUTH_CLIENT_ID=your-client-id\nOAUTH_CLIENT_SECRET=your-client-secret\nOAUTH_CALLBACK_URL=http://localhost:3000/auth/callback\n"
80
+ );
81
+ }
82
+
83
+ async function install_session(projectDir: string): Promise<void> {
84
+ const templateDir = path.resolve(
85
+ __dirname,
86
+ "../templates/extras/auth/session"
87
+ );
88
+ await copy_template_files(templateDir, projectDir);
89
+
90
+ await append_env_example(
91
+ projectDir,
92
+ "\n# Session Configuration\nSESSION_SECRET=your-super-secret-session-key-change-this\n"
93
+ );
94
+ }
95
+
96
+ async function copy_template_files(
97
+ templateDir: string,
98
+ projectDir: string
99
+ ): Promise<void> {
100
+ if (!(await fs.pathExists(templateDir))) {
101
+ return;
102
+ }
103
+
104
+ const srcDir = path.join(templateDir, "src");
105
+ if (await fs.pathExists(srcDir)) {
106
+ const targetSrcDir = path.join(projectDir, "src");
107
+ await fs.copy(srcDir, targetSrcDir, {
108
+ overwrite: false,
109
+ errorOnExist: false,
110
+ });
111
+ }
112
+
113
+ const templateFiles = await fs.readdir(templateDir);
114
+ for (const file of templateFiles) {
115
+ if (file !== "src" && (file.endsWith(".ts") || file.endsWith(".json"))) {
116
+ const srcFile = path.join(templateDir, file);
117
+ const destFile = path.join(projectDir, file);
118
+
119
+ if (!(await fs.pathExists(destFile))) {
120
+ await fs.copy(srcFile, destFile);
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ async function append_env_example(
127
+ projectDir: string,
128
+ content: string
129
+ ): Promise<void> {
130
+ const envPath = path.join(projectDir, ".env.example");
131
+ await fs.appendFile(envPath, content);
132
+ }
@@ -0,0 +1,16 @@
1
+ import { type InstallerOptions } from "../types/index.js";
2
+
3
+ /**
4
+ * Base installer - handles any base setup that's not covered by scaffold_project.
5
+ * Currently minimal since scaffold_project handles most base template copying.
6
+ *
7
+ * @param options - Installer options
8
+ */
9
+ export async function base_installer(
10
+ _options: InstallerOptions
11
+ ): Promise<void> {
12
+ // Base setup is handled by scaffold_project
13
+ // This installer exists for any additional base configuration
14
+ // that might be needed in the future
15
+ return Promise.resolve();
16
+ }
@@ -0,0 +1,127 @@
1
+ import path from "path";
2
+ import fs from "fs-extra";
3
+ import { fileURLToPath } from "url";
4
+ import { type InstallerOptions, AvailablePackages } from "../types/index.js";
5
+ import { add_package_dependency } from "../utils/add-package-dependency.js";
6
+ import { DEPENDENCY_VERSION_MAP } from "../utils/dependency-version-map.js";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ /**
12
+ * Cloud installer - handles AWS, GCP, Azure, and Cloudflare R2.
13
+ *
14
+ * @param options - Installer options with selected packages
15
+ */
16
+ export async function cloud_installer(
17
+ options: InstallerOptions
18
+ ): Promise<void> {
19
+ const { projectDir, packages } = options;
20
+
21
+ for (const pkg of packages) {
22
+ switch (pkg) {
23
+ case AvailablePackages.aws:
24
+ await install_aws(projectDir);
25
+ break;
26
+ case AvailablePackages.gcp:
27
+ await install_gcp(projectDir);
28
+ break;
29
+ case AvailablePackages.azure:
30
+ await install_azure(projectDir);
31
+ break;
32
+ case AvailablePackages.cloudflareR2:
33
+ await install_cloudflare_r2(projectDir);
34
+ break;
35
+ }
36
+ }
37
+ }
38
+
39
+ async function install_aws(projectDir: string): Promise<void> {
40
+ const templateDir = path.resolve(__dirname, "../templates/extras/cloud/aws");
41
+ await copy_template_files(templateDir, projectDir);
42
+
43
+ await add_package_dependency(projectDir, {
44
+ "@aws-sdk/client-s3": DEPENDENCY_VERSION_MAP["@aws-sdk/client-s3"],
45
+ "@aws-sdk/s3-request-presigner":
46
+ DEPENDENCY_VERSION_MAP["@aws-sdk/s3-request-presigner"],
47
+ });
48
+
49
+ await append_env_example(
50
+ projectDir,
51
+ "\n# AWS Configuration\nAWS_ACCESS_KEY_ID=your-aws-access-key\nAWS_SECRET_ACCESS_KEY=your-aws-secret-key\nAWS_REGION=us-east-1\nAWS_S3_BUCKET=your-bucket-name\n"
52
+ );
53
+ }
54
+
55
+ async function install_gcp(projectDir: string): Promise<void> {
56
+ const templateDir = path.resolve(__dirname, "../templates/extras/cloud/gcp");
57
+ await copy_template_files(templateDir, projectDir);
58
+
59
+ await add_package_dependency(projectDir, {
60
+ "@google-cloud/storage": DEPENDENCY_VERSION_MAP["@google-cloud/storage"],
61
+ });
62
+
63
+ await append_env_example(
64
+ projectDir,
65
+ "\n# Google Cloud Platform Configuration\nGCP_PROJECT_ID=your-project-id\nGCP_BUCKET_NAME=your-bucket-name\n# GCP_KEY_FILE=path/to/service-account-key.json\n"
66
+ );
67
+ }
68
+
69
+ async function install_azure(projectDir: string): Promise<void> {
70
+ const templateDir = path.resolve(
71
+ __dirname,
72
+ "../templates/extras/cloud/azure"
73
+ );
74
+ await copy_template_files(templateDir, projectDir);
75
+
76
+ await add_package_dependency(projectDir, {
77
+ "@azure/storage-blob": DEPENDENCY_VERSION_MAP["@azure/storage-blob"],
78
+ });
79
+
80
+ await append_env_example(
81
+ projectDir,
82
+ "\n# Azure Configuration\nAZURE_STORAGE_CONNECTION_STRING=your-connection-string\nAZURE_STORAGE_CONTAINER=your-container-name\n"
83
+ );
84
+ }
85
+
86
+ async function install_cloudflare_r2(projectDir: string): Promise<void> {
87
+ const templateDir = path.resolve(
88
+ __dirname,
89
+ "../templates/extras/cloud/cloudflare-r2"
90
+ );
91
+ await copy_template_files(templateDir, projectDir);
92
+
93
+ await add_package_dependency(projectDir, {
94
+ "@aws-sdk/client-s3": DEPENDENCY_VERSION_MAP["@aws-sdk/client-s3"],
95
+ });
96
+
97
+ await append_env_example(
98
+ projectDir,
99
+ "\n# Cloudflare R2 Configuration\nCLOUDFLARE_ACCOUNT_ID=your-account-id\nCLOUDFLARE_ACCESS_KEY_ID=your-access-key\nCLOUDFLARE_SECRET_ACCESS_KEY=your-secret-key\nCLOUDFLARE_R2_BUCKET=your-bucket-name\n"
100
+ );
101
+ }
102
+
103
+ async function copy_template_files(
104
+ templateDir: string,
105
+ projectDir: string
106
+ ): Promise<void> {
107
+ if (!(await fs.pathExists(templateDir))) {
108
+ return;
109
+ }
110
+
111
+ const srcDir = path.join(templateDir, "src");
112
+ if (await fs.pathExists(srcDir)) {
113
+ const targetSrcDir = path.join(projectDir, "src");
114
+ await fs.copy(srcDir, targetSrcDir, {
115
+ overwrite: false,
116
+ errorOnExist: false,
117
+ });
118
+ }
119
+ }
120
+
121
+ async function append_env_example(
122
+ projectDir: string,
123
+ content: string
124
+ ): Promise<void> {
125
+ const envPath = path.join(projectDir, ".env.example");
126
+ await fs.appendFile(envPath, content);
127
+ }