@donkeylabs/cli 1.1.17 → 1.1.19

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 (26) hide show
  1. package/package.json +1 -1
  2. package/src/commands/generate.ts +154 -3
  3. package/templates/sveltekit-app/package.json +3 -3
  4. package/templates/sveltekit-app/src/lib/permissions.ts +15 -5
  5. package/templates/sveltekit-app/src/routes/+page.server.ts +1 -1
  6. package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +1 -1
  7. package/templates/sveltekit-app/src/server/index.ts +1 -1
  8. package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +377 -0
  9. package/templates/sveltekit-app/src/server/plugins/auth/index.ts +7 -7
  10. package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +65 -0
  11. package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +369 -0
  12. package/templates/sveltekit-app/src/server/plugins/email/schema.ts +24 -0
  13. package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +10 -7
  14. package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +566 -0
  15. package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +67 -0
  16. package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +3 -2
  17. package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +4 -6
  18. package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +5 -8
  19. package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +4 -7
  20. package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +4 -6
  21. package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +4 -6
  22. package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +5 -8
  23. package/templates/sveltekit-app/src/server/routes/auth/index.ts +6 -7
  24. package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +3 -5
  25. package/templates/sveltekit-app/src/server/routes/permissions/index.ts +9 -9
  26. package/templates/sveltekit-app/src/server/routes/tenants/index.ts +18 -18
@@ -1,14 +1,12 @@
1
- import type { Handler, Routes, AppContext } from "$server/api";
2
-
3
1
  /**
4
2
  * Refresh Handler - Get new access token using refresh token
5
3
  * Only available with refresh-token strategy
6
4
  */
7
- export class RefreshHandler implements Handler<Routes.Auth.Refresh> {
8
- constructor(private ctx: AppContext) {}
5
+ export class RefreshHandler {
6
+ constructor(private ctx: any) {}
9
7
 
10
- async handle(input: Routes.Auth.Refresh.Input): Promise<Routes.Auth.Refresh.Output> {
11
- const tokens = await this.ctx.plugins.auth.refresh(input.refreshToken);
8
+ async handle(input: { refreshToken: string }) {
9
+ const tokens = await (this.ctx.plugins as any).auth.refresh(input.refreshToken);
12
10
 
13
11
  return {
14
12
  accessToken: tokens.accessToken,
@@ -1,13 +1,11 @@
1
- import type { Handler, Routes, AppContext } from "$server/api";
2
-
3
1
  /**
4
2
  * Register Handler - Create a new user account
5
3
  */
6
- export class RegisterHandler implements Handler<Routes.Auth.Register> {
7
- constructor(private ctx: AppContext) {}
4
+ export class RegisterHandler {
5
+ constructor(private ctx: any) {}
8
6
 
9
- async handle(input: Routes.Auth.Register.Input): Promise<Routes.Auth.Register.Output> {
10
- const result = await this.ctx.plugins.auth.register({
7
+ async handle(input: { email: string; password: string; name: string }) {
8
+ const result = await (this.ctx.plugins as any).auth.register({
11
9
  email: input.email,
12
10
  password: input.password,
13
11
  name: input.name,
@@ -1,20 +1,17 @@
1
- import type { Handler, Routes, AppContext } from "$server/api";
2
-
3
1
  /**
4
2
  * Update Profile Handler - Update current user's profile
5
3
  */
6
- export class UpdateProfileHandler implements Handler<Routes.Auth.UpdateProfile> {
7
- constructor(private ctx: AppContext) {}
4
+ export class UpdateProfileHandler {
5
+ constructor(private ctx: any) {}
8
6
 
9
- async handle(input: Routes.Auth.UpdateProfile.Input): Promise<Routes.Auth.UpdateProfile.Output> {
10
- // Get user from request context (set by auth middleware)
11
- const user = (this.ctx as any).user;
7
+ async handle(input: { name?: string; email?: string }) {
8
+ const user = this.ctx.user;
12
9
 
13
10
  if (!user) {
14
11
  throw this.ctx.errors.Unauthorized("Not authenticated");
15
12
  }
16
13
 
17
- const updated = await this.ctx.plugins.auth.updateProfile(user.id, {
14
+ const updated = await (this.ctx.plugins as any).auth.updateProfile(user.id, {
18
15
  name: input.name,
19
16
  email: input.email,
20
17
  });
@@ -5,9 +5,9 @@
5
5
  * - auth.register - Create new account
6
6
  * - auth.login - Login and get tokens
7
7
  * - auth.refresh - Refresh access token (refresh-token strategy only)
8
- * - auth.logout - Invalidate session/token (requires auth)
9
- * - auth.me - Get current user (optional auth)
10
- * - auth.updateProfile - Update profile (requires auth)
8
+ * - auth.logout - Invalidate session/token
9
+ * - auth.me - Get current user
10
+ * - auth.updateProfile - Update profile
11
11
  */
12
12
 
13
13
  import { createRouter } from "@donkeylabs/server";
@@ -51,22 +51,21 @@ export const authRouter = createRouter("auth")
51
51
  handle: RefreshHandler,
52
52
  })
53
53
 
54
- // Optional auth - returns user if logged in, null otherwise
55
- .middleware.auth({ required: false })
54
+ // Get current user (returns null if not authenticated)
56
55
  .route("me").typed({
57
56
  input: z.object({}),
58
57
  output: userSchema.nullable(),
59
58
  handle: MeHandler,
60
59
  })
61
60
 
62
- // Protected routes - require authentication
63
- .middleware.auth({ required: true })
61
+ // Logout (invalidate session/token)
64
62
  .route("logout").typed({
65
63
  input: z.object({}),
66
64
  output: logoutResponseSchema,
67
65
  handle: LogoutHandler,
68
66
  })
69
67
 
68
+ // Update profile
70
69
  .route("updateProfile").typed({
71
70
  input: updateProfileSchema,
72
71
  output: userSchema,
@@ -1,15 +1,13 @@
1
- import type { Handler, Routes, AppContext } from "$server/api";
2
-
3
1
  /**
4
2
  * Greet Handler
5
3
  *
6
4
  * Example handler demonstrating the feature module pattern.
7
5
  * Business logic lives directly in the handler - no separate model layer needed.
8
6
  */
9
- export class GreetHandler implements Handler<Routes.Example.Greet> {
10
- constructor(private ctx: AppContext) {}
7
+ export class GreetHandler {
8
+ constructor(private ctx: any) {}
11
9
 
12
- handle(input: Routes.Example.Greet.Input): Routes.Example.Greet.Output {
10
+ handle(input: { name: string; formal?: boolean }) {
13
11
  // Business logic directly in handler
14
12
  const greeting = input.formal
15
13
  ? `Good day, ${input.name}. How may I assist you?`
@@ -31,7 +31,7 @@ permissions.route("context").typed(
31
31
  throw ctx.errors.Unauthorized("Authentication required");
32
32
  }
33
33
 
34
- return ctx.plugins.permissions.getClientContext(ctx.user.id, input.tenantId);
34
+ return (ctx.plugins as any).permissions.getClientContext(ctx.user.id, input.tenantId);
35
35
  },
36
36
  })
37
37
  );
@@ -55,7 +55,7 @@ permissions.route("check").typed(
55
55
  const results: Record<string, boolean> = {};
56
56
 
57
57
  for (const permission of input.permissions) {
58
- results[permission] = await ctx.plugins.permissions.hasPermission(
58
+ results[permission] = await (ctx.plugins as any).permissions.hasPermission(
59
59
  ctx.user.id,
60
60
  input.tenantId,
61
61
  permission
@@ -92,7 +92,7 @@ permissions.route("canAccess").typed(
92
92
 
93
93
  for (const check of input.checks) {
94
94
  const key = `${check.resourceType}:${check.resourceId}:${check.action}`;
95
- results[key] = await ctx.plugins.permissions.canAccess(
95
+ results[key] = await (ctx.plugins as any).permissions.canAccess(
96
96
  ctx.user.id,
97
97
  input.tenantId,
98
98
  check.resourceType,
@@ -131,7 +131,7 @@ permissions.route("grants").typed(
131
131
  }
132
132
 
133
133
  // Check if user can admin this resource (to see grants)
134
- const canAdmin = await ctx.plugins.permissions.canAccess(
134
+ const canAdmin = await (ctx.plugins as any).permissions.canAccess(
135
135
  ctx.user.id,
136
136
  input.tenantId,
137
137
  input.resourceType,
@@ -143,7 +143,7 @@ permissions.route("grants").typed(
143
143
  throw ctx.errors.Forbidden("Cannot view grants for this resource");
144
144
  }
145
145
 
146
- return ctx.plugins.permissions.getResourceGrants(
146
+ return (ctx.plugins as any).permissions.getResourceGrants(
147
147
  input.tenantId,
148
148
  input.resourceType,
149
149
  input.resourceId
@@ -172,7 +172,7 @@ permissions.route("grant").typed(
172
172
  }
173
173
 
174
174
  // Check if user can admin this resource
175
- const canAdmin = await ctx.plugins.permissions.canAccess(
175
+ const canAdmin = await (ctx.plugins as any).permissions.canAccess(
176
176
  ctx.user.id,
177
177
  input.tenantId,
178
178
  input.resourceType,
@@ -184,7 +184,7 @@ permissions.route("grant").typed(
184
184
  throw ctx.errors.Forbidden("Cannot grant access to this resource");
185
185
  }
186
186
 
187
- await ctx.plugins.permissions.grantAccess({
187
+ await (ctx.plugins as any).permissions.grantAccess({
188
188
  tenantId: input.tenantId,
189
189
  resourceType: input.resourceType,
190
190
  resourceId: input.resourceId,
@@ -219,7 +219,7 @@ permissions.route("revoke").typed(
219
219
  }
220
220
 
221
221
  // Check if user can admin this resource
222
- const canAdmin = await ctx.plugins.permissions.canAccess(
222
+ const canAdmin = await (ctx.plugins as any).permissions.canAccess(
223
223
  ctx.user.id,
224
224
  input.tenantId,
225
225
  input.resourceType,
@@ -231,7 +231,7 @@ permissions.route("revoke").typed(
231
231
  throw ctx.errors.Forbidden("Cannot revoke access to this resource");
232
232
  }
233
233
 
234
- await ctx.plugins.permissions.revokeAccess({
234
+ await (ctx.plugins as any).permissions.revokeAccess({
235
235
  tenantId: input.tenantId,
236
236
  resourceType: input.resourceType,
237
237
  resourceId: input.resourceId,
@@ -24,7 +24,7 @@ tenants.route("mine").typed(
24
24
  throw ctx.errors.Unauthorized("Authentication required");
25
25
  }
26
26
 
27
- return ctx.plugins.permissions.getUserTenants(ctx.user.id);
27
+ return (ctx.plugins as any).permissions.getUserTenants(ctx.user.id);
28
28
  },
29
29
  })
30
30
  );
@@ -51,12 +51,12 @@ tenants.route("create").typed(
51
51
  }
52
52
 
53
53
  // Check if slug is taken
54
- const existing = await ctx.plugins.permissions.getTenantBySlug(input.slug);
54
+ const existing = await (ctx.plugins as any).permissions.getTenantBySlug(input.slug);
55
55
  if (existing) {
56
56
  throw ctx.errors.BadRequest("Tenant slug already taken");
57
57
  }
58
58
 
59
- return ctx.plugins.permissions.createTenant({
59
+ return (ctx.plugins as any).permissions.createTenant({
60
60
  name: input.name,
61
61
  slug: input.slug,
62
62
  ownerId: ctx.user.id,
@@ -86,12 +86,12 @@ tenants.route("get").typed(
86
86
  }
87
87
 
88
88
  // Verify membership
89
- const isMember = await ctx.plugins.permissions.isTenantMember(ctx.user.id, input.tenantId);
89
+ const isMember = await (ctx.plugins as any).permissions.isTenantMember(ctx.user.id, input.tenantId);
90
90
  if (!isMember) {
91
91
  throw ctx.errors.Forbidden("Not a member of this tenant");
92
92
  }
93
93
 
94
- return ctx.plugins.permissions.getTenant(input.tenantId);
94
+ return (ctx.plugins as any).permissions.getTenant(input.tenantId);
95
95
  },
96
96
  })
97
97
  );
@@ -118,13 +118,13 @@ tenants.route("roles").typed(
118
118
  }
119
119
 
120
120
  // Verify membership
121
- const isMember = await ctx.plugins.permissions.isTenantMember(ctx.user.id, input.tenantId);
121
+ const isMember = await (ctx.plugins as any).permissions.isTenantMember(ctx.user.id, input.tenantId);
122
122
  if (!isMember) {
123
123
  throw ctx.errors.Forbidden("Not a member of this tenant");
124
124
  }
125
125
 
126
- const roles = await ctx.plugins.permissions.getTenantRoles(input.tenantId);
127
- return roles.map(r => ({
126
+ const roles = await (ctx.plugins as any).permissions.getTenantRoles(input.tenantId);
127
+ return roles.map((r: any) => ({
128
128
  id: r.id,
129
129
  name: r.name,
130
130
  description: r.description,
@@ -163,7 +163,7 @@ tenants.route("createRole").typed(
163
163
  }
164
164
 
165
165
  // Check admin permission
166
- const hasAdmin = await ctx.plugins.permissions.hasPermission(
166
+ const hasAdmin = await (ctx.plugins as any).permissions.hasPermission(
167
167
  ctx.user.id,
168
168
  input.tenantId,
169
169
  "roles.manage"
@@ -172,7 +172,7 @@ tenants.route("createRole").typed(
172
172
  throw ctx.errors.Forbidden("Cannot manage roles");
173
173
  }
174
174
 
175
- const role = await ctx.plugins.permissions.createRole({
175
+ const role = await (ctx.plugins as any).permissions.createRole({
176
176
  tenantId: input.tenantId,
177
177
  name: input.name,
178
178
  description: input.description,
@@ -209,7 +209,7 @@ tenants.route("addMember").typed(
209
209
  }
210
210
 
211
211
  // Check permission
212
- const canInvite = await ctx.plugins.permissions.hasPermission(
212
+ const canInvite = await (ctx.plugins as any).permissions.hasPermission(
213
213
  ctx.user.id,
214
214
  input.tenantId,
215
215
  "members.invite"
@@ -218,7 +218,7 @@ tenants.route("addMember").typed(
218
218
  throw ctx.errors.Forbidden("Cannot invite members");
219
219
  }
220
220
 
221
- await ctx.plugins.permissions.addTenantMember(
221
+ await (ctx.plugins as any).permissions.addTenantMember(
222
222
  input.tenantId,
223
223
  input.userId,
224
224
  ctx.user.id
@@ -245,7 +245,7 @@ tenants.route("removeMember").typed(
245
245
  }
246
246
 
247
247
  // Check permission
248
- const canRemove = await ctx.plugins.permissions.hasPermission(
248
+ const canRemove = await (ctx.plugins as any).permissions.hasPermission(
249
249
  ctx.user.id,
250
250
  input.tenantId,
251
251
  "members.remove"
@@ -254,7 +254,7 @@ tenants.route("removeMember").typed(
254
254
  throw ctx.errors.Forbidden("Cannot remove members");
255
255
  }
256
256
 
257
- await ctx.plugins.permissions.removeTenantMember(input.tenantId, input.userId);
257
+ await (ctx.plugins as any).permissions.removeTenantMember(input.tenantId, input.userId);
258
258
 
259
259
  return { success: true };
260
260
  },
@@ -278,7 +278,7 @@ tenants.route("assignRole").typed(
278
278
  }
279
279
 
280
280
  // Check permission
281
- const canManage = await ctx.plugins.permissions.hasPermission(
281
+ const canManage = await (ctx.plugins as any).permissions.hasPermission(
282
282
  ctx.user.id,
283
283
  input.tenantId,
284
284
  "roles.assign"
@@ -287,7 +287,7 @@ tenants.route("assignRole").typed(
287
287
  throw ctx.errors.Forbidden("Cannot assign roles");
288
288
  }
289
289
 
290
- await ctx.plugins.permissions.assignRole(
290
+ await (ctx.plugins as any).permissions.assignRole(
291
291
  input.userId,
292
292
  input.roleId,
293
293
  input.tenantId,
@@ -316,7 +316,7 @@ tenants.route("revokeRole").typed(
316
316
  }
317
317
 
318
318
  // Check permission
319
- const canManage = await ctx.plugins.permissions.hasPermission(
319
+ const canManage = await (ctx.plugins as any).permissions.hasPermission(
320
320
  ctx.user.id,
321
321
  input.tenantId,
322
322
  "roles.assign"
@@ -325,7 +325,7 @@ tenants.route("revokeRole").typed(
325
325
  throw ctx.errors.Forbidden("Cannot revoke roles");
326
326
  }
327
327
 
328
- await ctx.plugins.permissions.revokeRole(
328
+ await (ctx.plugins as any).permissions.revokeRole(
329
329
  input.userId,
330
330
  input.roleId,
331
331
  input.tenantId