apps/.
apps/web — React frontend
- React 19 + Vite + Tailwind CSS v4
- Clerk (
@clerk/clerk-react) for authentication;useAuth()provides tokens - TanStack Query for all server state; queries live in
src/hooks/ - Jotai for lightweight client state
- CASL (
@casl/react) for UI-level permission checks, built fromstudio.currentUserRoleinAbilityProviderand consumed viauseAbility() - Typed API client at
src/lib/api.ts, always accessed throughuseApi() - Route guards:
AuthRoute→StudioRoute→AppRoute - Visual pinboard built on
react-konva; drag-and-drop via@dnd-kit
apps/server — Express backend
- Express + TypeScript, run with
tsxin dev andtscfor production builds - Prisma ORM against PostgreSQL; client singleton at
src/lib/prisma.ts - Clerk (
@clerk/express) validates JWTs via a globalclerkMiddleware() - Authorization via
requireStudioAbility(action, subject)middleware - AI agent for pinboard building (see Agentic pinboard)
apps/chrome-extension and apps/landing
The Chrome extension captures products from retailer sites into the studio
product library. The landing app is the marketing site.
Request flow
Clerk issues JWTs on the frontend.ApiProvider wraps createApi(getToken) so
every fetch carries a Bearer token. On the server, clerkMiddleware() decodes
it and requireStudioAbility does the DB lookup that attaches
req.studioMembership and req.ability to the request.