mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-14 04:16:49 +00:00
Update project overview to describe region tree refactor, document data pipeline scripts, clarify project structure, and align with current practices.
180 lines
8.0 KiB
Markdown
180 lines
8.0 KiB
Markdown
# Development Guidelines
|
|
|
|
## Development Commands
|
|
|
|
- `npm run dev` - Start development server with Turbopack
|
|
- `npm run dev:clean` - Same, after clearing the `.next` build cache
|
|
- `npm run build` - Build the application for production (writes `.next`)
|
|
- `npm run build:check` - Same build for local verification, into `.next-check`
|
|
- `npm start` - Start production server
|
|
- `npm run lint` - Run ESLint
|
|
- `npm test` - Run the test suite against the in-memory Redis fake
|
|
- `npm run test:watch` - Re-run tests on change
|
|
- `npm run test:integration` - Run the same suite against a local Redis
|
|
- `npm run redis:up` / `npm run redis:down` - Start/stop that local Redis
|
|
|
|
## Important Development Guidelines
|
|
|
|
### JavaScript Only
|
|
- This project uses **JavaScript exclusively**
|
|
- Never create or suggest TypeScript files (.ts, .tsx)
|
|
- All components and utilities should be .js or .jsx files
|
|
|
|
### Function Parameters
|
|
- All functions should use **individual parameters** instead of object destructuring
|
|
- Use `function(param1, param2)` instead of `function({param1, param2})`
|
|
- This applies to React components, utility functions, and API handlers
|
|
|
|
### File Modification Policy
|
|
- **Only modify source code files**, documentation (/docs), and plans (/plans)
|
|
- Configuration changes (package.json, next.config.mjs, eslint.config.mjs, components.json, etc.) should be highlighted for manual processing
|
|
- Environment files and build settings require manual review
|
|
|
|
### Security Best Practices
|
|
- Never expose or commit secret keys and sensitive information
|
|
- Server-side session management prevents client-side coordinate access (Redis TTL 30 min)
|
|
- All geographic calculations must be performed server-side
|
|
- Session cleanup after guess submission for security
|
|
|
|
### Environment Variables
|
|
|
|
**Redis (required)**:
|
|
- Either `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN` (vanilla Upstash)
|
|
- Or `KV_REST_API_URL` + `KV_REST_API_TOKEN` (Vercel Marketplace)
|
|
- Optional: `KEY_PREFIX` (default: `vngeoguessr:`) for multi-tenant DB sharing
|
|
|
|
**Mapillary (required)**:
|
|
- `MAPILLARY_ACCESS_TOKEN` - Mapillary API token for image fetching
|
|
|
|
For local Redis without an Upstash account, see *Running Upstash locally* below.
|
|
|
|
### Testing & Completion
|
|
|
|
Tests live in `tests/` and cover the logic in `src/lib/` (scoring and distance,
|
|
the Upstash key adapter, game sessions, the region tree, the panorama indexes
|
|
and the leaderboards), the API routes, and the leaderboard migration. Run
|
|
`npm test` after changing anything under `src/lib/` or `src/data/`.
|
|
|
|
The suite runs against two backing stores, from one set of test files:
|
|
|
|
- `npm test` uses `tests/fake-upstash-redis.js`, an in-memory stand-in mocked in
|
|
at the `@upstash/redis` boundary. No service, no Docker, well under a second.
|
|
This is the default.
|
|
- `npm run test:integration` runs the same files against a real Redis. Two of
|
|
them skip: one asserts a response shape only an older SDK produces, and one
|
|
fast-forwards half an hour to watch a session expire.
|
|
|
|
Running both is what keeps the fake honest. A behaviour the fake gets wrong
|
|
shows up as a green unit run and a red integration run.
|
|
|
|
Everything above `src/lib/` (UI, panorama viewer, map interaction, Mapillary
|
|
calls) is still manual: inform the user when work is complete. Do NOT start
|
|
development servers - user handles testing manually.
|
|
|
|
### When the dev server needs restarting
|
|
|
|
Almost never. Measured against this project:
|
|
|
|
| Change | Behaviour |
|
|
|---|---|
|
|
| Components, pages | Fast Refresh, ~200ms |
|
|
| API routes | Hot reloaded, ~1.6s |
|
|
| Server libs under `src/lib/` | Hot reloaded, ~1.8s |
|
|
| Index data under `src/data/` | Hot reloaded, ~1.1s |
|
|
| `next.config.mjs` | Next restarts itself |
|
|
| `.env` | Reloaded in place |
|
|
|
|
Do not run `npm run build` while a dev server is running. Both own the same
|
|
output directory, so the build replaces manifests the dev server is still
|
|
reading, and it logs a burst of `ENOENT: no such file or directory` errors for
|
|
manifest files under that directory. Measured: 110 such errors from one
|
|
overlapping build, and none when the two do not overlap. They are noise from the
|
|
collision, not a fault in the code being edited.
|
|
|
|
Use `npm run build:check` instead. It builds into `.next-check` and leaves the
|
|
dev server alone. The plain `build` script is unchanged, because that is what the
|
|
deployment platform runs and it must keep writing the default directory.
|
|
|
|
If a change genuinely will not take effect, the build cache is usually
|
|
inconsistent rather than the code being wrong. That happens after a build is
|
|
killed part-way or a different Next version writes into `.next`, and it shows up
|
|
as the dev server serving 500s for everything. `npm run dev:clean` clears the
|
|
cache and starts fresh; a plain restart will not fix it.
|
|
|
|
### Running Upstash locally
|
|
|
|
Upstash itself is cloud-only, but `@upstash/redis` speaks HTTP REST, so any
|
|
server exposing that REST surface is indistinguishable to the app.
|
|
`docker-compose.yml` runs the proxy Upstash's own docs recommend, SRH
|
|
(`hiett/serverless-redis-http`), in front of a real Redis:
|
|
|
|
```bash
|
|
npm run redis:up # redis + SRH on http://localhost:8079
|
|
npm run redis:down # stop and discard the data
|
|
```
|
|
|
|
`npm run test:integration` points at it automatically. To use it for `npm run
|
|
dev` as well, set in `.env`:
|
|
|
|
```
|
|
UPSTASH_REDIS_REST_URL=http://localhost:8079
|
|
UPSTASH_REDIS_REST_TOKEN=vngeoguessr-local-token
|
|
```
|
|
|
|
SRH accepts commands only as a JSON array POSTed to `/`, which is what the SDK
|
|
sends; the path form Upstash also supports (`GET /set/key/value`) returns 404.
|
|
|
|
### Rebuilding the generated region data
|
|
|
|
`src/data/regions/`, `src/data/boundaries/` and `src/data/panos/` are generated.
|
|
Run the scripts in this order -- each reads what the previous one wrote:
|
|
|
|
```bash
|
|
npm run data:boundaries # OSM/Nominatim -> boundaries + region tree
|
|
npm run data:panos # Mapillary z14 tiles -> per-province pano index
|
|
npm run data:districts # clip + partition panos by district; writes counts.js
|
|
```
|
|
|
|
Each underlying script's header comment carries its flags and its cost; read it
|
|
before running. Two things worth knowing up front:
|
|
|
|
- `npm run data:repartition` reruns the boundaries step with `--regenerate`
|
|
(rebuilds the provinces, the barrel and the tree from what is already on
|
|
disk) and then the districts step, with no network calls. Use it after
|
|
hand-editing a boundary.
|
|
- `npm run data:panos` spends Mapillary tile requests against a 50,000/day cap.
|
|
`npm run data:districts` spends none -- it only re-partitions indexes that
|
|
already exist, and refuses to rewrite `counts.js` on a partial run.
|
|
|
|
### Migrating leaderboards
|
|
|
|
`scripts/migrate-leaderboards.mjs` backfills the two boards whose region code
|
|
changed (Da Lat into Lam Dong, Duc Hoa into Long An). It is **dry-run by
|
|
default** and copies rather than moves, so the source boards survive.
|
|
|
|
Deploy first, then migrate. The new code writes to the new keys immediately; a
|
|
migration run before the deploy would copy a board that is still being written
|
|
to under its old name. Run `npm run leaderboard:migrate` with no flags for a
|
|
dry run; add `--apply --confirm-prefix=<key prefix>` to write (the confirm
|
|
flag must echo the deployment's own `KEY_PREFIX`, or the script refuses), and
|
|
`--restore=<backup.json> --confirm-prefix=<key prefix>` to undo from the
|
|
backup file the apply run wrote. `--dry-run` is a guard, not a mode switch --
|
|
combining it with `--apply` throws instead of guessing which one you meant.
|
|
|
|
## shadcn/ui Configuration
|
|
|
|
- **Style**: "new-york"
|
|
- **Path aliases**: Configured for `@/components`, `@/lib`, etc.
|
|
- **Components**: Use JavaScript (.js) not TypeScript
|
|
- **CSS variables**: Enabled for theming
|
|
- **Components location**: `src/components/ui/`
|
|
|
|
## Code Style Standards
|
|
|
|
- Follow existing code patterns in the codebase
|
|
- Use established libraries and utilities already present
|
|
- Maintain consistent naming conventions
|
|
- Write descriptive comments where the reason for the code is not obvious from
|
|
the code -- this codebase leans on them heavily to record why a measured
|
|
approach was chosen over the obvious one
|
|
- Prefer editing existing files over creating new ones |