Architecture overview
The directory layout and dependency rule every change in this codebase follows.
Ahvaan is a Next.js App Router app on top of Supabase (Postgres + Auth + Storage), with Resend for email. The full narrative review lives in docs/02-architecture-review.md; this page is the quick map.
Directory layout
src/app/— routes. A public tier (/,/login,/signup,/privacy,/terms,/docs), the auth-gateddashboard/tree (host UI — every page and action assumes an authenticated host), andevents/[slug]/(the public guest-facing event page and RSVP flow).src/components/— shared UI:ui/(design-system primitives),guest-dashboard/,builder/(page-builder editor chrome, dashboard-only),docs/(this site’s own nav/content primitives).src/lib/data/— the only layer that touches the Supabase client. Every host-scoped read/write goes through a named function here (events.ts,invites.ts,rsvps.ts,forms.ts,form-submissions.ts,email-log.ts,host-profile.ts,storage.ts,rate-limit.ts,custom-components.ts).src/lib/blocks/— the page-builder block system: the registry, types, the sandbox builder, shortcodes, starter layouts, layout controls, and one file per block underblocks/*.tsx. See adding a new block type.src/lib/forms/— the generic multi-form field-type system, modeled onlib/blocks: a registry, one config type + validator + Edit/Input pair per field kind.src/lib/schemas/— Zod validators; the single source of truth for both TS types and runtime validation of every JSONB column. See JSONB validation.src/lib/supabase/—server.ts(service-role client) andauth-server.ts(SSR auth client).src/lib/cache/keyed-cache.ts— a custom per-key cache used instead of Next’sunstable_cache. See caching.src/proxy.ts— Next middleware: generates the per-request CSP nonce and gates/dashboard/*and the auth pages.
The dependency rule
app/* may import from lib/data, lib/schemas, and components/*. lib/data may import from lib/schemas and the Supabase client. Nothing imports from app/. lib/blocks is pure (usable by the public guest page); components/builder/ is the editor-only UI on top of it and is a dashboard-only import.
No mutation API routes
The only two files under src/app/api/ (forms/[formId], rsvp) still route through the same lib/data/lib/schemas layers as everything else. Every host-side mutation is a server action, not a REST endpoint — this is a deliberate decision, not an oversight.
Good to know
host_id (see the host_id invariant), and every JSONB column is parsed through a Zod schema, never as-cast (see JSONB validation).