@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,12 +0,0 @@
1
- import { defineConfig } from "@donkeylabs/server";
2
-
3
- export default defineConfig({
4
- plugins: ["./src/server/plugins/**/index.ts"],
5
- outDir: ".@donkeylabs/server",
6
- entry: "./src/server/index.ts",
7
- routes: "./src/server/routes/**/{route,index}.ts",
8
- adapter: "@donkeylabs/adapter-sveltekit",
9
- client: {
10
- output: "./src/lib/api.ts",
11
- },
12
- });
@@ -1,38 +0,0 @@
1
- {
2
- "name": "my-sveltekit-app",
3
- "private": true,
4
- "version": "0.0.1",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "bun --bun vite dev",
8
- "build": "bun run gen:types && vite build",
9
- "preview": "bun build/server/entry.js",
10
- "prepare": "bun --bun svelte-kit sync && bun run gen:types || echo ''",
11
- "check": "bun --bun svelte-kit sync && bun --bun svelte-check --tsconfig ./tsconfig.json",
12
- "gen:types": "donkeylabs generate",
13
- "cli": "donkeylabs"
14
- },
15
- "devDependencies": {
16
- "@sveltejs/kit": "^2.49.1",
17
- "@sveltejs/vite-plugin-svelte": "^6.2.1",
18
- "@tailwindcss/vite": "^4.1.18",
19
- "svelte": "^5.45.6",
20
- "svelte-check": "^4.3.4",
21
- "tailwindcss": "^4.1.18",
22
- "typescript": "^5.9.3",
23
- "vite": "^7.2.6"
24
- },
25
- "dependencies": {
26
- "@donkeylabs/cli": "^2.0.14",
27
- "@donkeylabs/adapter-sveltekit": "^2.0.13",
28
- "@donkeylabs/server": "^2.0.16",
29
- "bits-ui": "^2.15.4",
30
- "clsx": "^2.1.1",
31
- "kysely": "^0.27.6",
32
- "kysely-bun-sqlite": "^0.3.2",
33
- "runed": "^0.23.4",
34
- "tailwind-merge": "^3.4.0",
35
- "tailwind-variants": "^3.2.2",
36
- "zod": "^3.24.0"
37
- }
38
- }
@@ -1,40 +0,0 @@
1
- @import "tailwindcss";
2
-
3
- @theme {
4
- --color-background: oklch(100% 0 0);
5
- --color-foreground: oklch(14.1% 0.005 285.82);
6
- --color-card: oklch(100% 0 0);
7
- --color-card-foreground: oklch(14.1% 0.005 285.82);
8
- --color-popover: oklch(100% 0 0);
9
- --color-popover-foreground: oklch(14.1% 0.005 285.82);
10
- --color-primary: oklch(20.5% 0.006 285.88);
11
- --color-primary-foreground: oklch(98.5% 0.002 247.86);
12
- --color-secondary: oklch(96.8% 0.003 264.54);
13
- --color-secondary-foreground: oklch(20.5% 0.006 285.88);
14
- --color-muted: oklch(96.8% 0.003 264.54);
15
- --color-muted-foreground: oklch(55.2% 0.016 285.94);
16
- --color-accent: oklch(96.8% 0.003 264.54);
17
- --color-accent-foreground: oklch(20.5% 0.006 285.88);
18
- --color-destructive: oklch(57.7% 0.245 27.33);
19
- --color-destructive-foreground: oklch(98.5% 0.002 247.86);
20
- --color-border: oklch(91.4% 0.007 264.53);
21
- --color-input: oklch(91.4% 0.007 264.53);
22
- --color-ring: oklch(70.7% 0.022 261.33);
23
- --color-success: oklch(72.3% 0.219 149.58);
24
- --color-success-foreground: oklch(20.5% 0.006 285.88);
25
- --color-warning: oklch(79.5% 0.184 86.05);
26
- --color-warning-foreground: oklch(20.5% 0.006 285.88);
27
- --radius-sm: 0.25rem;
28
- --radius-md: 0.375rem;
29
- --radius-lg: 0.5rem;
30
- --radius-xl: 0.75rem;
31
- }
32
-
33
- @layer base {
34
- * {
35
- @apply border-border;
36
- }
37
- body {
38
- @apply bg-background text-foreground;
39
- }
40
- }
@@ -1,12 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <link rel="icon" href="%sveltekit.assets%/favicon.png" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- %sveltekit.head%
8
- </head>
9
- <body data-sveltekit-preload-data="hover">
10
- <div style="display: contents">%sveltekit.body%</div>
11
- </body>
12
- </html>
@@ -1,4 +0,0 @@
1
- // SvelteKit hooks for @donkeylabs/adapter-sveltekit
2
- import { createHandle } from "@donkeylabs/adapter-sveltekit/hooks";
3
-
4
- export const handle = createHandle();
@@ -1,30 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { BadgeVariant } from "./index";
4
- import type { Snippet } from "svelte";
5
- import type { HTMLAttributes } from "svelte/elements";
6
-
7
- interface Props extends HTMLAttributes<HTMLDivElement> {
8
- variant?: BadgeVariant;
9
- class?: string;
10
- children?: Snippet;
11
- }
12
-
13
- let { variant = "default", class: className, children, ...restProps }: Props = $props();
14
-
15
- const baseStyles = "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2";
16
-
17
- const variants: Record<BadgeVariant, string> = {
18
- default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
19
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
20
- destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
21
- outline: "text-foreground",
22
- success: "border-transparent bg-success text-success-foreground shadow hover:bg-success/80",
23
- };
24
- </script>
25
-
26
- <div class={cn(baseStyles, variants[variant], className)} {...restProps}>
27
- {#if children}
28
- {@render children()}
29
- {/if}
30
- </div>
@@ -1,3 +0,0 @@
1
- import Root from "./badge.svelte";
2
- export type BadgeVariant = "default" | "secondary" | "destructive" | "outline" | "success";
3
- export { Root, Root as Badge };
@@ -1,48 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { ButtonVariant, ButtonSize } from "./index";
4
- import type { Snippet } from "svelte";
5
- import type { HTMLButtonAttributes } from "svelte/elements";
6
-
7
- interface Props extends HTMLButtonAttributes {
8
- variant?: ButtonVariant;
9
- size?: ButtonSize;
10
- class?: string;
11
- children?: Snippet;
12
- }
13
-
14
- let {
15
- variant = "default",
16
- size = "default",
17
- class: className,
18
- children,
19
- ...restProps
20
- }: Props = $props();
21
-
22
- const baseStyles = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50";
23
-
24
- const variants: Record<ButtonVariant, string> = {
25
- default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
26
- destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
27
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
28
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
29
- ghost: "hover:bg-accent hover:text-accent-foreground",
30
- link: "text-primary underline-offset-4 hover:underline",
31
- };
32
-
33
- const sizes: Record<ButtonSize, string> = {
34
- default: "h-9 px-4 py-2",
35
- sm: "h-8 rounded-md px-3 text-xs",
36
- lg: "h-10 rounded-md px-8",
37
- icon: "h-9 w-9",
38
- };
39
- </script>
40
-
41
- <button
42
- class={cn(baseStyles, variants[variant], sizes[size], className)}
43
- {...restProps}
44
- >
45
- {#if children}
46
- {@render children()}
47
- {/if}
48
- </button>
@@ -1,9 +0,0 @@
1
- import Root from "./button.svelte";
2
-
3
- export type ButtonVariant = "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
4
- export type ButtonSize = "default" | "sm" | "lg" | "icon";
5
-
6
- export {
7
- Root,
8
- Root as Button,
9
- };
@@ -1,18 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { Snippet } from "svelte";
4
- import type { HTMLAttributes } from "svelte/elements";
5
-
6
- interface Props extends HTMLAttributes<HTMLDivElement> {
7
- class?: string;
8
- children?: Snippet;
9
- }
10
-
11
- let { class: className, children, ...restProps }: Props = $props();
12
- </script>
13
-
14
- <div class={cn("p-6 pt-0", className)} {...restProps}>
15
- {#if children}
16
- {@render children()}
17
- {/if}
18
- </div>
@@ -1,18 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { Snippet } from "svelte";
4
- import type { HTMLAttributes } from "svelte/elements";
5
-
6
- interface Props extends HTMLAttributes<HTMLParagraphElement> {
7
- class?: string;
8
- children?: Snippet;
9
- }
10
-
11
- let { class: className, children, ...restProps }: Props = $props();
12
- </script>
13
-
14
- <p class={cn("text-sm text-muted-foreground", className)} {...restProps}>
15
- {#if children}
16
- {@render children()}
17
- {/if}
18
- </p>
@@ -1,18 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { Snippet } from "svelte";
4
- import type { HTMLAttributes } from "svelte/elements";
5
-
6
- interface Props extends HTMLAttributes<HTMLDivElement> {
7
- class?: string;
8
- children?: Snippet;
9
- }
10
-
11
- let { class: className, children, ...restProps }: Props = $props();
12
- </script>
13
-
14
- <div class={cn("flex items-center p-6 pt-0", className)} {...restProps}>
15
- {#if children}
16
- {@render children()}
17
- {/if}
18
- </div>
@@ -1,18 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { Snippet } from "svelte";
4
- import type { HTMLAttributes } from "svelte/elements";
5
-
6
- interface Props extends HTMLAttributes<HTMLDivElement> {
7
- class?: string;
8
- children?: Snippet;
9
- }
10
-
11
- let { class: className, children, ...restProps }: Props = $props();
12
- </script>
13
-
14
- <div class={cn("flex flex-col space-y-1.5 p-6", className)} {...restProps}>
15
- {#if children}
16
- {@render children()}
17
- {/if}
18
- </div>
@@ -1,18 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { Snippet } from "svelte";
4
- import type { HTMLAttributes } from "svelte/elements";
5
-
6
- interface Props extends HTMLAttributes<HTMLHeadingElement> {
7
- class?: string;
8
- children?: Snippet;
9
- }
10
-
11
- let { class: className, children, ...restProps }: Props = $props();
12
- </script>
13
-
14
- <h3 class={cn("font-semibold leading-none tracking-tight", className)} {...restProps}>
15
- {#if children}
16
- {@render children()}
17
- {/if}
18
- </h3>
@@ -1,21 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { Snippet } from "svelte";
4
- import type { HTMLAttributes } from "svelte/elements";
5
-
6
- interface Props extends HTMLAttributes<HTMLDivElement> {
7
- class?: string;
8
- children?: Snippet;
9
- }
10
-
11
- let { class: className, children, ...restProps }: Props = $props();
12
- </script>
13
-
14
- <div
15
- class={cn("rounded-xl border bg-card text-card-foreground shadow", className)}
16
- {...restProps}
17
- >
18
- {#if children}
19
- {@render children()}
20
- {/if}
21
- </div>
@@ -1,21 +0,0 @@
1
- import Root from "./card.svelte";
2
- import Content from "./card-content.svelte";
3
- import Description from "./card-description.svelte";
4
- import Footer from "./card-footer.svelte";
5
- import Header from "./card-header.svelte";
6
- import Title from "./card-title.svelte";
7
-
8
- export {
9
- Root,
10
- Content,
11
- Description,
12
- Footer,
13
- Header,
14
- Title,
15
- Root as Card,
16
- Content as CardContent,
17
- Description as CardDescription,
18
- Footer as CardFooter,
19
- Header as CardHeader,
20
- Title as CardTitle,
21
- };
@@ -1,4 +0,0 @@
1
- export * from "./button";
2
- export * from "./card";
3
- export * from "./input";
4
- export * from "./badge";
@@ -1,2 +0,0 @@
1
- import Root from "./input.svelte";
2
- export { Root, Root as Input };
@@ -1,20 +0,0 @@
1
- <script lang="ts">
2
- import { cn } from "$lib/utils";
3
- import type { HTMLInputAttributes } from "svelte/elements";
4
-
5
- interface Props extends HTMLInputAttributes {
6
- class?: string;
7
- value?: string | number;
8
- }
9
-
10
- let { class: className, value = $bindable(), ...restProps }: Props = $props();
11
- </script>
12
-
13
- <input
14
- class={cn(
15
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
16
- className
17
- )}
18
- bind:value
19
- {...restProps}
20
- />
@@ -1,213 +0,0 @@
1
- /**
2
- * Permissions Helper for Svelte UI
3
- *
4
- * Provides reactive permission checking for UI locking.
5
- *
6
- * Usage in +page.server.ts:
7
- * ```ts
8
- * export const load = async ({ locals }) => {
9
- * const api = createApi({ locals });
10
- * const permissions = await api.permissions.context({ tenantId });
11
- * return { permissions };
12
- * };
13
- * ```
14
- *
15
- * Usage in +page.svelte:
16
- * ```svelte
17
- * <script>
18
- * import { createPermissions } from "$lib/permissions";
19
- * let { data } = $props();
20
- * const can = createPermissions(data.permissions);
21
- * </script>
22
- *
23
- * {#if can.has("documents.create")}
24
- * <Button>Create Document</Button>
25
- * {/if}
26
- * ```
27
- */
28
-
29
- // Note: createApi is imported dynamically to avoid compile-time dependency
30
- // on the generated API file which may not have permissions routes yet
31
-
32
- export interface PermissionContext {
33
- tenantId: string;
34
- roles: Array<{ id: string; name: string }>;
35
- permissions: string[];
36
- }
37
-
38
- export interface PermissionsHelper {
39
- /** Check if user has a static permission */
40
- has: (permission: string) => boolean;
41
-
42
- /** Check if user has all of the specified permissions */
43
- hasAll: (...permissions: string[]) => boolean;
44
-
45
- /** Check if user has any of the specified permissions */
46
- hasAny: (...permissions: string[]) => boolean;
47
-
48
- /** Check if user has a specific role */
49
- hasRole: (roleName: string) => boolean;
50
-
51
- /** Get all permissions */
52
- all: () => string[];
53
-
54
- /** Get all roles */
55
- roles: () => Array<{ id: string; name: string }>;
56
-
57
- /** Get tenant ID */
58
- tenantId: () => string;
59
-
60
- /** Check resource access (async - makes API call) */
61
- canAccess: (
62
- resourceType: string,
63
- resourceId: string,
64
- action: "create" | "read" | "write" | "delete" | "admin",
65
- ownerId?: string
66
- ) => Promise<boolean>;
67
-
68
- /** Batch check resource access (async - makes single API call) */
69
- canAccessMany: (
70
- checks: Array<{
71
- resourceType: string;
72
- resourceId: string;
73
- action: "create" | "read" | "write" | "delete" | "admin";
74
- ownerId?: string;
75
- }>
76
- ) => Promise<Record<string, boolean>>;
77
- }
78
-
79
- /**
80
- * Create a permissions helper from context
81
- */
82
- export function createPermissions(context: PermissionContext, api?: any): PermissionsHelper {
83
- const permissionSet = new Set(context.permissions);
84
-
85
- // Lazy load API if not provided
86
- const getApi = () => {
87
- if (api) return api;
88
- // Dynamic import to avoid compile-time dependency
89
- const { createApi } = require("./api");
90
- return createApi();
91
- };
92
-
93
- return {
94
- has(permission: string): boolean {
95
- // Exact match
96
- if (permissionSet.has(permission)) return true;
97
-
98
- // Wildcard match
99
- if (permissionSet.has("*")) return true;
100
-
101
- // Resource wildcard (e.g., "documents.*")
102
- const [resource] = permission.split(".");
103
- if (permissionSet.has(`${resource}.*`)) return true;
104
-
105
- return false;
106
- },
107
-
108
- hasAll(...permissions: string[]): boolean {
109
- return permissions.every((p) => this.has(p));
110
- },
111
-
112
- hasAny(...permissions: string[]): boolean {
113
- return permissions.some((p) => this.has(p));
114
- },
115
-
116
- hasRole(roleName: string): boolean {
117
- return context.roles.some(
118
- (r) => r.name.toLowerCase() === roleName.toLowerCase()
119
- );
120
- },
121
-
122
- all(): string[] {
123
- return context.permissions;
124
- },
125
-
126
- roles(): Array<{ id: string; name: string }> {
127
- return context.roles;
128
- },
129
-
130
- tenantId(): string {
131
- return context.tenantId;
132
- },
133
-
134
- async canAccess(
135
- resourceType: string,
136
- resourceId: string,
137
- action: "create" | "read" | "write" | "delete" | "admin",
138
- ownerId?: string
139
- ): Promise<boolean> {
140
- const apiClient = getApi();
141
- const result = await apiClient.permissions.canAccess({
142
- tenantId: context.tenantId,
143
- checks: [{ resourceType, resourceId, action, ownerId }],
144
- });
145
- return result[`${resourceType}:${resourceId}:${action}`] ?? false;
146
- },
147
-
148
- async canAccessMany(
149
- checks: Array<{
150
- resourceType: string;
151
- resourceId: string;
152
- action: "create" | "read" | "write" | "delete" | "admin";
153
- ownerId?: string;
154
- }>
155
- ): Promise<Record<string, boolean>> {
156
- const apiClient = getApi();
157
- return apiClient.permissions.canAccess({
158
- tenantId: context.tenantId,
159
- checks,
160
- });
161
- },
162
- };
163
- }
164
-
165
- /**
166
- * Svelte component helper - use in templates
167
- *
168
- * Usage:
169
- * ```svelte
170
- * <script>
171
- * import { Can } from "$lib/permissions";
172
- * let { data } = $props();
173
- * </script>
174
- *
175
- * <Can permission="documents.create" context={data.permissions}>
176
- * <Button>Create</Button>
177
- * </Can>
178
- * ```
179
- */
180
- export function canCheck(
181
- context: PermissionContext,
182
- permission: string
183
- ): boolean {
184
- const helper = createPermissions(context);
185
- return helper.has(permission);
186
- }
187
-
188
- /**
189
- * Type helper for defining permissions in your app
190
- *
191
- * Usage:
192
- * ```ts
193
- * const PERMISSIONS = definePermissions({
194
- * documents: ["create", "read", "write", "delete", "admin"],
195
- * users: ["invite", "remove", "manage"],
196
- * billing: ["view", "manage"],
197
- * } as const);
198
- *
199
- * // Type: "documents.create" | "documents.read" | ...
200
- * type Permission = typeof PERMISSIONS[number];
201
- * ```
202
- */
203
- export function definePermissions<
204
- T extends Record<string, readonly string[]>
205
- >(permissions: T): Array<`${Extract<keyof T, string>}.${T[keyof T][number]}`> {
206
- const result: string[] = [];
207
- for (const [resource, actions] of Object.entries(permissions)) {
208
- for (const action of actions) {
209
- result.push(`${resource}.${action}`);
210
- }
211
- }
212
- return result as any;
213
- }
@@ -1,6 +0,0 @@
1
- import { clsx, type ClassValue } from "clsx";
2
- import { twMerge } from "tailwind-merge";
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs));
6
- }
@@ -1,8 +0,0 @@
1
- <script lang="ts">
2
- import "../app.css";
3
- import type { Snippet } from "svelte";
4
-
5
- let { children }: { children: Snippet } = $props();
6
- </script>
7
-
8
- {@render children()}
@@ -1,25 +0,0 @@
1
- // Test SSR direct service calls using the typed API client
2
- import type { PageServerLoad } from './$types';
3
- import { createApi } from '$lib/api';
4
-
5
- export const load: PageServerLoad = async ({ locals }) => {
6
- // Create API client with locals for direct SSR calls (no HTTP!)
7
- const client = createApi({ locals });
8
-
9
- try {
10
- // Direct service call through typed client
11
- const result = await (client as any).demo.counter.get({});
12
- return {
13
- count: result.count,
14
- loadedAt: new Date().toISOString(),
15
- isSSR: true,
16
- };
17
- } catch (e) {
18
- // Fallback if plugins not loaded yet
19
- return {
20
- count: 'N/A (plugins not loaded)',
21
- loadedAt: new Date().toISOString(),
22
- isSSR: true,
23
- };
24
- }
25
- };