Mobile-first browser tycoon game. Phase 1 bootstrap.
Next.js App Router · TypeScript · Tailwind · Supabase (Auth/Postgres/RLS/Edge Functions) · Google Maps JS (client) · Google Places API (server-only).
Topology, server/client boundary, and route groups are pinned by ADR-0001 (issue document).
apps/web/ Next.js App Router app
packages/sim/ Pure deterministic simulation engine
packages/shared/ Zod schemas + types shared between web and edge
supabase/ Migrations, Edge Functions, seed data
docs/ ADRs and living specs
- Node.js >= 20.11
- pnpm 10.x (
corepack enable && corepack prepare pnpm@10.32.1 --activate)
pnpm install
cp .env.example .env.local # then fill in real values
pnpm dev # http://localhost:3000pnpm lint # eslint across the workspace
pnpm typecheck # tsc --noEmit per package
pnpm test # vitest (deterministic-PRNG suite, more to come)
pnpm format:checkCI runs the same four commands on every PR. Required green before merge.
Defined in .env.example. Copy to .env.local and fill in.
| Var | Scope | Notes |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
browser+server | Supabase project URL. |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY |
browser+server | Publishable key (replaces the deprecated anon key). RLS-protected, OK to ship. |
SUPABASE_SERVICE_ROLE_KEY |
server only | Bypasses RLS. Must never reach the browser. Required when code calls createServiceRoleClient(); optional at build time until that path exists. |
NEXT_PUBLIC_GOOGLE_MAPS_BROWSER_KEY |
browser | Maps JS SDK only. HTTP referrer–restricted; see docs/google-cloud-keys.md. |
NEXT_PUBLIC_GOOGLE_MAPS_STYLE_ID |
browser | Cloud mapId for vector basemap / Advanced Markers. POI labels off per ADR-0004. See docs/google-cloud-keys.md. |
GOOGLE_PLACES_SERVER_KEY |
server only | Places API. app/api/places/* and Edge Functions only. API-restricted; store as Vercel Sensitive. See docs/google-cloud-keys.md. |
NEXT_PUBLIC_SITE_URL |
browser+server | Public URL for Supabase auth redirects. Local: http://localhost:3000. On Vercel, if unset, the app uses https://$VERCEL_URL; set explicitly for a custom domain so Supabase redirect URLs match. |
NEXT_PUBLIC_* vars are inlined into the browser bundle. Anything without that prefix is server-only. CI must never echo populated values into logs.
- Server-default rendering.
"use client"is a leaf-level opt-in. - Two Supabase factories:
lib/supabase/browser.ts(publishable) andlib/supabase/server.ts(publishable SSR + service-role). Server module is gated byimport "server-only". - Routes live under
app/(marketing),app/(game), orapp/(admin). Game ↔ admin cross-imports are blocked by ESLint per ADR-0001 §7. - Mobile-first 375×812 baseline. Tokens in
apps/web/app/globals.cssandapps/web/tailwind.config.ts. - Determinism:
packages/simis pure. NoDate.now(), noMath.random()— all randomness flows from seeded PRNG (mulberry32).