import {z} from 'zod' export type MarketIdParam = string; export type ChannelOption = 'airbnb' | 'vrbo' | 'booking'; export type ListingType = 'entire_place' | 'private_room' | 'shared_room'; export type PriceTier = 'budget' | 'economy' | 'midscale' | 'upscale' | 'luxury'; export type RealEstateType = 'apartment' | 'bed_and_breakfast' | 'house' | 'unique'; export type MarketMetricsFilterListWithPercentActiveSchema = Array<{field: 'accommodates', type: 'gt' | 'gte' | 'lt' | 'lte', value: number} | {field: 'accommodates', type: 'select', value: number} | {field: 'accommodates', type: 'range', value: Array} | {field: 'bathrooms', type: 'gt' | 'gte' | 'lt' | 'lte', value: number} | {field: 'bathrooms', type: 'select', value: number} | {field: 'bathrooms', type: 'range', value: Array} | {field: 'bedrooms', type: 'gt' | 'gte' | 'lt' | 'lte', value: number} | {field: 'bedrooms', type: 'select', value: number} | {field: 'bedrooms', type: 'range', value: Array} | {field: 'channels', type: 'jsonb_array', value: Array} | {field: 'channels', type: 'jsonb_array_subset', value: Array} | {field: 'channels', type: 'jsonb_array_exact', value: Array} | {field: 'listing_type', type: 'select', value: 'entire_place' | 'private_room' | 'shared_room'} | {field: 'listing_type', type: 'multi_select', value: Array} | {field: 'percent_active', type: 'gt' | 'gte' | 'lt' | 'lte', value: number} | {field: 'percent_active', type: 'select', value: number} | {field: 'percent_active', type: 'range', value: Array} | {field: 'price_tier', type: 'select', value: 'budget' | 'economy' | 'midscale' | 'upscale' | 'luxury'} | {field: 'price_tier', type: 'multi_select', value: Array} | {field: 'professionally_managed', type: 'select', value: boolean} | {field: 'ratings', type: 'gt' | 'gte' | 'lt' | 'lte', value: number} | {field: 'ratings', type: 'select', value: number} | {field: 'ratings', type: 'range', value: Array} | {field: 'real_estate_type', type: 'select', value: 'apartment' | 'bed_and_breakfast' | 'house' | 'unique'} | {field: 'real_estate_type', type: 'multi_select', value: Array}>; export type PercentilesRequestSchema = Array; export type MarketMetricsBaseRequestPercentilesSchema = {num_months: number, filters?: MarketMetricsFilterListWithPercentActiveSchema | undefined, percentiles?: PercentilesRequestSchema | undefined}; export type CreateApiMarketMarketIdMetricsOccupancyArgs = {marketId: MarketIdParam, body: MarketMetricsBaseRequestPercentilesSchema}; export type CreateApiMarketMarketIdMetricsOccupancyOptions = {baseUrl?: string | undefined, headerAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const marketOccupancyMetricsResponseSchema = z.object({metrics: z.array(z.object({date: z.string(), occupancy_rate: z.number().gte(0).lte(100), available_listings: z.number().int().gte(0), booked_listings: z.number().int().gte(0), days_available: z.number().int().gte(0), days_booked: z.number().int().gte(0), occupancy_rate_percentiles: z.array(z.number().nullable()).optional()})), monthly_pct_change: z.number().nullable(), yearly_pct_change: z.number().nullable()}); export const status = z.object({type: z.enum(["success", "error"]), response_id: z.string(), message: z.string()}); export const createApiMarketMarketIdMetricsOccupancyResponse = z.object({payload: marketOccupancyMetricsResponseSchema, status: status}); 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 }; /** * Fetch Occupancy Metrics for a Market. * * POST /market/{marketId}/metrics/occupancy */ export const createApiMarketMarketIdMetricsOccupancy = async (args: CreateApiMarketMarketIdMetricsOccupancyArgs, options?: CreateApiMarketMarketIdMetricsOccupancyOptions) => { const {baseUrl = "https://api.airdna.co/api/enterprise/v2", fetch: fetchFn = fetch, headerAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (headerAuth !== undefined) headers.set('authorization', `Bearer ${headerAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/market/${encodeURIComponent(String(args.marketId))}/metrics/occupancy${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(createApiMarketMarketIdMetricsOccupancyResponse, await res.json()) };