···1+# WebAuthn/Passkey Configuration
2+# In development, these default to localhost values
3+# Only needed when deploying to production
4+5+# Relying Party ID - your domain name
6+# Must match the domain where your app is hosted
7+# RP_ID=tacy-stack.app
8+9+# Origin - full URL of your app
10+# Must match exactly where users access your app
11+ORIGIN=http://localhost:3000
12+13+# Environment (set to 'production' in production)
14+NODE_ENV=development
+26
.gitignore
···00000000000000000000000000
···1+# Bun
2+node_modules/
3+bun.lockb
4+5+# Database
6+*.db
7+*.db-shm
8+*.db-wal
9+drizzle/
10+11+# Environment
12+.env
13+.env.local
14+15+# IDE
16+.vscode/
17+.idea/
18+*.swp
19+*.swo
20+21+# OS
22+.DS_Store
23+Thumbs.db
24+25+# Logs
26+*.log
···1+# Tacy Stack - Agent Guidelines
2+3+This is a minimal full-stack web application starter built on the Bun fullstack pattern. It demonstrates passkey authentication, user-specific data storage, and reactive web components.
4+5+## Project Overview
6+7+**What is Tacy Stack?**
8+A TypeScript-based web stack using:
9+- **Bun** - Fast JavaScript runtime and bundler
10+- **TypeScript** - Strict typing with decorators enabled
11+- **Lit** - Lightweight (~8-10KB) web components library
12+- **Drizzle ORM** - Type-safe SQLite database access
13+- **Passkeys (WebAuthn)** - Passwordless authentication
14+15+**Demo Application Features:**
16+- User registration and login via passkeys (no passwords!)
17+- User-specific click counter stored in database
18+- Reactive UI with Lit web components
19+- Session management with cookies
20+21+## Commands
22+23+```bash
24+# Install dependencies
25+bun install
26+27+# Development server with hot reload (default: localhost:3000)
28+bun dev
29+30+# Database management
31+bun run db:generate # Generate migration files from schema changes
32+bun run db:push # Push schema directly to database (for development)
33+bun run db:studio # Open Drizzle Studio (visual database browser)
34+35+# Testing
36+bun test # Run all tests
37+```
38+39+**IMPORTANT:** Never run `bun dev` yourself - the user always has it running already with HMR enabled.
40+41+## Tech Stack Philosophy
42+43+### NO FRAMEWORKS
44+45+**Explicitly forbidden:**
46+- React, React DOM
47+- Vue
48+- Svelte
49+- Angular
50+- Any framework with virtual DOM or large runtime
51+52+**Why?** This project prioritizes:
53+- **Speed:** Minimal JavaScript, fast load times
54+- **Small bundles:** Keep total JS under 50KB
55+- **Native web platform:** Web standards over framework abstractions
56+- **Simplicity:** Vanilla HTML, CSS, and TypeScript
57+58+**Allowed:**
59+- Lit (~8-10KB) for reactive web components
60+- Native Web Components API
61+- Plain JavaScript/TypeScript
62+- Native DOM APIs
63+64+### When to use Lit
65+66+**Use Lit for:**
67+- Components with reactive properties (auto-updates on data changes)
68+- Complex components needing scoped styles
69+- Form controls with internal state
70+- Components with lifecycle needs
71+72+**Skip Lit for:**
73+- Static content (use plain HTML)
74+- Simple one-off interactions (use vanilla JS)
75+- Anything without reactive state
76+77+## Project Structure
78+79+Based on Bun's fullstack pattern where HTML files are imported as modules:
80+81+```
82+src/
83+ index.ts # Server entry point, imports HTML routes
84+ db/
85+ db.ts # Drizzle database instance
86+ schema.ts # Database schema (Drizzle tables)
87+ lib/
88+ auth.ts # User/session management
89+ passkey.ts # WebAuthn passkey logic
90+ counter.ts # Counter business logic
91+ middleware.ts # Auth middleware
92+ client-passkey.ts # Client-side passkey helpers
93+ pages/
94+ index.html # Route entry point (imports components)
95+ components/
96+ auth.ts # Login/register Lit component
97+ counter.ts # Counter Lit component
98+ styles/
99+ main.css # Global styles
100+public/ # Static assets (if needed)
101+```
102+103+**File flow:**
104+1. `src/index.ts` imports HTML: `import indexHTML from "./pages/index.html"`
105+2. HTML imports components: `<script type="module" src="../components/auth.ts"></script>`
106+3. HTML links styles: `<link rel="stylesheet" href="../styles/main.css">`
107+4. Components self-register via `@customElement` decorator
108+5. Bun bundles everything automatically during development
109+110+## Database (Drizzle ORM)
111+112+**Database file:** `tacy-stack.db` (SQLite, auto-created)
113+114+**Schema location:** `src/db/schema.ts`
115+116+**Tables:**
117+- `users` - User accounts (id, email, name, avatar, created_at)
118+- `sessions` - Active sessions (id, user_id, ip, user_agent, expires_at)
119+- `passkeys` - WebAuthn credentials (id, user_id, credential_id, public_key, counter, etc.)
120+- `counters` - User click counters (user_id, count, updated_at)
121+122+**Making schema changes:**
123+1. Edit `src/db/schema.ts` (modify tables using Drizzle syntax)
124+2. Run `bun run db:push` to apply changes to database
125+3. For production, use `bun run db:generate` to create migrations
126+4. Schema changes are type-safe and auto-complete in your IDE
127+128+**Querying patterns:**
129+```typescript
130+import { eq } from "drizzle-orm";
131+import db from "../db/db";
132+import { users } from "../db/schema";
133+134+// Select with where clause
135+const user = db.select().from(users).where(eq(users.id, userId)).get();
136+137+// Insert
138+db.insert(users).values({ email, name, avatar }).run();
139+140+// Update
141+db.update(users).set({ name: "New Name" }).where(eq(users.id, userId)).run();
142+143+// Delete
144+db.delete(users).where(eq(users.id, userId)).run();
145+```
146+147+**Important:** Use `.get()` for single results, `.all()` for arrays, `.run()` for mutations.
148+149+## TypeScript Configuration
150+151+**Strict mode enabled:**
152+- `strict: true`
153+- `noFallthroughCasesInSwitch: true`
154+- `noUncheckedIndexedAccess: true`
155+- `noImplicitOverride: true`
156+157+**Decorators:**
158+- `experimentalDecorators: true` (required for Lit's `@customElement`, `@property`, etc.)
159+- `useDefineForClassFields: false` (required for Lit decorators)
160+161+**Module system:**
162+- `moduleResolution: "bundler"`
163+- `module: "Preserve"`
164+- Can import `.ts` extensions directly
165+- JSX: `preserve` (NOT react-jsx - we don't use React!)
166+167+**Deliberately disabled:**
168+- `noUnusedLocals: false`
169+- `noUnusedParameters: false`
170+- `noPropertyAccessFromIndexSignature: false`
171+172+## Bun Usage
173+174+**Always use Bun instead of Node.js:**
175+- ✅ `bun <file>` NOT `node <file>` or `ts-node <file>`
176+- ✅ `bun test` NOT `jest` or `vitest`
177+- ✅ `bun build <file>` NOT `webpack` or `esbuild`
178+- ✅ `bun install` NOT `npm install`
179+- ✅ `bun run <script>` NOT `npm run <script>`
180+181+**Bun built-in APIs (prefer over npm packages):**
182+- `Bun.serve()` for HTTP server (don't use Express)
183+- `bun:sqlite` for SQLite (but we use Drizzle which wraps it)
184+- `Bun.file()` for file I/O (prefer over `node:fs`)
185+- `.env` auto-loads (no dotenv package needed)
186+187+## Server Setup (Bun.serve)
188+189+Use `Bun.serve()` with the `routes` pattern:
190+191+```typescript
192+import indexHTML from "./pages/index.html";
193+194+Bun.serve({
195+ port: 3000,
196+ routes: {
197+ "/": indexHTML, // HTML route
198+ "/api/users/:id": { // API route with params
199+ GET: (req) => {
200+ return new Response(JSON.stringify({ id: req.params.id }));
201+ },
202+ },
203+ },
204+ development: {
205+ hmr: true, // Hot module reloading
206+ console: true, // Enhanced console output
207+ },
208+});
209+```
210+211+**Route params:** Access via `req.params.paramName`
212+213+**Request helpers:**
214+- `await req.json()` - Parse JSON body
215+- `req.headers.get("cookie")` - Get header
216+- `req.url` - Full URL
217+218+## Frontend Pattern
219+220+**HTML files import TypeScript modules directly:**
221+222+```html
223+<!DOCTYPE html>
224+<html lang="en">
225+<head>
226+ <meta charset="UTF-8">
227+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
228+ <title>Page Title - Tacy Stack</title>
229+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='0.9em' font-size='90'>🥞</text></svg>">
230+ <link rel="stylesheet" href="../styles/main.css">
231+</head>
232+<body>
233+ <header>
234+ <nav>
235+ <h1>🥞 Tacy Stack</h1>
236+ <auth-component></auth-component>
237+ </nav>
238+ </header>
239+240+ <main>
241+ <h1>Page Title</h1>
242+ <counter-component count="0"></counter-component>
243+ </main>
244+245+ <script type="module" src="../components/auth.ts"></script>
246+ <script type="module" src="../components/counter.ts"></script>
247+</body>
248+</html>
249+```
250+251+**Standard HTML template parts:**
252+- Pancake emoji favicon
253+- Proper meta tags (charset, viewport)
254+- Auth component in header nav
255+- Main content area
256+- Module scripts at end of body
257+258+**No build step needed:** Bun transpiles and bundles automatically during development.
259+260+## Lit Web Components
261+262+**Basic component structure:**
263+264+```typescript
265+import { LitElement, html, css } from "lit";
266+import { customElement, property, state } from "lit/decorators.js";
267+268+@customElement("my-component")
269+export class MyComponent extends LitElement {
270+ // Public reactive properties (can be set via HTML attributes)
271+ @property({ type: String }) name = "World";
272+273+ // Private reactive state (internal only)
274+ @state() private count = 0;
275+276+ // Scoped styles using css tagged template
277+ static override styles = css`
278+ :host {
279+ display: block;
280+ padding: 1rem;
281+ }
282+ .greeting {
283+ color: var(--primary);
284+ }
285+ `;
286+287+ // Render using html tagged template
288+ override render() {
289+ return html`
290+ <div class="greeting">
291+ Hello, ${this.name}!
292+ <button @click=${() => this.count++}>
293+ Clicked ${this.count} times
294+ </button>
295+ </div>
296+ `;
297+ }
298+}
299+```
300+301+**Key Lit features:**
302+- `@customElement("tag-name")` - Register component
303+- `@property()` - Public reactive property (triggers re-render)
304+- `@state()` - Private reactive state (triggers re-render)
305+- `html` - Template literal for rendering
306+- `css` - Scoped styles (don't leak out)
307+- Event handlers: `@click=${handler}` or `@click=${() => ...}`
308+- Automatic re-rendering when properties/state change
309+310+## Design System
311+312+**Color palette (CSS variables):**
313+```css
314+:root {
315+ /* Named colors */
316+ --yale-blue: #033f63ff; /* dark blue */
317+ --stormy-teal: #28666eff; /* medium teal */
318+ --muted-teal: #7c9885ff; /* soft teal */
319+ --dry-sage: #b5b682ff; /* sage green */
320+ --soft-peach: #fedc97ff; /* warm peach */
321+322+ /* Semantic assignments */
323+ --text: var(--yale-blue);
324+ --background: var(--soft-peach);
325+ --primary: var(--stormy-teal);
326+ --secondary: var(--muted-teal);
327+ --accent: var(--dry-sage);
328+}
329+```
330+331+**CRITICAL COLOR RULES:**
332+- ❌ NEVER hardcode colors: `#4f46e5`, `white`, `red`, etc.
333+- ✅ ALWAYS use CSS variables: `var(--primary)`, `var(--accent)`, `var(--text)`, etc.
334+335+**Dimension rules:**
336+- Use `rem` for all sizes (not `px`)
337+- Base: 16px = 1rem
338+- Common values: `0.5rem`, `1rem`, `1.5rem`, `2rem`, `3rem`
339+- Max widths: `48rem` (content), `56rem` (forms/data)
340+- Spacing scale: `0.25rem`, `0.5rem`, `0.75rem`, `1rem`, `1.5rem`, `2rem`, `3rem`
341+342+## Authentication & Sessions
343+344+**Flow:**
345+1. User enters email/name and clicks "Register"
346+2. `POST /api/auth/register` creates user and session
347+3. `GET /api/auth/passkey/register/options` gets WebAuthn options
348+4. Client calls `startRegistration()` from `@simplewebauthn/browser`
349+5. `POST /api/auth/passkey/register/verify` stores passkey
350+6. Session cookie set, user logged in
351+352+**Login flow:**
353+1. User clicks "Sign In"
354+2. `GET /api/auth/passkey/authenticate/options` gets WebAuthn challenge
355+3. Client calls `startAuthentication()` from `@simplewebauthn/browser`
356+4. `POST /api/auth/passkey/authenticate/verify` verifies and creates session
357+5. Session cookie set, user logged in
358+359+**Session management:**
360+- Sessions stored in `sessions` table
361+- Cookie name: `session`
362+- Duration: 7 days
363+- HTTPOnly, SameSite=Strict
364+365+**Auth helpers:**
366+- `getSessionFromRequest(req)` - Extract session ID from cookie
367+- `getUserBySession(sessionId)` - Get user from session
368+- `requireAuth(req)` - Middleware that throws if not authenticated
369+370+## API Response Patterns
371+372+**Success:**
373+```typescript
374+return new Response(JSON.stringify({ count: 42 }), {
375+ headers: { "Content-Type": "application/json" },
376+});
377+```
378+379+**Error:**
380+```typescript
381+return new Response(JSON.stringify({ error: "Not authenticated" }), {
382+ status: 401,
383+});
384+```
385+386+**With cookie:**
387+```typescript
388+return new Response(JSON.stringify(user), {
389+ headers: {
390+ "Content-Type": "application/json",
391+ "Set-Cookie": `session=${sessionId}; Path=/; HttpOnly; SameSite=Strict; Max-Age=604800`,
392+ },
393+});
394+```
395+396+## Code Conventions
397+398+**Naming:**
399+- PascalCase: Components, classes (`AuthComponent`, `CounterComponent`)
400+- camelCase: Functions, variables (`getUserByEmail`, `sessionId`)
401+- kebab-case: File names, custom element tags (`auth.ts`, `counter-component`)
402+403+**File organization:**
404+- Place tests next to code: `foo.ts` → `foo.test.ts`
405+- Keep related code together (e.g., all auth logic in `lib/auth.ts`)
406+- Components are self-contained (logic + styles + template)
407+408+**Before writing code:**
409+1. Check if library exists (look at imports, package.json)
410+2. Read similar code for patterns
411+3. Match existing style
412+4. Never assume libraries are available - verify first
413+414+## Testing
415+416+Use `bun test` with Bun's built-in test runner:
417+418+```typescript
419+import { test, expect } from "bun:test";
420+421+test("creates user with avatar", async () => {
422+ const user = await createUser("test@example.com", "Test User");
423+ expect(user.email).toBe("test@example.com");
424+ expect(user.avatar).toBeTruthy();
425+});
426+```
427+428+**What to test:**
429+- ✅ Security-critical functions (auth, sessions, passkeys)
430+- ✅ Complex business logic (counter operations)
431+- ✅ Edge cases (empty inputs, missing data)
432+- ❌ Simple getters/setters
433+- ❌ Framework/library code
434+- ❌ One-line utilities
435+436+**Test file naming:** `*.test.ts` (auto-discovered by Bun)
437+438+## Environment Variables
439+440+Copy `.env.example` to `.env`:
441+442+```bash
443+# WebAuthn/Passkey Configuration
444+RP_ID=localhost # Your domain (localhost for dev)
445+ORIGIN=http://localhost:3000 # Full app URL
446+447+# Environment
448+NODE_ENV=development
449+```
450+451+**Production values:**
452+- `RP_ID` - Your domain (e.g., `tacy-stack.app`)
453+- `ORIGIN` - Full public URL (e.g., `https://tacy-stack.app`)
454+455+**Important:** Bun auto-loads `.env`, no dotenv package needed.
456+457+## Common Tasks
458+459+### Adding a new route
460+1. Create HTML file in `src/pages/` (e.g., `about.html`)
461+2. Import in `src/index.ts`: `import aboutHTML from "./pages/about.html"`
462+3. Add to routes: `"/about": aboutHTML`
463+464+### Adding a new API endpoint
465+Add to `routes` object in `src/index.ts`:
466+```typescript
467+"/api/my-endpoint": {
468+ GET: async (req) => {
469+ const userId = await requireAuth(req);
470+ // ... your logic
471+ return new Response(JSON.stringify({ data }));
472+ },
473+},
474+```
475+476+### Adding a new component
477+1. Create `src/components/my-component.ts`
478+2. Use `@customElement("my-component")` decorator
479+3. Import in HTML: `<script type="module" src="../components/my-component.ts"></script>`
480+4. Use in HTML: `<my-component></my-component>`
481+482+### Adding a database table
483+1. Edit `src/db/schema.ts`:
484+```typescript
485+export const myTable = sqliteTable("my_table", {
486+ id: integer("id").primaryKey({ autoIncrement: true }),
487+ name: text("name").notNull(),
488+});
489+```
490+2. Run `bun run db:push` (pushes schema to database)
491+3. Query: `db.select().from(myTable).all()`
492+493+### Adding styles
494+- Global styles: Edit `src/styles/main.css`
495+- Component-scoped: Use `static styles = css\`...\`` in Lit component
496+- Always use CSS variables for colors
497+498+## Formatting & Linting
499+500+**Biome** is configured for formatting and linting:
501+- Indent style: Tabs
502+- Quote style: Double quotes
503+- Auto-organize imports enabled
504+505+**LSP support:** Biome provides IDE integration for formatting/linting.
506+507+## Gotchas
508+509+1. **Don't use Node.js commands** - Use `bun` not `node`, `npm`, `npx`
510+2. **Don't install Express/Vite** - Bun has built-in equivalents
511+3. **NEVER use React** - Explicitly forbidden, use Lit or vanilla JS
512+4. **Import .ts extensions** - Bun allows direct `.ts` imports
513+5. **No dotenv needed** - Bun loads `.env` automatically
514+6. **HTML imports are special** - They trigger Bun's bundler
515+7. **Bundle size matters** - Measure impact before adding libraries
516+8. **Drizzle queries** - Use `.get()` for single row, `.all()` for array, `.run()` for mutations
517+9. **Decorators required** - Must enable `experimentalDecorators` for Lit
518+10. **Session from cookies** - Use `getSessionFromRequest(req)` to extract session ID
519+520+## Development Workflow
521+522+1. Make changes to `.ts`, `.html`, or `.css` files
523+2. Bun's HMR automatically reloads in browser
524+3. Write tests in `*.test.ts` files
525+4. Run `bun test` to verify
526+5. Check database with `bun run db:studio` if needed
527+528+**Never run `bun dev` yourself** - user has it running with hot reload already.
529+530+## Resources
531+532+- [Bun Fullstack Documentation](https://bun.com/docs/bundler/fullstack)
533+- [Lit Documentation](https://lit.dev/)
534+- [Drizzle ORM Documentation](https://orm.drizzle.team/)
535+- [SimpleWebAuthn Documentation](https://simplewebauthn.dev/)
536+- [Web Components MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components)
537+- Bun API docs: `node_modules/bun-types/docs/**.md`
538+539+## Key Differences from Thistle
540+541+This project is based on Thistle but simplified:
542+- ✅ **Drizzle ORM** instead of raw `bun:sqlite` queries
543+- ✅ **Simpler demo** (just counter, no transcription service)
544+- ❌ **No subscriptions** (no Polar integration)
545+- ❌ **No email** (no MailChannels, verification codes)
546+- ❌ **No admin system** (no roles, no admin panel)
547+- ❌ **No rate limiting** (simplified for demo)
548+- ❌ **No password auth** (passkeys only)
549+550+Focus is on demonstrating the core pattern: Bun + TypeScript + Lit + Drizzle + Passkeys.
+21
LICENSE
···000000000000000000000
···1+MIT License
2+3+Copyright (c) 2025 Kieran Klukas
4+5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
+92
README.md
···45It uses Lit, Bun, and Drizzle as the main stack and they all work together to make a wonderful combo.
6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007The canonical repo for this is hosted on tangled over at [`dunkirk.sh/tacy-stack`](https://tangled.org/@dunkirk.sh/tacy-stack)
89<p align="center">
···45It uses Lit, Bun, and Drizzle as the main stack and they all work together to make a wonderful combo.
67+## How do I hack on it?
8+9+### Development
10+11+Getting started is super straightforward:
12+13+```bash
14+bun install
15+cp .env.example .env
16+bun run db:push
17+bun dev
18+```
19+20+Your server will be running at `http://localhost:3000` with hot module reloading. Just edit any `.ts`, `.html`, or `.css` file and watch it update in the browser.
21+22+## How does it work?
23+24+The development flow is really nice in my opinion. The server imports HTML files as route handlers. Those HTML files import TypeScript components using `<script type="module">`. The components are just Lit web components that self-register as custom elements. Bun sees all this and bundles everything automatically including linked styles.
25+26+```typescript
27+// src/index.ts - Server imports HTML as routes
28+import indexHTML from "./pages/index.html";
29+30+Bun.serve({
31+ port: 3000,
32+ routes: {
33+ "/": indexHTML,
34+ },
35+ development: {
36+ hmr: true,
37+ console: true,
38+ },
39+});
40+```
41+42+```html
43+<!-- src/pages/index.html -->
44+<!DOCTYPE html>
45+<html lang="en">
46+ <head>
47+ <link rel="stylesheet" href="../styles/main.css" />
48+ </head>
49+ <body>
50+ <counter-component count="0"></counter-component>
51+ <script type="module" src="../components/counter.ts"></script>
52+ </body>
53+</html>
54+```
55+56+```typescript
57+// src/components/counter.ts
58+import { LitElement, html, css } from "lit";
59+import { customElement, property } from "lit/decorators.js";
60+61+@customElement("counter-component")
62+export class CounterComponent extends LitElement {
63+ @property({ type: Number }) count = 0;
64+65+ static styles = css`
66+ :host {
67+ display: block;
68+ padding: 1rem;
69+ }
70+ `;
71+72+ render() {
73+ return html`
74+ <div>${this.count}</div>
75+ <button @click=${() => this.count++}>+</button>
76+ `;
77+ }
78+}
79+```
80+81+The database uses Drizzle ORM for type-safe SQLite access. Schema changes are handled through migrations:
82+83+```bash
84+# Make changes to src/db/schema.ts, then:
85+bun run db:push # Push schema to database
86+bun run db:studio # Visual database browser
87+```
88+89+## Commands
90+91+```bash
92+bun dev # Development server with hot reload
93+bun test # Run tests
94+bun run db:generate # Generate migrations from schema
95+bun run db:push # Push schema to database
96+bun run db:studio # Open Drizzle Studio
97+```
98+99The canonical repo for this is hosted on tangled over at [`dunkirk.sh/tacy-stack`](https://tangled.org/@dunkirk.sh/tacy-stack)
100101<p align="center">
···1+import { drizzle } from "drizzle-orm/bun-sqlite";
2+import { Database } from "bun:sqlite";
3+import * as schema from "./schema";
4+5+// Use test database when NODE_ENV is test
6+const dbPath =
7+ process.env.NODE_ENV === "test" ? "tacy-stack.test.db" : "tacy-stack.db";
8+9+const sqlite = new Database(dbPath);
10+export const db = drizzle(sqlite, { schema });
11+12+console.log(`[Database] Using database: ${dbPath}`);
13+14+export default db;