@donkeylabs/cli 2.0.15 → 2.0.16

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/package.json +1 -1
  2. package/src/commands/config.ts +610 -0
  3. package/src/commands/deploy-enhanced.ts +354 -0
  4. package/src/commands/deploy.ts +204 -0
  5. package/src/commands/init-enhanced.ts +1994 -0
  6. package/src/deployment/manager.ts +356 -0
  7. package/src/index.ts +47 -19
  8. package/templates/starter/.env.example +0 -44
  9. package/templates/starter/.gitignore.template +0 -4
  10. package/templates/starter/donkeylabs.config.ts +0 -6
  11. package/templates/starter/package.json +0 -21
  12. package/templates/starter/src/index.ts +0 -54
  13. package/templates/starter/src/plugins/stats/index.ts +0 -105
  14. package/templates/starter/src/routes/health/handlers/ping.ts +0 -22
  15. package/templates/starter/src/routes/health/index.ts +0 -19
  16. package/templates/starter/tsconfig.json +0 -27
  17. package/templates/sveltekit-app/.env.example +0 -59
  18. package/templates/sveltekit-app/README.md +0 -103
  19. package/templates/sveltekit-app/bun.lock +0 -683
  20. package/templates/sveltekit-app/donkeylabs.config.ts +0 -12
  21. package/templates/sveltekit-app/package.json +0 -38
  22. package/templates/sveltekit-app/src/app.css +0 -40
  23. package/templates/sveltekit-app/src/app.html +0 -12
  24. package/templates/sveltekit-app/src/hooks.server.ts +0 -4
  25. package/templates/sveltekit-app/src/lib/components/ui/badge/badge.svelte +0 -30
  26. package/templates/sveltekit-app/src/lib/components/ui/badge/index.ts +0 -3
  27. package/templates/sveltekit-app/src/lib/components/ui/button/button.svelte +0 -48
  28. package/templates/sveltekit-app/src/lib/components/ui/button/index.ts +0 -9
  29. package/templates/sveltekit-app/src/lib/components/ui/card/card-content.svelte +0 -18
  30. package/templates/sveltekit-app/src/lib/components/ui/card/card-description.svelte +0 -18
  31. package/templates/sveltekit-app/src/lib/components/ui/card/card-footer.svelte +0 -18
  32. package/templates/sveltekit-app/src/lib/components/ui/card/card-header.svelte +0 -18
  33. package/templates/sveltekit-app/src/lib/components/ui/card/card-title.svelte +0 -18
  34. package/templates/sveltekit-app/src/lib/components/ui/card/card.svelte +0 -21
  35. package/templates/sveltekit-app/src/lib/components/ui/card/index.ts +0 -21
  36. package/templates/sveltekit-app/src/lib/components/ui/index.ts +0 -4
  37. package/templates/sveltekit-app/src/lib/components/ui/input/index.ts +0 -2
  38. package/templates/sveltekit-app/src/lib/components/ui/input/input.svelte +0 -20
  39. package/templates/sveltekit-app/src/lib/permissions.ts +0 -213
  40. package/templates/sveltekit-app/src/lib/utils/index.ts +0 -6
  41. package/templates/sveltekit-app/src/routes/+layout.svelte +0 -8
  42. package/templates/sveltekit-app/src/routes/+page.server.ts +0 -25
  43. package/templates/sveltekit-app/src/routes/+page.svelte +0 -680
  44. package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +0 -23
  45. package/templates/sveltekit-app/src/routes/workflows/+page.svelte +0 -522
  46. package/templates/sveltekit-app/src/server/events.ts +0 -28
  47. package/templates/sveltekit-app/src/server/index.ts +0 -124
  48. package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +0 -377
  49. package/templates/sveltekit-app/src/server/plugins/auth/index.ts +0 -815
  50. package/templates/sveltekit-app/src/server/plugins/auth/migrations/001_create_users.ts +0 -25
  51. package/templates/sveltekit-app/src/server/plugins/auth/migrations/002_create_sessions.ts +0 -32
  52. package/templates/sveltekit-app/src/server/plugins/auth/migrations/003_create_refresh_tokens.ts +0 -33
  53. package/templates/sveltekit-app/src/server/plugins/auth/migrations/004_create_passkeys.ts +0 -60
  54. package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +0 -65
  55. package/templates/sveltekit-app/src/server/plugins/demo/index.ts +0 -262
  56. package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +0 -369
  57. package/templates/sveltekit-app/src/server/plugins/email/index.ts +0 -411
  58. package/templates/sveltekit-app/src/server/plugins/email/migrations/001_create_email_tokens.ts +0 -33
  59. package/templates/sveltekit-app/src/server/plugins/email/schema.ts +0 -24
  60. package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +0 -1048
  61. package/templates/sveltekit-app/src/server/plugins/permissions/migrations/001_create_tenants.ts +0 -63
  62. package/templates/sveltekit-app/src/server/plugins/permissions/migrations/002_create_roles.ts +0 -90
  63. package/templates/sveltekit-app/src/server/plugins/permissions/migrations/003_create_resource_grants.ts +0 -50
  64. package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +0 -566
  65. package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +0 -67
  66. package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +0 -198
  67. package/templates/sveltekit-app/src/server/routes/auth/auth.schemas.ts +0 -66
  68. package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +0 -18
  69. package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +0 -16
  70. package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +0 -20
  71. package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +0 -17
  72. package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +0 -19
  73. package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +0 -21
  74. package/templates/sveltekit-app/src/server/routes/auth/index.ts +0 -73
  75. package/templates/sveltekit-app/src/server/routes/demo.ts +0 -464
  76. package/templates/sveltekit-app/src/server/routes/example/example.schemas.ts +0 -22
  77. package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +0 -21
  78. package/templates/sveltekit-app/src/server/routes/example/index.ts +0 -28
  79. package/templates/sveltekit-app/src/server/routes/permissions/index.ts +0 -248
  80. package/templates/sveltekit-app/src/server/routes/tenants/index.ts +0 -339
  81. package/templates/sveltekit-app/static/robots.txt +0 -3
  82. package/templates/sveltekit-app/svelte.config.ts +0 -17
  83. package/templates/sveltekit-app/tsconfig.json +0 -20
  84. package/templates/sveltekit-app/vite.config.ts +0 -12
@@ -0,0 +1,354 @@
1
+ // packages/cli/src/commands/deploy-enhanced.ts
2
+ /**
3
+ * Enhanced deploy command with history, versioning, and rollbacks
4
+ */
5
+
6
+ import { execSync } from "child_process";
7
+ import { existsSync, readFileSync, writeFileSync } from "fs";
8
+ import { join } from "path";
9
+ import pc from "picocolors";
10
+ import { createDeploymentManager } from "../deployment/manager";
11
+
12
+ interface DeployOptions {
13
+ platform: "vercel" | "cloudflare" | "aws" | "vps";
14
+ environment: "production" | "staging" | "development";
15
+ version?: string;
16
+ versionBump?: "major" | "minor" | "patch";
17
+ skipBuild?: boolean;
18
+ }
19
+
20
+ export async function deployEnhancedCommand(args: string[]) {
21
+ const platform = args[0] as DeployOptions["platform"];
22
+
23
+ if (!platform) {
24
+ console.error(pc.red("āŒ Platform required"));
25
+ console.log(pc.gray("Usage: donkeylabs deploy <platform>"));
26
+ console.log(pc.gray("Platforms: vercel, cloudflare, aws, vps"));
27
+ process.exit(1);
28
+ }
29
+
30
+ const options = await promptForOptions(platform);
31
+ const manager = createDeploymentManager(process.cwd(), {
32
+ projectName: getProjectName(),
33
+ platform: options.platform,
34
+ environment: options.environment,
35
+ versionStrategy: "semver",
36
+ });
37
+
38
+ // Determine version
39
+ const version = options.version || manager.getNextVersion(options.versionBump);
40
+
41
+ console.log(pc.cyan(pc.bold(`\nšŸš€ Deploying v${version} to ${platform}\n`)));
42
+
43
+ // Pre-deployment checks
44
+ if (!(await runPreDeployChecks(options))) {
45
+ process.exit(1);
46
+ }
47
+
48
+ // Build
49
+ if (!options.skipBuild) {
50
+ console.log(pc.blue("šŸ“¦ Building..."));
51
+ try {
52
+ execSync("bun run build", { stdio: "inherit" });
53
+ } catch {
54
+ await manager.recordDeployment(version, platform, "failed");
55
+ console.error(pc.red("āŒ Build failed"));
56
+ process.exit(1);
57
+ }
58
+ }
59
+
60
+ // Deploy
61
+ let url: string | undefined;
62
+ try {
63
+ url = await deployToPlatform(options, version);
64
+ console.log(pc.green(`\nāœ… Deployed successfully!`));
65
+ console.log(pc.gray(`URL: ${url}`));
66
+ } catch (error) {
67
+ await manager.recordDeployment(version, platform, "failed");
68
+ console.error(pc.red("\nāŒ Deployment failed"));
69
+ process.exit(1);
70
+ }
71
+
72
+ // Record successful deployment
73
+ const deployment = await manager.recordDeployment(version, platform, "success", url, {
74
+ duration: Date.now(), // Would track actual duration
75
+ });
76
+
77
+ // Show deployment info
78
+ console.log(pc.cyan(`\nšŸ“Š Deployment recorded`));
79
+ console.log(pc.gray(`ID: ${deployment.id}`));
80
+ console.log(pc.gray(`Version: ${deployment.version}`));
81
+ console.log(pc.gray(`Git: ${deployment.gitSha} - ${deployment.gitMessage}`));
82
+
83
+ // Update package.json version
84
+ updatePackageVersion(version);
85
+
86
+ console.log(pc.green(`\nšŸŽ‰ Done!`));
87
+ }
88
+
89
+ async function promptForOptions(platform: string): Promise<DeployOptions> {
90
+ const prompts = await import("prompts");
91
+
92
+ const environment = await prompts.default({
93
+ type: "select",
94
+ name: "value",
95
+ message: "Environment:",
96
+ choices: [
97
+ { title: "Production", value: "production" },
98
+ { title: "Staging", value: "staging" },
99
+ { title: "Preview", value: "preview" },
100
+ ],
101
+ initial: 0,
102
+ }).then((r: any) => r.value);
103
+
104
+ const versionBump = await prompts.default({
105
+ type: "select",
106
+ name: "value",
107
+ message: "Version bump:",
108
+ choices: [
109
+ { title: "patch (bug fixes)", value: "patch" },
110
+ { title: "minor (new features)", value: "minor" },
111
+ { title: "major (breaking changes)", value: "major" },
112
+ { title: "custom version", value: "custom" },
113
+ ],
114
+ initial: 0,
115
+ }).then((r: any) => r.value);
116
+
117
+ let version: string | undefined;
118
+ if (versionBump === "custom") {
119
+ version = await prompts.default({
120
+ type: "text",
121
+ name: "value",
122
+ message: "Version:",
123
+ initial: "1.0.0",
124
+ }).then((r: any) => r.value);
125
+ }
126
+
127
+ return {
128
+ platform: platform as DeployOptions["platform"],
129
+ environment,
130
+ version,
131
+ versionBump: versionBump === "custom" ? undefined : versionBump,
132
+ };
133
+ }
134
+
135
+ async function runPreDeployChecks(options: DeployOptions): Promise<boolean> {
136
+ const checks = [
137
+ { name: "Git clean", fn: () => checkGitClean() },
138
+ { name: "Tests pass", fn: () => checkTests() },
139
+ { name: "TypeScript compiles", fn: () => checkTypescript() },
140
+ { name: "Environment variables", fn: () => checkEnvVars(options.platform) },
141
+ ];
142
+
143
+ console.log(pc.blue("\nšŸ” Pre-deployment checks:\n"));
144
+
145
+ for (const check of checks) {
146
+ process.stdout.write(` ${check.name}... `);
147
+ try {
148
+ await check.fn();
149
+ console.log(pc.green("āœ“"));
150
+ } catch (error) {
151
+ console.log(pc.red("āœ—"));
152
+ console.error(pc.red(` ${error}`));
153
+ return false;
154
+ }
155
+ }
156
+
157
+ console.log();
158
+ return true;
159
+ }
160
+
161
+ async function deployToPlatform(options: DeployOptions, version: string): Promise<string> {
162
+ switch (options.platform) {
163
+ case "vercel":
164
+ return deployToVercel(options, version);
165
+ case "cloudflare":
166
+ return deployToCloudflare(options, version);
167
+ case "aws":
168
+ return deployToAWS(options, version);
169
+ case "vps":
170
+ return deployToVPS(options, version);
171
+ default:
172
+ throw new Error(`Unknown platform: ${options.platform}`);
173
+ }
174
+ }
175
+
176
+ async function deployToVercel(options: DeployOptions, version: string): Promise<string> {
177
+ // Ensure vercel.json exists
178
+ setupVercelConfig();
179
+
180
+ const args = options.environment === "production" ? ["--prod"] : [];
181
+ const output = execSync(`vercel ${args.join(" ")}`, {
182
+ encoding: "utf-8",
183
+ stdio: ["pipe", "pipe", "pipe"],
184
+ });
185
+
186
+ // Extract URL from output
187
+ const urlMatch = output.match(/https:\/\/[^\s]+/);
188
+ return urlMatch ? urlMatch[0] : "";
189
+ }
190
+
191
+ async function deployToCloudflare(options: DeployOptions, version: string): Promise<string> {
192
+ // Ensure wrangler.toml exists
193
+ setupWranglerConfig(version);
194
+
195
+ execSync("wrangler deploy", { stdio: "inherit" });
196
+
197
+ // Get deployment URL
198
+ const output = execSync("wrangler deployment list --limit 1", {
199
+ encoding: "utf-8",
200
+ });
201
+
202
+ const urlMatch = output.match(/https:\/\/[^\s]+/);
203
+ return urlMatch ? urlMatch[0] : "";
204
+ }
205
+
206
+ async function deployToAWS(options: DeployOptions, version: string): Promise<string> {
207
+ // SAM or Serverless Framework deployment
208
+ execSync("sam deploy", { stdio: "inherit" });
209
+
210
+ // Get API Gateway URL
211
+ const output = execSync("sam list stack-outputs", {
212
+ encoding: "utf-8",
213
+ });
214
+
215
+ const urlMatch = output.match(/https:\/\/[^\s]+\.amazonaws\.com/);
216
+ return urlMatch ? urlMatch[0] : "";
217
+ }
218
+
219
+ async function deployToVPS(options: DeployOptions, version: string): Promise<string> {
220
+ // Docker deployment to VPS
221
+ execSync("docker-compose build", { stdio: "inherit" });
222
+ execSync("docker-compose up -d", { stdio: "inherit" });
223
+
224
+ return "http://your-vps-ip:3000"; // Would get actual IP
225
+ }
226
+
227
+ // Helper functions
228
+ function getProjectName(): string {
229
+ const pkgPath = join(process.cwd(), "package.json");
230
+ if (!existsSync(pkgPath)) return "unknown";
231
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
232
+ return pkg.name;
233
+ }
234
+
235
+ function checkGitClean(): void {
236
+ const status = execSync("git status --porcelain", { encoding: "utf-8" });
237
+ if (status.trim()) {
238
+ throw new Error("Uncommitted changes. Commit or stash first.");
239
+ }
240
+ }
241
+
242
+ function checkTests(): void {
243
+ try {
244
+ execSync("bun test", { stdio: "pipe" });
245
+ } catch {
246
+ throw new Error("Tests failed");
247
+ }
248
+ }
249
+
250
+ function checkTypescript(): void {
251
+ try {
252
+ execSync("bun --bun tsc --noEmit", { stdio: "pipe" });
253
+ } catch {
254
+ throw new Error("TypeScript errors");
255
+ }
256
+ }
257
+
258
+ function checkEnvVars(platform: string): void {
259
+ // Platform-specific env checks
260
+ const envPath = join(process.cwd(), ".env.local");
261
+ if (!existsSync(envPath)) {
262
+ throw new Error(".env.local not found");
263
+ }
264
+ }
265
+
266
+ function setupVercelConfig(): void {
267
+ // Create vercel.json if not exists
268
+ }
269
+
270
+ function setupWranglerConfig(version: string): void {
271
+ // Create wrangler.toml if not exists
272
+ }
273
+
274
+ function updatePackageVersion(version: string): void {
275
+ const pkgPath = join(process.cwd(), "package.json");
276
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
277
+ pkg.version = version;
278
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
279
+ }
280
+
281
+ // Additional CLI commands
282
+ export async function deployHistoryCommand(args: string[]) {
283
+ const manager = createDeploymentManager(process.cwd(), {
284
+ projectName: getProjectName(),
285
+ platform: "vercel",
286
+ environment: "production",
287
+ versionStrategy: "semver",
288
+ });
289
+
290
+ const history = manager.listDeployments({ limit: 10 });
291
+
292
+ console.log(pc.cyan(pc.bold("\nšŸ“œ Deployment History\n")));
293
+
294
+ for (const deployment of history) {
295
+ const statusIcon = deployment.status === "success" ? pc.green("āœ“") :
296
+ deployment.status === "failed" ? pc.red("āœ—") : pc.yellow("↺");
297
+
298
+ console.log(`${statusIcon} ${pc.bold(deployment.version)} ${pc.gray(deployment.platform)}`);
299
+ console.log(` ${deployment.gitSha} - ${deployment.gitMessage}`);
300
+ console.log(` ${deployment.timestamp}${deployment.url ? ` - ${deployment.url}` : ""}`);
301
+ console.log();
302
+ }
303
+ }
304
+
305
+ export async function deployRollbackCommand(args: string[]) {
306
+ const version = args[0];
307
+
308
+ const manager = createDeploymentManager(process.cwd(), {
309
+ projectName: getProjectName(),
310
+ platform: "vercel",
311
+ environment: "production",
312
+ versionStrategy: "semver",
313
+ });
314
+
315
+ console.log(pc.yellow(pc.bold("\nāš ļø Rollback\n")));
316
+
317
+ if (version) {
318
+ console.log(`Rolling back to version: ${version}`);
319
+ } else {
320
+ console.log("Rolling back to previous version");
321
+ }
322
+
323
+ try {
324
+ const deployment = await manager.rollback(version);
325
+ if (deployment) {
326
+ console.log(pc.green(`\nāœ… Rolled back to v${deployment.version}`));
327
+ }
328
+ } catch (error: any) {
329
+ console.error(pc.red(`\nāŒ Rollback failed: ${error.message}`));
330
+ process.exit(1);
331
+ }
332
+ }
333
+
334
+ export async function deployStatsCommand() {
335
+ const manager = createDeploymentManager(process.cwd(), {
336
+ projectName: getProjectName(),
337
+ platform: "vercel",
338
+ environment: "production",
339
+ versionStrategy: "semver",
340
+ });
341
+
342
+ const stats = manager.getStats();
343
+
344
+ console.log(pc.cyan(pc.bold("\nšŸ“Š Deployment Statistics\n")));
345
+ console.log(`Total deployments: ${stats.totalDeployments}`);
346
+ console.log(`Successful: ${pc.green(stats.successfulDeployments.toString())}`);
347
+ console.log(`Failed: ${pc.red(stats.failedDeployments.toString())}`);
348
+ console.log(`Rollbacks: ${pc.yellow(stats.rollbackCount.toString())}`);
349
+
350
+ console.log(pc.cyan("\nBy Platform:"));
351
+ for (const [platform, count] of Object.entries(stats.deploymentsByPlatform)) {
352
+ console.log(` ${platform}: ${count}`);
353
+ }
354
+ }
@@ -0,0 +1,204 @@
1
+ // packages/cli/src/commands/deploy.ts
2
+ /**
3
+ * Deploy command for serverless platforms
4
+ * Currently supports Vercel
5
+ */
6
+
7
+ import { execSync } from "child_process";
8
+ import { existsSync, readFileSync, writeFileSync } from "fs";
9
+ import { join } from "path";
10
+ import pc from "picocolors";
11
+
12
+ export async function deployCommand(args: string[]) {
13
+ const platform = args[0] || "vercel";
14
+
15
+ console.log(pc.cyan(pc.bold(`\nšŸš€ Deploy to ${platform}\n`)));
16
+
17
+ switch (platform) {
18
+ case "vercel":
19
+ await deployToVercel();
20
+ break;
21
+ case "help":
22
+ showHelp();
23
+ break;
24
+ default:
25
+ console.error(pc.red(`āŒ Unknown platform: ${platform}`));
26
+ console.log(pc.yellow("Supported platforms: vercel"));
27
+ process.exit(1);
28
+ }
29
+ }
30
+
31
+ async function deployToVercel() {
32
+ // Check if vercel CLI is installed
33
+ try {
34
+ execSync("which vercel", { stdio: "pipe" });
35
+ } catch {
36
+ console.log(pc.yellow("āš ļø Vercel CLI not found. Installing..."));
37
+ try {
38
+ execSync("npm install -g vercel", { stdio: "inherit" });
39
+ } catch {
40
+ console.error(pc.red("āŒ Failed to install Vercel CLI"));
41
+ console.log(pc.gray("Install manually: npm install -g vercel"));
42
+ process.exit(1);
43
+ }
44
+ }
45
+
46
+ // Check project structure
47
+ const projectDir = process.cwd();
48
+
49
+ if (!existsSync(join(projectDir, "vercel.json"))) {
50
+ console.log(pc.yellow("āš ļø No vercel.json found. Creating serverless configuration..."));
51
+ await setupVercelProject(projectDir);
52
+ }
53
+
54
+ // Check for required environment variables
55
+ const envFile = join(projectDir, ".env.local");
56
+ if (existsSync(envFile)) {
57
+ const envContent = readFileSync(envFile, "utf-8");
58
+ if (!envContent.includes("DATABASE_URL")) {
59
+ console.error(pc.red("āŒ DATABASE_URL not found in .env.local"));
60
+ console.log(pc.yellow("Serverless requires PostgreSQL. SQLite won't work."));
61
+ console.log(pc.gray("Set up a PostgreSQL database and add DATABASE_URL to .env.local"));
62
+ process.exit(1);
63
+ }
64
+ }
65
+
66
+ // Build the project
67
+ console.log(pc.blue("šŸ“¦ Building project..."));
68
+ try {
69
+ execSync("bun run build", { stdio: "inherit" });
70
+ } catch {
71
+ console.error(pc.red("āŒ Build failed"));
72
+ process.exit(1);
73
+ }
74
+
75
+ // Deploy
76
+ console.log(pc.blue("\nšŸš€ Deploying to Vercel...\n"));
77
+ try {
78
+ execSync("vercel --prod", { stdio: "inherit" });
79
+ } catch {
80
+ console.error(pc.red("āŒ Deployment failed"));
81
+ process.exit(1);
82
+ }
83
+
84
+ console.log(pc.green("\nāœ… Deployment complete!"));
85
+ console.log(pc.gray("\nYour API is live at the URL shown above."));
86
+ console.log(pc.gray("Test it: curl https://your-app.vercel.app/api.health"));
87
+ }
88
+
89
+ async function setupVercelProject(projectDir: string) {
90
+ // Create vercel.json
91
+ const vercelConfig = {
92
+ version: 2,
93
+ builds: [
94
+ {
95
+ src: "api/index.ts",
96
+ use: "@vercel/node",
97
+ config: {
98
+ includeFiles: "dist/**",
99
+ },
100
+ },
101
+ ],
102
+ routes: [
103
+ {
104
+ src: "/api/(.*)",
105
+ dest: "/api/index.ts",
106
+ },
107
+ {
108
+ src: "/(.*)",
109
+ dest: "/api/index.ts",
110
+ },
111
+ ],
112
+ env: {
113
+ NODE_ENV: "production",
114
+ },
115
+ };
116
+
117
+ writeFileSync(
118
+ join(projectDir, "vercel.json"),
119
+ JSON.stringify(vercelConfig, null, 2)
120
+ );
121
+
122
+ // Create api/index.ts if it doesn't exist
123
+ const apiDir = join(projectDir, "api");
124
+ if (!existsSync(apiDir)) {
125
+ require("fs").mkdirSync(apiDir, { recursive: true });
126
+ }
127
+
128
+ if (!existsSync(join(apiDir, "index.ts"))) {
129
+ const handlerContent = `import { createVercelHandler } from "@donkeylabs/adapter-serverless";
130
+ import { createServer } from "../src/server";
131
+
132
+ // Create server instance
133
+ const server = createServer();
134
+
135
+ // Export handler for Vercel
136
+ export default createVercelHandler(() => server);
137
+ `;
138
+ writeFileSync(join(apiDir, "index.ts"), handlerContent);
139
+ }
140
+
141
+ // Create src/server/index.ts that exports createServer
142
+ const serverDir = join(projectDir, "src", "server");
143
+ if (!existsSync(join(serverDir, "index.ts"))) {
144
+ const serverContent = `import { AppServer } from "@donkeylabs/server";
145
+ import { db } from "./db";
146
+
147
+ export function createServer() {
148
+ return new AppServer({
149
+ db,
150
+ logger: { level: "info" },
151
+ });
152
+ }
153
+ `;
154
+ writeFileSync(join(serverDir, "index.ts"), serverContent);
155
+ }
156
+
157
+ // Update package.json with serverless adapter
158
+ const pkgPath = join(projectDir, "package.json");
159
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
160
+
161
+ if (!pkg.dependencies["@donkeylabs/adapter-serverless"]) {
162
+ pkg.dependencies["@donkeylabs/adapter-serverless"] = "latest";
163
+ }
164
+
165
+ if (!pkg.dependencies.pg) {
166
+ pkg.dependencies.pg = "^8.11.0";
167
+ }
168
+
169
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
170
+
171
+ console.log(pc.green("āœ… Vercel configuration created"));
172
+ console.log(pc.gray("Created:"));
173
+ console.log(pc.gray(" - vercel.json"));
174
+ console.log(pc.gray(" - api/index.ts (handler entry)"));
175
+ console.log(pc.gray(" - src/server/index.ts (server factory)"));
176
+ }
177
+
178
+ function showHelp() {
179
+ console.log(`
180
+ ${pc.bold("Deploy Command")}
181
+
182
+ Usage:
183
+ donkeylabs deploy [platform]
184
+
185
+ Platforms:
186
+ vercel Deploy to Vercel (serverless)
187
+
188
+ Examples:
189
+ donkeylabs deploy vercel
190
+
191
+ Prerequisites:
192
+ - Vercel CLI: npm install -g vercel
193
+ - PostgreSQL database (SQLite won't work on serverless)
194
+ - DATABASE_URL in .env.local
195
+
196
+ First time setup:
197
+ 1. Run: vercel login
198
+ 2. Run: donkeylabs deploy vercel
199
+ 3. Follow prompts to link project
200
+
201
+ Note: Serverless deployment requires PostgreSQL.
202
+ SQLite is file-based and won't persist across serverless invocations.
203
+ `);
204
+ }