This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
SvelteForge Admin is a SvelteKit admin dashboard using Svelte 5, Tailwind CSS v4, custom session-based auth with Arctic OAuth, and Drizzle ORM with SQLite.
pnpm dev # Start dev server
pnpm build # Production build
pnpm preview # Preview production build
pnpm check # Type-check with svelte-check
pnpm check:watch # Type-check in watch mode
pnpm db:generate # Generate Drizzle migrations from schema
pnpm db:push # Push schema changes directly to database
pnpm db:studio # Open Drizzle Studio GUI
pnpm db:seed # Seed database with sample data (npx tsx)
pnpm test # Run all unit tests (Vitest)
pnpm test:watch # Run tests in watch mode
pnpm test:e2e # Run E2E tests (Playwright)
# Run a single test file
npx vitest run src/routes/\(app\)/users/users.test.ts
pnpm lint # ESLint
pnpm format # Prettier (write)
pnpm format:check # Prettier (check only)- Svelte 5 with runes API (
$props,$state,$derived,{@render}) - Tailwind CSS v4 — native CSS with
@themedirective insrc/app.css, no JS config file. OKLCH color system - shadcn-svelte — UI components in
$lib/components/ui/, added vianpx shadcn-svelte@latest add <component> - Custom session auth — SHA-256 hashed tokens with @oslojs/crypto, Argon2id password hashing, optional OAuth via Arctic (Google, GitHub)
- Drizzle ORM — SQLite with better-sqlite3, WAL mode. Schema in
src/lib/server/db/schema.ts - LayerChart v2 — D3-based charts. Marked
noExternalinvite.config.tsalongsidesvelte-uxfor SSR compatibility - Package manager: pnpm
Routes use SvelteKit route groups for layout separation:
(app)/— Protected routes. Auth guard in(app)/+layout.server.tsredirects unauthenticated users to/login(auth)/— Public auth routes (login, register, OAuth callbacks atlogin/google/,login/github/)(public)/— Public pages (pricing)logout/— Standalone logout action (server-only)api/search/— Search endpoint for command palettesitemap.xml/— Auto-generated sitemap
Session validation runs on every request via hooks.server.ts, populating event.locals.user and event.locals.session. OAuth providers are environment-driven — see .env.example for configuration.
src/lib/server/— Server-only code (auth, OAuth, database). Never import from client-side codesrc/lib/server/auth.ts— Session management (create, validate, invalidate, cookies)src/lib/server/oauth.ts— Arctic OAuth providers (conditional on env vars)src/lib/server/db/schema.ts— Drizzle schema (users, sessions, pages, notifications, oauthAccounts, appSettings, passwordResetTokens)src/lib/server/db/seed.ts— Database seeder (run viapnpm db:seed, usesnpx tsxnot SvelteKit aliases)src/lib/server/id.ts— Crypto ID generator (generateId())src/lib/components/ui/— shadcn-svelte components (don't edit directly, re-add to update)src/lib/components/— App-level components (sidebar, theme toggle, command palette, notification bell)src/lib/hooks/— Svelte 5 reactive utilities (e.g.,is-mobile.svelte.ts)src/lib/utils.ts—cn()helper (clsx + tailwind-merge) and component type utilitiessrc/lib/utils/— Export utilities (CSV/JSON), user-agent parser
SQLite database file: svelteforge.db (project root, gitignored). Roles enum: admin | editor | viewer. First registered user gets admin role.
Tests co-locate with their route: e.g., src/routes/(app)/users/users.test.ts tests the users/+page.server.ts load and actions.
Test DB pattern: Tests mock $lib/server/db/index.js with a getter that returns an in-memory SQLite database created via createTestDb() from test-utils.ts. The mock must be set up before dynamically importing the server module:
vi.mock("$lib/server/db/index.js", () => ({
get db() { return testDb; },
}));
const { load, actions } = await import("./+page.server.js");After modifying schema.ts, also update the SCHEMA_SQL in test-utils.ts and run pnpm db:push.
- Forms use SvelteKit form actions with
use:enhancefor progressive enhancement - Dark/light mode via
mode-watcher— usemode.current(runes object), NOT$mode - App shell layout: sidebar (
app-sidebar.svelte) + topbar with breadcrumbs (generated from URL pathname) App.Localstyped insrc/app.d.ts—user: SessionUser | null,session: Session | nullseed.tsruns outside SvelteKit context — use relative imports (not$lib/) andgenerateId()from$lib/server/id.js