# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Overview YesChef is a family shopping list and recipe app. Single-tenant per deployment, gated by a shared `FAMILY_CODE` invite phrase at registration. Real-time list collaboration is delivered over SignalR. The repo is a small monorepo with two deployables plus an e2e harness at the root: - `src/backend/YesChef.Api/` — ASP.NET Core (net10.0) minimal API + EF Core (Npgsql) + SignalR - `src/frontend/` — SvelteKit 2 (Svelte 5 runes) + TypeScript + Tailwind v4, deployed via `@sveltejs/adapter-node` - `docker-compose.yml` (root) — production-style stack: Postgres 17 + backend + frontend, fronted by Traefik (TLS via Let's Encrypt). Frontend and backend share the same `DOMAIN`; Traefik routes `/api`, `/hubs`, and `/health` to the backend, everything else to the frontend. - `test-e2e.mjs` / `test-e2e-multiuser.mjs` — Playwright scripts run against `http://localhost:5173` (Vite dev) hitting a local backend. ## Common commands Frontend (run from `src/frontend`, use `npm --prefix src/frontend ` to avoid `cd`): ```powershell npm --prefix src/frontend install npm --prefix src/frontend run dev # Vite dev server on :5173 npm --prefix src/frontend run build # adapter-node build → src/frontend/build npm --prefix src/frontend run check # svelte-kit sync + svelte-check (type/lint check) ``` Backend (use `dotnet --project` to avoid `cd`): ```powershell dotnet run --project src/backend/YesChef.Api # listens per launchSettings.json dotnet build src/backend/YesChef.Api/YesChef.Api.csproj dotnet ef migrations add --project src/backend/YesChef.Api dotnet ef database update --project src/backend/YesChef.Api ``` Note: `Program.cs` calls `db.Database.MigrateAsync()` on startup, so running the API auto-applies pending migrations against the configured `ConnectionStrings:DefaultConnection`. Backend tests (TUnit on Microsoft.Testing.Platform; integration tests use Testcontainers + Postgres). The `src/backend/global.json` opts `dotnet test` into MTP mode, so use `--solution` / `--project` flags: ```powershell dotnet test --solution src/backend/YesChef.slnx # all tests dotnet test --project src/backend/YesChef.Api.UnitTests/YesChef.Api.UnitTests.csproj dotnet test --project src/backend/YesChef.Api.IntegrationTests/YesChef.Api.IntegrationTests.csproj ``` Integration tests start a single Postgres 17 container per test session, create a migrated `yeschef_template` database, then clone a fresh DB per test via `CREATE DATABASE … TEMPLATE …`. Tests run in parallel (capped by `IntegrationTestParallelLimit`) and require Docker (Rancher Desktop or Docker Desktop). Full stack via Docker (requires `.env` populated from `.env.example`): ```powershell docker compose up -d --build docker compose logs -f backend ``` E2E (requires frontend running on :5173 and backend reachable): ```powershell node test-e2e.mjs node test-e2e-multiuser.mjs ``` There is no backend test project and no test runner configured in the root `package.json` — `npm test` is a no-op stub. ## Architecture ### Backend — feature-folder minimal API `Program.cs` is the composition root. It wires EF Core (`YesChefDb`), JWT bearer auth, SignalR, runs migrations, then maps endpoints by feature folder: - `Auth/` — `/api/auth` register/login/me. Registration requires the `FamilyCode` config value (env `FamilyCode`). Passwords use `PasswordHasher`. JWTs are signed with HS256 from `Jwt:Secret`; issuer/audience are NOT validated. The `OnMessageReceived` event also accepts `?access_token=` for the `/hubs/*` SignalR paths. - `Features/Stores/`, `Features/ShoppingLists/`, `Features/Recipes/` — each exposes a static `Map…Endpoints(this RouteGroupBuilder)` extension. All are mapped under `RequireAuthorization()`. - `Features/ShoppingLists/ShoppingListHub.cs` — SignalR hub at `/hubs/shopping-list` for live list updates. - `Data/YesChefDb.cs` — single `DbContext`. Notable model rules: `User.Name` and `Store.Name` are unique; `ShoppingList → Items` cascade-deletes; `ShoppingListItem.CheckedByUser` and `ShoppingListItem.Recipe` set null on delete; `Recipe → Ingredients` cascades. - `Auth/ClaimsPrincipalExtensions.cs` — `GetUserId()` / `GetUserName()` helpers used by endpoints to scope writes. - `Migrations/` — EF Core migrations. Add new ones with `dotnet ef migrations add` (see commands above). When adding a feature: create `Features//Endpoints.cs` with a `MapEndpoints` extension, register the group in `Program.cs`, and add any new entities to `Entities/` plus configuration in `YesChefDb.OnModelCreating`. ### Frontend — SvelteKit with runes - `svelte.config.js` forces `runes: true` for all non-`node_modules` files. Use Svelte 5 runes (`$state`, `$derived`, `$effect`); do not use legacy reactive `$:` syntax. - `src/lib/api.ts` — `api(path, opts)` helper. Stores the JWT in `localStorage` under `token`, attaches `Authorization: Bearer …`, and on 401 it clears the token and `goto('/login')`. - `src/lib/auth.svelte.ts` — runes-based auth state. - `src/lib/signalr.ts` — SignalR client wiring (passes the JWT via the `?access_token=` query string the backend's `OnMessageReceived` handler expects). - `src/routes/` — file-based routes: `/login`, `/lists`, `/lists/[id]`, `/recipes`, `/recipes/new`, `/recipes/[id]`, `/stores`. - `src/service-worker.ts` — PWA service worker. - `$lib` is aliased to `src/lib` (see `svelte.config.js`). - The frontend calls relative paths like `/api/...`; in production Traefik routes those to the backend on the same host. For local dev against a backend on a different port, configure a Vite proxy or run the backend behind the same origin. ### Configuration & secrets `.env.example` documents the four required environment variables for the Docker stack: `POSTGRES_PASSWORD`, `JWT_SECRET`, `FAMILY_CODE`, `DOMAIN`. The backend reads `Jwt:Secret`, `FamilyCode`, and `ConnectionStrings:DefaultConnection` via `IConfiguration` — these can come from `appsettings.Development.json` for local runs or from environment variables (double-underscore form, e.g. `ConnectionStrings__DefaultConnection`) in containers.