46 lines
2.3 KiB
Markdown
46 lines
2.3 KiB
Markdown
# CODING_STANDARDS.md
|
|
|
|
## Language & style
|
|
- Node 20+, CommonJS, `'use strict'` not needed in CJS modules but harmless.
|
|
- 2-space indent, semicolons, single quotes, trailing commas in multiline literals.
|
|
- `const` by default; `let` only if reassigned; never `var`.
|
|
- Names: `camelCase` functions/vars, `PascalCase` classes, `UPPER_SNAKE` constants, `snake_case` DB columns.
|
|
- Convert DB `snake_case` rows → `camelCase` objects at the service boundary (one small mapper per entity).
|
|
- Functions do one thing and fit on a screen (< 40 lines). Early returns over deep nesting.
|
|
- Comments explain *why*, not *what*. Add a one-line header comment to each file describing its purpose.
|
|
|
|
## Errors
|
|
Create `src/utils/errors.js`:
|
|
```js
|
|
class AppError extends Error {
|
|
constructor(code, message, status = 400) { super(message); this.code = code; this.status = status; }
|
|
}
|
|
```
|
|
- Services throw `AppError` for expected failures (`new AppError('INSUFFICIENT_ENERGY','Not enough energy.',422)`).
|
|
- Routes use an async wrapper so thrown errors reach `errorHandler`:
|
|
```js
|
|
const wrap = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
|
|
```
|
|
- `errorHandler` maps `AppError` → envelope; `ZodError` → 400 `VALIDATION_ERROR`; anything else → 500 generic message + server log.
|
|
|
|
## Pure game code
|
|
- `src/game/*` exports plain functions; no I/O; no `Date.now()`; no `Math.random()` — take `rng` / `nowMs` params.
|
|
- Return new objects instead of mutating inputs.
|
|
|
|
## Database access
|
|
- One prepared statement per query, created once at module load (or lazily cached), not inside loops.
|
|
- Wrap multi-write operations in `db.transaction`.
|
|
- Never `SELECT *` in services for public output; select what you return.
|
|
|
|
## Frontend JS
|
|
- `public/js/api.js` exports a small `api(method, path, body)` wrapper that handles JSON, error envelope, and 401 → redirect to login.
|
|
- DOM updates via `textContent` / `createElement`. No `innerHTML` with dynamic data.
|
|
- One JS file per page + shared `api.js` and `ui.js` (toasts, formatting).
|
|
- No global state beyond a single `state` object per page.
|
|
|
|
## Logging
|
|
`src/utils/logger.js`: tiny wrapper over `console` with levels; silent in tests unless `LOG=1`.
|
|
|
|
## Dependencies
|
|
Pinned in package.json via `npm install --save-exact`. No dependency without need (NOTES.md §2.3).
|