bosia 0.6.23 → 0.6.24
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/cli/add.ts +2 -2
- package/src/cli/block.ts +7 -0
- package/src/cli/create.ts +0 -1
- package/src/cli/feat.ts +1 -1
- package/src/cli/index.ts +3 -3
- package/src/core/dev.ts +98 -2
- package/src/core/html.ts +10 -4
- package/templates/default/package.json +1 -1
- package/templates/demo/package.json +1 -1
- package/templates/shop/package.json +1 -1
- package/templates/todo/.env.example +0 -2
- package/templates/todo/.prettierignore +0 -7
- package/templates/todo/.prettierrc.json +0 -9
- package/templates/todo/README.md +0 -69
- package/templates/todo/_gitignore +0 -12
- package/templates/todo/bosia.config.ts +0 -10
- package/templates/todo/instructions.txt +0 -3
- package/templates/todo/package.json +0 -24
- package/templates/todo/public/.gitkeep +0 -0
- package/templates/todo/public/favicon.svg +0 -14
- package/templates/todo/public/logo-dark.svg +0 -14
- package/templates/todo/public/logo-light.svg +0 -14
- package/templates/todo/src/app.css +0 -134
- package/templates/todo/src/app.d.ts +0 -14
- package/templates/todo/src/app.html +0 -11
- package/templates/todo/src/hooks.server.ts +0 -20
- package/templates/todo/src/lib/utils.ts +0 -1
- package/templates/todo/src/routes/(public)/+page.svelte +0 -53
- package/templates/todo/src/routes/+error.svelte +0 -19
- package/templates/todo/src/routes/+layout.server.ts +0 -8
- package/templates/todo/src/routes/+layout.svelte +0 -6
- package/templates/todo/template.json +0 -6
- package/templates/todo/tsconfig.json +0 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosia",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS. File-based routing inspired by SvelteKit. No Node.js, no Vite, no adapters.",
|
|
6
6
|
"keywords": [
|
package/src/cli/add.ts
CHANGED
|
@@ -75,7 +75,7 @@ export async function runAdd(names: string[], flags: string[] = []) {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Resolve the full registry path for a component using the index.
|
|
78
|
-
* - "
|
|
78
|
+
* - "<name>" → "<name>" (exact match in index)
|
|
79
79
|
* - "button" → "ui/button" (suffix match in index)
|
|
80
80
|
* - "shop/cart" → "shop/cart" (explicit path used as-is)
|
|
81
81
|
*/
|
|
@@ -83,7 +83,7 @@ function resolveDestPath(name: string): string {
|
|
|
83
83
|
if (name.includes("/")) return name;
|
|
84
84
|
|
|
85
85
|
if (registryIndex) {
|
|
86
|
-
// Exact match (
|
|
86
|
+
// Exact match (bare name present in index)
|
|
87
87
|
if (registryIndex.components.includes(name)) return name;
|
|
88
88
|
// Suffix match (e.g. "button" → "ui/button")
|
|
89
89
|
const match = registryIndex.components.find((c) => c.endsWith(`/${name}`));
|
package/src/cli/block.ts
CHANGED
|
@@ -30,6 +30,10 @@ interface BlockMeta {
|
|
|
30
30
|
npmDeps: Record<string, string>;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// Track already-installed blocks within a session to avoid redundant work
|
|
34
|
+
// when multiple features/blocks declare the same block dependency.
|
|
35
|
+
const installed = new Set<string>();
|
|
36
|
+
|
|
33
37
|
export async function runAddBlock(
|
|
34
38
|
name: string | undefined,
|
|
35
39
|
flags: string[] = [],
|
|
@@ -58,6 +62,9 @@ export async function runAddBlock(
|
|
|
58
62
|
await initAddRegistry(registryRoot);
|
|
59
63
|
ensureUtils(resolvedOptions.cwd);
|
|
60
64
|
|
|
65
|
+
if (installed.has(name)) return;
|
|
66
|
+
installed.add(name);
|
|
67
|
+
|
|
61
68
|
console.log(`⬡ Installing block: ${name}\n`);
|
|
62
69
|
|
|
63
70
|
const meta = await readRegistryJSON<BlockMeta>(registryRoot, "blocks", name, "meta.json");
|
package/src/cli/create.ts
CHANGED
|
@@ -14,7 +14,6 @@ const BOSIA_VERSION: string = BOSIA_PKG.version;
|
|
|
14
14
|
const TEMPLATE_DESCRIPTIONS: Record<string, string> = {
|
|
15
15
|
default: "Minimal starter with routing and Tailwind",
|
|
16
16
|
demo: "Full-featured demo with hooks, API routes, form actions, and more",
|
|
17
|
-
todo: "Todo app with PostgreSQL + Drizzle ORM",
|
|
18
17
|
shop: "Online store starter with auth, RBAC, S3 uploads, products/orders/cart",
|
|
19
18
|
};
|
|
20
19
|
|
package/src/cli/feat.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { recordFeature, readManifest } from "./manifest.ts";
|
|
|
16
16
|
// ─── bun x bosia@latest feat <feature> [--local] ─────────
|
|
17
17
|
// Fetches a feature scaffold from the GitHub registry (or local
|
|
18
18
|
// registry with --local) and copies route/lib files, installs npm deps.
|
|
19
|
-
// Supports nested feature dependencies (e.g.
|
|
19
|
+
// Supports nested feature dependencies (e.g. shop → auth).
|
|
20
20
|
|
|
21
21
|
type FileStrategy =
|
|
22
22
|
| "write" // overwrite (prompt if interactive)
|
package/src/cli/index.ts
CHANGED
|
@@ -126,7 +126,7 @@ Commands:
|
|
|
126
126
|
|
|
127
127
|
Examples:
|
|
128
128
|
bun x bosia@latest create my-app
|
|
129
|
-
bun x bosia@latest create my-app --template
|
|
129
|
+
bun x bosia@latest create my-app --template shop
|
|
130
130
|
bun x bosia dev
|
|
131
131
|
bun x bosia build
|
|
132
132
|
bun x bosia start
|
|
@@ -137,8 +137,8 @@ Examples:
|
|
|
137
137
|
bun x bosia@latest add button card input → install multiple at once
|
|
138
138
|
bun x bosia@latest add -y button card → auto-confirm overwrites (CI / scripts)
|
|
139
139
|
bun x bosia@latest add shop/cart → src/lib/components/shop/cart/
|
|
140
|
-
bun x bosia@latest add block cards/feature
|
|
141
|
-
bun x bosia@latest add blocks/cards/feature
|
|
140
|
+
bun x bosia@latest add block cards/feature
|
|
141
|
+
bun x bosia@latest add blocks/cards/feature (alias for: add block cards/feature)
|
|
142
142
|
bun x bosia@latest add theme editorial
|
|
143
143
|
bun x bosia@latest add font "Fredoka" "https://fonts.googleapis.com/css2?family=Fredoka:wght@400;700&display=swap"
|
|
144
144
|
bun x bosia@latest feat login
|
package/src/core/dev.ts
CHANGED
|
@@ -42,7 +42,27 @@ const BACKOFF_SCHEDULE_MS = [500, 1_000, 2_000, 4_000, 5_000];
|
|
|
42
42
|
|
|
43
43
|
// ─── SSE Broadcast ────────────────────────────────────────
|
|
44
44
|
|
|
45
|
+
// Reload-hold control (driven by titoko via /__bosia/hold + /__bosia/resume).
|
|
46
|
+
// While held, rebuilds keep happening (latest code stays ready) but the reload
|
|
47
|
+
// broadcast is suppressed; on resume a single reload is flushed if any rebuild
|
|
48
|
+
// fired meanwhile. Defaults to off, so a plain `bosia dev` developer never sees
|
|
49
|
+
// any behaviour change.
|
|
50
|
+
let reloadHeld = false;
|
|
51
|
+
let reloadQueuedWhileHeld = false;
|
|
52
|
+
let holdSafetyTimer: ReturnType<typeof setTimeout> | null = null;
|
|
53
|
+
// Safety net for a *dead* orchestrator only — NOT a task duration cap. While an
|
|
54
|
+
// agent run is healthy the orchestrator heartbeats `/__bosia/hold` (re-arming
|
|
55
|
+
// this timer), so it never fires mid-task no matter how long the task runs. It
|
|
56
|
+
// fires only if the heartbeats stop (titoko crash, network partition), so a
|
|
57
|
+
// missed resume can't freeze the preview forever. Must comfortably exceed the
|
|
58
|
+
// heartbeat interval so one dropped ping doesn't trip it.
|
|
59
|
+
const HOLD_SAFETY_MS = 90_000;
|
|
60
|
+
|
|
45
61
|
function broadcastReload() {
|
|
62
|
+
if (reloadHeld) {
|
|
63
|
+
reloadQueuedWhileHeld = true;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
46
66
|
const msg = new TextEncoder().encode("event: reload\ndata: ok\n\n");
|
|
47
67
|
for (const ctrl of sseClients) {
|
|
48
68
|
try {
|
|
@@ -56,6 +76,41 @@ function broadcastReload() {
|
|
|
56
76
|
}
|
|
57
77
|
}
|
|
58
78
|
|
|
79
|
+
// 503 body served while the inner app server is mid-restart (rebuild after an
|
|
80
|
+
// edit). Must be HTML carrying the SAME SSE reload client as the dev-500 page —
|
|
81
|
+
// the bare text/plain version had no live `/__bosia/sse` connection, so once an
|
|
82
|
+
// iframe landed here (e.g. a reload racing into a rebuild window) it stayed stuck
|
|
83
|
+
// until a manual reload. With the client, the next `broadcastReload()` once the
|
|
84
|
+
// app binds reloads this page automatically. Keep the literal phrase
|
|
85
|
+
// "App server is starting" in the body: titoko's proxy retry matches on it.
|
|
86
|
+
const STARTING_PAGE = `<!doctype html>
|
|
87
|
+
<html lang="en">
|
|
88
|
+
<head>
|
|
89
|
+
<meta charset="utf-8" />
|
|
90
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
91
|
+
<title>Starting…</title>
|
|
92
|
+
<style>
|
|
93
|
+
html,body{margin:0;padding:0;height:100%;background:#0a0a0a;color:#e5e5e5;font:14px/1.5 ui-sans-serif,system-ui,-apple-system,sans-serif}
|
|
94
|
+
.wrap{min-height:100%;display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box}
|
|
95
|
+
.dot{display:inline-block;width:10px;height:10px;background:#16a34a;border-radius:50%;margin-right:8px;vertical-align:middle;animation:p 1.4s ease-in-out infinite}
|
|
96
|
+
@keyframes p{0%,100%{opacity:1}50%{opacity:.3}}
|
|
97
|
+
h1{font-size:16px;font-weight:600;margin:0}
|
|
98
|
+
</style>
|
|
99
|
+
</head>
|
|
100
|
+
<body>
|
|
101
|
+
<div class="wrap"><h1><span class="dot"></span>App server is starting…</h1></div>
|
|
102
|
+
<script>
|
|
103
|
+
!function r(){
|
|
104
|
+
try{
|
|
105
|
+
var e=new EventSource("/__bosia/sse");
|
|
106
|
+
e.addEventListener("reload",function(){location.reload()});
|
|
107
|
+
e.onerror=function(){e.close();setTimeout(r,2000)};
|
|
108
|
+
}catch(_){setTimeout(r,2000)}
|
|
109
|
+
}();
|
|
110
|
+
</script>
|
|
111
|
+
</body>
|
|
112
|
+
</html>`;
|
|
113
|
+
|
|
59
114
|
// ─── Build ────────────────────────────────────────────────
|
|
60
115
|
|
|
61
116
|
const BUILD_SCRIPT = join(import.meta.dir, "build.ts");
|
|
@@ -235,6 +290,47 @@ const devServer = Bun.serve({
|
|
|
235
290
|
);
|
|
236
291
|
}
|
|
237
292
|
|
|
293
|
+
// Reload-hold control — host orchestrator (titoko) brackets an AI agent run
|
|
294
|
+
// so the preview reloads once when the agent finishes, not once per file
|
|
295
|
+
// edit. Both routes are idempotent and return small JSON.
|
|
296
|
+
//
|
|
297
|
+
// POST /__bosia/hold doubles as the heartbeat: the FIRST hold opens a fresh
|
|
298
|
+
// window (clears any stale queued reload); subsequent holds only re-arm the
|
|
299
|
+
// safety timer and MUST preserve `reloadQueuedWhileHeld`, or a heartbeat
|
|
300
|
+
// landing after a suppressed rebuild would drop the pending reload and
|
|
301
|
+
// resume would flush nothing.
|
|
302
|
+
if (url.pathname === "/__bosia/hold" && req.method === "POST") {
|
|
303
|
+
if (!reloadHeld) {
|
|
304
|
+
reloadHeld = true;
|
|
305
|
+
reloadQueuedWhileHeld = false;
|
|
306
|
+
}
|
|
307
|
+
if (holdSafetyTimer) clearTimeout(holdSafetyTimer);
|
|
308
|
+
holdSafetyTimer = setTimeout(() => {
|
|
309
|
+
holdSafetyTimer = null;
|
|
310
|
+
if (!reloadHeld) return;
|
|
311
|
+
console.warn("⏱️ Reload hold safety timeout — auto-resuming");
|
|
312
|
+
reloadHeld = false;
|
|
313
|
+
if (reloadQueuedWhileHeld) {
|
|
314
|
+
reloadQueuedWhileHeld = false;
|
|
315
|
+
broadcastReload();
|
|
316
|
+
}
|
|
317
|
+
}, HOLD_SAFETY_MS);
|
|
318
|
+
holdSafetyTimer.unref?.();
|
|
319
|
+
return Response.json({ ok: true, held: true });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (url.pathname === "/__bosia/resume" && req.method === "POST") {
|
|
323
|
+
if (holdSafetyTimer) {
|
|
324
|
+
clearTimeout(holdSafetyTimer);
|
|
325
|
+
holdSafetyTimer = null;
|
|
326
|
+
}
|
|
327
|
+
reloadHeld = false;
|
|
328
|
+
const flushed = reloadQueuedWhileHeld;
|
|
329
|
+
reloadQueuedWhileHeld = false;
|
|
330
|
+
if (flushed) broadcastReload();
|
|
331
|
+
return Response.json({ ok: true, held: false, flushed });
|
|
332
|
+
}
|
|
333
|
+
|
|
238
334
|
// Proxy everything else to the app server. Inject X-Forwarded-Host/Proto so
|
|
239
335
|
// the app's CSRF origin check (gated behind TRUST_PROXY=true, also set in the
|
|
240
336
|
// app env above) reconstructs the public-facing origin from the dev proxy
|
|
@@ -283,9 +379,9 @@ const devServer = Bun.serve({
|
|
|
283
379
|
await Bun.sleep(250);
|
|
284
380
|
continue;
|
|
285
381
|
}
|
|
286
|
-
return new Response(
|
|
382
|
+
return new Response(STARTING_PAGE, {
|
|
287
383
|
status: 503,
|
|
288
|
-
headers: { "Content-Type": "text/
|
|
384
|
+
headers: { "Content-Type": "text/html; charset=utf-8", "Retry-After": "1" },
|
|
289
385
|
});
|
|
290
386
|
}
|
|
291
387
|
}
|
package/src/core/html.ts
CHANGED
|
@@ -19,6 +19,12 @@ export const distManifest: { js: string[]; css: string[]; entry: string } = (()
|
|
|
19
19
|
export const isDev = process.env.NODE_ENV !== "production";
|
|
20
20
|
const cacheBust = isDev ? `?v=${Date.now()}` : "";
|
|
21
21
|
|
|
22
|
+
/** Inline theme bootstrap — runs before paint to avoid FOUC. theme ∈ light|dark|system (missing = system). */
|
|
23
|
+
const THEME_INIT_JS =
|
|
24
|
+
"try{var t=localStorage.getItem('theme');" +
|
|
25
|
+
"document.documentElement.classList.toggle('dark'," +
|
|
26
|
+
"t==='dark'||((t===null||t==='system')&&window.matchMedia('(prefers-color-scheme: dark)').matches))}catch(_){}";
|
|
27
|
+
|
|
22
28
|
// ─── Safe JSON Serialization ──────────────────────────────
|
|
23
29
|
|
|
24
30
|
/** Escapes JSON for safe embedding inside <script> tags. Prevents XSS via </script> injection. */
|
|
@@ -156,7 +162,7 @@ export function buildHtml(
|
|
|
156
162
|
headOpenInterpolated +
|
|
157
163
|
`\n ${faviconLine}${cssLinks}\n` +
|
|
158
164
|
` <link rel="stylesheet" href="/bosia-tw.css${cacheBust}">\n` +
|
|
159
|
-
` <script${n}
|
|
165
|
+
` <script${n}>${THEME_INIT_JS}</script>\n` +
|
|
160
166
|
` ${fallbackTitle}${head}` +
|
|
161
167
|
headCloseInterpolated +
|
|
162
168
|
(body ? "" : `\n${SPINNER}`) +
|
|
@@ -175,7 +181,7 @@ export function buildHtml(
|
|
|
175
181
|
${head}
|
|
176
182
|
${cssLinks}
|
|
177
183
|
<link rel="stylesheet" href="/bosia-tw.css${cacheBust}">
|
|
178
|
-
<script${n}
|
|
184
|
+
<script${n}>${THEME_INIT_JS}</script>
|
|
179
185
|
</head>
|
|
180
186
|
<body>
|
|
181
187
|
<div id="app">${body}</div>${scripts}${bodyEnd}
|
|
@@ -208,7 +214,7 @@ export function buildHtmlShellOpen(
|
|
|
208
214
|
headOpenInterpolated +
|
|
209
215
|
`\n ${faviconLine}${cssLinks}\n` +
|
|
210
216
|
` <link rel="stylesheet" href="/bosia-tw.css${cacheBust}">\n` +
|
|
211
|
-
` <script${n}
|
|
217
|
+
` <script${n}>${THEME_INIT_JS}</script>\n` +
|
|
212
218
|
` <link rel="modulepreload" href="/dist/client/${distManifest.entry}${cacheBust}">`
|
|
213
219
|
);
|
|
214
220
|
}
|
|
@@ -220,7 +226,7 @@ export function buildHtmlShellOpen(
|
|
|
220
226
|
` <link rel="icon" type="image/svg+xml" href="/favicon.svg">\n` +
|
|
221
227
|
` ${cssLinks}\n` +
|
|
222
228
|
` <link rel="stylesheet" href="/bosia-tw.css${cacheBust}">\n` +
|
|
223
|
-
` <script${n}
|
|
229
|
+
` <script${n}>${THEME_INIT_JS}</script>\n` +
|
|
224
230
|
` <link rel="modulepreload" href="/dist/client/${distManifest.entry}${cacheBust}">`
|
|
225
231
|
);
|
|
226
232
|
}
|
package/templates/todo/README.md
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# {{PROJECT_NAME}}
|
|
2
|
-
|
|
3
|
-
A fullstack app built with [Bosia](https://github.com/bosapi/bosia) + Drizzle ORM + PostgreSQL.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
|
|
7
|
-
- [Bun](https://bun.sh/) v1.1+
|
|
8
|
-
- [PostgreSQL](https://www.postgresql.org/) running locally or remotely
|
|
9
|
-
|
|
10
|
-
## Getting Started
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
# Copy env and set your DATABASE_URL
|
|
14
|
-
cp .env.example .env
|
|
15
|
-
|
|
16
|
-
# Push schema to database
|
|
17
|
-
bun run db:push
|
|
18
|
-
|
|
19
|
-
# Seed initial data
|
|
20
|
-
bun run db:seed
|
|
21
|
-
|
|
22
|
-
# Start dev server
|
|
23
|
-
bun x bosia dev
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Visit [http://localhost:9000](http://localhost:9000) to see the app.
|
|
27
|
-
|
|
28
|
-
## Scripts
|
|
29
|
-
|
|
30
|
-
| Command | Description |
|
|
31
|
-
| --------------------- | -------------------------------------- |
|
|
32
|
-
| `bun x bosia dev` | Start dev server with HMR |
|
|
33
|
-
| `bun x bosia build` | Production build |
|
|
34
|
-
| `bun x bosia start` | Start production server |
|
|
35
|
-
| `bun run db:generate` | Generate migration from schema changes |
|
|
36
|
-
| `bun run db:migrate` | Apply pending migrations |
|
|
37
|
-
| `bun run db:push` | Push schema directly (dev shortcut) |
|
|
38
|
-
| `bun run db:studio` | Open Drizzle Studio GUI |
|
|
39
|
-
| `bun run db:seed` | Run pending seed files |
|
|
40
|
-
|
|
41
|
-
## Project Structure
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
src/
|
|
45
|
-
├── features/
|
|
46
|
-
│ ├── drizzle/ # DB infrastructure
|
|
47
|
-
│ │ ├── index.ts # Connection singleton
|
|
48
|
-
│ │ ├── schemas.ts # Schema aggregator
|
|
49
|
-
│ │ ├── migrations/ # Drizzle Kit output
|
|
50
|
-
│ │ └── seeds/ # Seed files + runner
|
|
51
|
-
│ └── todo/ # Business feature
|
|
52
|
-
│ ├── schemas/ # Table definitions
|
|
53
|
-
│ ├── queries.ts # Typed CRUD
|
|
54
|
-
│ └── types.ts # Inferred types
|
|
55
|
-
├── lib/components/todo/ # UI components
|
|
56
|
-
└── routes/
|
|
57
|
-
├── todos/ # CRUD page with form actions
|
|
58
|
-
└── api/todos/ # REST API
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Adding Features
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
# Add DB support to any Bosia app
|
|
65
|
-
bosia feat drizzle
|
|
66
|
-
|
|
67
|
-
# Add the todo feature (requires drizzle)
|
|
68
|
-
bosia feat todo
|
|
69
|
-
```
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "bosia";
|
|
2
|
-
import { inspector } from "bosia/plugins/inspector";
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
plugins: [
|
|
6
|
-
// Dev-only: Alt+click any element on the page to open its source in your editor.
|
|
7
|
-
// Change `editor` to "cursor" or "zed" if you don't use VS Code.
|
|
8
|
-
inspector({ editor: "code" }),
|
|
9
|
-
],
|
|
10
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
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
|
-
"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
|
-
}
|
|
24
|
-
}
|
|
File without changes
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<!-- Top block -->
|
|
3
|
-
<rect fill="currentColor" x="50" y="50" width="28" height="28" rx="6"/>
|
|
4
|
-
<rect fill="currentColor" x="86" y="50" width="60" height="28" rx="6"/>
|
|
5
|
-
|
|
6
|
-
<!-- Middle block -->
|
|
7
|
-
<rect fill="currentColor" x="86" y="86" width="72" height="28" rx="6"/>
|
|
8
|
-
|
|
9
|
-
<!-- Bottom block -->
|
|
10
|
-
<rect fill="currentColor" x="86" y="122" width="60" height="28" rx="6"/>
|
|
11
|
-
|
|
12
|
-
<!-- Connector bar on left -->
|
|
13
|
-
<rect fill="currentColor" x="50" y="50" width="28" height="100" rx="6"/>
|
|
14
|
-
</svg>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<!-- Top block -->
|
|
3
|
-
<rect fill="#f0f0f0" x="50" y="50" width="28" height="28" rx="6"/>
|
|
4
|
-
<rect fill="#f0f0f0" x="86" y="50" width="60" height="28" rx="6"/>
|
|
5
|
-
|
|
6
|
-
<!-- Middle block -->
|
|
7
|
-
<rect fill="#f0f0f0" x="86" y="86" width="72" height="28" rx="6"/>
|
|
8
|
-
|
|
9
|
-
<!-- Bottom block -->
|
|
10
|
-
<rect fill="#f0f0f0" x="86" y="122" width="60" height="28" rx="6"/>
|
|
11
|
-
|
|
12
|
-
<!-- Connector bar on left -->
|
|
13
|
-
<rect fill="#f0f0f0" x="50" y="50" width="28" height="100" rx="6"/>
|
|
14
|
-
</svg>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<!-- Top block -->
|
|
3
|
-
<rect fill="#1a1a1a" x="50" y="50" width="28" height="28" rx="6"/>
|
|
4
|
-
<rect fill="#1a1a1a" x="86" y="50" width="60" height="28" rx="6"/>
|
|
5
|
-
|
|
6
|
-
<!-- Middle block -->
|
|
7
|
-
<rect fill="#1a1a1a" x="86" y="86" width="72" height="28" rx="6"/>
|
|
8
|
-
|
|
9
|
-
<!-- Bottom block -->
|
|
10
|
-
<rect fill="#1a1a1a" x="86" y="122" width="60" height="28" rx="6"/>
|
|
11
|
-
|
|
12
|
-
<!-- Connector bar on left -->
|
|
13
|
-
<rect fill="#1a1a1a" x="50" y="50" width="28" height="100" rx="6"/>
|
|
14
|
-
</svg>
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
@import "tailwindcss";
|
|
2
|
-
@source "../src";
|
|
3
|
-
|
|
4
|
-
@custom-variant dark (&:where(.dark, .dark *));
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
* ─── shadcn-inspired Design Tokens ──────────────────────
|
|
8
|
-
* CSS custom properties for light & dark themes.
|
|
9
|
-
* Uses HSL values so Tailwind can apply opacity modifiers.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
@theme {
|
|
13
|
-
--color-background: hsl(var(--background));
|
|
14
|
-
--color-foreground: hsl(var(--foreground));
|
|
15
|
-
|
|
16
|
-
--color-card: hsl(var(--card));
|
|
17
|
-
--color-card-foreground: hsl(var(--card-foreground));
|
|
18
|
-
|
|
19
|
-
--color-popover: hsl(var(--popover));
|
|
20
|
-
--color-popover-foreground: hsl(var(--popover-foreground));
|
|
21
|
-
|
|
22
|
-
--color-primary: hsl(var(--primary));
|
|
23
|
-
--color-primary-foreground: hsl(var(--primary-foreground));
|
|
24
|
-
|
|
25
|
-
--color-secondary: hsl(var(--secondary));
|
|
26
|
-
--color-secondary-foreground: hsl(var(--secondary-foreground));
|
|
27
|
-
|
|
28
|
-
--color-muted: hsl(var(--muted));
|
|
29
|
-
--color-muted-foreground: hsl(var(--muted-foreground));
|
|
30
|
-
|
|
31
|
-
--color-accent: hsl(var(--accent));
|
|
32
|
-
--color-accent-foreground: hsl(var(--accent-foreground));
|
|
33
|
-
|
|
34
|
-
--color-destructive: hsl(var(--destructive));
|
|
35
|
-
--color-destructive-foreground: hsl(var(--destructive-foreground));
|
|
36
|
-
|
|
37
|
-
--color-border: hsl(var(--border));
|
|
38
|
-
--color-input: hsl(var(--input));
|
|
39
|
-
--color-ring: hsl(var(--ring));
|
|
40
|
-
|
|
41
|
-
--radius-sm: calc(var(--radius) - 4px);
|
|
42
|
-
--radius-md: calc(var(--radius) - 2px);
|
|
43
|
-
--radius-lg: var(--radius);
|
|
44
|
-
--radius-xl: calc(var(--radius) + 4px);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/* ─── Light Theme (Default) ─────────────────────────────── */
|
|
48
|
-
|
|
49
|
-
:root {
|
|
50
|
-
--background: 0 0% 100%;
|
|
51
|
-
--foreground: 222.2 84% 4.9%;
|
|
52
|
-
|
|
53
|
-
--card: 0 0% 100%;
|
|
54
|
-
--card-foreground: 222.2 84% 4.9%;
|
|
55
|
-
|
|
56
|
-
--popover: 0 0% 100%;
|
|
57
|
-
--popover-foreground: 222.2 84% 4.9%;
|
|
58
|
-
|
|
59
|
-
--primary: 222.2 47.4% 11.2%;
|
|
60
|
-
--primary-foreground: 210 40% 98%;
|
|
61
|
-
|
|
62
|
-
--secondary: 210 40% 96.1%;
|
|
63
|
-
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
64
|
-
|
|
65
|
-
--muted: 210 40% 96.1%;
|
|
66
|
-
--muted-foreground: 215.4 16.3% 46.9%;
|
|
67
|
-
|
|
68
|
-
--accent: 210 40% 96.1%;
|
|
69
|
-
--accent-foreground: 222.2 47.4% 11.2%;
|
|
70
|
-
|
|
71
|
-
--destructive: 0 84.2% 60.2%;
|
|
72
|
-
--destructive-foreground: 210 40% 98%;
|
|
73
|
-
|
|
74
|
-
--border: 214.3 31.8% 91.4%;
|
|
75
|
-
--input: 214.3 31.8% 91.4%;
|
|
76
|
-
--ring: 222.2 84% 4.9%;
|
|
77
|
-
|
|
78
|
-
--radius: 0.5rem;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/* ─── Dark Theme ─────────────────────────────────────────── */
|
|
82
|
-
|
|
83
|
-
.dark {
|
|
84
|
-
--background: 222.2 84% 4.9%;
|
|
85
|
-
--foreground: 210 40% 98%;
|
|
86
|
-
|
|
87
|
-
--card: 222.2 84% 4.9%;
|
|
88
|
-
--card-foreground: 210 40% 98%;
|
|
89
|
-
|
|
90
|
-
--popover: 222.2 84% 4.9%;
|
|
91
|
-
--popover-foreground: 210 40% 98%;
|
|
92
|
-
|
|
93
|
-
--primary: 210 40% 98%;
|
|
94
|
-
--primary-foreground: 222.2 47.4% 11.2%;
|
|
95
|
-
|
|
96
|
-
--secondary: 217.2 32.6% 17.5%;
|
|
97
|
-
--secondary-foreground: 210 40% 98%;
|
|
98
|
-
|
|
99
|
-
--muted: 217.2 32.6% 17.5%;
|
|
100
|
-
--muted-foreground: 215 20.2% 65.1%;
|
|
101
|
-
|
|
102
|
-
--accent: 217.2 32.6% 17.5%;
|
|
103
|
-
--accent-foreground: 210 40% 98%;
|
|
104
|
-
|
|
105
|
-
--destructive: 0 62.8% 30.6%;
|
|
106
|
-
--destructive-foreground: 210 40% 98%;
|
|
107
|
-
|
|
108
|
-
--border: 217.2 32.6% 17.5%;
|
|
109
|
-
--input: 217.2 32.6% 17.5%;
|
|
110
|
-
--ring: 212.7 26.8% 83.9%;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/* ─── Base Styles ────────────────────────────────────────── */
|
|
114
|
-
|
|
115
|
-
@layer base {
|
|
116
|
-
* {
|
|
117
|
-
border-color: theme(--color-border);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
body {
|
|
121
|
-
background-color: theme(--color-background);
|
|
122
|
-
color: theme(--color-foreground);
|
|
123
|
-
font-family:
|
|
124
|
-
"Inter",
|
|
125
|
-
system-ui,
|
|
126
|
-
-apple-system,
|
|
127
|
-
BlinkMacSystemFont,
|
|
128
|
-
"Segoe UI",
|
|
129
|
-
Roboto,
|
|
130
|
-
"Helvetica Neue",
|
|
131
|
-
Arial,
|
|
132
|
-
sans-serif;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/// <reference types="svelte" />
|
|
2
|
-
|
|
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;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
declare namespace App {
|
|
10
|
-
interface Locals {
|
|
11
|
-
db: import("./features/drizzle").Database;
|
|
12
|
-
requestTime: number;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { sequence } from "bosia";
|
|
2
|
-
import type { Handle } from "bosia";
|
|
3
|
-
import { db } from "./features/drizzle";
|
|
4
|
-
|
|
5
|
-
const dbHandle: Handle = async ({ event, resolve }) => {
|
|
6
|
-
event.locals.db = db;
|
|
7
|
-
return resolve(event);
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const loggingHandle: Handle = async ({ event, resolve }) => {
|
|
11
|
-
const start = Date.now();
|
|
12
|
-
event.locals.requestTime = start;
|
|
13
|
-
const res = await resolve(event);
|
|
14
|
-
const ms = Date.now() - start;
|
|
15
|
-
console.log(`[${event.request.method}] ${event.url.pathname} ${res.status} (${ms}ms)`);
|
|
16
|
-
res.headers.set("X-Response-Time", `${ms}ms`);
|
|
17
|
-
return res;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const handle = sequence(dbHandle, loggingHandle);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { cn } from "bosia";
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { LayoutData } from "./$types";
|
|
3
|
-
let { data }: { data: LayoutData } = $props();
|
|
4
|
-
</script>
|
|
5
|
-
|
|
6
|
-
<svelte:head>
|
|
7
|
-
<title>{data.appName}</title>
|
|
8
|
-
</svelte:head>
|
|
9
|
-
|
|
10
|
-
<div class="flex min-h-screen flex-col bg-background text-foreground">
|
|
11
|
-
<div class="flex flex-1 flex-col items-center justify-center gap-8 px-4">
|
|
12
|
-
<div class="text-center">
|
|
13
|
-
<img src="/favicon.svg" alt="" class="mx-auto mb-6 size-16" />
|
|
14
|
-
<h1 class="text-4xl font-bold tracking-tight">{data.appName}</h1>
|
|
15
|
-
<p class="mt-2 text-lg text-muted-foreground">
|
|
16
|
-
Fullstack app powered by Bosia + Drizzle ORM + PostgreSQL
|
|
17
|
-
</p>
|
|
18
|
-
</div>
|
|
19
|
-
|
|
20
|
-
<div class="flex gap-4">
|
|
21
|
-
<a
|
|
22
|
-
href="/todos"
|
|
23
|
-
class="inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
|
|
24
|
-
>
|
|
25
|
-
Todo App
|
|
26
|
-
</a>
|
|
27
|
-
<a
|
|
28
|
-
href="/api/todos"
|
|
29
|
-
target="_blank"
|
|
30
|
-
class="inline-flex items-center justify-center rounded-md border border-input bg-background px-6 py-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-colors"
|
|
31
|
-
>
|
|
32
|
-
REST API
|
|
33
|
-
</a>
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
<div class="mt-8 grid max-w-2xl grid-cols-1 gap-4 sm:grid-cols-3">
|
|
37
|
-
<div class="rounded-lg border border-border p-4 text-center">
|
|
38
|
-
<p class="text-sm font-medium">Bosia</p>
|
|
39
|
-
<p class="mt-1 text-xs text-muted-foreground">Bun + Svelte 5 + Elysia</p>
|
|
40
|
-
</div>
|
|
41
|
-
<div class="rounded-lg border border-border p-4 text-center">
|
|
42
|
-
<p class="text-sm font-medium">Drizzle ORM</p>
|
|
43
|
-
<p class="mt-1 text-xs text-muted-foreground">Type-safe SQL queries</p>
|
|
44
|
-
</div>
|
|
45
|
-
<div class="rounded-lg border border-border p-4 text-center">
|
|
46
|
-
<p class="text-sm font-medium">PostgreSQL</p>
|
|
47
|
-
<p class="mt-1 text-xs text-muted-foreground">Production-ready database</p>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<footer class="border-t py-4 text-center text-sm text-muted-foreground">Powered by Bosia</footer>
|
|
53
|
-
</div>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { ErrorProps } from "./$types";
|
|
3
|
-
let { error }: ErrorProps = $props();
|
|
4
|
-
</script>
|
|
5
|
-
|
|
6
|
-
<svelte:head>
|
|
7
|
-
<title>{error.status} — {error.message}</title>
|
|
8
|
-
</svelte:head>
|
|
9
|
-
|
|
10
|
-
<div class="min-h-screen flex flex-col items-center justify-center gap-4 text-center px-4">
|
|
11
|
-
<p class="text-8xl font-bold text-gray-200">{error.status}</p>
|
|
12
|
-
<p class="text-2xl font-semibold text-gray-700">{error.message}</p>
|
|
13
|
-
<a
|
|
14
|
-
href="/"
|
|
15
|
-
class="mt-4 px-5 py-2 rounded-lg bg-gray-900 text-white text-sm hover:bg-gray-700 transition-colors"
|
|
16
|
-
>
|
|
17
|
-
Go home
|
|
18
|
-
</a>
|
|
19
|
-
</div>
|
|
@@ -1,22 +0,0 @@
|
|
|
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"]
|
|
22
|
-
}
|