Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 0 additions & 108 deletions .claude/agents/orchestrator.md

This file was deleted.

76 changes: 44 additions & 32 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
# ezmode.games Platform
# Rafters Studio Platform

Infrastructure for all Rafters surfaces. Auth, database, API, middleware.

## Apps

| App | Path | What |
| -------- | ------------ | ------------------------------------------------- |
| **web** | `apps/web/` | Cloudflare Worker -- Hono API, better-auth, D1 |
| **ctrl** | `apps/ctrl/` | Internal operations SPA -- Vite + TanStack Router |

## Stack

- **Runtime**: Cloudflare Workers
- **API**: Hono + `@hono/zod-openapi` + `stoker`
- **API Docs**: Scalar (`@scalar/hono-api-reference`)
- **Auth**: better-auth (native D1, OAuth2 for v2, API key for v1)
- **API**: Hono + `@hono/zod-validator`. Plain RPC, `AppType` exported for `hono/client`.
- **Auth**: better-auth (native D1, passkeys + OAuth2). No email/password. Ever.
- **Database**: Cloudflare D1, Drizzle as query builder only
- **Schemas**: Zod is source of truth. `drizzle-zod` bridges DB to API schemas.
- **Audit/Soft-delete/GDPR**: `@ezmode-games/drizzle-ledger`
- **Client**: `openapi-typescript` + `openapi-fetch` + `openapi-react-query`
- **Audit/Soft-delete/GDPR**: `@rafters/ledger`
- **Client**: `hono/client` with `AppType` for end-to-end type safety
- **State**: TanStack Query (cache/fetch), TanStack Forms (validation/submission)
- **UI**: Rafters for all surfaces, with ezmode-specific composite library
- **Router**: TanStack Router (ctrl SPA, file-based, type-safe)
- **UI**: Rafters design system for all surfaces
- **Lint/Format**: OXC (oxlint + oxc-format)
- **TypeScript**: v6 (Go parser)
- **Package Manager**: pnpm (never npm/yarn)
- **IDs**: UUIDv7 for all identifiers

## API Versioning

Two API versions served from the same Hono app:

- **v1**: Nexus-compatible endpoints. API key auth. Exists so MO2 and Wabbajack work without changes -- users swap the base URL and API key, existing mod managers just work. Thin translation layer over shared business logic.
- **v2**: Our real API, internal and external. OAuth2 auth. OpenAPI spec published for third-party developers. Internal-only endpoints are unpublished and require session + additional headers.

Both versions have their own OpenAPI spec and Scalar docs page.
No OpenAPI. The API is internal-only, ctrl is the only consumer. Removed in PR #92.

## Migrations

Expand All @@ -45,6 +47,17 @@ NEVER:

Drizzle schema files exist for type inference and query building. Wrangler handles all migration state. Breaking this rule corrupts wrangler's migration tracking.

## Schema Pattern

Every table gets two files:

1. **Drizzle schema**: `apps/web/src/db/schema/*.ts` -- table definitions for query building
2. **Zod companion**: `apps/web/src/db/schema/*.zod.ts` -- insert/select schemas via `drizzle-zod`

No barrel files. Import directly from the specific schema file.

`wrangler types` generates the Env type. Never manually type it.

---

# Testing
Expand All @@ -58,25 +71,20 @@ Tests are tools, not theater. Every test file has one job based on its extension
All tests live in `tests/` mirroring the source tree:

```
src/
apps/web/src/
api/routes/
notifications.ts
apps/ctrl/src/
routes/
mods/
mods.handlers.ts
mods.routes.ts
components/
mod-card/
mod-card.tsx
_ctrl/dashboard.tsx

tests/
routes/
mods/
mods.handlers.test.ts # unit
mods.routes.test.ts # unit
components/
mod-card/
mod-card.spec.ts # component behavior
api/routes/
notifications.test.ts # unit
ctrl/
dashboard.spec.ts # component behavior
flows/
publish-mod.e2e.ts # end-to-end
auth-login.e2e.ts # end-to-end
```

Tests are NEVER colocated with source files. The `tests/` folder is the single location.
Expand Down Expand Up @@ -147,8 +155,8 @@ Tests are NEVER colocated with source files. The `tests/` folder is the single l

**What belongs here**:

- Complete user journeys (sign up, upload mod, get paid)
- Auth flows (OAuth2 login, API key validation, session expiry)
- Complete user journeys (login, manage properties, review content)
- Auth flows (passkey login, OAuth2, session expiry)
- Cross-page navigation and deep linking
- Real API calls through the full Workers stack
- Visual regression on critical pages
Expand Down Expand Up @@ -214,3 +222,7 @@ pnpm test:all # everything
- No arbitrary Tailwind values (`-[400px]`). Use design tokens.
- No CSS positioning (`absolute`, `relative`, `fixed`) unless no alternative. Use flexbox/grid.
- Prefer container queries (`@container`, `@md:`) over media queries for component-level responsiveness.
- No barrel files on the edge. Import directly from the specific file.
- `wrangler types` generates Env. Never manually type it.
- better-auth generates its own schema/migrations. Do not hand-write auth tables.
- Every Drizzle schema file gets a `.zod.ts` companion.
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
"@astrojs/mdx": "^5.0.2",
"@astrojs/react": "^5.0.1",
"@better-auth/passkey": "^1.5.5",
"@ezmode-games/drizzle-ledger": "^0.0.1",
"@hono/zod-validator": "^0.7.6",
"@polar-sh/better-auth": "^1.8.3",
"@polar-sh/checkout": "^0.2.0",
"@polar-sh/sdk": "^0.46.6",
"@rafters/ledger": "^0.2.0",
"@tailwindcss/vite": "^4.2.2",
"@tanstack/react-form": "^1.28.5",
"@tanstack/react-query": "^5.91.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { organization } from "better-auth/plugins/organization";
import { passkey } from "@better-auth/passkey";
import { checkout, polar, webhooks } from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";
import { ledgerPlugin } from "@ezmode-games/drizzle-ledger/better-auth";
import { ledgerPlugin } from "@rafters/ledger/better-auth";
import { uuidv7 } from "uuidv7";
import { createDb } from "../db/client";
import { auditLog } from "../db/schema/audit";
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"packageManager": "pnpm@10.32.1",
"pnpm": {
"onlyBuiltDependencies": [
"@ezmode-games/drizzle-ledger",
"@rafters/ledger",
"esbuild",
"sharp",
"workerd"
Expand Down
42 changes: 22 additions & 20 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading