import {z} from 'zod' export type CreateApiJourneySearchEuInwardArgs = {body: {searchId: string, journeyId: string, alternativeIds: Array}}; export type CreateApiJourneySearchEuInwardOptions = {baseUrl: string, fetch?: typeof fetch | undefined}; export const createApiJourneySearchEuInwardResponse = z.object({data: z.object({brands: z.record(z.string(), z.object({id: z.string(), name: z.string(), code: z.string().optional()})).optional(), carriers: z.record(z.string(), z.object({id: z.string(), name: z.string(), code: z.string().optional(), ricsCode: z.union([z.string(), z.unknown()]).optional()})), discountCards: z.record(z.string(), z.object({id: z.string(), name: z.string(), code: z.string(), connections: z.array(z.string()).optional(), prefix: z.string().optional(), hasNumber: z.boolean().optional(), isNumberRequired: z.union([z.unknown(), z.boolean()]).optional(), length: z.union([z.unknown(), z.number()]).optional(), hasExpirationDate: z.boolean().optional(), validationAlgorithm: z.string().optional(), isLoyaltyCard: z.boolean().optional(), sellableUrl: z.string().optional(), canBeDisabled: z.boolean().optional(), isBusinessOnly: z.boolean().optional(), hasItinerary: z.boolean().optional(), hasVia: z.boolean().optional(), hasSpecificOriginDestination: z.boolean().optional(), validationRegex: z.string().optional(), language: z.string().optional(), ageRange: z.object({lowerBound: z.number().optional(), upperBound: z.number().optional()}).optional(), type: z.string().optional(), operator: z.union([z.unknown(), z.string()]).optional(), description: z.string().optional(), group: z.union([z.unknown(), z.string()]).optional(), defaultItineraryDescription: z.union([z.unknown(), z.string()]).optional()})), disruptions: z.record(z.string(), z.object({id: z.string().optional(), status: z.string().optional(), lastUpdated: z.string().optional(), message: z.string().optional(), severity: z.number().optional(), disruptionUrl: z.string().optional(), source: z.object({}).optional()})).optional(), fareTypes: z.record(z.string(), z.object({id: z.string(), name: z.string(), code: z.string(), secondaryFare: z.boolean().nullable().optional(), farePricingCategory: z.string().nullable().optional(), conditions: z.array(z.object({title: z.string(), description: z.string()})).optional(), validityPeriods: z.union([z.unknown(), z.object({outward: z.string(), inward: z.string().optional()})]).optional(), conditionsSummary: z.string().nullable().optional()})), journeySearch: z.object({id: z.string(), journeys: z.record(z.string(), z.object({id: z.string(), direction: z.enum(["outward", "inward"]), legs: z.array(z.string()), sections: z.array(z.string()), overtaken: z.object({isOvertaken: z.boolean().optional()}).optional(), departAt: z.string().optional(), arriveAt: z.string().optional(), duration: z.string().optional(), hash: z.string(), hasFirstClassRecommendedStationPair: z.boolean().optional(), co2EmissionInGrams: z.number().optional(), co2EmissionComparison: z.object({carPetrol: z.object({co2EmissionInGrams: z.number().optional(), percentageSavedByUsingBase: z.number().optional()}).optional(), carDiesel: z.object({co2EmissionInGrams: z.number().optional(), percentageSavedByUsingBase: z.number().optional()}).optional(), plane: z.object({co2EmissionInGrams: z.number().optional(), percentageSavedByUsingBase: z.number().optional()}).optional()}).optional(), unsellableReason: z.union([z.unknown(), z.object({code: z.string().optional(), longDescription: z.string().optional(), shortDescription: z.string().optional()})]).optional(), distanceInKm: z.number().optional(), bikeRestrictionStatus: z.union([z.unknown(), z.enum(["allowed", "allowedWithReservation", "notAllowed"])]).optional(), sponsorshipDefinition: z.union([z.unknown(), z.object({experiment: z.union([z.unknown(), z.string()]), text: z.string(), label: z.string(), type: z.enum(["Paid", "Featured", "Promoted"]), tag: z.string(), engagementSourceId: z.string().optional()})]).optional()})), alternatives: z.record(z.string(), z.object({id: z.string(), composition: z.array(z.unknown()).optional(), fares: z.array(z.string()), price: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), fullPrice: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), priceIncludingBookingFee: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), fullPriceIncludingBookingFee: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), farePassengers: z.array(z.object({fareId: z.string(), passengers: z.array(z.object({id: z.string(), passengerType: z.string()}))})), isPartial: z.boolean(), isOutOfCustomPolicy: z.boolean().optional(), hash: z.string(), cards: z.array(z.string()).optional(), correlationToken: z.string().optional(), comfortClassCorrelationToken: z.string().optional(), affiliation: z.union([z.unknown(), z.object({href: z.string().optional(), label: z.string().optional()})]).optional(), travellingGroups: z.array(z.object({farePassengerIds: z.array(z.string())})).optional(), availableExtras: z.union([z.unknown(), z.array(z.object({id: z.string().optional(), extraType: z.object({id: z.string(), code: z.string(), name: z.string(), description: z.union([z.unknown(), z.string()]).optional(), group: z.union([z.unknown(), z.string()]).optional(), conditions: z.union([z.unknown(), z.string()]).optional()}).optional(), price: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}), appliesTo: z.union([z.enum(["transportMode", "fareType"]), z.unknown()]).optional(), selectionStateChangeable: z.boolean(), selectedQuantity: z.union([z.number().int(), z.unknown()]).optional(), availableQuantity: z.union([z.number().int(), z.unknown()]).optional(), legIds: z.array(z.string()).optional(), farePassengerIds: z.array(z.string()).optional()}))]).optional(), selectedExtras: z.union([z.array(z.object({id: z.string().optional(), extraType: z.object({id: z.string(), code: z.string(), name: z.string(), description: z.union([z.unknown(), z.string()]).optional(), group: z.union([z.unknown(), z.string()]).optional(), conditions: z.union([z.unknown(), z.string()]).optional()}).optional(), price: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}), appliesTo: z.union([z.enum(["transportMode", "fareType"]), z.unknown()]).optional(), selectionStateChangeable: z.boolean(), selectedQuantity: z.union([z.number().int(), z.unknown()]).optional(), availableQuantity: z.union([z.number().int(), z.unknown()]).optional(), legIds: z.array(z.string()).optional(), farePassengerIds: z.array(z.string()).optional()})), z.unknown()]).optional(), flexibility: z.object({code: z.enum(["urn:trainline:flex:nonflexi", "urn:trainline:flex:semiflexi", "urn:trainline:flex:flexi"]).optional(), name: z.string().optional(), language: z.string().optional()}).optional(), billableUnits: z.array(z.object({correlationToken: z.string().optional(), fareLegIds: z.array(z.string()), originalPrice: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}), price: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}), passengerIds: z.array(z.string())})).optional(), calculatedPrice: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), savings: z.array(z.object({price: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}), savingClassification: z.string(), estimated: z.boolean().optional(), reference: z.string().optional()})).optional(), expiresAt: z.string().optional(), upsellProposals: z.array(z.object({id: z.string(), proposedPrice: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), proposedPriceIncludingBookingFee: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), proposedSavingPrice: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}), upsellConditions: z.array(z.object({id: z.string(), subjectType: z.string(), passengerIds: z.array(z.string()).optional(), subjects: z.array(z.object({id: z.string(), url: z.string(), type: z.string(), cardType: z.string().optional(), code: z.string().optional()}))}))})).optional(), nonContractualTerms: z.array(z.unknown()).optional(), vendorInformation: z.object({id: z.string().optional(), name: z.string(), code: z.string(), description: z.union([z.string(), z.unknown()]).optional()}).optional()})), fares: z.record(z.string(), z.object({id: z.string(), price: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), fullPrice: z.object({amount: z.number().gte(0), currencyCode: z.string().min(3).max(3), currencyConversionApplied: z.boolean().optional()}).optional(), deliveryOptions: z.array(z.string()), vouchers: z.array(z.object({voucherCode: z.string().optional(), id: z.string().optional()})).optional(), nonContractualTerms: z.array(z.object({code: z.string().optional(), pictogramKey: z.string().optional(), title: z.string().optional()})).optional(), fareLegs: z.array(z.object({id: z.string().optional(), legId: z.string(), travelClass: z.object({id: z.string(), name: z.string(), code: z.string().optional()}).optional(), comfort: z.object({id: z.string(), code: z.string(), description: z.string().nullable().optional(), allowsComfortChoice: z.boolean().nullable().optional(), name: z.string(), imageUrl: z.string().nullable().optional()}).optional(), boardBeforeGuaranteed: z.number().nullable().optional(), localTransport: z.array(z.object({id: z.string().optional(), carrier: z.object({url: z.string().optional(), id: z.string().optional()}).optional(), includeInFare: z.boolean().optional(), locations: z.array(z.object({url: z.string().optional(), id: z.string().optional()})).optional()})).nullable().optional(), reservationType: z.enum(["none", "reserved", "onTicket", "overbooked", "afterPayment", "corridor", "childOnLap"]).optional()})), fareType: z.string().optional(), fareTags: z.union([z.unknown(), z.array(z.string())]).optional(), fareCategory: z.string().optional(), cardIds: z.array(z.string()).optional(), routeRestriction: z.string().optional(), fareRestriction: z.string().optional(), validUntil: z.object({outward: z.string().optional(), inward: z.string().optional()}).optional(), origin: z.string(), destination: z.string(), hash: z.string(), availability: z.object({status: z.enum(["available", "limited"]).optional(), remaining: z.number().int().optional()}).optional(), permittedLocations: z.object({origin: z.array(z.string()).optional(), destination: z.array(z.string()).optional()}).optional(), appliedDiscount: z.string().optional()})), responseCards: z.record(z.string(), z.object({id: z.string(), code: z.string().optional(), cardType: z.string()})), legs: z.record(z.string(), z.object({id: z.string(), arrivalLocation: z.string(), brand: z.string().optional(), departureLocation: z.string(), duration: z.string(), arriveAt: z.string(), departAt: z.string(), timetableId: z.string().optional(), timetable: z.string().optional(), timetableConfirmed: z.boolean().optional(), carrier: z.string().optional(), finalDestinations: z.array(z.string()).optional(), vendorQuotaIdentifier: z.string().optional(), transportDesignation: z.string().optional(), route: z.string().optional(), routeColour: z.string().optional(), vehicleAttributes: z.array(z.string()).optional(), platforms: z.object({origin: z.object({dataSource: z.string().optional(), platform: z.string().optional()}).optional(), destination: z.object({dataSource: z.string().optional(), platform: z.string().optional()}).optional()}).optional(), realTime: z.object({isCancelled: z.boolean().optional(), disruptions: z.array(z.string()).optional(), origin: z.object({arrival: z.object({type: z.string().optional(), at: z.string().optional(), isDelayed: z.boolean().optional()}).optional(), departure: z.object({type: z.string().optional(), at: z.string().optional(), isDelayed: z.boolean().optional()}).optional()}).optional(), destination: z.object({arrival: z.object({type: z.string().optional(), at: z.string().optional(), isDelayed: z.boolean().optional()}).optional(), departure: z.object({type: z.string().optional(), at: z.string().optional(), isDelayed: z.boolean().optional()}).optional()}).optional()}).optional(), hash: z.string(), transportMode: z.string(), replacementServiceInfo: z.object({isReplacementService: z.boolean().optional()}).optional(), co2EmissionInGramsPerPassenger: z.number().optional(), distanceInKm: z.union([z.unknown(), z.number()]).optional(), isReservable: z.boolean().nullable().optional(), isReservationMandatory: z.boolean().nullable().optional(), bikeRestrictionStatus: z.union([z.unknown(), z.enum(["allowed", "allowedWithReservation", "notAllowed"])]).optional(), shouldShowDetails: z.array(z.string()).nullable().optional()})), sections: z.record(z.string(), z.object({id: z.string(), alternatives: z.array(z.string()), mixedLegComforts: z.union([z.unknown(), z.boolean()]).optional(), deliveryOptions: z.array(z.string()).optional()})), deliveryOptions: z.record(z.string(), z.object({id: z.string(), method: z.string(), fulfillmentDelay: z.number()})), passengers: z.record(z.string(), z.object({id: z.string(), gender: z.string().optional(), age: z.union([z.unknown(), z.string()]).optional(), dateOfBirth: z.string(), cardIds: z.array(z.string()).optional(), cards: z.array(z.object({})).optional(), disabilityInformation: z.array(z.object({disabilityCardLevel: z.enum(["disabledPerson", "legallyBlind", "none", "thirdPartyNeeded"]), wheelchairUser: z.boolean()})).optional()})), createdAt: z.string(), expiresAt: z.string().optional(), pagination: z.object({next: z.object({outward: z.boolean(), inward: z.boolean()}), previous: z.object({outward: z.boolean(), inward: z.boolean()})}), warnings: z.array(z.object({code: z.string().optional(), severity: z.enum(["correctable", "intermittent", "unrecoverable", "unexpected"]).optional(), detail: z.string().optional(), target: z.array(z.object({id: z.string().optional(), type: z.string().optional()})).optional()})).optional(), inwardSearch: z.boolean().optional(), transportModes: z.array(z.string()).optional(), travelPolicyResults: z.object({}).optional(), sponsoredSearchExperiment: z.string().nullable().optional()}), routeRestrictions: z.record(z.string(), z.object({id: z.string(), description: z.string().optional(), code: z.string().optional()})).optional(), fareRestrictions: z.record(z.string(), z.object({id: z.string(), code: z.string().optional(), internalResourceUrl: z.string(), externalResourceUrl: z.string()})).optional(), travelClasses: z.record(z.string(), z.object({id: z.string(), name: z.string(), code: z.string().optional()})).optional(), locations: z.record(z.string(), z.object({id: z.string(), code: z.string().optional(), locationType: z.string().optional(), name: z.string(), nonDiacriticName: z.string().optional(), timezone: z.string().optional(), language: z.string().nullable().optional(), shortName: z.string().nullable().optional(), aliases: z.union([z.unknown(), z.array(z.string())]).optional(), longitude: z.number().nullable().optional(), latitude: z.number().nullable().optional(), address: z.union([z.unknown(), z.array(z.string())]).optional(), countryCode: z.string().nullable().optional(), connections: z.array(z.string()).optional(), parents: z.unknown()})), passengerTypes: z.record(z.string(), z.object({id: z.string(), name: z.string(), ageRestriction: z.object({lowerBound: z.number(), outerBound: z.number().optional()}), code: z.string().optional()})).optional(), fareCategories: z.record(z.string(), z.object({id: z.string().optional(), code: z.string().optional(), name: z.string().optional()})).optional(), transportModes: z.record(z.string(), z.object({id: z.string().optional(), code: z.string().optional(), name: z.string().optional(), mode: z.string().optional()})).optional(), appliedDiscounts: z.record(z.string(), z.object({id: z.string().optional(), code: z.string().optional(), industryCode: z.string().optional(), name: z.string().optional()})).optional(), travelPolicyResutls: z.record(z.string(), z.object({policyId: z.string().nullable().optional(), policyType: z.string().nullable().optional(), corporateId: z.string().optional(), journeyAlternatives: z.array(z.object({id: z.string().nullable().optional(), journeyId: z.string().nullable().optional(), direction: z.string().nullable().optional(), inPolicy: z.boolean().optional(), priceMark: z.number().nullable().optional(), sectionAlternatives: z.array(z.object({sectionId: z.string().nullable().optional(), alternativeId: z.string().nullable().optional()})).nullable().optional()})).nullable().optional()})).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 }; /** * Returns a Journey Search result for an EU return journey * * POST /journey-search-eu-inward */ export const createApiJourneySearchEuInward = async (args: CreateApiJourneySearchEuInwardArgs, options: CreateApiJourneySearchEuInwardOptions) => { const {baseUrl, fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/journey-search-eu-inward${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(createApiJourneySearchEuInwardResponse, await res.json()) };