@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.
- package/package.json +1 -1
- package/src/commands/config.ts +610 -0
- package/src/commands/deploy-enhanced.ts +354 -0
- package/src/commands/deploy.ts +204 -0
- package/src/commands/generate.ts +11 -13
- package/src/commands/init-enhanced.ts +1994 -0
- package/src/deployment/manager.ts +356 -0
- package/src/index.ts +47 -19
- package/templates/starter/.env.example +0 -44
- package/templates/starter/.gitignore.template +0 -4
- package/templates/starter/donkeylabs.config.ts +0 -6
- package/templates/starter/package.json +0 -21
- package/templates/starter/src/index.ts +0 -54
- package/templates/starter/src/plugins/stats/index.ts +0 -105
- package/templates/starter/src/routes/health/handlers/ping.ts +0 -22
- package/templates/starter/src/routes/health/index.ts +0 -19
- package/templates/starter/tsconfig.json +0 -27
- package/templates/sveltekit-app/.env.example +0 -59
- package/templates/sveltekit-app/README.md +0 -103
- package/templates/sveltekit-app/bun.lock +0 -683
- package/templates/sveltekit-app/donkeylabs.config.ts +0 -12
- package/templates/sveltekit-app/package.json +0 -38
- package/templates/sveltekit-app/src/app.css +0 -40
- package/templates/sveltekit-app/src/app.html +0 -12
- package/templates/sveltekit-app/src/hooks.server.ts +0 -4
- package/templates/sveltekit-app/src/lib/components/ui/badge/badge.svelte +0 -30
- package/templates/sveltekit-app/src/lib/components/ui/badge/index.ts +0 -3
- package/templates/sveltekit-app/src/lib/components/ui/button/button.svelte +0 -48
- package/templates/sveltekit-app/src/lib/components/ui/button/index.ts +0 -9
- package/templates/sveltekit-app/src/lib/components/ui/card/card-content.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-description.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-footer.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-header.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card-title.svelte +0 -18
- package/templates/sveltekit-app/src/lib/components/ui/card/card.svelte +0 -21
- package/templates/sveltekit-app/src/lib/components/ui/card/index.ts +0 -21
- package/templates/sveltekit-app/src/lib/components/ui/index.ts +0 -4
- package/templates/sveltekit-app/src/lib/components/ui/input/index.ts +0 -2
- package/templates/sveltekit-app/src/lib/components/ui/input/input.svelte +0 -20
- package/templates/sveltekit-app/src/lib/permissions.ts +0 -213
- package/templates/sveltekit-app/src/lib/utils/index.ts +0 -6
- package/templates/sveltekit-app/src/routes/+layout.svelte +0 -8
- package/templates/sveltekit-app/src/routes/+page.server.ts +0 -25
- package/templates/sveltekit-app/src/routes/+page.svelte +0 -680
- package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +0 -23
- package/templates/sveltekit-app/src/routes/workflows/+page.svelte +0 -522
- package/templates/sveltekit-app/src/server/events.ts +0 -28
- package/templates/sveltekit-app/src/server/index.ts +0 -124
- package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +0 -377
- package/templates/sveltekit-app/src/server/plugins/auth/index.ts +0 -815
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/001_create_users.ts +0 -25
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/002_create_sessions.ts +0 -32
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/003_create_refresh_tokens.ts +0 -33
- package/templates/sveltekit-app/src/server/plugins/auth/migrations/004_create_passkeys.ts +0 -60
- package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +0 -65
- package/templates/sveltekit-app/src/server/plugins/demo/index.ts +0 -262
- package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +0 -369
- package/templates/sveltekit-app/src/server/plugins/email/index.ts +0 -411
- package/templates/sveltekit-app/src/server/plugins/email/migrations/001_create_email_tokens.ts +0 -33
- package/templates/sveltekit-app/src/server/plugins/email/schema.ts +0 -24
- package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +0 -1048
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/001_create_tenants.ts +0 -63
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/002_create_roles.ts +0 -90
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/003_create_resource_grants.ts +0 -50
- package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +0 -566
- package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +0 -67
- package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +0 -198
- package/templates/sveltekit-app/src/server/routes/auth/auth.schemas.ts +0 -66
- package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +0 -18
- package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +0 -16
- package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +0 -20
- package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +0 -17
- package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +0 -19
- package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +0 -21
- package/templates/sveltekit-app/src/server/routes/auth/index.ts +0 -73
- package/templates/sveltekit-app/src/server/routes/demo.ts +0 -464
- package/templates/sveltekit-app/src/server/routes/example/example.schemas.ts +0 -22
- package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +0 -21
- package/templates/sveltekit-app/src/server/routes/example/index.ts +0 -28
- package/templates/sveltekit-app/src/server/routes/permissions/index.ts +0 -248
- package/templates/sveltekit-app/src/server/routes/tenants/index.ts +0 -339
- package/templates/sveltekit-app/static/robots.txt +0 -3
- package/templates/sveltekit-app/svelte.config.ts +0 -17
- package/templates/sveltekit-app/tsconfig.json +0 -20
- 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,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,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,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,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,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
|
-
};
|