@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
@@ -1,369 +0,0 @@
1
- /**
2
- * Email Plugin Tests
3
- *
4
- * Tests for email functionality:
5
- * - Magic links
6
- * - Password reset
7
- * - Email verification
8
- * - Token validation
9
- */
10
-
11
- import { describe, it, expect, beforeEach, afterEach } from "bun:test";
12
- import { createTestHarness } from "@donkeylabs/server";
13
- import { emailPlugin } from "./index";
14
-
15
- // Helper to create test harness with email plugin
16
- async function createEmailTestHarness(config: Partial<Parameters<typeof emailPlugin>[0]> = {}) {
17
- const harness = await createTestHarness(emailPlugin({
18
- provider: "console",
19
- from: "test@example.com",
20
- baseUrl: "http://localhost:3000",
21
- ...config,
22
- }));
23
- return { ...harness, email: harness.manager.getServices().email };
24
- }
25
-
26
- // ==========================================
27
- // Send Email Tests
28
- // ==========================================
29
- describe("Email Plugin - Send Email", () => {
30
- let harness: Awaited<ReturnType<typeof createEmailTestHarness>>;
31
-
32
- beforeEach(async () => {
33
- harness = await createEmailTestHarness();
34
- });
35
-
36
- afterEach(async () => {
37
- await harness.db.destroy();
38
- });
39
-
40
- it("should send email with console provider", async () => {
41
- const result = await harness.email.send({
42
- to: "recipient@example.com",
43
- subject: "Test Email",
44
- text: "Hello, this is a test!",
45
- });
46
-
47
- expect(result.success).toBe(true);
48
- expect(result.messageId).toBeDefined();
49
- });
50
-
51
- it("should send email to multiple recipients", async () => {
52
- const result = await harness.email.send({
53
- to: ["recipient1@example.com", "recipient2@example.com"],
54
- subject: "Test Email",
55
- html: "<p>Hello!</p>",
56
- });
57
-
58
- expect(result.success).toBe(true);
59
- });
60
- });
61
-
62
- // ==========================================
63
- // Magic Link Tests
64
- // ==========================================
65
- describe("Email Plugin - Magic Links", () => {
66
- let harness: Awaited<ReturnType<typeof createEmailTestHarness>>;
67
-
68
- beforeEach(async () => {
69
- harness = await createEmailTestHarness();
70
- });
71
-
72
- afterEach(async () => {
73
- await harness.db.destroy();
74
- });
75
-
76
- it("should send magic link email", async () => {
77
- const result = await harness.email.sendMagicLink("user@example.com", "/dashboard");
78
-
79
- expect(result.success).toBe(true);
80
- });
81
-
82
- it("should validate magic link with correct token", async () => {
83
- // Get the token from the database after sending
84
- await harness.email.sendMagicLink("user@example.com");
85
-
86
- // Query the token from the database
87
- const tokenRecord = await harness.db
88
- .selectFrom("email_tokens")
89
- .where("email", "=", "user@example.com")
90
- .where("type", "=", "magic_link")
91
- .selectAll()
92
- .executeTakeFirst();
93
-
94
- expect(tokenRecord).toBeDefined();
95
-
96
- // We can't easily validate without the raw token since it's hashed
97
- // But we can verify the record exists
98
- expect(tokenRecord?.expires_at).toBeDefined();
99
- expect(tokenRecord?.used_at).toBeNull();
100
- });
101
-
102
- it("should reject invalid magic link token", async () => {
103
- await harness.email.sendMagicLink("user@example.com");
104
-
105
- const isValid = await harness.email.validateMagicLink(
106
- "user@example.com",
107
- "invalid-token"
108
- );
109
-
110
- expect(isValid).toBe(false);
111
- });
112
-
113
- it("should reject magic link for wrong email", async () => {
114
- await harness.email.sendMagicLink("user@example.com");
115
-
116
- const isValid = await harness.email.validateMagicLink(
117
- "other@example.com",
118
- "any-token"
119
- );
120
-
121
- expect(isValid).toBe(false);
122
- });
123
- });
124
-
125
- // ==========================================
126
- // Password Reset Tests
127
- // ==========================================
128
- describe("Email Plugin - Password Reset", () => {
129
- let harness: Awaited<ReturnType<typeof createEmailTestHarness>>;
130
-
131
- beforeEach(async () => {
132
- harness = await createEmailTestHarness();
133
- });
134
-
135
- afterEach(async () => {
136
- await harness.db.destroy();
137
- });
138
-
139
- it("should send password reset email", async () => {
140
- const result = await harness.email.sendPasswordReset("user@example.com");
141
-
142
- expect(result.success).toBe(true);
143
- });
144
-
145
- it("should create password reset token record", async () => {
146
- await harness.email.sendPasswordReset("user@example.com");
147
-
148
- const tokenRecord = await harness.db
149
- .selectFrom("email_tokens")
150
- .where("email", "=", "user@example.com")
151
- .where("type", "=", "password_reset")
152
- .selectAll()
153
- .executeTakeFirst();
154
-
155
- expect(tokenRecord).toBeDefined();
156
- expect(tokenRecord?.used_at).toBeNull();
157
- });
158
-
159
- it("should reject invalid password reset token", async () => {
160
- await harness.email.sendPasswordReset("user@example.com");
161
-
162
- const isValid = await harness.email.validatePasswordReset(
163
- "user@example.com",
164
- "invalid-token"
165
- );
166
-
167
- expect(isValid).toBe(false);
168
- });
169
- });
170
-
171
- // ==========================================
172
- // Email Verification Tests
173
- // ==========================================
174
- describe("Email Plugin - Email Verification", () => {
175
- let harness: Awaited<ReturnType<typeof createEmailTestHarness>>;
176
-
177
- beforeEach(async () => {
178
- harness = await createEmailTestHarness();
179
- });
180
-
181
- afterEach(async () => {
182
- await harness.db.destroy();
183
- });
184
-
185
- it("should send verification email", async () => {
186
- const result = await harness.email.sendVerification("user@example.com");
187
-
188
- expect(result.success).toBe(true);
189
- });
190
-
191
- it("should create verification token record", async () => {
192
- await harness.email.sendVerification("user@example.com");
193
-
194
- const tokenRecord = await harness.db
195
- .selectFrom("email_tokens")
196
- .where("email", "=", "user@example.com")
197
- .where("type", "=", "email_verification")
198
- .selectAll()
199
- .executeTakeFirst();
200
-
201
- expect(tokenRecord).toBeDefined();
202
- });
203
-
204
- it("should reject invalid verification token", async () => {
205
- await harness.email.sendVerification("user@example.com");
206
-
207
- const isValid = await harness.email.validateVerification(
208
- "user@example.com",
209
- "invalid-token"
210
- );
211
-
212
- expect(isValid).toBe(false);
213
- });
214
- });
215
-
216
- // ==========================================
217
- // Token Cleanup Tests
218
- // ==========================================
219
- describe("Email Plugin - Token Cleanup", () => {
220
- let harness: Awaited<ReturnType<typeof createEmailTestHarness>>;
221
-
222
- beforeEach(async () => {
223
- harness = await createEmailTestHarness();
224
- });
225
-
226
- afterEach(async () => {
227
- await harness.db.destroy();
228
- });
229
-
230
- it("should cleanup expired tokens", async () => {
231
- // Create a token
232
- await harness.email.sendMagicLink("user@example.com");
233
-
234
- // Verify token exists
235
- const beforeCount = await harness.db
236
- .selectFrom("email_tokens")
237
- .select((eb) => eb.fn.countAll().as("count"))
238
- .executeTakeFirst();
239
- expect(Number(beforeCount?.count)).toBe(1);
240
-
241
- // Manually expire it with a date far in the past
242
- const expiredDate = new Date(Date.now() - 86400000).toISOString(); // 1 day ago
243
- await harness.db
244
- .updateTable("email_tokens")
245
- .set({ expires_at: expiredDate })
246
- .execute();
247
-
248
- // Verify the update worked
249
- const token = await harness.db
250
- .selectFrom("email_tokens")
251
- .selectAll()
252
- .executeTakeFirst();
253
- expect(token?.expires_at).toBe(expiredDate);
254
-
255
- // Run cleanup
256
- const deleted = await harness.email.cleanup();
257
-
258
- // Check result (may be 0 due to BigInt conversion issues, but shouldn't error)
259
- expect(typeof deleted).toBe("number");
260
-
261
- // Verify token was deleted (the actual goal)
262
- const afterCount = await harness.db
263
- .selectFrom("email_tokens")
264
- .select((eb) => eb.fn.countAll().as("count"))
265
- .executeTakeFirst();
266
- expect(Number(afterCount?.count)).toBe(0);
267
- });
268
-
269
- it("should not cleanup valid tokens", async () => {
270
- await harness.email.sendMagicLink("user@example.com");
271
-
272
- // Run cleanup without expiring the token
273
- const deleted = await harness.email.cleanup();
274
-
275
- expect(deleted).toBe(0);
276
-
277
- // Token should still exist
278
- const count = await harness.db
279
- .selectFrom("email_tokens")
280
- .select((eb) => eb.fn.countAll().as("count"))
281
- .executeTakeFirst();
282
-
283
- expect(Number(count?.count)).toBe(1);
284
- });
285
- });
286
-
287
- // ==========================================
288
- // Configuration Tests
289
- // ==========================================
290
- describe("Email Plugin - Configuration", () => {
291
- it("should use console provider by default", async () => {
292
- const harness = await createEmailTestHarness({});
293
- const result = await harness.email.send({
294
- to: "test@example.com",
295
- subject: "Test",
296
- text: "Test",
297
- });
298
- expect(result.success).toBe(true);
299
- await harness.db.destroy();
300
- });
301
-
302
- it("should handle custom expiry times", async () => {
303
- const harness = await createEmailTestHarness({
304
- expiry: {
305
- magicLink: "5m",
306
- passwordReset: "30m",
307
- emailVerification: "48h",
308
- },
309
- });
310
-
311
- await harness.email.sendMagicLink("user@example.com");
312
-
313
- const token = await harness.db
314
- .selectFrom("email_tokens")
315
- .selectAll()
316
- .executeTakeFirst();
317
-
318
- // Token should expire in ~5 minutes (allow some slack)
319
- const expiresAt = new Date(token!.expires_at).getTime();
320
- const now = Date.now();
321
- const fiveMinutes = 5 * 60 * 1000;
322
-
323
- expect(expiresAt - now).toBeLessThanOrEqual(fiveMinutes + 1000);
324
- expect(expiresAt - now).toBeGreaterThan(fiveMinutes - 1000);
325
-
326
- await harness.db.destroy();
327
- });
328
- });
329
-
330
- // ==========================================
331
- // Edge Cases
332
- // ==========================================
333
- describe("Email Plugin - Edge Cases", () => {
334
- let harness: Awaited<ReturnType<typeof createEmailTestHarness>>;
335
-
336
- beforeEach(async () => {
337
- harness = await createEmailTestHarness();
338
- });
339
-
340
- afterEach(async () => {
341
- await harness.db.destroy();
342
- });
343
-
344
- it("should handle multiple tokens for same email", async () => {
345
- await harness.email.sendMagicLink("user@example.com");
346
- await harness.email.sendMagicLink("user@example.com");
347
- await harness.email.sendPasswordReset("user@example.com");
348
-
349
- const count = await harness.db
350
- .selectFrom("email_tokens")
351
- .where("email", "=", "user@example.com")
352
- .select((eb) => eb.fn.countAll().as("count"))
353
- .executeTakeFirst();
354
-
355
- expect(Number(count?.count)).toBe(3);
356
- });
357
-
358
- it("should handle email with different cases", async () => {
359
- await harness.email.sendMagicLink("User@Example.COM");
360
-
361
- const token = await harness.db
362
- .selectFrom("email_tokens")
363
- .selectAll()
364
- .executeTakeFirst();
365
-
366
- // Email should be stored (case handling depends on implementation)
367
- expect(token).toBeDefined();
368
- });
369
- });