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,22 +1,22 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "allowJs": true,
8
- "skipLibCheck": true,
9
- "allowImportingTsExtensions": true,
10
- "noEmit": true,
11
- "verbatimModuleSyntax": true,
12
- "types": ["bun-types"],
13
- "lib": ["dom", "dom.iterable", "esnext"],
14
- "rootDirs": [".", ".bosia/types"],
15
- "paths": {
16
- "$lib": ["./src/lib"],
17
- "$lib/*": ["./src/lib/*"]
18
- }
19
- },
20
- "include": ["src/**/*"],
21
- "exclude": ["node_modules", "dist"]
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "allowJs": true,
8
+ "skipLibCheck": true,
9
+ "allowImportingTsExtensions": true,
10
+ "noEmit": true,
11
+ "verbatimModuleSyntax": true,
12
+ "types": ["bun-types"],
13
+ "lib": ["dom", "dom.iterable", "esnext"],
14
+ "rootDirs": [".", ".bosia/types"],
15
+ "paths": {
16
+ "$lib": ["./src/lib"],
17
+ "$lib/*": ["./src/lib/*"]
18
+ }
19
+ },
20
+ "include": ["src/**/*"],
21
+ "exclude": ["node_modules", "dist"]
22
22
  }
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ build
4
+ .bosia
5
+ bun.lock
@@ -0,0 +1,9 @@
1
+ {
2
+ "useTabs": true,
3
+ "tabWidth": 4,
4
+ "singleQuote": false,
5
+ "trailingComma": "all",
6
+ "printWidth": 100,
7
+ "plugins": ["prettier-plugin-svelte"],
8
+ "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
9
+ }
@@ -12,15 +12,15 @@ bun x bosia start # run production server
12
12
 
13
13
  ## Routes
14
14
 
15
- | URL | File | Description |
16
- |-----|------|-------------|
17
- | `/` | `(public)/+page.svelte` | Home page |
18
- | `/about` | `(public)/about/+page.svelte` | About page |
19
- | `/blog` | `(public)/blog/+page.svelte` | Blog listing |
20
- | `/blog/:slug` | `(public)/blog/[slug]/+page.svelte` | Blog post — fetched via server loader |
21
- | `/api/hello` | `api/hello/+server.ts` | Multi-method JSON API |
22
- | `/actions-test` | `actions-test/+page.svelte` | Form actions demo |
23
- | `/*` | `(public)/[...catchall]/+page.svelte` | 404 catch-all |
15
+ | URL | File | Description |
16
+ | --------------- | ------------------------------------- | ------------------------------------- |
17
+ | `/` | `(public)/+page.svelte` | Home page |
18
+ | `/about` | `(public)/about/+page.svelte` | About page |
19
+ | `/blog` | `(public)/blog/+page.svelte` | Blog listing |
20
+ | `/blog/:slug` | `(public)/blog/[slug]/+page.svelte` | Blog post — fetched via server loader |
21
+ | `/api/hello` | `api/hello/+server.ts` | Multi-method JSON API |
22
+ | `/actions-test` | `actions-test/+page.svelte` | Form actions demo |
23
+ | `/*` | `(public)/[...catchall]/+page.svelte` | 404 catch-all |
24
24
 
25
25
  ## Learn More
26
26
 
@@ -1,19 +1,24 @@
1
1
  {
2
- "name": "{{PROJECT_NAME}}",
3
- "private": true,
4
- "type": "module",
5
- "scripts": {
6
- "dev": "bosia dev",
7
- "build": "bosia build",
8
- "start": "bosia start"
9
- },
10
- "dependencies": {
11
- "bosia": "^{{BOSIA_VERSION}}",
12
- "svelte": "^5.20.0",
13
- "tailwind-merge": "^3.5.0"
14
- },
15
- "devDependencies": {
16
- "@types/bun": "latest",
17
- "typescript": "^5"
18
- }
2
+ "name": "{{PROJECT_NAME}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "bosia dev",
7
+ "build": "bosia build",
8
+ "start": "bosia start",
9
+ "check": "tsc --noEmit && prettier --check .",
10
+ "format": "prettier --write .",
11
+ "format:check": "prettier --check ."
12
+ },
13
+ "dependencies": {
14
+ "bosia": "^{{BOSIA_VERSION}}",
15
+ "svelte": "^5.20.0",
16
+ "tailwind-merge": "^3.5.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/bun": "latest",
20
+ "prettier": "^3.3.0",
21
+ "prettier-plugin-svelte": "^3.2.0",
22
+ "typescript": "^5"
23
+ }
19
24
  }
@@ -8,125 +8,125 @@
8
8
  */
9
9
 
10
10
  @theme {
11
- --color-background: hsl(var(--background));
12
- --color-foreground: hsl(var(--foreground));
11
+ --color-background: hsl(var(--background));
12
+ --color-foreground: hsl(var(--foreground));
13
13
 
14
- --color-card: hsl(var(--card));
15
- --color-card-foreground: hsl(var(--card-foreground));
14
+ --color-card: hsl(var(--card));
15
+ --color-card-foreground: hsl(var(--card-foreground));
16
16
 
17
- --color-popover: hsl(var(--popover));
18
- --color-popover-foreground: hsl(var(--popover-foreground));
17
+ --color-popover: hsl(var(--popover));
18
+ --color-popover-foreground: hsl(var(--popover-foreground));
19
19
 
20
- --color-primary: hsl(var(--primary));
21
- --color-primary-foreground: hsl(var(--primary-foreground));
20
+ --color-primary: hsl(var(--primary));
21
+ --color-primary-foreground: hsl(var(--primary-foreground));
22
22
 
23
- --color-secondary: hsl(var(--secondary));
24
- --color-secondary-foreground: hsl(var(--secondary-foreground));
23
+ --color-secondary: hsl(var(--secondary));
24
+ --color-secondary-foreground: hsl(var(--secondary-foreground));
25
25
 
26
- --color-muted: hsl(var(--muted));
27
- --color-muted-foreground: hsl(var(--muted-foreground));
26
+ --color-muted: hsl(var(--muted));
27
+ --color-muted-foreground: hsl(var(--muted-foreground));
28
28
 
29
- --color-accent: hsl(var(--accent));
30
- --color-accent-foreground: hsl(var(--accent-foreground));
29
+ --color-accent: hsl(var(--accent));
30
+ --color-accent-foreground: hsl(var(--accent-foreground));
31
31
 
32
- --color-destructive: hsl(var(--destructive));
33
- --color-destructive-foreground: hsl(var(--destructive-foreground));
32
+ --color-destructive: hsl(var(--destructive));
33
+ --color-destructive-foreground: hsl(var(--destructive-foreground));
34
34
 
35
- --color-border: hsl(var(--border));
36
- --color-input: hsl(var(--input));
37
- --color-ring: hsl(var(--ring));
35
+ --color-border: hsl(var(--border));
36
+ --color-input: hsl(var(--input));
37
+ --color-ring: hsl(var(--ring));
38
38
 
39
- --radius-sm: calc(var(--radius) - 4px);
40
- --radius-md: calc(var(--radius) - 2px);
41
- --radius-lg: var(--radius);
42
- --radius-xl: calc(var(--radius) + 4px);
39
+ --radius-sm: calc(var(--radius) - 4px);
40
+ --radius-md: calc(var(--radius) - 2px);
41
+ --radius-lg: var(--radius);
42
+ --radius-xl: calc(var(--radius) + 4px);
43
43
  }
44
44
 
45
45
  /* ─── Light Theme (Default) ─────────────────────────────── */
46
46
 
47
47
  :root {
48
- --background: 0 0% 100%;
49
- --foreground: 222.2 84% 4.9%;
48
+ --background: 0 0% 100%;
49
+ --foreground: 222.2 84% 4.9%;
50
50
 
51
- --card: 0 0% 100%;
52
- --card-foreground: 222.2 84% 4.9%;
51
+ --card: 0 0% 100%;
52
+ --card-foreground: 222.2 84% 4.9%;
53
53
 
54
- --popover: 0 0% 100%;
55
- --popover-foreground: 222.2 84% 4.9%;
54
+ --popover: 0 0% 100%;
55
+ --popover-foreground: 222.2 84% 4.9%;
56
56
 
57
- --primary: 222.2 47.4% 11.2%;
58
- --primary-foreground: 210 40% 98%;
57
+ --primary: 222.2 47.4% 11.2%;
58
+ --primary-foreground: 210 40% 98%;
59
59
 
60
- --secondary: 210 40% 96.1%;
61
- --secondary-foreground: 222.2 47.4% 11.2%;
60
+ --secondary: 210 40% 96.1%;
61
+ --secondary-foreground: 222.2 47.4% 11.2%;
62
62
 
63
- --muted: 210 40% 96.1%;
64
- --muted-foreground: 215.4 16.3% 46.9%;
63
+ --muted: 210 40% 96.1%;
64
+ --muted-foreground: 215.4 16.3% 46.9%;
65
65
 
66
- --accent: 210 40% 96.1%;
67
- --accent-foreground: 222.2 47.4% 11.2%;
66
+ --accent: 210 40% 96.1%;
67
+ --accent-foreground: 222.2 47.4% 11.2%;
68
68
 
69
- --destructive: 0 84.2% 60.2%;
70
- --destructive-foreground: 210 40% 98%;
69
+ --destructive: 0 84.2% 60.2%;
70
+ --destructive-foreground: 210 40% 98%;
71
71
 
72
- --border: 214.3 31.8% 91.4%;
73
- --input: 214.3 31.8% 91.4%;
74
- --ring: 222.2 84% 4.9%;
72
+ --border: 214.3 31.8% 91.4%;
73
+ --input: 214.3 31.8% 91.4%;
74
+ --ring: 222.2 84% 4.9%;
75
75
 
76
- --radius: 0.5rem;
76
+ --radius: 0.5rem;
77
77
  }
78
78
 
79
79
  /* ─── Dark Theme ─────────────────────────────────────────── */
80
80
 
81
81
  .dark {
82
- --background: 222.2 84% 4.9%;
83
- --foreground: 210 40% 98%;
82
+ --background: 222.2 84% 4.9%;
83
+ --foreground: 210 40% 98%;
84
84
 
85
- --card: 222.2 84% 4.9%;
86
- --card-foreground: 210 40% 98%;
85
+ --card: 222.2 84% 4.9%;
86
+ --card-foreground: 210 40% 98%;
87
87
 
88
- --popover: 222.2 84% 4.9%;
89
- --popover-foreground: 210 40% 98%;
88
+ --popover: 222.2 84% 4.9%;
89
+ --popover-foreground: 210 40% 98%;
90
90
 
91
- --primary: 210 40% 98%;
92
- --primary-foreground: 222.2 47.4% 11.2%;
91
+ --primary: 210 40% 98%;
92
+ --primary-foreground: 222.2 47.4% 11.2%;
93
93
 
94
- --secondary: 217.2 32.6% 17.5%;
95
- --secondary-foreground: 210 40% 98%;
94
+ --secondary: 217.2 32.6% 17.5%;
95
+ --secondary-foreground: 210 40% 98%;
96
96
 
97
- --muted: 217.2 32.6% 17.5%;
98
- --muted-foreground: 215 20.2% 65.1%;
97
+ --muted: 217.2 32.6% 17.5%;
98
+ --muted-foreground: 215 20.2% 65.1%;
99
99
 
100
- --accent: 217.2 32.6% 17.5%;
101
- --accent-foreground: 210 40% 98%;
100
+ --accent: 217.2 32.6% 17.5%;
101
+ --accent-foreground: 210 40% 98%;
102
102
 
103
- --destructive: 0 62.8% 30.6%;
104
- --destructive-foreground: 210 40% 98%;
103
+ --destructive: 0 62.8% 30.6%;
104
+ --destructive-foreground: 210 40% 98%;
105
105
 
106
- --border: 217.2 32.6% 17.5%;
107
- --input: 217.2 32.6% 17.5%;
108
- --ring: 212.7 26.8% 83.9%;
106
+ --border: 217.2 32.6% 17.5%;
107
+ --input: 217.2 32.6% 17.5%;
108
+ --ring: 212.7 26.8% 83.9%;
109
109
  }
110
110
 
111
111
  /* ─── Base Styles ────────────────────────────────────────── */
112
112
 
113
113
  @layer base {
114
- * {
115
- border-color: theme(--color-border);
116
- }
117
-
118
- body {
119
- background-color: theme(--color-background);
120
- color: theme(--color-foreground);
121
- font-family:
122
- "Inter",
123
- system-ui,
124
- -apple-system,
125
- BlinkMacSystemFont,
126
- "Segoe UI",
127
- Roboto,
128
- "Helvetica Neue",
129
- Arial,
130
- sans-serif;
131
- }
114
+ * {
115
+ border-color: theme(--color-border);
116
+ }
117
+
118
+ body {
119
+ background-color: theme(--color-background);
120
+ color: theme(--color-foreground);
121
+ font-family:
122
+ "Inter",
123
+ system-ui,
124
+ -apple-system,
125
+ BlinkMacSystemFont,
126
+ "Segoe UI",
127
+ Roboto,
128
+ "Helvetica Neue",
129
+ Arial,
130
+ sans-serif;
131
+ }
132
132
  }
@@ -1,7 +1,7 @@
1
1
  /// <reference types="svelte" />
2
2
 
3
3
  declare module "*.svelte" {
4
- import type { Component } from "svelte";
5
- const component: Component<Record<string, any>, Record<string, any>, any>;
6
- export default component;
4
+ import type { Component } from "svelte";
5
+ const component: Component<Record<string, any>, Record<string, any>, any>;
6
+ export default component;
7
7
  }
@@ -3,19 +3,19 @@ import type { Handle } from "bosia";
3
3
 
4
4
  // Sets locals that every loader and API handler can read
5
5
  const authHandle: Handle = async ({ event, resolve }) => {
6
- event.locals.requestTime = Date.now();
7
- event.locals.user = null; // replace with real session logic
8
- return resolve(event);
6
+ event.locals.requestTime = Date.now();
7
+ event.locals.user = null; // replace with real session logic
8
+ return resolve(event);
9
9
  };
10
10
 
11
11
  // Logs each request with method, path, status, and duration
12
12
  const loggingHandle: Handle = async ({ event, resolve }) => {
13
- const start = Date.now();
14
- const res = await resolve(event);
15
- const ms = Date.now() - start;
16
- console.log(`[${event.request.method}] ${event.url.pathname} ${res.status} (${ms}ms)`);
17
- res.headers.set("X-Response-Time", `${ms}ms`);
18
- return res;
13
+ const start = Date.now();
14
+ const res = await resolve(event);
15
+ const ms = Date.now() - start;
16
+ console.log(`[${event.request.method}] ${event.url.pathname} ${res.status} (${ms}ms)`);
17
+ res.headers.set("X-Response-Time", `${ms}ms`);
18
+ return res;
19
19
  };
20
20
 
21
21
  export const handle = sequence(authHandle, loggingHandle);
@@ -1,31 +1,53 @@
1
1
  <script lang="ts">
2
- import type { LayoutData } from '../$types';
2
+ import type { LayoutData } from "../$types";
3
3
 
4
- let { children, data }: { children: any; data: LayoutData } = $props();
4
+ let { children, data }: { children: any; data: LayoutData } = $props();
5
5
  </script>
6
6
 
7
7
  <div class="flex min-h-screen flex-col bg-background text-foreground">
8
- <header class="sticky top-0 z-10 border-b bg-background/80 backdrop-blur">
9
- <nav class="mx-auto flex max-w-4xl items-center gap-6 px-4 py-3">
10
- <a href="/" class="font-bold tracking-tight flex items-center gap-2"><img src="/favicon.svg" alt="" class="size-5" /> {{PROJECT_NAME}}</a>
11
- <a href="/" class="text-sm text-muted-foreground hover:text-foreground transition-colors">Home</a>
12
- <a href="/about" class="text-sm text-muted-foreground hover:text-foreground transition-colors">About</a>
13
- <a href="/blog" class="text-sm text-muted-foreground hover:text-foreground transition-colors">Blog</a>
14
- <a href="/all/foo/bar" class="text-sm text-muted-foreground hover:text-foreground transition-colors">Catch-all</a>
15
- <a href="/api/hello" target="_blank" class="text-sm text-muted-foreground hover:text-foreground transition-colors">API</a>
16
- </nav>
17
- </header>
8
+ <header class="sticky top-0 z-10 border-b bg-background/80 backdrop-blur">
9
+ <nav class="mx-auto flex max-w-4xl items-center gap-6 px-4 py-3">
10
+ <a href="/" class="font-bold tracking-tight flex items-center gap-2"
11
+ ><img src="/favicon.svg" alt="" class="size-5" /> {data.appName}</a
12
+ >
13
+ <a
14
+ href="/"
15
+ class="text-sm text-muted-foreground hover:text-foreground transition-colors"
16
+ >Home</a
17
+ >
18
+ <a
19
+ href="/about"
20
+ class="text-sm text-muted-foreground hover:text-foreground transition-colors"
21
+ >About</a
22
+ >
23
+ <a
24
+ href="/blog"
25
+ class="text-sm text-muted-foreground hover:text-foreground transition-colors"
26
+ >Blog</a
27
+ >
28
+ <a
29
+ href="/all/foo/bar"
30
+ class="text-sm text-muted-foreground hover:text-foreground transition-colors"
31
+ >Catch-all</a
32
+ >
33
+ <a
34
+ href="/api/hello"
35
+ target="_blank"
36
+ class="text-sm text-muted-foreground hover:text-foreground transition-colors">API</a
37
+ >
38
+ </nav>
39
+ </header>
18
40
 
19
- <main class="mx-auto w-full max-w-4xl flex-1 px-4 py-8">
20
- {@render children()}
21
- </main>
41
+ <main class="mx-auto w-full max-w-4xl flex-1 px-4 py-8">
42
+ {@render children()}
43
+ </main>
22
44
 
23
- <footer class="border-t py-4 text-center text-sm text-muted-foreground">
24
- Powered by Bosia
25
- {#if data.requestTime}
26
- <span class="ml-2 opacity-40 font-mono text-xs">
27
- req at {new Date(data.requestTime).toISOString()}
28
- </span>
29
- {/if}
30
- </footer>
45
+ <footer class="border-t py-4 text-center text-sm text-muted-foreground">
46
+ Powered by Bosia
47
+ {#if data.requestTime}
48
+ <span class="ml-2 opacity-40 font-mono text-xs">
49
+ req at {new Date(data.requestTime).toISOString()}
50
+ </span>
51
+ {/if}
52
+ </footer>
31
53
  </div>
@@ -1,79 +1,108 @@
1
1
  <script lang="ts">
2
- let count = $state(0);
2
+ let count = $state(0);
3
3
 
4
- const features = [
5
- { icon: "📂", label: "File-based routing", desc: "+page.svelte, +layout.svelte, route groups, dynamic [params]" },
6
- { icon: "", label: "SSR + Hydration", desc: "Server renders HTML, Svelte hydrates on the client" },
7
- { icon: "🔁", label: "Server loaders", desc: "+page.server.ts and +layout.server.ts with parent() threading" },
8
- { icon: "🪝", label: "Hooks", desc: "sequence() middleware — auth, logging, locals" },
9
- { icon: "📡", label: "API routes", desc: "+server.ts exports GET, POST, PUT, PATCH, DELETE" },
10
- { icon: "🧩", label: "Component registry", desc: "bosia add button — shadcn-style, code you own" },
11
- { icon: "", label: "feat system", desc: "bosia feat login — scaffold entire features, not just components" },
12
- ];
4
+ const features = [
5
+ {
6
+ icon: "📂",
7
+ label: "File-based routing",
8
+ desc: "+page.svelte, +layout.svelte, route groups, dynamic [params]",
9
+ },
10
+ {
11
+ icon: "",
12
+ label: "SSR + Hydration",
13
+ desc: "Server renders HTML, Svelte hydrates on the client",
14
+ },
15
+ {
16
+ icon: "🔁",
17
+ label: "Server loaders",
18
+ desc: "+page.server.ts and +layout.server.ts with parent() threading",
19
+ },
20
+ { icon: "🪝", label: "Hooks", desc: "sequence() middleware — auth, logging, locals" },
21
+ {
22
+ icon: "📡",
23
+ label: "API routes",
24
+ desc: "+server.ts exports GET, POST, PUT, PATCH, DELETE",
25
+ },
26
+ {
27
+ icon: "🧩",
28
+ label: "Component registry",
29
+ desc: "bosia add button — shadcn-style, code you own",
30
+ },
31
+ {
32
+ icon: "✨",
33
+ label: "feat system",
34
+ desc: "bosia feat login — scaffold entire features, not just components",
35
+ },
36
+ ];
13
37
  </script>
14
38
 
15
39
  <svelte:head>
16
- <title>{{PROJECT_NAME}}</title>
17
- <meta name="description" content="{{PROJECT_NAME}} — SSR + Svelte 5 + Bun + ElysiaJS" />
40
+ <title>Bosia Demo</title>
41
+ <meta name="description" content="Bosia Demo — SSR + Svelte 5 + Bun + ElysiaJS" />
18
42
  </svelte:head>
19
43
 
20
44
  <div class="space-y-12">
21
- <!-- Hero -->
22
- <div class="space-y-3 pt-4">
23
- <h1 class="text-5xl font-bold tracking-tight flex items-center gap-3"><img src="/favicon.svg" alt="" class="size-10" /> {{PROJECT_NAME}}</h1>
24
- <p class="text-xl text-muted-foreground max-w-xl">
25
- A minimalist fullstack framework — SSR, Svelte 5 Runes, Bun, and ElysiaJS.
26
- </p>
27
- </div>
45
+ <!-- Hero -->
46
+ <div class="space-y-3 pt-4">
47
+ <h1 class="text-5xl font-bold tracking-tight flex items-center gap-3">
48
+ <img src="/favicon.svg" alt="" class="size-10" />
49
+ Bosia Demo
50
+ </h1>
51
+ <p class="text-xl text-muted-foreground max-w-xl">
52
+ A minimalist fullstack framework — SSR, Svelte 5 Runes, Bun, and ElysiaJS.
53
+ </p>
54
+ </div>
28
55
 
29
- <!-- Svelte 5 reactivity demo -->
30
- <div class="rounded-lg border bg-card p-6 space-y-3 max-w-sm">
31
- <p class="text-sm font-medium text-muted-foreground">Svelte 5 $state demo</p>
32
- <p class="text-5xl font-bold tabular-nums">{count}</p>
33
- <div class="flex gap-2">
34
- <button
35
- onclick={() => count++}
36
- class="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
37
- >
38
- +1
39
- </button>
40
- <button
41
- onclick={() => count = 0}
42
- class="rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition-colors"
43
- >
44
- Reset
45
- </button>
46
- </div>
47
- </div>
56
+ <!-- Svelte 5 reactivity demo -->
57
+ <div class="rounded-lg border bg-card p-6 space-y-3 max-w-sm">
58
+ <p class="text-sm font-medium text-muted-foreground">Svelte 5 $state demo</p>
59
+ <p class="text-5xl font-bold tabular-nums">{count}</p>
60
+ <div class="flex gap-2">
61
+ <button
62
+ onclick={() => count++}
63
+ class="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
64
+ >
65
+ +1
66
+ </button>
67
+ <button
68
+ onclick={() => (count = 0)}
69
+ class="rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition-colors"
70
+ >
71
+ Reset
72
+ </button>
73
+ </div>
74
+ </div>
48
75
 
49
- <!-- Features grid -->
50
- <div class="space-y-4">
51
- <h2 class="text-2xl font-semibold tracking-tight">Features</h2>
52
- <div class="grid gap-4 sm:grid-cols-2">
53
- {#each features as f}
54
- <div class="rounded-lg border bg-card p-4 space-y-1">
55
- <p class="font-medium">{f.icon} {f.label}</p>
56
- <p class="text-sm text-muted-foreground">{f.desc}</p>
57
- </div>
58
- {/each}
59
- </div>
60
- </div>
76
+ <!-- Features grid -->
77
+ <div class="space-y-4">
78
+ <h2 class="text-2xl font-semibold tracking-tight">Features</h2>
79
+ <div class="grid gap-4 sm:grid-cols-2">
80
+ {#each features as f}
81
+ <div class="rounded-lg border bg-card p-4 space-y-1">
82
+ <p class="font-medium">{f.icon} {f.label}</p>
83
+ <p class="text-sm text-muted-foreground">{f.desc}</p>
84
+ </div>
85
+ {/each}
86
+ </div>
87
+ </div>
61
88
 
62
- <!-- Navigation demo -->
63
- <div class="space-y-4">
64
- <h2 class="text-2xl font-semibold tracking-tight">Routes in this demo</h2>
65
- <div class="flex flex-wrap gap-2">
66
- {#each ["/", "/about", "/blog", "/blog/hello-world", "/blog/route-groups", "/all/foo/bar", "/missing-page"] as href}
67
- <a
68
- {href}
69
- class="rounded-md border px-3 py-1.5 text-sm font-mono hover:bg-muted transition-colors"
70
- >{href}</a>
71
- {/each}
72
- <a
73
- href="/api/hello"
74
- target="_blank"
75
- class="rounded-md border px-3 py-1.5 text-sm font-mono hover:bg-muted transition-colors"
76
- >/api/hello ↗</a>
77
- </div>
78
- </div>
89
+ <!-- Navigation demo -->
90
+ <div class="space-y-4">
91
+ <h2 class="text-2xl font-semibold tracking-tight">Routes in this demo</h2>
92
+ <div class="flex flex-wrap gap-2">
93
+ {#each ["/", "/about", "/blog", "/blog/hello-world", "/blog/route-groups", "/all/foo/bar", "/missing-page"] as href}
94
+ <a
95
+ {href}
96
+ class="rounded-md border px-3 py-1.5 text-sm font-mono hover:bg-muted transition-colors"
97
+ >{href}</a
98
+ >
99
+ {/each}
100
+ <a
101
+ href="/api/hello"
102
+ target="_blank"
103
+ class="rounded-md border px-3 py-1.5 text-sm font-mono hover:bg-muted transition-colors"
104
+ >/api/hello ↗</a
105
+ >
106
+ </div>
107
+ </div>
79
108
  </div>