Structure
Apps, packages and the rules between them.
skiffr/
├─ apps/
│ ├─ web/ landing page, next
│ ├─ dashboard/ profile, answers, queue, stats, next
│ ├─ api/ orpc on bun
│ ├─ extension/ chrome extension, wxt and react
│ ├─ e2e/ playwright suite for the extension
│ └─ docs/ this site, fumadocs on next
├─ packages/
│ ├─ ui/ shadcn components and theme tokens
│ ├─ common/ shared zod schemas, constants and utils
│ ├─ orpc/ procedures and clients, built on common
│ ├─ db/ drizzle schema, migrations, seed scenario
│ ├─ ai/ field mapping and answer drafts, vercel ai sdk
│ ├─ auth/ better-auth setup
│ ├─ env/ zod-validated env
│ ├─ storage/ s3 compatible storage
│ └─ config/ shared tsconfig
├─ docker/ local postgres compose file
└─ turbo.jsonRules
apps/apiis the only app that touches the database, auth and providers.web,dashboardand the extension call it through@skiffr/orpc, so secrets and data access live in one place- anything used by more than one app or package goes in
@skiffr/common: schemas and their types, constants, small utils. Don't redeclare a type in an app when one exists there - the theme lives in
packages/ui/src/styles/theme.css, use its tokens instead of raw colors - the database schema is in
packages/db/src/schema, change it and runpnpm db:generateto get a migration, never write one by hand
Adding an endpoint
- put the input and output schemas in
packages/common/src/schemas - add the procedure in
packages/orpc/src/routers - call it from an app with
orpc.<router>.<procedure>in client components orcall((api) => ...)on the server