@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,25 +0,0 @@
1
- import type { Kysely } from "kysely";
2
-
3
- export async function up(db: Kysely<any>): Promise<void> {
4
- await db.schema
5
- .createTable("users")
6
- .ifNotExists()
7
- .addColumn("id", "text", (col) => col.primaryKey())
8
- .addColumn("email", "text", (col) => col.notNull().unique())
9
- .addColumn("password_hash", "text", (col) => col.notNull())
10
- .addColumn("name", "text")
11
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo("CURRENT_TIMESTAMP"))
12
- .addColumn("updated_at", "text", (col) => col.notNull())
13
- .execute();
14
-
15
- await db.schema
16
- .createIndex("idx_users_email")
17
- .ifNotExists()
18
- .on("users")
19
- .column("email")
20
- .execute();
21
- }
22
-
23
- export async function down(db: Kysely<any>): Promise<void> {
24
- await db.schema.dropTable("users").ifExists().execute();
25
- }
@@ -1,32 +0,0 @@
1
- import type { Kysely } from "kysely";
2
-
3
- export async function up(db: Kysely<any>): Promise<void> {
4
- await db.schema
5
- .createTable("sessions")
6
- .ifNotExists()
7
- .addColumn("id", "text", (col) => col.primaryKey())
8
- .addColumn("user_id", "text", (col) =>
9
- col.notNull().references("users.id").onDelete("cascade")
10
- )
11
- .addColumn("expires_at", "text", (col) => col.notNull())
12
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo("CURRENT_TIMESTAMP"))
13
- .execute();
14
-
15
- await db.schema
16
- .createIndex("idx_sessions_user_id")
17
- .ifNotExists()
18
- .on("sessions")
19
- .column("user_id")
20
- .execute();
21
-
22
- await db.schema
23
- .createIndex("idx_sessions_expires_at")
24
- .ifNotExists()
25
- .on("sessions")
26
- .column("expires_at")
27
- .execute();
28
- }
29
-
30
- export async function down(db: Kysely<any>): Promise<void> {
31
- await db.schema.dropTable("sessions").ifExists().execute();
32
- }
@@ -1,33 +0,0 @@
1
- import type { Kysely } from "kysely";
2
-
3
- export async function up(db: Kysely<any>): Promise<void> {
4
- await db.schema
5
- .createTable("refresh_tokens")
6
- .ifNotExists()
7
- .addColumn("id", "text", (col) => col.primaryKey())
8
- .addColumn("user_id", "text", (col) =>
9
- col.notNull().references("users.id").onDelete("cascade")
10
- )
11
- .addColumn("token_hash", "text", (col) => col.notNull())
12
- .addColumn("expires_at", "text", (col) => col.notNull())
13
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo("CURRENT_TIMESTAMP"))
14
- .execute();
15
-
16
- await db.schema
17
- .createIndex("idx_refresh_tokens_user_id")
18
- .ifNotExists()
19
- .on("refresh_tokens")
20
- .column("user_id")
21
- .execute();
22
-
23
- await db.schema
24
- .createIndex("idx_refresh_tokens_expires_at")
25
- .ifNotExists()
26
- .on("refresh_tokens")
27
- .column("expires_at")
28
- .execute();
29
- }
30
-
31
- export async function down(db: Kysely<any>): Promise<void> {
32
- await db.schema.dropTable("refresh_tokens").ifExists().execute();
33
- }
@@ -1,60 +0,0 @@
1
- import type { Kysely } from "kysely";
2
-
3
- export async function up(db: Kysely<any>): Promise<void> {
4
- // Passkey credentials (WebAuthn)
5
- await db.schema
6
- .createTable("passkeys")
7
- .ifNotExists()
8
- .addColumn("id", "text", (col) => col.primaryKey())
9
- .addColumn("user_id", "text", (col) =>
10
- col.notNull().references("users.id").onDelete("cascade")
11
- )
12
- .addColumn("credential_id", "text", (col) => col.notNull().unique())
13
- .addColumn("public_key", "text", (col) => col.notNull()) // Base64 encoded
14
- .addColumn("counter", "integer", (col) => col.notNull().defaultTo(0))
15
- .addColumn("device_type", "text") // platform, cross-platform
16
- .addColumn("backed_up", "integer", (col) => col.notNull().defaultTo(0))
17
- .addColumn("transports", "text") // JSON array
18
- .addColumn("name", "text") // User-friendly name
19
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo("CURRENT_TIMESTAMP"))
20
- .addColumn("last_used_at", "text")
21
- .execute();
22
-
23
- await db.schema
24
- .createIndex("idx_passkeys_user_id")
25
- .ifNotExists()
26
- .on("passkeys")
27
- .column("user_id")
28
- .execute();
29
-
30
- await db.schema
31
- .createIndex("idx_passkeys_credential_id")
32
- .ifNotExists()
33
- .on("passkeys")
34
- .column("credential_id")
35
- .execute();
36
-
37
- // Passkey challenges (temporary storage)
38
- await db.schema
39
- .createTable("passkey_challenges")
40
- .ifNotExists()
41
- .addColumn("id", "text", (col) => col.primaryKey())
42
- .addColumn("challenge", "text", (col) => col.notNull())
43
- .addColumn("user_id", "text") // Null for registration
44
- .addColumn("type", "text", (col) => col.notNull()) // registration, authentication
45
- .addColumn("expires_at", "text", (col) => col.notNull())
46
- .addColumn("created_at", "text", (col) => col.notNull().defaultTo("CURRENT_TIMESTAMP"))
47
- .execute();
48
-
49
- await db.schema
50
- .createIndex("idx_passkey_challenges_expires_at")
51
- .ifNotExists()
52
- .on("passkey_challenges")
53
- .column("expires_at")
54
- .execute();
55
- }
56
-
57
- export async function down(db: Kysely<any>): Promise<void> {
58
- await db.schema.dropTable("passkey_challenges").ifExists().execute();
59
- await db.schema.dropTable("passkeys").ifExists().execute();
60
- }
@@ -1,65 +0,0 @@
1
- /**
2
- * This file was generated by kysely-codegen.
3
- * Please do not edit it manually.
4
- */
5
-
6
- import type { ColumnType } from "kysely";
7
-
8
- export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
9
- ? ColumnType<S, I | undefined, U>
10
- : ColumnType<T, T | undefined, T>;
11
-
12
- export interface PasskeyChallenges {
13
- challenge: string;
14
- created_at: Generated<string>;
15
- expires_at: string;
16
- id: string | null;
17
- type: string;
18
- user_id: string | null;
19
- }
20
-
21
- export interface Passkeys {
22
- backed_up: Generated<number>;
23
- counter: Generated<number>;
24
- created_at: Generated<string>;
25
- credential_id: string;
26
- device_type: string | null;
27
- id: string | null;
28
- last_used_at: string | null;
29
- name: string | null;
30
- public_key: string;
31
- transports: string | null;
32
- user_id: string;
33
- }
34
-
35
- export interface RefreshTokens {
36
- created_at: Generated<string>;
37
- expires_at: string;
38
- id: string | null;
39
- token_hash: string;
40
- user_id: string;
41
- }
42
-
43
- export interface Sessions {
44
- created_at: Generated<string>;
45
- expires_at: string;
46
- id: string | null;
47
- user_id: string;
48
- }
49
-
50
- export interface Users {
51
- created_at: Generated<string>;
52
- email: string;
53
- id: string | null;
54
- name: string | null;
55
- password_hash: string;
56
- updated_at: string;
57
- }
58
-
59
- export interface DB {
60
- passkey_challenges: PasskeyChallenges;
61
- passkeys: Passkeys;
62
- refresh_tokens: RefreshTokens;
63
- sessions: Sessions;
64
- users: Users;
65
- }
@@ -1,262 +0,0 @@
1
- // Demo plugin with all core service integrations
2
- import { createPlugin } from "@donkeylabs/server";
3
-
4
-
5
- // Random event messages for SSE demo
6
- const eventMessages = [
7
- "User logged in",
8
- "New order placed",
9
- "Payment received",
10
- "Item shipped",
11
- "Review submitted",
12
- "Comment added",
13
- "File uploaded",
14
- "Task completed",
15
- "Alert triggered",
16
- "Sync finished",
17
- ];
18
-
19
-
20
- export const demoPlugin = createPlugin.define({
21
- name: "demo",
22
- service: async (ctx) => {
23
- let counter = 0;
24
-
25
- return {
26
- // Counter
27
- getCounter: () => counter,
28
- increment: () => ++counter,
29
- decrement: () => --counter,
30
- reset: () => { counter = 0; return counter; },
31
-
32
- // Cache helpers
33
- cacheSet: async (key: string, value: any, ttl?: number) => {
34
- await ctx.core.cache.set(key, value, ttl);
35
- return { success: true };
36
- },
37
- cacheGet: async (key: string) => {
38
- const value = await ctx.core.cache.get(key);
39
- const exists = await ctx.core.cache.has(key);
40
- return { value, exists };
41
- },
42
- cacheDelete: async (key: string) => {
43
- await ctx.core.cache.delete(key);
44
- return { success: true };
45
- },
46
- cacheKeys: async () => {
47
- const keys = await ctx.core.cache.keys();
48
- return { keys, size: keys.length };
49
- },
50
-
51
- // Jobs helpers
52
- enqueueJob: async (name: string, data: any, delay?: number) => {
53
- let jobId: string;
54
- if (delay && delay > 0) {
55
- const runAt = new Date(Date.now() + delay);
56
- jobId = await ctx.core.jobs.schedule(name, data, runAt);
57
- } else {
58
- jobId = await ctx.core.jobs.enqueue(name, data);
59
- }
60
- return { jobId };
61
- },
62
- getJobStats: async () => {
63
- const pending = await ctx.core.jobs.getByName("demo-job", "pending");
64
- const running = await ctx.core.jobs.getByName("demo-job", "running");
65
- const completed = await ctx.core.jobs.getByName("demo-job", "completed");
66
- return {
67
- pending: pending.length,
68
- running: running.length,
69
- completed: completed.length,
70
- };
71
- },
72
-
73
- // Cron helpers
74
- getCronTasks: () => ctx.core.cron.list().map(t => ({
75
- id: t.id,
76
- name: t.name,
77
- expression: t.expression,
78
- enabled: t.enabled,
79
- lastRun: t.lastRun?.toISOString(),
80
- nextRun: t.nextRun?.toISOString(),
81
- })),
82
-
83
- // Rate limiter helpers
84
- checkRateLimit: async (key: string, limit: number, window: number) => {
85
- return ctx.core.rateLimiter.check(key, limit, window);
86
- },
87
- resetRateLimit: async (key: string) => {
88
- await ctx.core.rateLimiter.reset(key);
89
- return { success: true };
90
- },
91
-
92
- // Events helpers (internal pub/sub)
93
- emitEvent: async (event: string, data: any) => {
94
- await ctx.core.events.emit(event, data);
95
- return { success: true };
96
- },
97
-
98
- // SSE broadcast
99
- broadcast: (channel: string, event: string, data: any) => {
100
- ctx.core.sse.broadcast(channel, event, data);
101
- return { success: true };
102
- },
103
- getSSEClients: () => ({
104
- total: ctx.core.sse.getClients().length,
105
- byChannel: ctx.core.sse.getClientsByChannel("events").length,
106
- }),
107
-
108
- // Audit helpers - compliance and tracking
109
- auditLog: async (action: string, resource: string, resourceId?: string, metadata?: Record<string, any>) => {
110
- const id = await ctx.core.audit.log({
111
- action,
112
- actor: "demo-user", // In real apps, get from auth context
113
- resource,
114
- resourceId,
115
- metadata,
116
- });
117
- return { id };
118
- },
119
- auditQuery: async (filters: { action?: string; resource?: string; limit?: number }) => {
120
- const entries = await ctx.core.audit.query({
121
- action: filters.action,
122
- resource: filters.resource,
123
- limit: filters.limit ?? 10,
124
- });
125
- return {
126
- entries: entries.map(e => ({
127
- id: e.id,
128
- timestamp: e.timestamp.toISOString(),
129
- action: e.action,
130
- actor: e.actor,
131
- resource: e.resource,
132
- resourceId: e.resourceId,
133
- metadata: e.metadata,
134
- })),
135
- };
136
- },
137
- auditGetByResource: async (resource: string, resourceId: string) => {
138
- const entries = await ctx.core.audit.getByResource(resource, resourceId);
139
- return {
140
- entries: entries.map(e => ({
141
- id: e.id,
142
- timestamp: e.timestamp.toISOString(),
143
- action: e.action,
144
- actor: e.actor,
145
- metadata: e.metadata,
146
- })),
147
- };
148
- },
149
-
150
- // WebSocket helpers - bidirectional real-time communication
151
- wsBroadcast: (channel: string, event: string, data: any) => {
152
- ctx.core.websocket.broadcast(channel, event, data);
153
- return { success: true };
154
- },
155
- wsBroadcastAll: (event: string, data: any) => {
156
- ctx.core.websocket.broadcastAll(event, data);
157
- return { success: true };
158
- },
159
- wsGetClients: (channel?: string) => {
160
- const allClients = ctx.core.websocket.getClients();
161
- // Filter by channel if provided
162
- const clients = channel
163
- ? allClients.filter((c) => c.channels.includes(channel))
164
- : allClients;
165
- return {
166
- count: clients.length,
167
- clients,
168
- };
169
- },
170
- wsGetClientCount: (channel?: string) => {
171
- return { count: ctx.core.websocket.getClientCount(channel) };
172
- },
173
- };
174
- },
175
- init: async (ctx) => {
176
- // Register job handler for demo
177
- ctx.core.jobs.register("demo-job", async (data) => {
178
- ctx.core.logger.info("Demo job executed", { data });
179
- // Broadcast job completion via SSE
180
- ctx.core.sse.broadcast("events", "job-completed", {
181
- id: Date.now(),
182
- message: `Job completed: ${data.message || "No message"}`,
183
- timestamp: new Date().toISOString(),
184
- });
185
- });
186
-
187
- // Schedule cron job to broadcast SSE events every 5 seconds
188
- ctx.core.cron.schedule("*/5 * * * * *", () => {
189
- const message = eventMessages[Math.floor(Math.random() * eventMessages.length)];
190
- ctx.core.sse.broadcast("events", "cron-event", {
191
- id: Date.now(),
192
- message,
193
- timestamp: new Date().toISOString(),
194
- source: "cron",
195
- });
196
- }, { name: "sse-broadcaster" });
197
-
198
- // Listen for internal events and broadcast to SSE
199
- ctx.core.events.on("demo.*", (data) => {
200
- ctx.core.sse.broadcast("events", "internal-event", {
201
- id: Date.now(),
202
- message: `Internal event: ${JSON.stringify(data)}`,
203
- timestamp: new Date().toISOString(),
204
- source: "events",
205
- });
206
- });
207
-
208
- // WebSocket message handler - echo messages back and broadcast to channel
209
- ctx.core.websocket.onMessage(async (clientId, event, data) => {
210
- ctx.core.logger.info("WebSocket message received", { clientId, event, data });
211
-
212
- // Echo the message back to the sender
213
- if (event === "echo") {
214
- ctx.core.websocket.send(clientId, "echo-reply", {
215
- original: data,
216
- timestamp: new Date().toISOString(),
217
- });
218
- }
219
-
220
- // Broadcast to a channel if requested
221
- if (event === "broadcast" && data?.channel) {
222
- ctx.core.websocket.broadcast(data.channel, "ws-broadcast", {
223
- from: clientId,
224
- message: data.message,
225
- timestamp: new Date().toISOString(),
226
- });
227
- }
228
-
229
- // Log WebSocket activity to audit trail
230
- await ctx.core.audit.log({
231
- action: "websocket.message",
232
- actor: clientId,
233
- resource: "websocket",
234
- resourceId: event,
235
- metadata: { event, dataSize: JSON.stringify(data).length },
236
- });
237
- });
238
-
239
- // Audit important events for compliance tracking
240
- ctx.core.events.on("job.completed", async (data: any) => {
241
- await ctx.core.audit.log({
242
- action: "job.completed",
243
- actor: "system",
244
- resource: "job",
245
- resourceId: data.jobId,
246
- metadata: { name: data.name },
247
- });
248
- });
249
-
250
- ctx.core.events.on("workflow.completed", async (data: any) => {
251
- await ctx.core.audit.log({
252
- action: "workflow.completed",
253
- actor: "system",
254
- resource: "workflow",
255
- resourceId: data.instanceId,
256
- metadata: { workflowName: data.workflowName },
257
- });
258
- });
259
-
260
- ctx.core.logger.info("Demo plugin initialized with all core services (including audit & websocket)");
261
- },
262
- });