import {z} from 'zod' export type Id = string; export type Revision = number; export type GetApiPlacesIdHistoryRevisionArgs = {id: Id, revision: Revision}; export type GetApiPlacesIdHistoryRevisionOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const id = z.string().min(32).max(32); export const license = z.string().min(1); export const placeRoot = z.object({id: id, lic: license}); export const createdAt = z.number().int(); export const userEmail = z.string().min(3); export const activity = z.object({at: createdAt, by: userEmail.optional()}); export const revision = z.number().int().gte(0); export const title = z.string().min(1); export const description = z.string().min(1); export const latLonDeg = z.array(z.number()); export const street = z.string(); export const city = z.string(); export const zipCode = z.string(); export const country = z.string(); export const state = z.string(); export const address = z.object({street: street.optional(), city: city.optional(), zip: zipCode.optional(), country: country.optional(), state: state.optional()}); export const location = z.object({deg: latLonDeg, adr: address.optional()}); export const contactName = z.string(); export const contactPhone = z.string(); export const contactEmail = z.string().min(3); export const contact = z.object({name: contactName.optional(), phone: contactPhone.optional(), email: contactEmail.optional()}); export const openingHours = z.string().min(4); export const foundingDate = z.string(); export const url = z.string().min(8); export const imageUrl = z.string().min(8); export const imageLink = z.string().min(8); export const placeLinks = z.object({www: url.optional(), img: imageUrl.optional(), img_href: imageLink.optional()}); export const tag = z.string().min(1); export const tagArray = z.array(tag); export const placeRevision = z.object({created: activity, rev: revision, tit: title, dsc: description, loc: location.optional(), cnt: contact.optional(), hrs: openingHours.optional(), fnd: foundingDate.optional(), lnk: placeLinks.optional(), tag: tagArray.optional()}); export const activityContext = z.string(); export const activityComment = z.string(); export const activityLog = z.object({at: createdAt, by: userEmail.optional(), ctx: activityContext.optional(), comment: activityComment.optional()}); export const reviewStatus = z.enum(["created", "confirmed", "rejected", "archived"]); export const reviewStatusLog = z.object({rev: revision.optional(), act: activityLog.optional(), status: reviewStatus.optional()}); export const reviewStatusLogArray = z.array(reviewStatusLog); export const placeRevisionLog = z.array(z.union([placeRevision, reviewStatusLogArray])); export const placeRevisionLogArray = z.array(placeRevisionLog); export const placeHistory = z.object({place: placeRoot, revisions: placeRevisionLogArray.optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * History of place revisions * * GET /places/{id}/history/{revision} */ export const getApiPlacesIdHistoryRevision = async (args: GetApiPlacesIdHistoryRevisionArgs, options?: GetApiPlacesIdHistoryRevisionOptions) => { const {baseUrl = "https://api.ofdb.io/v0", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/places/${encodeURIComponent(String(args.id))}/history/${encodeURIComponent(String(args.revision))}${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(placeHistory, await res.json()) };