bosia 0.2.3 → 0.3.1

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 (86) hide show
  1. package/README.md +39 -39
  2. package/package.json +56 -54
  3. package/src/ambient.d.ts +31 -0
  4. package/src/cli/add.ts +120 -114
  5. package/src/cli/build.ts +10 -10
  6. package/src/cli/create.ts +142 -137
  7. package/src/cli/dev.ts +7 -9
  8. package/src/cli/feat.ts +266 -258
  9. package/src/cli/index.ts +51 -42
  10. package/src/cli/registry.ts +136 -115
  11. package/src/cli/start.ts +17 -17
  12. package/src/cli/test.ts +25 -0
  13. package/src/core/build.ts +72 -56
  14. package/src/core/client/App.svelte +177 -156
  15. package/src/core/client/appState.svelte.ts +33 -31
  16. package/src/core/client/enhance.ts +83 -78
  17. package/src/core/client/hydrate.ts +95 -81
  18. package/src/core/client/prefetch.ts +101 -94
  19. package/src/core/client/router.svelte.ts +64 -51
  20. package/src/core/cookies.ts +70 -66
  21. package/src/core/cors.ts +44 -35
  22. package/src/core/csrf.ts +38 -38
  23. package/src/core/dedup.ts +17 -17
  24. package/src/core/dev.ts +196 -168
  25. package/src/core/env.ts +160 -148
  26. package/src/core/envCodegen.ts +73 -73
  27. package/src/core/errors.ts +48 -49
  28. package/src/core/hooks.ts +50 -50
  29. package/src/core/html.ts +184 -145
  30. package/src/core/matcher.ts +130 -121
  31. package/src/core/paths.ts +8 -10
  32. package/src/core/plugin.ts +113 -107
  33. package/src/core/prerender.ts +191 -122
  34. package/src/core/renderer.ts +359 -286
  35. package/src/core/routeFile.ts +140 -127
  36. package/src/core/routeTypes.ts +144 -83
  37. package/src/core/scanner.ts +125 -95
  38. package/src/core/server.ts +538 -424
  39. package/src/core/types.ts +25 -20
  40. package/src/lib/index.ts +8 -8
  41. package/src/lib/utils.ts +44 -30
  42. package/templates/default/.prettierignore +5 -0
  43. package/templates/default/.prettierrc.json +9 -0
  44. package/templates/default/README.md +5 -5
  45. package/templates/default/package.json +22 -18
  46. package/templates/default/src/app.css +80 -80
  47. package/templates/default/src/app.d.ts +3 -3
  48. package/templates/default/src/routes/+error.svelte +7 -10
  49. package/templates/default/src/routes/+layout.svelte +2 -2
  50. package/templates/default/src/routes/+page.svelte +30 -32
  51. package/templates/default/src/routes/about/+page.svelte +3 -3
  52. package/templates/default/tsconfig.json +20 -20
  53. package/templates/demo/.prettierignore +5 -0
  54. package/templates/demo/.prettierrc.json +9 -0
  55. package/templates/demo/README.md +9 -9
  56. package/templates/demo/package.json +22 -17
  57. package/templates/demo/src/app.css +80 -80
  58. package/templates/demo/src/app.d.ts +3 -3
  59. package/templates/demo/src/hooks.server.ts +9 -9
  60. package/templates/demo/src/routes/(public)/+layout.svelte +45 -23
  61. package/templates/demo/src/routes/(public)/+page.svelte +96 -67
  62. package/templates/demo/src/routes/(public)/about/+page.svelte +13 -25
  63. package/templates/demo/src/routes/(public)/all/[...catchall]/+page.svelte +24 -28
  64. package/templates/demo/src/routes/(public)/blog/+page.svelte +55 -46
  65. package/templates/demo/src/routes/(public)/blog/[slug]/+page.server.ts +36 -38
  66. package/templates/demo/src/routes/(public)/blog/[slug]/+page.svelte +60 -42
  67. package/templates/demo/src/routes/+error.svelte +10 -7
  68. package/templates/demo/src/routes/+layout.server.ts +4 -4
  69. package/templates/demo/src/routes/+layout.svelte +2 -2
  70. package/templates/demo/src/routes/actions-test/+page.server.ts +16 -16
  71. package/templates/demo/src/routes/actions-test/+page.svelte +49 -49
  72. package/templates/demo/src/routes/api/hello/+server.ts +25 -25
  73. package/templates/demo/tsconfig.json +20 -20
  74. package/templates/todo/.prettierignore +5 -0
  75. package/templates/todo/.prettierrc.json +9 -0
  76. package/templates/todo/README.md +9 -9
  77. package/templates/todo/package.json +22 -17
  78. package/templates/todo/src/app.css +80 -80
  79. package/templates/todo/src/app.d.ts +7 -7
  80. package/templates/todo/src/hooks.server.ts +9 -9
  81. package/templates/todo/src/routes/+error.svelte +10 -7
  82. package/templates/todo/src/routes/+layout.server.ts +4 -4
  83. package/templates/todo/src/routes/+layout.svelte +2 -2
  84. package/templates/todo/src/routes/+page.svelte +44 -44
  85. package/templates/todo/template.json +1 -1
  86. package/templates/todo/tsconfig.json +20 -20
@@ -1,31 +1,19 @@
1
1
  <svelte:head>
2
- <title>About | {{PROJECT_NAME}}</title>
2
+ <title>About | Bosia Demo</title>
3
3
  </svelte:head>
4
4
 
5
5
  <div class="space-y-6 max-w-2xl">
6
- <h1 class="text-4xl font-bold tracking-tight">About {{PROJECT_NAME}}</h1>
7
- <p class="text-muted-foreground text-lg">
8
- A minimalist fullstack framework built on Bun, ElysiaJS, and Svelte 5.
9
- </p>
6
+ <h1 class="text-4xl font-bold tracking-tight">About Bosia Demo</h1>
7
+ <p class="text-muted-foreground text-lg">
8
+ A minimalist fullstack framework built on Bun, ElysiaJS, and Svelte 5.
9
+ </p>
10
10
 
11
- <ul class="space-y-2 text-foreground">
12
- {#each [
13
- "Bun runtime — fast builds, native TypeScript",
14
- "ElysiaJS — HTTP server with type-safe routing",
15
- "Svelte 5 Runes — fine-grained reactivity",
16
- "Isomorphic SSR with client hydration",
17
- "File-based routing (SvelteKit-compatible conventions)",
18
- "Nested layouts and route groups (public), (auth), (admin)",
19
- "Dynamic params [slug] and catch-all [...rest]",
20
- "Server loaders with parent() data threading",
21
- "Hooks — sequence() middleware for auth, logging, etc.",
22
- "Component registry — bosia add button",
23
- "Feature registry — bosia feat login",
24
- ] as item}
25
- <li class="flex items-start gap-2">
26
- <span class="text-primary mt-0.5">✓</span>
27
- <span>{item}</span>
28
- </li>
29
- {/each}
30
- </ul>
11
+ <ul class="space-y-2 text-foreground">
12
+ {#each ["Bun runtime — fast builds, native TypeScript", "ElysiaJS — HTTP server with type-safe routing", "Svelte 5 Runes — fine-grained reactivity", "Isomorphic SSR with client hydration", "File-based routing (SvelteKit-compatible conventions)", "Nested layouts and route groups (public), (auth), (admin)", "Dynamic params [slug] and catch-all [...rest]", "Server loaders with parent() data threading", "Hooks — sequence() middleware for auth, logging, etc.", "Component registry — bosia add button", "Feature registry — bosia feat login"] as item}
13
+ <li class="flex items-start gap-2">
14
+ <span class="text-primary mt-0.5">✓</span>
15
+ <span>{item}</span>
16
+ </li>
17
+ {/each}
18
+ </ul>
31
19
  </div>
@@ -1,38 +1,34 @@
1
1
  <script lang="ts">
2
- let { data = {} }: { data?: Record<string, any> } = $props();
3
- const segments = $derived(
4
- ((data as any)?.params?.catchall ?? "")
5
- .split("/")
6
- .filter(Boolean)
7
- );
2
+ let { data = {} }: { data?: Record<string, any> } = $props();
3
+ const segments = $derived(((data as any)?.params?.catchall ?? "").split("/").filter(Boolean));
8
4
  </script>
9
5
 
10
6
  <svelte:head>
11
- <title>Catch-all Demo | {{PROJECT_NAME}}</title>
7
+ <title>Catch-all Demo | Bosia Demo</title>
12
8
  </svelte:head>
13
9
 
14
10
  <div class="flex flex-col items-center justify-center py-24 text-center space-y-6">
15
- <h1 class="text-3xl font-bold">Catch-all Route Demo</h1>
16
- <p class="text-muted-foreground text-sm">
17
- This page matches <code class="bg-muted rounded px-1 font-mono">/all/[...catchall]</code>
18
- </p>
11
+ <h1 class="text-3xl font-bold">Catch-all Route Demo</h1>
12
+ <p class="text-muted-foreground text-sm">
13
+ This page matches <code class="bg-muted rounded px-1 font-mono">/all/[...catchall]</code>
14
+ </p>
19
15
 
20
- {#if segments.length > 0}
21
- <div class="mt-2 space-y-1">
22
- <p class="text-sm text-muted-foreground">Captured segments:</p>
23
- <div class="flex gap-2 flex-wrap justify-center">
24
- {#each segments as segment, i}
25
- <span class="bg-muted rounded px-2 py-1 font-mono text-sm">
26
- [{i}] {segment}
27
- </span>
28
- {/each}
29
- </div>
30
- </div>
31
- {:else}
32
- <p class="text-muted-foreground text-sm italic">No segments captured</p>
33
- {/if}
16
+ {#if segments.length > 0}
17
+ <div class="mt-2 space-y-1">
18
+ <p class="text-sm text-muted-foreground">Captured segments:</p>
19
+ <div class="flex gap-2 flex-wrap justify-center">
20
+ {#each segments as segment, i}
21
+ <span class="bg-muted rounded px-2 py-1 font-mono text-sm">
22
+ [{i}] {segment}
23
+ </span>
24
+ {/each}
25
+ </div>
26
+ </div>
27
+ {:else}
28
+ <p class="text-muted-foreground text-sm italic">No segments captured</p>
29
+ {/if}
34
30
 
35
- <a href="/" class="mt-4 rounded-md border px-4 py-2 text-sm hover:bg-muted transition-colors">
36
- Go Home
37
- </a>
31
+ <a href="/" class="mt-4 rounded-md border px-4 py-2 text-sm hover:bg-muted transition-colors">
32
+ Go Home
33
+ </a>
38
34
  </div>
@@ -1,55 +1,64 @@
1
1
  <script lang="ts">
2
- const posts = [
3
- {
4
- slug: "hello-world",
5
- title: "Hello, World!",
6
- date: "2026-03-05",
7
- excerpt: "The first post in the demo — a quick intro to the framework.",
8
- tags: ["intro", "bosia"],
9
- },
10
- {
11
- slug: "route-groups",
12
- title: "Route Groups Explained",
13
- date: "2026-03-04",
14
- excerpt: "How (public), (auth), (admin) groups work — invisible in URLs, share layouts.",
15
- tags: ["routing", "layouts"],
16
- },
17
- {
18
- slug: "dynamic-params",
19
- title: "Dynamic Params with [slug]",
20
- date: "2026-03-03",
21
- excerpt: "Using [slug] segments to match any value and pass it to server loaders.",
22
- tags: ["routing", "dynamic"],
23
- },
24
- ];
2
+ const posts = [
3
+ {
4
+ slug: "hello-world",
5
+ title: "Hello, World!",
6
+ date: "2026-03-05",
7
+ excerpt: "The first post in the demo — a quick intro to the framework.",
8
+ tags: ["intro", "bosia"],
9
+ },
10
+ {
11
+ slug: "route-groups",
12
+ title: "Route Groups Explained",
13
+ date: "2026-03-04",
14
+ excerpt:
15
+ "How (public), (auth), (admin) groups work — invisible in URLs, share layouts.",
16
+ tags: ["routing", "layouts"],
17
+ },
18
+ {
19
+ slug: "dynamic-params",
20
+ title: "Dynamic Params with [slug]",
21
+ date: "2026-03-03",
22
+ excerpt: "Using [slug] segments to match any value and pass it to server loaders.",
23
+ tags: ["routing", "dynamic"],
24
+ },
25
+ ];
25
26
  </script>
26
27
 
27
28
  <svelte:head>
28
- <title>Blog | {{PROJECT_NAME}}</title>
29
+ <title>Blog | Bosia Demo</title>
29
30
  </svelte:head>
30
31
 
31
32
  <div class="space-y-8">
32
- <div class="space-y-2">
33
- <h1 class="text-4xl font-bold tracking-tight">Blog</h1>
34
- <p class="text-muted-foreground">Routing patterns and framework internals.</p>
35
- </div>
33
+ <div class="space-y-2">
34
+ <h1 class="text-4xl font-bold tracking-tight">Blog</h1>
35
+ <p class="text-muted-foreground">Routing patterns and framework internals.</p>
36
+ </div>
36
37
 
37
- <div class="grid gap-4" data-bosia-preload="hover">
38
- {#each posts as post}
39
- <a href="/blog/{post.slug}" class="group block rounded-lg border bg-card p-5 hover:border-primary transition-colors">
40
- <div class="flex items-start justify-between gap-4">
41
- <div class="space-y-1">
42
- <p class="font-semibold group-hover:text-primary transition-colors">{post.title}</p>
43
- <p class="text-xs text-muted-foreground font-mono">{post.date}</p>
44
- </div>
45
- <div class="flex gap-1 shrink-0">
46
- {#each post.tags as tag}
47
- <span class="rounded-full bg-secondary px-2 py-0.5 text-xs text-secondary-foreground">{tag}</span>
48
- {/each}
49
- </div>
50
- </div>
51
- <p class="mt-2 text-sm text-muted-foreground">{post.excerpt}</p>
52
- </a>
53
- {/each}
54
- </div>
38
+ <div class="grid gap-4" data-bosia-preload="hover">
39
+ {#each posts as post}
40
+ <a
41
+ href="/blog/{post.slug}"
42
+ class="group block rounded-lg border bg-card p-5 hover:border-primary transition-colors"
43
+ >
44
+ <div class="flex items-start justify-between gap-4">
45
+ <div class="space-y-1">
46
+ <p class="font-semibold group-hover:text-primary transition-colors">
47
+ {post.title}
48
+ </p>
49
+ <p class="text-xs text-muted-foreground font-mono">{post.date}</p>
50
+ </div>
51
+ <div class="flex gap-1 shrink-0">
52
+ {#each post.tags as tag}
53
+ <span
54
+ class="rounded-full bg-secondary px-2 py-0.5 text-xs text-secondary-foreground"
55
+ >{tag}</span
56
+ >
57
+ {/each}
58
+ </div>
59
+ </div>
60
+ <p class="mt-2 text-sm text-muted-foreground">{post.excerpt}</p>
61
+ </a>
62
+ {/each}
63
+ </div>
55
64
  </div>
@@ -1,62 +1,60 @@
1
1
  import type { LoadEvent, MetadataEvent } from "bosia";
2
2
 
3
3
  const posts: Record<string, { title: string; date: string; tags: string[]; content: string }> = {
4
- "hello-world": {
5
- title: "Hello, World!",
6
- date: "2026-03-05",
7
- tags: ["intro", "bosia"],
8
- content: `Welcome to Bosia! This page was loaded by a +page.server.ts file.
4
+ "hello-world": {
5
+ title: "Hello, World!",
6
+ date: "2026-03-05",
7
+ tags: ["intro", "bosia"],
8
+ content: `Welcome to Bosia! This page was loaded by a +page.server.ts file.
9
9
 
10
10
  The slug param was extracted from the URL by the route matcher and passed to the load() function as params.slug.
11
11
 
12
12
  This is standard SvelteKit-compatible server loading.`,
13
- },
14
- "route-groups": {
15
- title: "Route Groups Explained",
16
- date: "2026-03-04",
17
- tags: ["routing", "layouts"],
18
- content: `Route groups like (public), (auth), and (admin) are directory names that are invisible in the URL.
13
+ },
14
+ "route-groups": {
15
+ title: "Route Groups Explained",
16
+ date: "2026-03-04",
17
+ tags: ["routing", "layouts"],
18
+ content: `Route groups like (public), (auth), and (admin) are directory names that are invisible in the URL.
19
19
 
20
20
  They let you share layouts across a set of routes without adding a URL segment. A directory named (public) applies its +layout.svelte to all routes inside it, but /public never appears in the browser URL.
21
21
 
22
22
  This page lives at routes/(public)/blog/[slug]/+page.svelte but is served at /blog/route-groups.`,
23
- },
24
- "dynamic-params": {
25
- title: "Dynamic Params with [slug]",
26
- date: "2026-03-03",
27
- tags: ["routing", "dynamic"],
28
- content: `A directory named [slug] creates a dynamic route segment that matches any URL value.
23
+ },
24
+ "dynamic-params": {
25
+ title: "Dynamic Params with [slug]",
26
+ date: "2026-03-03",
27
+ tags: ["routing", "dynamic"],
28
+ content: `A directory named [slug] creates a dynamic route segment that matches any URL value.
29
29
 
30
30
  The matched value is available as params.slug inside +page.server.ts load() and inside the page component via data.params.slug.
31
31
 
32
32
  The route matcher uses 3-pass priority: exact matches first, then dynamic segments, then catch-all routes.`,
33
- },
33
+ },
34
34
  };
35
35
 
36
36
  export function metadata({ params }: MetadataEvent) {
37
- // In production this would be a DB query for the post
38
- const post = posts[params.slug] ?? null;
39
- return {
40
- title: post ? `${post.title} — Blog` : `Post not found`,
41
- description: post ? `A blog post about ${params.slug}` : undefined,
42
- meta: post
43
- ? [{ property: "og:title", content: post.title }]
44
- : [],
45
- // Pass fetched post to load() — avoids duplicate query
46
- data: { post },
47
- };
37
+ // In production this would be a DB query for the post
38
+ const post = posts[params.slug] ?? null;
39
+ return {
40
+ title: post ? `${post.title} — Blog` : `Post not found`,
41
+ description: post ? `A blog post about ${params.slug}` : undefined,
42
+ meta: post ? [{ property: "og:title", content: post.title }] : [],
43
+ // Pass fetched post to load() — avoids duplicate query
44
+ data: { post },
45
+ };
48
46
  }
49
47
 
50
48
  export async function load({ params, parent, metadata }: LoadEvent) {
51
- // parent() gives us data from +layout.server.ts (appName, requestTime)
52
- const parentData = await parent();
49
+ // parent() gives us data from +layout.server.ts (appName, requestTime)
50
+ const parentData = await parent();
53
51
 
54
- // Reuse post from metadata() — no duplicate DB query
55
- const post = metadata?.post ?? posts[params.slug] ?? null;
52
+ // Reuse post from metadata() — no duplicate DB query
53
+ const post = metadata?.post ?? posts[params.slug] ?? null;
56
54
 
57
- return {
58
- post,
59
- slug: params.slug,
60
- appName: parentData.appName as string,
61
- };
55
+ return {
56
+ post,
57
+ slug: params.slug,
58
+ appName: parentData.appName as string,
59
+ };
62
60
  }
@@ -1,53 +1,71 @@
1
1
  <script lang="ts">
2
- import type { PageData } from './$types';
2
+ import type { PageData } from "./$types";
3
3
 
4
- let { data }: { data: PageData } = $props();
4
+ let { data }: { data: PageData } = $props();
5
5
 
6
- const post = $derived(data.post);
7
- const slug = $derived(data.slug ?? data.params.slug ?? "");
6
+ const post = $derived(data.post);
7
+ const slug = $derived(data.slug ?? data.params.slug ?? "");
8
8
  </script>
9
9
 
10
10
  <svelte:head>
11
- <title>{post ? post.title : "Post Not Found"} | {{PROJECT_NAME}}</title>
11
+ <title>{post ? post.title : "Post Not Found"} | Bosia Demo</title>
12
12
  </svelte:head>
13
13
 
14
14
  {#if post}
15
- <article class="space-y-6 max-w-2xl">
16
- <a href="/blog" class="text-sm text-muted-foreground hover:text-foreground transition-colors">← Blog</a>
17
-
18
- <div class="space-y-2">
19
- <div class="flex flex-wrap gap-1">
20
- {#each post.tags as tag}
21
- <span class="rounded-full bg-secondary px-2 py-0.5 text-xs text-secondary-foreground">{tag}</span>
22
- {/each}
23
- </div>
24
- <h1 class="text-4xl font-bold tracking-tight">{post.title}</h1>
25
- <p class="text-sm text-muted-foreground font-mono">{post.date}</p>
26
- </div>
27
-
28
- <hr class="border-border" />
29
-
30
- <div class="space-y-4">
31
- {#each post.content.split("\n\n") as paragraph}
32
- <p class="text-foreground leading-relaxed">{paragraph}</p>
33
- {/each}
34
- </div>
35
-
36
- <hr class="border-border" />
37
-
38
- <!-- Route debug box — demonstrates params flowing from server to client -->
39
- <div class="rounded-lg border bg-muted/40 p-4 space-y-1 text-sm font-mono">
40
- <p class="font-sans text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2">Route debug</p>
41
- <p><span class="text-muted-foreground">pattern: </span>/blog/[slug]</p>
42
- <p><span class="text-muted-foreground">params.slug: </span><span class="text-primary font-semibold">{slug}</span></p>
43
- <p><span class="text-muted-foreground">loaded by: </span>+page.server.ts</p>
44
- <p><span class="text-muted-foreground">parent data: </span>{data.appName}</p>
45
- </div>
46
- </article>
15
+ <article class="space-y-6 max-w-2xl">
16
+ <a
17
+ href="/blog"
18
+ class="text-sm text-muted-foreground hover:text-foreground transition-colors">← Blog</a
19
+ >
20
+
21
+ <div class="space-y-2">
22
+ <div class="flex flex-wrap gap-1">
23
+ {#each post.tags as tag}
24
+ <span
25
+ class="rounded-full bg-secondary px-2 py-0.5 text-xs text-secondary-foreground"
26
+ >{tag}</span
27
+ >
28
+ {/each}
29
+ </div>
30
+ <h1 class="text-4xl font-bold tracking-tight">{post.title}</h1>
31
+ <p class="text-sm text-muted-foreground font-mono">{post.date}</p>
32
+ </div>
33
+
34
+ <hr class="border-border" />
35
+
36
+ <div class="space-y-4">
37
+ {#each post.content.split("\n\n") as paragraph}
38
+ <p class="text-foreground leading-relaxed">{paragraph}</p>
39
+ {/each}
40
+ </div>
41
+
42
+ <hr class="border-border" />
43
+
44
+ <!-- Route debug box — demonstrates params flowing from server to client -->
45
+ <div class="rounded-lg border bg-muted/40 p-4 space-y-1 text-sm font-mono">
46
+ <p
47
+ class="font-sans text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2"
48
+ >
49
+ Route debug
50
+ </p>
51
+ <p><span class="text-muted-foreground">pattern: </span>/blog/[slug]</p>
52
+ <p>
53
+ <span class="text-muted-foreground">params.slug: </span><span
54
+ class="text-primary font-semibold">{slug}</span
55
+ >
56
+ </p>
57
+ <p><span class="text-muted-foreground">loaded by: </span>+page.server.ts</p>
58
+ <p><span class="text-muted-foreground">parent data: </span>{data.appName}</p>
59
+ </div>
60
+ </article>
47
61
  {:else}
48
- <div class="flex flex-col items-center justify-center py-20 text-center space-y-4">
49
- <p class="text-7xl font-bold text-destructive">404</p>
50
- <p class="text-xl text-muted-foreground">Post "<span class="font-mono">{slug}</span>" not found.</p>
51
- <a href="/blog" class="rounded-md border px-4 py-2 text-sm hover:bg-muted transition-colors">Back to Blog</a>
52
- </div>
62
+ <div class="flex flex-col items-center justify-center py-20 text-center space-y-4">
63
+ <p class="text-7xl font-bold text-destructive">404</p>
64
+ <p class="text-xl text-muted-foreground">
65
+ Post "<span class="font-mono">{slug}</span>" not found.
66
+ </p>
67
+ <a href="/blog" class="rounded-md border px-4 py-2 text-sm hover:bg-muted transition-colors"
68
+ >Back to Blog</a
69
+ >
70
+ </div>
53
71
  {/if}
@@ -1,15 +1,18 @@
1
1
  <script lang="ts">
2
- let { error }: { error: { status: number; message: string } } = $props();
2
+ let { error }: { error: { status: number; message: string } } = $props();
3
3
  </script>
4
4
 
5
5
  <svelte:head>
6
- <title>{error.status} — {error.message}</title>
6
+ <title>{error.status} — {error.message}</title>
7
7
  </svelte:head>
8
8
 
9
9
  <div class="min-h-screen flex flex-col items-center justify-center gap-4 text-center px-4">
10
- <p class="text-8xl font-bold text-gray-200">{error.status}</p>
11
- <p class="text-2xl font-semibold text-gray-700">{error.message}</p>
12
- <a href="/" class="mt-4 px-5 py-2 rounded-lg bg-gray-900 text-white text-sm hover:bg-gray-700 transition-colors">
13
- Go home
14
- </a>
10
+ <p class="text-8xl font-bold text-gray-200">{error.status}</p>
11
+ <p class="text-2xl font-semibold text-gray-700">{error.message}</p>
12
+ <a
13
+ href="/"
14
+ class="mt-4 px-5 py-2 rounded-lg bg-gray-900 text-white text-sm hover:bg-gray-700 transition-colors"
15
+ >
16
+ Go home
17
+ </a>
15
18
  </div>
@@ -3,8 +3,8 @@ import type { LoadEvent } from "bosia";
3
3
  // Data returned here is available to all child loaders via parent()
4
4
  // and to all layouts via the `data` prop.
5
5
  export async function load({ locals }: LoadEvent) {
6
- return {
7
- appName: "{{PROJECT_NAME}}",
8
- requestTime: locals.requestTime as number | null ?? null,
9
- };
6
+ return {
7
+ appName: "Bosia Demo",
8
+ requestTime: (locals.requestTime as number | null) ?? null,
9
+ };
10
10
  }
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
- import "../app.css";
3
- let { children }: { children: any } = $props();
2
+ import "../app.css";
3
+ let { children }: { children: any } = $props();
4
4
  </script>
5
5
 
6
6
  {@render children()}
@@ -2,27 +2,27 @@ import { fail, redirect } from "bosia";
2
2
  import type { RequestEvent } from "bosia";
3
3
 
4
4
  export async function load() {
5
- return { greeting: "Test form actions" };
5
+ return { greeting: "Test form actions" };
6
6
  }
7
7
 
8
8
  export const actions = {
9
- default: async ({ request }: RequestEvent) => {
10
- const data = await request.formData();
11
- const email = data.get("email") as string;
12
- const name = data.get("name") as string;
9
+ default: async ({ request }: RequestEvent) => {
10
+ const data = await request.formData();
11
+ const email = data.get("email") as string;
12
+ const name = data.get("name") as string;
13
13
 
14
- const errors: Record<string, string> = {};
15
- if (!email) errors.email = "Email is required";
16
- if (!name) errors.name = "Name is required";
14
+ const errors: Record<string, string> = {};
15
+ if (!email) errors.email = "Email is required";
16
+ if (!name) errors.name = "Name is required";
17
17
 
18
- if (Object.keys(errors).length > 0) {
19
- return fail(400, { email, name, errors });
20
- }
18
+ if (Object.keys(errors).length > 0) {
19
+ return fail(400, { email, name, errors });
20
+ }
21
21
 
22
- return { success: true, email, name };
23
- },
22
+ return { success: true, email, name };
23
+ },
24
24
 
25
- reset: async () => {
26
- return { cleared: true };
27
- },
25
+ reset: async () => {
26
+ return { cleared: true };
27
+ },
28
28
  };