A place from the TREK index can carry its website the way Overture has it, such as fr.wikipedia.org/wiki/Chapelle_Sainte-Barbe_du_Faouët, and saving it to a collection or a trip failed with "website must be an http or https URL" over a field the dialog does not show (#2483). OpenStreetMap, Amap and booking mails hand over websites the same way. Every place a website comes in from outside now goes through one shared helper, normalizePlaceWebsite: the index client and its nearby search, the Overpass and OSM detail mappers, Google search and details, Amap, plugin search and the booking import. It completes a bare or protocol-relative host to https, keeps http and https as they are, and drops every other scheme and anything that is no address. Only the scheme is added, so an accented path is stored as written. placeWebsiteSchema completes a bare host the same way before its check, so an older client, a cached offline result, an MCP agent or a plugin is no longer refused, while javascript:, data: and mailto: still are. The REST controller, the plugin RPC and the enrichment now store the parsed value instead of the raw one, and create_place_accommodation takes the website through the same schema as create_place. safeHttpUrl still links only a value already stored in its final form, so nothing changes on screen. Tests: SHARED-WEBSITE-001 to 012, SHARED-PLACE-030 to 034, SHARED-COLLFILE-2483-01, COLLECTIONS-E2E-089 and 090, PLACES-E2E-2483-01 and 02, PLACES-CTRL-2483-01 (the louvre.fr case adjusted), MCP-PLACES-2483-01 to 03, MCP-ACCOM-2483-01, MCP-COLL-2483-01, PLACES-RPC-2483-01, MAPS-2483-01 to 05, MAPS-POIS-015, MAPS-GERS-010, AMAP-012b, TREK-PLACES-2483-01, PLUGIN-SEARCH-2483-01, BOOKING-IMPORT-2483-01, ENRICH-052e, ENRICH-102 extended, ENRICH-120 adjusted, FE-UTIL-SAFEURL-007.
@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.