Skip to content

Latest commit

 

History

History
115 lines (96 loc) · 5.56 KB

File metadata and controls

115 lines (96 loc) · 5.56 KB

Local development setup

Getting the worker running on your machine, and the conventions for adding to it. Read ../../CONTRIBUTING.md for the PR process and ../../CLAUDE.md for the rules the gates enforce.

Run it

npm install
npm run db:migrate      # apply D1 migrations locally
npm run dev:hmr         # Vite dev server with HMR — the fast iteration loop

npm run dev is the other one: it runs the full react-router build and then wrangler dev against the real bundled worker. No HMR, but it is production-shaped, so use it to verify built behaviour before you push.

npm run dev:tunnel is dev plus a Cloudflare Quick Tunnel. You need it to exercise the collaborative-editing WebSockets: the session cookie is Secure, and a page served over http://localhost opens ws://, which browsers do not treat as a secure scheme — so the cookie is withheld and the handshake 401s. Over the tunnel both presence and Y.Doc sync work. The tunnel URL is a new origin (log in again there) and it is public while running — stop it when you are done.

Commands

Command Purpose
npm run dev Build + run the real bundled worker locally
npm run dev:hmr Vite dev server with HMR
npm run dev:tunnel dev over a public Cloudflare Quick Tunnel (needed for WebSockets)
npm run build react-router build — bundles server/ + app/ into one worker
npm run deploy standalone: build + wrangler deploy
npm run deploy:saas saas: build + deploy with wrangler.saas.jsonc
npm run type-check Full: typegen, then app + api + e2e + tests passes, serially
npm run type-check:api The server/ program only — the fastest loop for API work
npm run type-check:app The app/worker program (brings the api project up to date first)
npm run lint ESLint + the lint:* conformance gates
npm run test:unit API unit tests (vitest.api.config.ts)
npm run test:web Web unit tests (vitest.config.ts)
npm run test:workers Real-workerd tests (vitest.workers.config.ts)
npm run test:e2e Playwright
npm run db:generate Generate a forward migration from schema changes
npm run db:migrate Apply migrations to local D1
npm run db:check Drift gate — schema and migrations/ must agree

Do not hand-run what the pre-commit hook already runs. The hook does a tiered type-check, lint-staged, and every conformance gate declared at the precommit rung in scripts/lib/gate-registry.mjs — two dozen of them, run in one node process by scripts/run-gates.mjs, not the three this paragraph used to name. Commits with no staged .ts/.tsx skip the type-check entirely. Run the full suite once before pushing, not after every edit.

That registry is the list; do not maintain a copy of it here. npm run lint (pre-push and CI) runs every gate, at both rungs.

Project structure

workers/app.ts        Single-worker entry: Hono mounts the API in-process and
                      delegates page routes to React Router SSR
server/               Hono API — business logic, D1, R2
  api/                route modules
  lib/validations/    Zod schemas (never inline in a handler)
  lib/db/schema/      Drizzle schema — the source of truth for migrations
  services/           business logic
app/                  React Router web UI (SSR on Workers)
  routes/             page + resource routes, registered in app/routes.ts
packages/shared-ui/   Design System 0523 components
packages/api-types/   Hono app type re-export for end-to-end type safety
migrations/           D1 SQL, generated by drizzle-kit
messages/             i18n catalogue (paraglide)
tests/                unit / workers / e2e — see develop/testing.md
scripts/              setup, seed, backup, key rotation, and the lint:* gates

Adding a page

  1. Create the route module in app/routes/.
  2. Register it in app/routes.ts — placement matters: the auth layout, the settings layout, and the library layout each wrap their children.
  3. Load data in the route's loader, calling the in-process API through createApi(context). Client code must never fetch('/api/...') — that request carries no session and is unauthenticated.
  4. Build the UI from packages/shared-ui and Design System tokens (bg-ih-primary, text-ih-fg-1). Raw palette utilities fail lint:ds.

Adding an API endpoint

  1. Add or extend a route module under server/api/.
  2. Put the Zod schema in server/lib/validations/ — every endpoint that takes user input validates with Zod. TypeScript generics on c.req.json<T>() are not validation.
  3. Business logic goes in server/services/, using ScopedDB so the tenant filter cannot be forgotten.
  4. Register the route in server/index.ts.
  5. Declare route metadata — see conventions/route-metadata.md. The gate fails the build without it.

Adding a mode-dependent behaviour

Read a capability off the deployment profile; never branch on env.APP_MODE yourself. The seam is server/lib/deployment-profile.ts and there is a gate that keeps it the only reader. See ../reference/deployment-modes.md.

Further reading