Spaces:
Sleeping
Sleeping
File size: 3,237 Bytes
cb43fbd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | import { z } from 'zod';
/**
* Maps / geo API contract — single source of truth for the /api/maps endpoints.
*
* The legacy Express route (server/src/routes/maps.ts) is a thin layer over
* services/mapsService.ts, which talks to Nominatim/Overpass (and optionally
* Google Places when a key is configured) and applies the SSRF guard on every
* outbound URL. The place objects these return are provider-shaped and vary by
* source, so the response schemas keep them as open records — the contract pins
* down the request shapes and the stable envelope fields, not the provider blobs.
*
* The bespoke 400 validation messages and the per-endpoint kill-switch responses
* are reproduced in the controller, not derived from these schemas, so the bodies
* stay byte-identical to Express.
*/
const latLng = z.object({ lat: z.number(), lng: z.number() });
export const mapsSearchRequestSchema = z.object({
query: z.string().min(1),
});
export type MapsSearchRequest = z.infer<typeof mapsSearchRequestSchema>;
export const mapsAutocompleteRequestSchema = z.object({
input: z.string().min(1).max(200),
lang: z.string().optional(),
locationBias: z.object({ low: latLng, high: latLng }).optional(),
});
export type MapsAutocompleteRequest = z.infer<
typeof mapsAutocompleteRequestSchema
>;
export const mapsReverseQuerySchema = z.object({
lat: z.string().min(1),
lng: z.string().min(1),
lang: z.string().optional(),
});
export type MapsReverseQuery = z.infer<typeof mapsReverseQuerySchema>;
export const mapsResolveUrlRequestSchema = z.object({
url: z.string().min(1),
});
export type MapsResolveUrlRequest = z.infer<typeof mapsResolveUrlRequestSchema>;
/** Provider-shaped place blob (Google/OSM fields differ); kept open by design. */
const placeRecord = z.record(z.string(), z.unknown());
export const mapsSearchResultSchema = z.object({
places: z.array(placeRecord),
source: z.string(),
});
export type MapsSearchResult = z.infer<typeof mapsSearchResultSchema>;
export const mapsAutocompleteSuggestionSchema = z.object({
placeId: z.string(),
mainText: z.string(),
secondaryText: z.string(),
});
export const mapsAutocompleteResultSchema = z.object({
suggestions: z.array(mapsAutocompleteSuggestionSchema),
source: z.string(),
});
export type MapsAutocompleteResult = z.infer<
typeof mapsAutocompleteResultSchema
>;
export const mapsPlaceDetailsResultSchema = z.object({
place: placeRecord.nullable(),
disabled: z.boolean().optional(),
});
export type MapsPlaceDetailsResult = z.infer<
typeof mapsPlaceDetailsResultSchema
>;
export const mapsPlacePhotoResultSchema = z.object({
photoUrl: z.string().nullable(),
attribution: z.string().nullable().optional(),
});
export type MapsPlacePhotoResult = z.infer<typeof mapsPlacePhotoResultSchema>;
export const mapsReverseResultSchema = z.object({
name: z.string().nullable(),
address: z.string().nullable(),
});
export type MapsReverseResult = z.infer<typeof mapsReverseResultSchema>;
export const mapsResolveUrlResultSchema = z.object({
lat: z.number(),
lng: z.number(),
name: z.string().nullable(),
address: z.string().nullable(),
});
export type MapsResolveUrlResult = z.infer<typeof mapsResolveUrlResultSchema>;
|