@donkeylabs/cli 2.0.14 → 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 (85) 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/generate.ts +11 -13
  6. package/src/commands/init-enhanced.ts +1994 -0
  7. package/src/deployment/manager.ts +356 -0
  8. package/src/index.ts +47 -19
  9. package/templates/starter/.env.example +0 -44
  10. package/templates/starter/.gitignore.template +0 -4
  11. package/templates/starter/donkeylabs.config.ts +0 -6
  12. package/templates/starter/package.json +0 -21
  13. package/templates/starter/src/index.ts +0 -54
  14. package/templates/starter/src/plugins/stats/index.ts +0 -105
  15. package/templates/starter/src/routes/health/handlers/ping.ts +0 -22
  16. package/templates/starter/src/routes/health/index.ts +0 -19
  17. package/templates/starter/tsconfig.json +0 -27
  18. package/templates/sveltekit-app/.env.example +0 -59
  19. package/templates/sveltekit-app/README.md +0 -103
  20. package/templates/sveltekit-app/bun.lock +0 -683
  21. package/templates/sveltekit-app/donkeylabs.config.ts +0 -12
  22. package/templates/sveltekit-app/package.json +0 -38
  23. package/templates/sveltekit-app/src/app.css +0 -40
  24. package/templates/sveltekit-app/src/app.html +0 -12
  25. package/templates/sveltekit-app/src/hooks.server.ts +0 -4
  26. package/templates/sveltekit-app/src/lib/components/ui/badge/badge.svelte +0 -30
  27. package/templates/sveltekit-app/src/lib/components/ui/badge/index.ts +0 -3
  28. package/templates/sveltekit-app/src/lib/components/ui/button/button.svelte +0 -48
  29. package/templates/sveltekit-app/src/lib/components/ui/button/index.ts +0 -9
  30. package/templates/sveltekit-app/src/lib/components/ui/card/card-content.svelte +0 -18
  31. package/templates/sveltekit-app/src/lib/components/ui/card/card-description.svelte +0 -18
  32. package/templates/sveltekit-app/src/lib/components/ui/card/card-footer.svelte +0 -18
  33. package/templates/sveltekit-app/src/lib/components/ui/card/card-header.svelte +0 -18
  34. package/templates/sveltekit-app/src/lib/components/ui/card/card-title.svelte +0 -18
  35. package/templates/sveltekit-app/src/lib/components/ui/card/card.svelte +0 -21
  36. package/templates/sveltekit-app/src/lib/components/ui/card/index.ts +0 -21
  37. package/templates/sveltekit-app/src/lib/components/ui/index.ts +0 -4
  38. package/templates/sveltekit-app/src/lib/components/ui/input/index.ts +0 -2
  39. package/templates/sveltekit-app/src/lib/components/ui/input/input.svelte +0 -20
  40. package/templates/sveltekit-app/src/lib/permissions.ts +0 -213
  41. package/templates/sveltekit-app/src/lib/utils/index.ts +0 -6
  42. package/templates/sveltekit-app/src/routes/+layout.svelte +0 -8
  43. package/templates/sveltekit-app/src/routes/+page.server.ts +0 -25
  44. package/templates/sveltekit-app/src/routes/+page.svelte +0 -680
  45. package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +0 -23
  46. package/templates/sveltekit-app/src/routes/workflows/+page.svelte +0 -522
  47. package/templates/sveltekit-app/src/server/events.ts +0 -28
  48. package/templates/sveltekit-app/src/server/index.ts +0 -124
  49. package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +0 -377
  50. package/templates/sveltekit-app/src/server/plugins/auth/index.ts +0 -815
  51. package/templates/sveltekit-app/src/server/plugins/auth/migrations/001_create_users.ts +0 -25
  52. package/templates/sveltekit-app/src/server/plugins/auth/migrations/002_create_sessions.ts +0 -32
  53. package/templates/sveltekit-app/src/server/plugins/auth/migrations/003_create_refresh_tokens.ts +0 -33
  54. package/templates/sveltekit-app/src/server/plugins/auth/migrations/004_create_passkeys.ts +0 -60
  55. package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +0 -65
  56. package/templates/sveltekit-app/src/server/plugins/demo/index.ts +0 -262
  57. package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +0 -369
  58. package/templates/sveltekit-app/src/server/plugins/email/index.ts +0 -411
  59. package/templates/sveltekit-app/src/server/plugins/email/migrations/001_create_email_tokens.ts +0 -33
  60. package/templates/sveltekit-app/src/server/plugins/email/schema.ts +0 -24
  61. package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +0 -1048
  62. package/templates/sveltekit-app/src/server/plugins/permissions/migrations/001_create_tenants.ts +0 -63
  63. package/templates/sveltekit-app/src/server/plugins/permissions/migrations/002_create_roles.ts +0 -90
  64. package/templates/sveltekit-app/src/server/plugins/permissions/migrations/003_create_resource_grants.ts +0 -50
  65. package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +0 -566
  66. package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +0 -67
  67. package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +0 -198
  68. package/templates/sveltekit-app/src/server/routes/auth/auth.schemas.ts +0 -66
  69. package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +0 -18
  70. package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +0 -16
  71. package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +0 -20
  72. package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +0 -17
  73. package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +0 -19
  74. package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +0 -21
  75. package/templates/sveltekit-app/src/server/routes/auth/index.ts +0 -73
  76. package/templates/sveltekit-app/src/server/routes/demo.ts +0 -464
  77. package/templates/sveltekit-app/src/server/routes/example/example.schemas.ts +0 -22
  78. package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +0 -21
  79. package/templates/sveltekit-app/src/server/routes/example/index.ts +0 -28
  80. package/templates/sveltekit-app/src/server/routes/permissions/index.ts +0 -248
  81. package/templates/sveltekit-app/src/server/routes/tenants/index.ts +0 -339
  82. package/templates/sveltekit-app/static/robots.txt +0 -3
  83. package/templates/sveltekit-app/svelte.config.ts +0 -17
  84. package/templates/sveltekit-app/tsconfig.json +0 -20
  85. 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
+ }
@@ -769,18 +769,11 @@ export async function generateCommand(_args: string[]): Promise<void> {
769
769
 
770
770
  // Generate client using adapter (required for SvelteKit projects)
771
771
  if (config.adapter) {
772
- // Resolve the adapter path from the project's node_modules
773
- const adapterPath = join(process.cwd(), "node_modules", config.adapter, "src/generator/index.ts");
774
-
775
- if (!existsSync(adapterPath)) {
776
- console.error(pc.red(`Adapter not found: ${config.adapter}`));
777
- console.error(pc.dim(`Expected path: ${adapterPath}`));
778
- console.error(pc.dim(`Run: bun install`));
779
- process.exit(1);
780
- }
772
+ // Use the package's subpath export (e.g., @donkeylabs/adapter-sveltekit/generator)
773
+ const adapterGeneratorPath = `${config.adapter}/generator`;
781
774
 
782
775
  try {
783
- const adapterModule = await import(adapterPath);
776
+ const adapterModule = await import(adapterGeneratorPath);
784
777
  if (!adapterModule.generateClient) {
785
778
  console.error(pc.red(`Adapter ${config.adapter} does not export generateClient`));
786
779
  process.exit(1);
@@ -788,9 +781,14 @@ export async function generateCommand(_args: string[]): Promise<void> {
788
781
  await adapterModule.generateClient(config, serverRoutes, clientOutput);
789
782
  generated.push(`client (${config.adapter})`);
790
783
  } catch (e: any) {
791
- console.error(pc.red(`Failed to run adapter generator: ${config.adapter}`));
792
- console.error(pc.dim(e.message));
793
- if (e.stack) console.error(pc.dim(e.stack));
784
+ if (e.code === 'ERR_MODULE_NOT_FOUND' || e.message?.includes('Cannot find')) {
785
+ console.error(pc.red(`Adapter not found: ${config.adapter}`));
786
+ console.error(pc.dim(`Make sure the adapter is installed: bun add ${config.adapter}`));
787
+ } else {
788
+ console.error(pc.red(`Failed to run adapter generator: ${config.adapter}`));
789
+ console.error(pc.dim(e.message));
790
+ if (e.stack) console.error(pc.dim(e.stack));
791
+ }
794
792
  process.exit(1);
795
793
  }
796
794
  } else {