import {z} from 'zod' export type GetApiShoppingHotelOffersArgs = {hotelIds: Array, adults?: number | undefined, checkInDate?: string | undefined, checkOutDate?: string | undefined, countryOfResidence?: string | undefined, roomQuantity?: number | undefined, priceRange?: string | undefined, currency?: string | undefined, paymentPolicy?: ('GUARANTEE' | 'DEPOSIT' | 'NONE') | undefined, boardType?: ('ROOM_ONLY' | 'BREAKFAST' | 'HALF_BOARD' | 'FULL_BOARD' | 'ALL_INCLUSIVE') | undefined, includeClosed?: boolean | undefined, bestRateOnly?: boolean | undefined, lang?: string | undefined}; export type GetApiShoppingHotelOffersOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const type = z.literal("hotel-offer"); export const hotelProductRateFamily = z.object({code: z.string().optional(), type: z.string().optional()}); export const qualifiedFreeText = z.object({text: z.string().optional(), lang: z.string().optional()}); export const hotelProductCommission = z.object({percentage: z.string().optional(), amount: z.string().optional(), description: qualifiedFreeText.optional()}); export const boardType = z.enum(["ROOM_ONLY", "BREAKFAST", "HALF_BOARD", "FULL_BOARD", "ALL_INCLUSIVE", "BUFFET_BREAKFAST", "CARIBBEAN_BREAKFAST", "CONTINENTAL_BREAKFAST", "ENGLISH_BREAKFAST", "FULL_BREAKFAST", "DINNER_BED_AND_BREAKFAST", "LUNCH", "DINNER", "FAMILY_PLAN", "AS_BROCHURED", "SELF_CATERING", "BERMUDA", "AMERICAN", "FAMILY_AMERICAN", "MODIFIED"]); export const hotelProductEstimatedRoomType = z.object({category: z.string().optional(), beds: z.number().int().optional(), bedType: z.string().optional()}); export const hotelProductRoomDetails = z.object({type: z.string().optional(), typeEstimated: hotelProductEstimatedRoomType.optional(), description: qualifiedFreeText.optional()}); export const hotelProductGuests = z.object({adults: z.number().int().gte(1).lte(9).optional(), childAges: z.array(z.number().int().gte(0).lte(20)).optional()}); export const tax = z.object({amount: z.string().optional(), currency: z.string().min(1).max(3).optional(), code: z.string().optional(), percentage: z.string().optional(), included: z.boolean().optional(), description: z.string().optional(), pricingFrequency: z.string().optional(), pricingMode: z.string().optional()}); export const markup = z.object({amount: z.string().optional()}); export const price = z.object({currency: z.string().optional(), sellingTotal: z.string().optional(), total: z.string().optional(), base: z.string().optional(), markups: z.array(markup).optional()}); export const hotelProductPriceVariation = z.object({startDate: z.string(), endDate: z.string(), currency: z.string().optional(), sellingTotal: z.string().optional(), total: z.string().optional(), base: z.string().optional(), markups: z.array(markup).optional()}); export const hotelProductPriceVariations = z.object({average: price.optional(), changes: z.array(hotelProductPriceVariation).optional()}); export const hotelProductHotelPrice = z.object({currency: z.string().optional(), sellingTotal: z.string().optional(), total: z.string().optional(), base: z.string().optional(), taxes: z.array(tax).optional(), markups: z.array(markup).optional(), variations: hotelProductPriceVariations.optional()}); export const paymentType = z.enum(["GUARANTEE", "DEPOSIT", "PREPAY", "HOLDTIME"]); export const method = z.enum(["CREDIT_CARD", "AGENCY_ACCOUNT", "TRAVEL_AGENT_ID", "CORPORATE_ID", "HOTEL_GUEST_ID", "CHECK", "MISC_CHARGE_ORDER", "ADVANCE_DEPOSIT", "COMPANY_ADDRESS", "VCC_BILLBACK", "VCC_B2B_WALLET", "DEFERED_PAYMENT", "TRAVEL_AGENT_IMMEDIATE"]); export const hotelProductPaymentPolicy = z.object({creditCards: z.array(z.string().min(2).max(2)).optional(), methods: z.array(method).optional()}); export const hotelProductGuaranteePolicy = z.object({description: qualifiedFreeText.optional(), acceptedPayments: hotelProductPaymentPolicy.optional()}); export const hotelProductDepositPolicy = z.object({amount: z.string().optional(), deadline: z.string().optional(), description: qualifiedFreeText.optional(), acceptedPayments: hotelProductPaymentPolicy.optional()}); export const hotelProductHoldPolicy = z.object({deadline: z.string()}); export const cancellationType = z.literal("FULL_STAY"); export const hotelProductCancellationPolicy = z.object({type: cancellationType.optional(), amount: z.string().optional(), numberOfNights: z.number().int().gte(0).optional(), percentage: z.string().optional(), deadline: z.string().optional(), description: qualifiedFreeText.optional()}); export const hotelProductCheckInOutPolicy = z.object({checkIn: z.string().optional(), checkInDescription: qualifiedFreeText.optional(), checkOut: z.string().optional(), checkOutDescription: qualifiedFreeText.optional()}); export const hotelProductPolicyDetails = z.object({paymentType: paymentType.optional(), guarantee: hotelProductGuaranteePolicy.optional(), deposit: hotelProductDepositPolicy.optional(), prepay: hotelProductDepositPolicy.optional(), holdTime: hotelProductHoldPolicy.optional(), cancellations: z.array(hotelProductCancellationPolicy).optional(), checkInOut: hotelProductCheckInOutPolicy.optional()}); export const hotelOffer = z.object({type: type.optional(), id: z.string().min(2).max(100), checkInDate: z.string().optional(), checkOutDate: z.string().optional(), roomQuantity: z.string().optional(), rateCode: z.string().min(3).max(3), rateFamilyEstimated: hotelProductRateFamily.optional(), category: z.string().optional(), description: qualifiedFreeText.optional(), commission: hotelProductCommission.optional(), boardType: boardType.optional(), room: hotelProductRoomDetails, guests: hotelProductGuests.optional(), price: hotelProductHotelPrice, policies: hotelProductPolicyDetails.optional(), self: z.string().optional()}); export const hotel = z.object({hotelId: z.string().optional(), chainCode: z.string().optional(), brandCode: z.string().optional(), dupeId: z.string().optional(), name: z.string().optional(), cityCode: z.string().optional()}); export const hotelOffers = z.object({type: z.string().optional(), available: z.boolean().optional(), self: z.string().optional(), offers: z.array(hotelOffer).optional(), hotel: hotel.optional()}); export const multiResponse = z.object({data: hotelOffers.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 }; /** * getMultiHotelOffers * * GET /shopping/hotel-offers */ export const getApiShoppingHotelOffers = async (args: GetApiShoppingHotelOffersArgs, options?: GetApiShoppingHotelOffersOptions) => { const {baseUrl = "https://test.api.amadeus.com/v3", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/vnd.amadeus+json") for (const item of args.hotelIds ?? []) query.append("hotelIds", String(item)) if (args.adults != null) query.append("adults", String(args.adults)) if (args.checkInDate != null) query.append("checkInDate", String(args.checkInDate)) if (args.checkOutDate != null) query.append("checkOutDate", String(args.checkOutDate)) if (args.countryOfResidence != null) query.append("countryOfResidence", String(args.countryOfResidence)) if (args.roomQuantity != null) query.append("roomQuantity", String(args.roomQuantity)) if (args.priceRange != null) query.append("priceRange", String(args.priceRange)) if (args.currency != null) query.append("currency", String(args.currency)) if (args.paymentPolicy != null) query.append("paymentPolicy", String(args.paymentPolicy)) if (args.boardType != null) query.append("boardType", String(args.boardType)) if (args.includeClosed != null) query.append("includeClosed", String(args.includeClosed)) if (args.bestRateOnly != null) query.append("bestRateOnly", String(args.bestRateOnly)) if (args.lang != null) query.append("lang", String(args.lang)) const url = `${baseUrl.replace(/\/+$/, '')}/shopping/hotel-offers${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(multiResponse, await res.json()) };