An ordinary place could not become a fuel stop without going through the full form, and the number in the rail is exactly what changes when it does: a service stop has none. So the number is the control. Click the 3, pick the pump, and the 3 becomes an orange disc while everything below renumbers itself. The disc of a service stop opens the same picker the other way, with a row to make it an ordinary destination again. Six coloured discs rather than six lines of text: at that size the colour and the shape are the label, and anyone who has seen one petrol stop on the map knows the orange pump before reading anything. Centred on the column rather than on the number, which sits hard against the left edge and would hang the panel half over the map. The click stops at the disc, so picking a kind never also selects the place underneath. Also in this change, from the same round: the corridor search offers hotels, a hit somebody could sleep at opens the popup in an overnight mode with a check-out day instead of a dwell, and booking one writes the place, its day and the row in day_accommodations together. A hotel keeps its number, because sleeping there ends the day rather than interrupting the drive, which is why the category list and the stop-kind list are now two lists instead of one.
@trek/shared
Single source of truth for TREK's API contracts, expressed as Zod schemas and consumed by both the server (request validation + inferred DTO types) and the client (typed requests/responses).
This package is part of the incremental NestJS + React 19 migration (see the "Brownfield Rewrite" board). It is intentionally dormant until modules start importing it — adding it changes nothing for users.
Rules
- One folder per domain:
src/<domain>/<domain>.schema.ts(+.spec.ts). - Domain-agnostic building blocks live in
src/common/. - A route is only considered migrated once its contract lives here.
- Schemas are the source of truth; server DTOs and client types are inferred from them
(
z.infer<typeof schema>), never hand-duplicated.
Consumption (dev)
Both apps resolve @trek/shared to this package's TypeScript source:
- Server (
tsx): viapathsinserver/tsconfig.json. - Client (
vite): viaresolve.aliasinclient/vite.config.ts(+pathsfor the type-checker).
Production packaging (Docker / workspace wiring) is introduced in card F2, when the server first depends on this package at runtime. Until then prod builds are untouched.
Not yet here
The canonical error envelope is finalised in card F5 (it must match TREK's current Express error responses byte-for-byte), so it is deliberately not invented in F1.