@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
@@ -1,522 +0,0 @@
1
- <script lang="ts">
2
- import { browser } from "$app/environment";
3
- import { onMount } from "svelte";
4
- import { Button } from "$lib/components/ui/button";
5
- import {
6
- Card,
7
- CardContent,
8
- CardDescription,
9
- CardHeader,
10
- CardTitle,
11
- } from "$lib/components/ui/card";
12
- import { Badge } from "$lib/components/ui/badge";
13
- import { createApi } from "$lib/api";
14
-
15
- // Workflow steps for visual display
16
- const WORKFLOW_STEPS = [
17
- { id: "validate", label: "Validate Order", icon: "1" },
18
- { id: "payment", label: "Process Payment", icon: "2" },
19
- { id: "fulfill", label: "Fulfill Order", icon: "3", parallel: true },
20
- { id: "complete", label: "Complete", icon: "4" },
21
- ];
22
-
23
- // Parallel sub-steps
24
- const PARALLEL_STEPS = {
25
- fulfill: [
26
- { id: "send-email", label: "Send Email" },
27
- { id: "prepare-shipment", label: "Prepare Shipment" },
28
- ],
29
- };
30
-
31
- let { data } = $props();
32
-
33
- const client = createApi();
34
-
35
- // Active workflow state
36
- let activeWorkflow = $state<any>(null);
37
- let workflowProgress = $state(0);
38
- let currentStep = $state<string | null>(null);
39
- let stepStatuses = $state<Record<string, string>>({});
40
- let stepResults = $state<Record<string, any>>({});
41
- let isStarting = $state(false);
42
-
43
- // History state
44
- let instances = $state(data.instances || []);
45
-
46
- // Event log for debugging
47
- let eventLog = $state<Array<{ time: string; event: string; data: any }>>([]);
48
-
49
- function getStepStatus(stepId: string): "pending" | "running" | "completed" | "failed" {
50
- return (stepStatuses[stepId] as any) || "pending";
51
- }
52
-
53
- function getStatusColor(status: string): "default" | "secondary" | "destructive" | "outline" | "success" {
54
- switch (status) {
55
- case "completed":
56
- return "success";
57
- case "running":
58
- return "default";
59
- case "failed":
60
- return "destructive";
61
- case "cancelled":
62
- return "secondary";
63
- default:
64
- return "outline";
65
- }
66
- }
67
-
68
- function getStepBgClass(status: string): string {
69
- switch (status) {
70
- case "completed":
71
- return "bg-green-500 text-white";
72
- case "running":
73
- return "bg-blue-500 text-white animate-pulse";
74
- case "failed":
75
- return "bg-red-500 text-white";
76
- default:
77
- return "bg-muted text-muted-foreground";
78
- }
79
- }
80
-
81
- function getStepBorderClass(status: string): string {
82
- switch (status) {
83
- case "completed":
84
- return "border-green-500";
85
- case "running":
86
- return "border-blue-500";
87
- case "failed":
88
- return "border-red-500";
89
- default:
90
- return "border-muted";
91
- }
92
- }
93
-
94
- async function startWorkflow() {
95
- isStarting = true;
96
- stepStatuses = {};
97
- stepResults = {};
98
- workflowProgress = 0;
99
- currentStep = null;
100
- eventLog = [];
101
-
102
- try {
103
- const result = await client.api.workflow.start({});
104
- activeWorkflow = { id: result.instanceId, status: "pending" };
105
-
106
- // Subscribe to this specific workflow's SSE channel
107
- const unsubscribe = client.sse.subscribe(
108
- [`workflow:${result.instanceId}`],
109
- handleWorkflowEvent
110
- );
111
-
112
- // Store unsubscribe for cleanup
113
- (activeWorkflow as any)._unsubscribe = unsubscribe;
114
- } catch (e) {
115
- console.error("Failed to start workflow:", e);
116
- } finally {
117
- isStarting = false;
118
- }
119
- }
120
-
121
- function handleWorkflowEvent(eventType: string, eventData: any) {
122
- // Log the event
123
- eventLog = [
124
- { time: new Date().toLocaleTimeString(), event: eventType, data: eventData },
125
- ...eventLog,
126
- ].slice(0, 20);
127
-
128
- switch (eventType) {
129
- case "workflow.started":
130
- activeWorkflow = { ...activeWorkflow, status: "running" };
131
- break;
132
-
133
- case "workflow.progress":
134
- workflowProgress = eventData.progress || 0;
135
- currentStep = eventData.currentStep;
136
- break;
137
-
138
- case "workflow.step.started":
139
- stepStatuses = { ...stepStatuses, [eventData.stepName]: "running" };
140
- currentStep = eventData.stepName;
141
- break;
142
-
143
- case "workflow.step.completed":
144
- stepStatuses = { ...stepStatuses, [eventData.stepName]: "completed" };
145
- if (eventData.output) {
146
- stepResults = { ...stepResults, [eventData.stepName]: eventData.output };
147
- }
148
- break;
149
-
150
- case "workflow.step.failed":
151
- stepStatuses = { ...stepStatuses, [eventData.stepName]: "failed" };
152
- break;
153
-
154
- case "workflow.completed":
155
- activeWorkflow = { ...activeWorkflow, status: "completed", output: eventData.output };
156
- workflowProgress = 100;
157
- refreshInstances();
158
- break;
159
-
160
- case "workflow.failed":
161
- activeWorkflow = { ...activeWorkflow, status: "failed", error: eventData.error };
162
- refreshInstances();
163
- break;
164
-
165
- case "workflow.cancelled":
166
- activeWorkflow = { ...activeWorkflow, status: "cancelled" };
167
- refreshInstances();
168
- break;
169
- }
170
- }
171
-
172
- async function cancelWorkflow() {
173
- if (!activeWorkflow) return;
174
- try {
175
- await client.api.workflow.cancel({ instanceId: activeWorkflow.id });
176
- } catch (e) {
177
- console.error("Failed to cancel workflow:", e);
178
- }
179
- }
180
-
181
- async function refreshInstances() {
182
- try {
183
- const result = await client.api.workflow.list({});
184
- instances = result.instances || [];
185
- } catch (e) {
186
- console.error("Failed to refresh instances:", e);
187
- }
188
- }
189
-
190
- async function viewWorkflowDetails(instanceId: string) {
191
- try {
192
- const status = await client.api.workflow.status({ instanceId });
193
- if (status) {
194
- activeWorkflow = status;
195
- stepStatuses = {};
196
- stepResults = {};
197
- workflowProgress = status.status === "completed" ? 100 : 0;
198
-
199
- // Rebuild step statuses from stepResults
200
- if (status.stepResults) {
201
- for (const [stepName, result] of Object.entries(status.stepResults as Record<string, any>)) {
202
- stepStatuses[stepName] = result.status || "completed";
203
- if (result.output) {
204
- stepResults[stepName] = result.output;
205
- }
206
- }
207
- }
208
- }
209
- } catch (e) {
210
- console.error("Failed to get workflow status:", e);
211
- }
212
- }
213
-
214
- function clearWorkflow() {
215
- if (activeWorkflow?._unsubscribe) {
216
- activeWorkflow._unsubscribe();
217
- }
218
- activeWorkflow = null;
219
- stepStatuses = {};
220
- stepResults = {};
221
- workflowProgress = 0;
222
- currentStep = null;
223
- eventLog = [];
224
- }
225
-
226
- onMount(() => {
227
- if (!browser) return;
228
-
229
- // Subscribe to workflow-updates channel for list updates
230
- const unsubscribe = client.sse.subscribe(
231
- ["workflow-updates"],
232
- (eventType, eventData) => {
233
- if (["workflow.completed", "workflow.failed", "workflow.cancelled"].includes(eventType)) {
234
- refreshInstances();
235
- }
236
- }
237
- );
238
-
239
- return () => {
240
- unsubscribe();
241
- if (activeWorkflow?._unsubscribe) {
242
- activeWorkflow._unsubscribe();
243
- }
244
- };
245
- });
246
- </script>
247
-
248
- <div class="min-h-screen bg-background">
249
- <div class="container mx-auto max-w-6xl py-8 px-4">
250
- <!-- Header -->
251
- <div class="text-center mb-8">
252
- <h1 class="text-3xl font-bold tracking-tight">Workflow Demo</h1>
253
- <p class="text-muted-foreground mt-2">
254
- Step Function Orchestration with Real-time Progress
255
- </p>
256
- <div class="flex gap-2 justify-center mt-4">
257
- <a href="/">
258
- <Button variant="outline" size="sm">Back to Main Demo</Button>
259
- </a>
260
- </div>
261
- </div>
262
-
263
- <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
264
- <!-- Workflow Runner -->
265
- <div class="lg:col-span-2 space-y-6">
266
- <!-- Control Panel -->
267
- <Card>
268
- <CardHeader>
269
- <CardTitle>Order Processing Workflow</CardTitle>
270
- <CardDescription>
271
- Multi-step workflow with validation, payment, and parallel fulfillment
272
- </CardDescription>
273
- </CardHeader>
274
- <CardContent>
275
- <div class="flex gap-3 items-center">
276
- <Button
277
- onclick={startWorkflow}
278
- disabled={isStarting || (activeWorkflow && activeWorkflow.status === "running")}
279
- >
280
- {isStarting ? "Starting..." : "Start New Workflow"}
281
- </Button>
282
- {#if activeWorkflow && activeWorkflow.status === "running"}
283
- <Button variant="destructive" onclick={cancelWorkflow}>
284
- Cancel
285
- </Button>
286
- {/if}
287
- {#if activeWorkflow && activeWorkflow.status !== "running"}
288
- <Button variant="outline" onclick={clearWorkflow}>
289
- Clear
290
- </Button>
291
- {/if}
292
- {#if activeWorkflow}
293
- <Badge variant={getStatusColor(activeWorkflow.status)}>
294
- {activeWorkflow.status.toUpperCase()}
295
- </Badge>
296
- {/if}
297
- </div>
298
- </CardContent>
299
- </Card>
300
-
301
- <!-- Progress Visualization -->
302
- {#if activeWorkflow}
303
- <Card>
304
- <CardHeader>
305
- <CardTitle class="text-lg">Progress</CardTitle>
306
- <CardDescription>
307
- Instance: <code class="bg-muted px-1 rounded text-xs">{activeWorkflow.id}</code>
308
- </CardDescription>
309
- </CardHeader>
310
- <CardContent>
311
- <!-- Progress Bar -->
312
- <div class="mb-6">
313
- <div class="flex justify-between text-sm mb-1">
314
- <span>Progress</span>
315
- <span>{Math.round(workflowProgress)}%</span>
316
- </div>
317
- <div class="h-2 bg-muted rounded-full overflow-hidden">
318
- <div
319
- class="h-full bg-primary transition-all duration-500 ease-out"
320
- style="width: {workflowProgress}%"
321
- ></div>
322
- </div>
323
- </div>
324
-
325
- <!-- Step Visualization -->
326
- <div class="space-y-4">
327
- {#each WORKFLOW_STEPS as step, i}
328
- {@const status = getStepStatus(step.id)}
329
- <div class="flex items-start gap-4">
330
- <!-- Step Number/Icon -->
331
- <div
332
- class="w-10 h-10 rounded-full flex items-center justify-center font-bold text-sm transition-all duration-300 {getStepBgClass(status)}"
333
- >
334
- {#if status === "completed"}
335
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
336
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
337
- </svg>
338
- {:else if status === "running"}
339
- <svg class="w-5 h-5 animate-spin" fill="none" viewBox="0 0 24 24">
340
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
341
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
342
- </svg>
343
- {:else if status === "failed"}
344
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
345
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
346
- </svg>
347
- {:else}
348
- {step.icon}
349
- {/if}
350
- </div>
351
-
352
- <!-- Step Content -->
353
- <div class="flex-1">
354
- <div class="flex items-center gap-2">
355
- <span class="font-medium">{step.label}</span>
356
- {#if step.parallel}
357
- <Badge variant="outline" class="text-xs">Parallel</Badge>
358
- {/if}
359
- </div>
360
-
361
- <!-- Parallel Sub-steps -->
362
- {#if step.parallel && PARALLEL_STEPS[step.id as keyof typeof PARALLEL_STEPS]}
363
- <div class="mt-2 ml-4 space-y-2">
364
- {#each PARALLEL_STEPS[step.id as keyof typeof PARALLEL_STEPS] as subStep}
365
- {@const subStatus = getStepStatus(subStep.id)}
366
- <div class="flex items-center gap-2 text-sm">
367
- <div
368
- class="w-6 h-6 rounded-full flex items-center justify-center text-xs transition-all duration-300 {getStepBgClass(subStatus)}"
369
- >
370
- {#if subStatus === "completed"}
371
- <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
372
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
373
- </svg>
374
- {:else if subStatus === "running"}
375
- <svg class="w-3 h-3 animate-spin" fill="none" viewBox="0 0 24 24">
376
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
377
- </svg>
378
- {:else}
379
- -
380
- {/if}
381
- </div>
382
- <span class={subStatus === "completed" ? "text-green-600" : subStatus === "running" ? "text-blue-600" : "text-muted-foreground"}>
383
- {subStep.label}
384
- </span>
385
- </div>
386
- {/each}
387
- </div>
388
- {/if}
389
-
390
- <!-- Step Result -->
391
- {#if stepResults[step.id]}
392
- <pre class="mt-2 text-xs bg-muted p-2 rounded overflow-auto max-h-20">{JSON.stringify(stepResults[step.id], null, 2)}</pre>
393
- {/if}
394
- </div>
395
- </div>
396
-
397
- <!-- Connector Line -->
398
- {#if i < WORKFLOW_STEPS.length - 1}
399
- <div class="ml-5 w-0.5 h-4 bg-muted"></div>
400
- {/if}
401
- {/each}
402
- </div>
403
-
404
- <!-- Final Output -->
405
- {#if activeWorkflow.status === "completed" && activeWorkflow.output}
406
- <div class="mt-6 p-4 bg-green-50 dark:bg-green-950 rounded-lg border border-green-200 dark:border-green-800">
407
- <h4 class="font-medium text-green-800 dark:text-green-200 mb-2">Workflow Complete</h4>
408
- <pre class="text-xs overflow-auto">{JSON.stringify(activeWorkflow.output, null, 2)}</pre>
409
- </div>
410
- {/if}
411
-
412
- {#if activeWorkflow.status === "failed" && activeWorkflow.error}
413
- <div class="mt-6 p-4 bg-red-50 dark:bg-red-950 rounded-lg border border-red-200 dark:border-red-800">
414
- <h4 class="font-medium text-red-800 dark:text-red-200 mb-2">Workflow Failed</h4>
415
- <p class="text-sm text-red-600 dark:text-red-400">{activeWorkflow.error}</p>
416
- </div>
417
- {/if}
418
- </CardContent>
419
- </Card>
420
- {/if}
421
-
422
- <!-- Event Log -->
423
- {#if eventLog.length > 0}
424
- <Card>
425
- <CardHeader>
426
- <CardTitle class="text-lg">SSE Event Log</CardTitle>
427
- <CardDescription>Real-time events from the server</CardDescription>
428
- </CardHeader>
429
- <CardContent>
430
- <ul class="space-y-1 max-h-48 overflow-y-auto font-mono text-xs">
431
- {#each eventLog as log}
432
- <li class="flex gap-2 p-1 hover:bg-muted rounded">
433
- <span class="text-muted-foreground">{log.time}</span>
434
- <Badge variant="outline" class="text-xs">{log.event.replace("workflow.", "")}</Badge>
435
- {#if log.data.stepName}
436
- <span class="text-primary">{log.data.stepName}</span>
437
- {/if}
438
- {#if log.data.progress !== undefined}
439
- <span class="text-green-600">{log.data.progress}%</span>
440
- {/if}
441
- </li>
442
- {/each}
443
- </ul>
444
- </CardContent>
445
- </Card>
446
- {/if}
447
- </div>
448
-
449
- <!-- Sidebar - History -->
450
- <div class="space-y-6">
451
- <Card>
452
- <CardHeader>
453
- <CardTitle class="text-lg">Workflow History</CardTitle>
454
- <CardDescription>Recent workflow instances</CardDescription>
455
- </CardHeader>
456
- <CardContent>
457
- <div class="flex gap-2 mb-4">
458
- <Button size="sm" variant="outline" onclick={refreshInstances}>
459
- Refresh
460
- </Button>
461
- </div>
462
- {#if instances.length === 0}
463
- <p class="text-sm text-muted-foreground italic">No workflows yet</p>
464
- {:else}
465
- <ul class="space-y-2 max-h-96 overflow-y-auto">
466
- {#each instances as instance}
467
- <li>
468
- <button
469
- class="w-full text-left p-3 rounded-lg border bg-card hover:bg-muted/50 transition-colors"
470
- onclick={() => viewWorkflowDetails(instance.id)}
471
- >
472
- <div class="flex items-center justify-between">
473
- <code class="text-xs truncate max-w-[120px]">{instance.id}</code>
474
- <Badge variant={getStatusColor(instance.status)} class="text-xs">
475
- {instance.status}
476
- </Badge>
477
- </div>
478
- {#if instance.currentStep}
479
- <p class="text-xs text-muted-foreground mt-1">
480
- Step: {instance.currentStep}
481
- </p>
482
- {/if}
483
- <p class="text-xs text-muted-foreground mt-1">
484
- {new Date(instance.createdAt).toLocaleString()}
485
- </p>
486
- </button>
487
- </li>
488
- {/each}
489
- </ul>
490
- {/if}
491
- </CardContent>
492
- </Card>
493
-
494
- <!-- Workflow Info -->
495
- <Card>
496
- <CardHeader>
497
- <CardTitle class="text-lg">About This Demo</CardTitle>
498
- </CardHeader>
499
- <CardContent class="text-sm space-y-2">
500
- <p>
501
- This demo shows a <strong>step function workflow</strong> for order processing:
502
- </p>
503
- <ol class="list-decimal list-inside space-y-1 text-muted-foreground">
504
- <li>Validate order (1s)</li>
505
- <li>Process payment (2s)</li>
506
- <li>Parallel fulfillment:
507
- <ul class="list-disc list-inside ml-4">
508
- <li>Send confirmation email</li>
509
- <li>Prepare shipment</li>
510
- </ul>
511
- </li>
512
- <li>Complete with summary</li>
513
- </ol>
514
- <p class="text-muted-foreground">
515
- Progress is streamed via <strong>Server-Sent Events (SSE)</strong> for real-time updates.
516
- </p>
517
- </CardContent>
518
- </Card>
519
- </div>
520
- </div>
521
- </div>
522
- </div>
@@ -1,28 +0,0 @@
1
- import { z } from "zod";
2
- import { defineEvents } from "@donkeylabs/server";
3
-
4
- /**
5
- * Server-level events.
6
- * These events are typed and available across the app.
7
- * Use ctx.core.events.emit("event.name", data) to emit.
8
- * Use ctx.core.events.on("event.name", handler) to subscribe.
9
- */
10
- export const events = defineEvents({
11
- "order.created": z.object({
12
- orderId: z.string(),
13
- userId: z.string(),
14
- total: z.number(),
15
- }),
16
- "order.shipped": z.object({
17
- orderId: z.string(),
18
- trackingNumber: z.string(),
19
- shippedAt: z.string(),
20
- }),
21
- "user.signup": z.object({
22
- userId: z.string(),
23
- email: z.string(),
24
- }),
25
- "user.verified": z.object({
26
- userId: z.string(),
27
- }),
28
- });
@@ -1,124 +0,0 @@
1
- // Server entry for @donkeylabs/adapter-sveltekit
2
- import { AppServer } from "@donkeylabs/server";
3
- import { Kysely } from "kysely";
4
- import { BunSqliteDialect } from "kysely-bun-sqlite";
5
- import { Database } from "bun:sqlite";
6
- import { demoPlugin } from "./plugins/demo";
7
- import { workflowDemoPlugin } from "./plugins/workflow-demo";
8
- import { authPlugin } from "./plugins/auth";
9
- import { emailPlugin } from "./plugins/email";
10
- import { permissionsPlugin } from "./plugins/permissions";
11
- import demoRoutes from "./routes/demo";
12
- import { exampleRouter } from "./routes/example";
13
- import { authRouter } from "./routes/auth";
14
- import { permissionsRouter } from "./routes/permissions";
15
- import { tenantsRouter } from "./routes/tenants";
16
-
17
- // Simple in-memory database
18
- const db = new Kysely<{}>({
19
- dialect: new BunSqliteDialect({ database: new Database(":memory:") }),
20
- });
21
-
22
- // Create server
23
- // Note: Client types are generated by `donkeylabs generate` (via vite plugin or CLI)
24
- export const server = new AppServer({
25
- db,
26
- port: 0, // Port managed by adapter
27
- // Admin dashboard - enabled in dev mode
28
- admin: {
29
- enabled: true,
30
- prefix: "admin",
31
- },
32
- });
33
-
34
- // =============================================================================
35
- // AUTH PLUGIN CONFIGURATION
36
- // =============================================================================
37
- // Choose your auth strategy:
38
- //
39
- // 1. SESSION (default) - Stateful, stores sessions in database
40
- // Best for: Web apps, server-rendered pages
41
- // server.registerPlugin(authPlugin());
42
- //
43
- // 2. JWT - Stateless tokens, no database lookup needed
44
- // Best for: Mobile apps, microservices, APIs
45
- // server.registerPlugin(authPlugin({
46
- // strategy: "jwt",
47
- // jwt: { secret: process.env.JWT_SECRET! },
48
- // }));
49
- //
50
- // 3. REFRESH-TOKEN - Short-lived access + long-lived refresh token
51
- // Best for: SPAs, mobile apps needing token refresh
52
- // server.registerPlugin(authPlugin({
53
- // strategy: "refresh-token",
54
- // jwt: {
55
- // secret: process.env.JWT_SECRET!,
56
- // accessExpiry: "15m",
57
- // refreshExpiry: "30d",
58
- // },
59
- // cookie: { httpOnly: true, secure: true },
60
- // }));
61
- //
62
- // =============================================================================
63
-
64
- // Using default session strategy for this template
65
- server.registerPlugin(authPlugin({}));
66
-
67
- // Email plugin - supports Resend or console (for development)
68
- // Configure with process.env.RESEND_API_KEY for production
69
- server.registerPlugin(emailPlugin({
70
- provider: process.env.RESEND_API_KEY ? "resend" : "console",
71
- resend: process.env.RESEND_API_KEY ? { apiKey: process.env.RESEND_API_KEY } : undefined,
72
- from: process.env.EMAIL_FROM || "noreply@example.com",
73
- baseUrl: process.env.PUBLIC_BASE_URL || "http://localhost:5173",
74
- }));
75
-
76
- // =============================================================================
77
- // PERMISSIONS PLUGIN - Multi-tenant RBAC
78
- // =============================================================================
79
- // Define your app's permissions here for type-safe checking.
80
- // Client can use: api.permissions.check({ permissions: ["documents.create"] })
81
- //
82
- server.registerPlugin(permissionsPlugin({
83
- permissions: {
84
- // Documents
85
- documents: ["create", "read", "write", "delete", "admin"],
86
- // Members & Roles
87
- members: ["invite", "remove", "list"],
88
- roles: ["create", "assign", "manage"],
89
- // Billing (example)
90
- billing: ["view", "manage"],
91
- } as const,
92
- defaultRoles: [
93
- {
94
- name: "Admin",
95
- permissions: ["*"], // Full access
96
- isDefault: false,
97
- },
98
- {
99
- name: "Member",
100
- permissions: [
101
- "documents.create",
102
- "documents.read",
103
- "documents.write",
104
- "members.list",
105
- ],
106
- isDefault: true, // Auto-assigned to new members
107
- },
108
- {
109
- name: "Viewer",
110
- permissions: ["documents.read", "members.list"],
111
- isDefault: false,
112
- },
113
- ],
114
- }));
115
-
116
- server.registerPlugin(demoPlugin);
117
- server.registerPlugin(workflowDemoPlugin);
118
-
119
- // Register routes
120
- server.use(authRouter);
121
- server.use(permissionsRouter);
122
- server.use(tenantsRouter);
123
- server.use(demoRoutes);
124
- server.use(exampleRouter);