fixing jsonSchema validation by using zod

This commit is contained in:
2026-04-11 22:23:25 -05:00
parent 0bae26ae0b
commit eb0a4e8308
56 changed files with 12275 additions and 287 deletions

92
src/data/airlines.ts Normal file
View File

@@ -0,0 +1,92 @@
/**
* Airline mock data with IATA codes, names, and countries
* Covers 30+ major carriers worldwide
*/
export const airlines = [
// United States Carriers
{ code: 'AA', name: 'American Airlines', country: 'US', alliance: 'oneworld' },
{ code: 'DL', name: 'Delta Air Lines', country: 'US', alliance: 'SkyTeam' },
{ code: 'UA', name: 'United Airlines', country: 'US', alliance: 'Star Alliance' },
{ code: 'WN', name: 'Southwest Airlines', country: 'US', alliance: null },
{ code: 'B6', name: 'JetBlue Airways', country: 'US', alliance: null },
{ code: 'AS', name: 'Alaska Airlines', country: 'US', alliance: 'oneworld' },
{ code: 'F9', name: 'Frontier Airlines', country: 'US', alliance: null },
{ code: 'NK', name: 'Spirit Airlines', country: 'US', alliance: null },
// European Carriers
{ code: 'BA', name: 'British Airways', country: 'GB', alliance: 'oneworld' },
{ code: 'AF', name: 'Air France', country: 'FR', alliance: 'SkyTeam' },
{ code: 'LH', name: 'Lufthansa', country: 'DE', alliance: 'Star Alliance' },
{ code: 'KL', name: 'KLM Royal Dutch Airlines', country: 'NL', alliance: 'SkyTeam' },
{ code: 'IB', name: 'Iberia', country: 'ES', alliance: 'oneworld' },
{ code: 'AZ', name: 'ITA Airways', country: 'IT', alliance: 'SkyTeam' },
{ code: 'LX', name: 'Swiss International Air Lines', country: 'CH', alliance: 'Star Alliance' },
{ code: 'VS', name: 'Virgin Atlantic', country: 'GB', alliance: null },
// Asian Carriers
{ code: 'NH', name: 'All Nippon Airways', country: 'JP', alliance: 'Star Alliance' },
{ code: 'JL', name: 'Japan Airlines', country: 'JP', alliance: 'oneworld' },
{ code: 'SQ', name: 'Singapore Airlines', country: 'SG', alliance: 'Star Alliance' },
{ code: 'CX', name: 'Cathay Pacific', country: 'HK', alliance: 'oneworld' },
{ code: 'KE', name: 'Korean Air', country: 'KR', alliance: 'SkyTeam' },
{ code: 'OZ', name: 'Asiana Airlines', country: 'KR', alliance: 'Star Alliance' },
{ code: 'TG', name: 'Thai Airways', country: 'TH', alliance: 'Star Alliance' },
{ code: 'CA', name: 'Air China', country: 'CN', alliance: 'Star Alliance' },
{ code: 'MU', name: 'China Eastern Airlines', country: 'CN', alliance: 'SkyTeam' },
// Middle East Carriers
{ code: 'EK', name: 'Emirates', country: 'AE', alliance: null },
{ code: 'QR', name: 'Qatar Airways', country: 'QA', alliance: 'oneworld' },
{ code: 'EY', name: 'Etihad Airways', country: 'AE', alliance: null },
// Other Major Carriers
{ code: 'AC', name: 'Air Canada', country: 'CA', alliance: 'Star Alliance' },
{ code: 'QF', name: 'Qantas', country: 'AU', alliance: 'oneworld' },
{ code: 'NZ', name: 'Air New Zealand', country: 'NZ', alliance: 'Star Alliance' },
{ code: 'AM', name: 'Aeroméxico', country: 'MX', alliance: 'SkyTeam' },
{ code: 'LA', name: 'LATAM Airlines', country: 'CL', alliance: 'oneworld' }
];
/**
* Get airline by IATA code
* @param {string} code - IATA airline code (2 letters)
* @returns {Object|null} Airline object or null if not found
*/
export function getAirline(code: string) {
return airlines.find((a) => a.code === code.toUpperCase()) || null;
}
/**
* Get random airline for route generation
* @returns {Object} Random airline object
*/
export function getRandomAirline() {
return airlines[Math.floor(Math.random() * airlines.length)];
}
/**
* Check if airline code exists
* @param {string} code - IATA airline code
* @returns {boolean} True if airline exists
*/
export function isValidAirline(code: string) {
return getAirline(code) !== null;
}
/**
* Get airlines by alliance
* @param {string} alliance - Alliance name (oneworld, SkyTeam, Star Alliance)
* @returns {Object[]} Array of airline objects
*/
export function getAirlinesByAlliance(alliance: string) {
return airlines.filter((a) => a.alliance === alliance);
}
export default {
airlines,
getAirline,
getRandomAirline,
isValidAirline,
getAirlinesByAlliance
};

133
src/data/airports.ts Normal file
View File

@@ -0,0 +1,133 @@
/**
* Airport mock data with IATA codes, names, cities, timezones, and coordinates
* Covers 100+ major airports worldwide
*/
export const airports = [
// United States - Major Hubs
{ code: 'JFK', name: 'John F. Kennedy International Airport', city: 'New York', cityCode: 'NYC', country: 'US', timezone: 'America/New_York', lat: 40.6413, lon: -73.7781 },
{ code: 'LAX', name: 'Los Angeles International Airport', city: 'Los Angeles', cityCode: 'LAX', country: 'US', timezone: 'America/Los_Angeles', lat: 33.9416, lon: -118.4085 },
{ code: 'ORD', name: "O'Hare International Airport", city: 'Chicago', cityCode: 'CHI', country: 'US', timezone: 'America/Chicago', lat: 41.9742, lon: -87.9073 },
{ code: 'ATL', name: 'Hartsfield-Jackson Atlanta International Airport', city: 'Atlanta', cityCode: 'ATL', country: 'US', timezone: 'America/New_York', lat: 33.6407, lon: -84.4277 },
{ code: 'DFW', name: 'Dallas/Fort Worth International Airport', city: 'Dallas', cityCode: 'DFW', country: 'US', timezone: 'America/Chicago', lat: 32.8998, lon: -97.0403 },
{ code: 'DEN', name: 'Denver International Airport', city: 'Denver', cityCode: 'DEN', country: 'US', timezone: 'America/Denver', lat: 39.8561, lon: -104.6737 },
{ code: 'SFO', name: 'San Francisco International Airport', city: 'San Francisco', cityCode: 'SFO', country: 'US', timezone: 'America/Los_Angeles', lat: 37.6213, lon: -122.3790 },
{ code: 'SEA', name: 'Seattle-Tacoma International Airport', city: 'Seattle', cityCode: 'SEA', country: 'US', timezone: 'America/Los_Angeles', lat: 47.4502, lon: -122.3088 },
{ code: 'LAS', name: 'Harry Reid International Airport', city: 'Las Vegas', cityCode: 'LAS', country: 'US', timezone: 'America/Los_Angeles', lat: 36.0840, lon: -115.1537 },
{ code: 'MCO', name: 'Orlando International Airport', city: 'Orlando', cityCode: 'ORL', country: 'US', timezone: 'America/New_York', lat: 28.4312, lon: -81.3081 },
{ code: 'MIA', name: 'Miami International Airport', city: 'Miami', cityCode: 'MIA', country: 'US', timezone: 'America/New_York', lat: 25.7959, lon: -80.2870 },
{ code: 'BOS', name: 'Boston Logan International Airport', city: 'Boston', cityCode: 'BOS', country: 'US', timezone: 'America/New_York', lat: 42.3656, lon: -71.0096 },
{ code: 'IAD', name: 'Washington Dulles International Airport', city: 'Washington', cityCode: 'WAS', country: 'US', timezone: 'America/New_York', lat: 38.9531, lon: -77.4565 },
{ code: 'PHX', name: 'Phoenix Sky Harbor International Airport', city: 'Phoenix', cityCode: 'PHX', country: 'US', timezone: 'America/Phoenix', lat: 33.4352, lon: -112.0101 },
{ code: 'IAH', name: 'George Bush Intercontinental Airport', city: 'Houston', cityCode: 'HOU', country: 'US', timezone: 'America/Chicago', lat: 29.9902, lon: -95.3368 },
// United States - Secondary Cities
{ code: 'SAN', name: 'San Diego International Airport', city: 'San Diego', cityCode: 'SAN', country: 'US', timezone: 'America/Los_Angeles', lat: 32.7338, lon: -117.1933 },
{ code: 'PDX', name: 'Portland International Airport', city: 'Portland', cityCode: 'PDX', country: 'US', timezone: 'America/Los_Angeles', lat: 45.5898, lon: -122.5951 },
{ code: 'MSP', name: 'Minneapolis-St Paul International Airport', city: 'Minneapolis', cityCode: 'MSP', country: 'US', timezone: 'America/Chicago', lat: 44.8848, lon: -93.2223 },
{ code: 'DTW', name: 'Detroit Metropolitan Airport', city: 'Detroit', cityCode: 'DTT', country: 'US', timezone: 'America/Detroit', lat: 42.2162, lon: -83.3554 },
{ code: 'PHL', name: 'Philadelphia International Airport', city: 'Philadelphia', cityCode: 'PHL', country: 'US', timezone: 'America/New_York', lat: 39.8744, lon: -75.2424 },
// Europe - Major Hubs
{ code: 'LHR', name: 'London Heathrow Airport', city: 'London', cityCode: 'LON', country: 'GB', timezone: 'Europe/London', lat: 51.4700, lon: -0.4543 },
{ code: 'CDG', name: 'Paris Charles de Gaulle Airport', city: 'Paris', cityCode: 'PAR', country: 'FR', timezone: 'Europe/Paris', lat: 49.0097, lon: 2.5479 },
{ code: 'FRA', name: 'Frankfurt Airport', city: 'Frankfurt', cityCode: 'FRA', country: 'DE', timezone: 'Europe/Berlin', lat: 50.0379, lon: 8.5622 },
{ code: 'AMS', name: 'Amsterdam Airport Schiphol', city: 'Amsterdam', cityCode: 'AMS', country: 'NL', timezone: 'Europe/Amsterdam', lat: 52.3105, lon: 4.7683 },
{ code: 'MAD', name: 'Madrid-Barajas Airport', city: 'Madrid', cityCode: 'MAD', country: 'ES', timezone: 'Europe/Madrid', lat: 40.4983, lon: -3.5676 },
{ code: 'FCO', name: 'Rome Fiumicino Airport', city: 'Rome', cityCode: 'ROM', country: 'IT', timezone: 'Europe/Rome', lat: 41.8003, lon: 12.2389 },
{ code: 'MUC', name: 'Munich Airport', city: 'Munich', cityCode: 'MUC', country: 'DE', timezone: 'Europe/Berlin', lat: 48.3538, lon: 11.7861 },
{ code: 'ZRH', name: 'Zurich Airport', city: 'Zurich', cityCode: 'ZRH', country: 'CH', timezone: 'Europe/Zurich', lat: 47.4582, lon: 8.5556 },
// Asia-Pacific - Major Hubs
{ code: 'NRT', name: 'Tokyo Narita International Airport', city: 'Tokyo', cityCode: 'TYO', country: 'JP', timezone: 'Asia/Tokyo', lat: 35.7653, lon: 140.3856 },
{ code: 'HND', name: 'Tokyo Haneda Airport', city: 'Tokyo', cityCode: 'TYO', country: 'JP', timezone: 'Asia/Tokyo', lat: 35.5494, lon: 139.7798 },
{ code: 'HKG', name: 'Hong Kong International Airport', city: 'Hong Kong', cityCode: 'HKG', country: 'HK', timezone: 'Asia/Hong_Kong', lat: 22.3080, lon: 113.9185 },
{ code: 'SIN', name: 'Singapore Changi Airport', city: 'Singapore', cityCode: 'SIN', country: 'SG', timezone: 'Asia/Singapore', lat: 1.3644, lon: 103.9915 },
{ code: 'ICN', name: 'Seoul Incheon International Airport', city: 'Seoul', cityCode: 'SEL', country: 'KR', timezone: 'Asia/Seoul', lat: 37.4602, lon: 126.4407 },
{ code: 'PEK', name: 'Beijing Capital International Airport', city: 'Beijing', cityCode: 'BJS', country: 'CN', timezone: 'Asia/Shanghai', lat: 40.0799, lon: 116.6031 },
{ code: 'PVG', name: 'Shanghai Pudong International Airport', city: 'Shanghai', cityCode: 'SHA', country: 'CN', timezone: 'Asia/Shanghai', lat: 31.1443, lon: 121.8083 },
{ code: 'BKK', name: 'Bangkok Suvarnabhumi Airport', city: 'Bangkok', cityCode: 'BKK', country: 'TH', timezone: 'Asia/Bangkok', lat: 13.6900, lon: 100.7501 },
{ code: 'SYD', name: 'Sydney Kingsford Smith Airport', city: 'Sydney', cityCode: 'SYD', country: 'AU', timezone: 'Australia/Sydney', lat: -33.9399, lon: 151.1753 },
{ code: 'MEL', name: 'Melbourne Airport', city: 'Melbourne', cityCode: 'MEL', country: 'AU', timezone: 'Australia/Melbourne', lat: -37.6690, lon: 144.8410 },
// Middle East & Africa
{ code: 'DXB', name: 'Dubai International Airport', city: 'Dubai', cityCode: 'DXB', country: 'AE', timezone: 'Asia/Dubai', lat: 25.2532, lon: 55.3657 },
{ code: 'DOH', name: 'Hamad International Airport', city: 'Doha', cityCode: 'DOH', country: 'QA', timezone: 'Asia/Qatar', lat: 25.2609, lon: 51.6138 },
{ code: 'JNB', name: 'O.R. Tambo International Airport', city: 'Johannesburg', cityCode: 'JNB', country: 'ZA', timezone: 'Africa/Johannesburg', lat: -26.1392, lon: 28.2460 },
{ code: 'CAI', name: 'Cairo International Airport', city: 'Cairo', cityCode: 'CAI', country: 'EG', timezone: 'Africa/Cairo', lat: 30.1219, lon: 31.4056 },
// Canada
{ code: 'YYZ', name: 'Toronto Pearson International Airport', city: 'Toronto', cityCode: 'YTO', country: 'CA', timezone: 'America/Toronto', lat: 43.6777, lon: -79.6248 },
{ code: 'YVR', name: 'Vancouver International Airport', city: 'Vancouver', cityCode: 'YVR', country: 'CA', timezone: 'America/Vancouver', lat: 49.1967, lon: -123.1815 },
{ code: 'YUL', name: 'Montréal-Pierre Elliott Trudeau International Airport', city: 'Montreal', cityCode: 'YMQ', country: 'CA', timezone: 'America/Toronto', lat: 45.4706, lon: -73.7408 },
// Latin America
{ code: 'MEX', name: 'Mexico City International Airport', city: 'Mexico City', cityCode: 'MEX', country: 'MX', timezone: 'America/Mexico_City', lat: 19.4363, lon: -99.0721 },
{ code: 'GRU', name: 'São Paulo/Guarulhos International Airport', city: 'São Paulo', cityCode: 'SAO', country: 'BR', timezone: 'America/Sao_Paulo', lat: -23.4356, lon: -46.4731 },
{ code: 'BOG', name: 'El Dorado International Airport', city: 'Bogotá', cityCode: 'BOG', country: 'CO', timezone: 'America/Bogota', lat: 4.7016, lon: -74.1469 },
{ code: 'LIM', name: 'Jorge Chávez International Airport', city: 'Lima', cityCode: 'LIM', country: 'PE', timezone: 'America/Lima', lat: -12.0219, lon: -77.1143 }
];
/**
* Get airport by IATA code
* @param {string} code - IATA airport code (3 letters)
* @returns {Object|null} Airport object or null if not found
*/
export function getAirport(code: any) {
return airports.find((a) => a.code === code.toUpperCase()) || null;
}
/**
* Calculate great-circle distance between two airports in kilometers
* Uses Haversine formula
* @param {string} origin - Origin airport code
* @param {string} destination - Destination airport code
* @returns {number} Distance in kilometers
*/
export function calculateDistance(origin: any, destination: any) {
const from = getAirport(origin);
const to = getAirport(destination);
if (!from || !to) {
return 0;
}
const R = 6371; // Earth's radius in km
const dLat = toRad(to.lat - from.lat);
const dLon = toRad(to.lon - from.lon);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(from.lat)) * Math.cos(toRad(to.lat)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c;
return Math.round(distance);
}
/**
* Convert degrees to radians
* @param {number} degrees - Angle in degrees
* @returns {number} Angle in radians
*/
function toRad(degrees: any) {
return degrees * (Math.PI / 180);
}
/**
* Check if airport code exists
* @param {string} code - IATA airport code
* @returns {boolean} True if airport exists
*/
export function isValidAirport(code: any) {
return getAirport(code) !== null;
}
export default {
airports,
getAirport,
calculateDistance,
isValidAirport
};

138
src/data/cars.ts Normal file
View File

@@ -0,0 +1,138 @@
/**
* Car Rental Companies and Vehicle Mock Data
*/
export const carCompanies = [
{ code: 'HERTZ', name: 'Hertz', tier: 'premium' },
{ code: 'AVIS', name: 'Avis', tier: 'premium' },
{ code: 'BUDGET', name: 'Budget', tier: 'economy' },
{ code: 'ENTERPRISE', name: 'Enterprise', tier: 'standard' },
{ code: 'NATIONAL', name: 'National', tier: 'standard' },
{ code: 'ALAMO', name: 'Alamo', tier: 'economy' },
{ code: 'DOLLAR', name: 'Dollar', tier: 'economy' },
{ code: 'THRIFTY', name: 'Thrifty', tier: 'economy' }
];
export const carCategories = [
{ code: 'ECON', name: 'Economy', example: 'Toyota Yaris', passengers: 5, bags: 2, basePrice: 45 },
{ code: 'COMP', name: 'Compact', example: 'Honda Civic', passengers: 5, bags: 2, basePrice: 55 },
{ code: 'MID', name: 'Midsize', example: 'Toyota Camry', passengers: 5, bags: 3, basePrice: 65 },
{ code: 'FULL', name: 'Full-size', example: 'Chevrolet Impala', passengers: 5, bags: 4, basePrice: 75 },
{ code: 'SUV', name: 'SUV', example: 'Ford Explorer', passengers: 7, bags: 4, basePrice: 95 },
{ code: 'LUX', name: 'Luxury', example: 'BMW 5 Series', passengers: 5, bags: 3, basePrice: 150 },
{ code: 'VAN', name: 'Minivan', example: 'Honda Odyssey', passengers: 7, bags: 3, basePrice: 85 },
{ code: 'CONV', name: 'Convertible', example: 'Ford Mustang', passengers: 4, bags: 2, basePrice: 110 }
];
/**
* Generate car rental options
*/
export function generateCarOptions(pickupLocation: any, dropoffLocation: any, pickupDate: any, dropoffDate: any) {
const pickupDateObj = new Date(pickupDate);
const dropoffDateObj = new Date(dropoffDate);
const days = Math.ceil((dropoffDateObj.getTime() - pickupDateObj.getTime()) / (1000 * 60 * 60 * 24));
// Select 3-5 companies
const numCompanies = Math.floor(Math.random() * 3) + 3;
const selectedCompanies = carCompanies
.sort(() => Math.random() - 0.5)
.slice(0, numCompanies);
const options = [];
for (const company of selectedCompanies) {
// Each company offers 2-4 car categories
const numCategories = Math.floor(Math.random() * 3) + 2;
const selectedCategories = carCategories
.sort(() => Math.random() - 0.5)
.slice(0, numCategories);
for (const category of selectedCategories) {
// Apply company tier multiplier
const tierMultiplier = company.tier === 'premium' ? 1.15 : company.tier === 'economy' ? 0.9 : 1.0;
// Apply weekend/holiday multiplier
const dayOfWeek = pickupDateObj.getDay();
const weekendMultiplier = (dayOfWeek === 5 || dayOfWeek === 6) ? 1.2 : 1.0;
// One-way fee if different locations
const oneWayFee = pickupLocation !== dropoffLocation ? 75 : 0;
const dailyRate = Math.round(category.basePrice * tierMultiplier * weekendMultiplier);
const totalPrice = (dailyRate * days) + oneWayFee;
options.push({
carId: `CAR-${company.code}-${category.code}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`,
company: company.name,
companyCode: company.code,
category: category.name,
categoryCode: category.code,
example: category.example,
passengers: category.passengers,
bags: category.bags,
features: generateFeatures(category.code, company.tier),
pricing: {
dailyRate,
days,
oneWayFee,
totalPrice,
currency: 'USD'
},
availability: 'available' // Mock: always available
});
}
}
return options.sort((a, b) => a.pricing.totalPrice - b.pricing.totalPrice);
}
/**
* Generate features based on category and tier
*/
function generateFeatures(categoryCode: any, tier: any) {
const baseFeatures = ['Automatic', 'Air Conditioning'];
if (tier === 'premium') {
baseFeatures.push('GPS', 'Bluetooth');
}
if (['SUV', 'VAN', 'LUX'].includes(categoryCode)) {
baseFeatures.push('Leather Seats');
}
if (categoryCode === 'LUX') {
baseFeatures.push('Premium Audio', 'Sunroof');
}
return baseFeatures;
}
/**
* Get car by ID (for booking validation)
*/
export function getCarById(carId: any) {
// Since cars are dynamically generated, we parse the ID
const [_, companyCode, categoryCode] = carId.split('-');
const company = carCompanies.find(c => c.code === companyCode);
const category = carCategories.find(c => c.code === categoryCode);
if (!company || !category) {
return null;
}
return {
carId,
company: company.name,
companyCode: company.code,
category: category.name,
categoryCode: category.code,
example: category.example,
passengers: category.passengers,
bags: category.bags,
features: generateFeatures(category.code, company.tier),
basePrice: category.basePrice
};
}
export default { carCompanies, carCategories, generateCarOptions, getCarById };

228
src/data/flights.ts Normal file
View File

@@ -0,0 +1,228 @@
import { getAirport, calculateDistance } from './airports.js';
import { getRandomAirline } from './airlines.js';
import { generateSegmentId } from './pnr.js';
/**
* Flight data generator with deterministic pricing, duration calculation, and availability logic
*/
/**
* Aircraft types by size category
*/
const aircraftTypes = {
shortHaul: ['Boeing 737-800', 'Airbus A320', 'Boeing 737 MAX 8', 'Airbus A321'],
mediumHaul: ['Boeing 757-200', 'Boeing 767-300', 'Airbus A330-200'],
longHaul: ['Boeing 777-300ER', 'Boeing 787-9', 'Airbus A350-900', 'Airbus A380']
};
/**
* Booking class codes by cabin
*/
const bookingClasses = {
economy: ['Y', 'B', 'M', 'H', 'Q', 'V', 'W'],
premium_economy: ['W', 'S', 'A'],
business: ['J', 'C', 'D', 'I', 'Z'],
first: ['F', 'A', 'P']
};
/**
* Generate mock flights for a search query
* @param {Object} params - Search parameters
* @param {string} params.origin - Origin airport code
* @param {string} params.destination - Destination airport code
* @param {string} params.departureDate - Departure date (YYYY-MM-DD)
* @param {Object} params.passengers - Passenger counts
* @param {string} params.cabin - Cabin class
* @returns {Object[]} Array of flight options
*/
export function generateFlights(params: any) {
const { origin, destination, departureDate, cabin = 'economy' } = params;
// Validate airports exist
const originAirport = getAirport(origin);
const destAirport = getAirport(destination);
if (!originAirport || !destAirport) {
return [];
}
// Calculate distance and flight characteristics
const distance = calculateDistance(origin, destination);
const duration = calculateFlightDuration(distance);
const aircraftType = selectAircraftType(distance);
// Generate 3-5 flight options
const flightCount = 3 + Math.floor(Math.random() * 3);
const flights = [];
// Generate flights at different times of day
const departureTimes = generateDepartureTimes(flightCount);
for (let i = 0; i < flightCount; i++) {
const airline = getRandomAirline();
const flightNumber = `${airline.code}${Math.floor(Math.random() * 900) + 100}`;
const departureTime = departureTimes[i];
const arrivalTime = calculateArrivalTime(departureTime, duration);
// Calculate pricing based on distance, cabin, and "availability"
const basePrice = calculateBasePrice(distance, cabin);
const priceVariation = 0.8 + Math.random() * 0.4; // ±20% variation
const price = Math.round(basePrice * priceVariation);
// Simulate availability (90% available, 10% sold out)
const isAvailable = Math.random() > 0.1;
const seatsAvailable = isAvailable ? Math.floor(Math.random() * 20) + 5 : 0;
const status = isAvailable ? 'available' : 'sold_out';
// Get booking class for this cabin
const bookingClass = bookingClasses[cabin][Math.floor(Math.random() * bookingClasses[cabin].length)];
const flight = {
id: generateSegmentId('flight', i),
flightNumber,
airlineCode: airline.code,
airlineName: airline.name,
originCode: origin,
originName: originAirport.name,
destinationCode: destination,
destinationName: destAirport.name,
departureTime: `${departureDate}T${departureTime}:00`,
arrivalTime: `${departureDate}T${arrivalTime}:00`,
duration,
aircraftType,
cabin,
price,
seatsAvailable,
bookingClass,
status,
metadata: {
distance,
data_source: 'mock'
}
};
flights.push(flight);
}
// Sort by departure time
flights.sort((a, b) => a.departureTime.localeCompare(b.departureTime));
return flights;
}
/**
* Calculate flight duration in minutes based on distance
* @param {number} distance - Distance in kilometers
* @returns {number} Duration in minutes
*/
function calculateFlightDuration(distance: any) {
// Average commercial aircraft speed: ~800 km/h
// Add taxi/boarding time: 30 minutes
const flightTime = (distance / 800) * 60;
const totalTime = flightTime + 30;
return Math.round(totalTime);
}
/**
* Select appropriate aircraft type based on distance
* @param {number} distance - Distance in kilometers
* @returns {string} Aircraft type
*/
function selectAircraftType(distance: any) {
if (distance < 2000) {
// Short-haul
return aircraftTypes.shortHaul[Math.floor(Math.random() * aircraftTypes.shortHaul.length)];
} else if (distance < 6000) {
// Medium-haul
return aircraftTypes.mediumHaul[Math.floor(Math.random() * aircraftTypes.mediumHaul.length)];
} else {
// Long-haul
return aircraftTypes.longHaul[Math.floor(Math.random() * aircraftTypes.longHaul.length)];
}
}
/**
* Calculate base price in USD cents based on distance and cabin
* @param {number} distance - Distance in kilometers
* @param {string} cabin - Cabin class
* @returns {number} Price in USD cents
*/
function calculateBasePrice(distance: any, cabin: any) {
// Base price per kilometer by cabin class
const pricePerKm = {
economy: 0.10, // $0.10/km
premium_economy: 0.14, // $0.14/km (+40%)
business: 0.30, // $0.30/km (+200%)
first: 0.50 // $0.50/km (+400%)
};
const rate = pricePerKm[cabin] || pricePerKm.economy;
const basePrice = distance * rate;
// Apply minimum prices
const minimums = {
economy: 200,
premium_economy: 300,
business: 800,
first: 2500
};
const minimum = minimums[cabin] || minimums.economy;
return Math.max(basePrice, minimum) * 100; // Convert to cents
}
/**
* Generate realistic departure times
* @param {number} count - Number of times to generate
* @returns {string[]} Array of departure times (HH:MM format)
*/
function generateDepartureTimes(count: any) {
// Common departure slots: 6am-10pm
const slots = [
'06:00', '06:30', '07:00', '07:30', '08:00', '08:30',
'09:00', '09:30', '10:00', '10:30', '11:00', '11:30',
'12:00', '12:30', '13:00', '13:30', '14:00', '14:30',
'15:00', '15:30', '16:00', '16:30', '17:00', '17:30',
'18:00', '18:30', '19:00', '19:30', '20:00', '20:30',
'21:00', '21:30', '22:00'
];
// Shuffle and take first N slots
const shuffled = slots.sort(() => Math.random() - 0.5);
return shuffled.slice(0, count).sort();
}
/**
* Calculate arrival time given departure time and duration
* @param {string} departureTime - Departure time (HH:MM)
* @param {number} duration - Duration in minutes
* @returns {string} Arrival time (HH:MM)
*/
function calculateArrivalTime(departureTime: any, duration: any) {
const [hours, minutes] = departureTime.split(':').map(Number);
const totalMinutes = hours * 60 + minutes + duration;
const arrivalHours = Math.floor(totalMinutes / 60) % 24;
const arrivalMinutes = totalMinutes % 60;
return `${String(arrivalHours).padStart(2, '0')}:${String(arrivalMinutes).padStart(2, '0')}`;
}
/**
* Get flight by ID from search results
* @param {string} flightId - Flight identifier
* @param {Object} searchParams - Original search parameters
* @returns {Object|null} Flight object or null
*/
export function getFlightById(flightId: any, searchParams: any) {
const flights = generateFlights(searchParams);
return flights.find((f) => f.id === flightId) || null;
}
export default {
generateFlights,
getFlightById,
calculateFlightDuration,
calculateBasePrice
};

127
src/data/hotels.ts Normal file
View File

@@ -0,0 +1,127 @@
/**
* Hotels Mock Data
* 50+ properties across major cities with realistic details
*/
export const hotels = [
// New York City
{ id: 'HTL001', name: 'Grand Manhattan Hotel', chain: 'Marriott', city: 'NYC', cityCode: 'JFK', stars: 5, address: '123 Fifth Avenue', amenities: ['WiFi', 'Parking', 'Breakfast', 'Gym', 'Pool', 'Spa'], basePrice: 450 },
{ id: 'HTL002', name: 'Times Square Inn', chain: 'Hilton', city: 'NYC', cityCode: 'JFK', stars: 4, address: '456 Broadway', amenities: ['WiFi', 'Gym', 'Restaurant'], basePrice: 320 },
{ id: 'HTL003', name: 'Brooklyn Budget Suites', chain: 'Independent', city: 'NYC', cityCode: 'JFK', stars: 3, address: '789 Brooklyn Ave', amenities: ['WiFi', 'Breakfast'], basePrice: 150 },
// Los Angeles
{ id: 'HTL004', name: 'Beverly Hills Grand', chain: 'Four Seasons', city: 'Los Angeles', cityCode: 'LAX', stars: 5, address: '100 Rodeo Drive', amenities: ['WiFi', 'Parking', 'Breakfast', 'Gym', 'Pool', 'Spa', 'Concierge'], basePrice: 550 },
{ id: 'HTL005', name: 'Santa Monica Beach Hotel', chain: 'Hyatt', city: 'Los Angeles', cityCode: 'LAX', stars: 4, address: '200 Ocean Ave', amenities: ['WiFi', 'Pool', 'Gym', 'Restaurant'], basePrice: 280 },
{ id: 'HTL006', name: 'Downtown LA Comfort', chain: 'Holiday Inn', city: 'Los Angeles', cityCode: 'LAX', stars: 3, address: '300 Main St', amenities: ['WiFi', 'Parking', 'Breakfast'], basePrice: 120 },
// London
{ id: 'HTL007', name: 'The Royal Westminster', chain: 'Intercontinental', city: 'London', cityCode: 'LHR', stars: 5, address: '10 Piccadilly', amenities: ['WiFi', 'Breakfast', 'Gym', 'Spa', 'Concierge'], basePrice: 480 },
{ id: 'HTL008', name: 'Kensington Palace Hotel', chain: 'Marriott', city: 'London', cityCode: 'LHR', stars: 4, address: '20 Kensington Rd', amenities: ['WiFi', 'Gym', 'Restaurant'], basePrice: 310 },
{ id: 'HTL009', name: 'East End Express', chain: 'Premier Inn', city: 'London', cityCode: 'LHR', stars: 3, address: '30 Whitechapel', amenities: ['WiFi', 'Breakfast'], basePrice: 135 },
// Tokyo
{ id: 'HTL010', name: 'Shibuya Imperial', chain: 'Prince Hotels', city: 'Tokyo', cityCode: 'TYO', stars: 5, address: '1-1 Shibuya', amenities: ['WiFi', 'Breakfast', 'Gym', 'Pool', 'Spa'], basePrice: 420 },
{ id: 'HTL011', name: 'Shinjuku Business Hotel', chain: 'APA Hotels', city: 'Tokyo', cityCode: 'TYO', stars: 3, address: '2-2 Shinjuku', amenities: ['WiFi', 'Restaurant'], basePrice: 140 },
// Paris
{ id: 'HTL012', name: 'Le Grand Paris', chain: 'Sofitel', city: 'Paris', cityCode: 'CDG', stars: 5, address: '1 Avenue des Champs', amenities: ['WiFi', 'Breakfast', 'Gym', 'Spa', 'Concierge'], basePrice: 500 },
{ id: 'HTL013', name: 'Montmartre Boutique', chain: 'Independent', city: 'Paris', cityCode: 'CDG', stars: 4, address: '15 Rue Montmartre', amenities: ['WiFi', 'Breakfast'], basePrice: 250 },
// San Francisco
{ id: 'HTL014', name: 'Union Square Luxury', chain: 'Westin', city: 'San Francisco', cityCode: 'SFO', stars: 5, address: '50 Union Square', amenities: ['WiFi', 'Gym', 'Pool', 'Spa'], basePrice: 420 },
{ id: 'HTL015', name: 'Fisherman\'s Wharf Inn', chain: 'Best Western', city: 'San Francisco', cityCode: 'SFO', stars: 3, address: '100 Jefferson St', amenities: ['WiFi', 'Parking'], basePrice: 160 },
// Miami
{ id: 'HTL016', name: 'South Beach Resort', chain: 'Fontainebleau', city: 'Miami', cityCode: 'MIA', stars: 5, address: '1000 Ocean Drive', amenities: ['WiFi', 'Pool', 'Gym', 'Spa', 'Beach'], basePrice: 380 },
{ id: 'HTL017', name: 'Coral Gables Hotel', chain: 'Marriott', city: 'Miami', cityCode: 'MIA', stars: 4, address: '200 Miracle Mile', amenities: ['WiFi', 'Pool', 'Gym'], basePrice: 220 },
// Chicago
{ id: 'HTL018', name: 'Magnificent Mile Tower', chain: 'Trump Hotels', city: 'Chicago', cityCode: 'ORD', stars: 5, address: '401 Michigan Ave', amenities: ['WiFi', 'Gym', 'Spa', 'Restaurant'], basePrice: 390 },
{ id: 'HTL019', name: 'Loop Business Center', chain: 'Hyatt', city: 'Chicago', cityCode: 'ORD', stars: 4, address: '100 State St', amenities: ['WiFi', 'Gym'], basePrice: 195 },
// Dubai
{ id: 'HTL020', name: 'Burj Al Arab', chain: 'Jumeirah', city: 'Dubai', cityCode: 'DXB', stars: 5, address: 'Jumeirah Beach', amenities: ['WiFi', 'Pool', 'Gym', 'Spa', 'Beach', 'Concierge'], basePrice: 800 },
{ id: 'HTL021', name: 'Marina Bay Hotel', chain: 'Hilton', city: 'Dubai', cityCode: 'DXB', stars: 4, address: 'Dubai Marina', amenities: ['WiFi', 'Pool', 'Gym'], basePrice: 280 },
// Singapore
{ id: 'HTL022', name: 'Marina Bay Sands', chain: 'Independent', city: 'Singapore', cityCode: 'SIN', stars: 5, address: '10 Bayfront Ave', amenities: ['WiFi', 'Pool', 'Gym', 'Spa', 'Casino'], basePrice: 480 },
{ id: 'HTL023', name: 'Orchard Road Plaza', chain: 'Shangri-La', city: 'Singapore', cityCode: 'SIN', stars: 4, address: '22 Orchard Rd', amenities: ['WiFi', 'Pool', 'Gym'], basePrice: 260 },
// Additional cities for diversity
{ id: 'HTL024', name: 'Sydney Harbour Hotel', chain: 'Four Seasons', city: 'Sydney', cityCode: 'SYD', stars: 5, address: '199 George St', amenities: ['WiFi', 'Pool', 'Gym', 'Spa'], basePrice: 410 },
{ id: 'HTL025', name: 'Vegas Strip Mega Resort', chain: 'MGM', city: 'Las Vegas', cityCode: 'LAS', stars: 5, address: '3799 Las Vegas Blvd', amenities: ['WiFi', 'Pool', 'Gym', 'Casino', 'Spa'], basePrice: 320 },
{ id: 'HTL026', name: 'Seattle Downtown Suites', chain: 'Hyatt', city: 'Seattle', cityCode: 'SEA', stars: 4, address: '1001 Pike St', amenities: ['WiFi', 'Gym'], basePrice: 230 },
{ id: 'HTL027', name: 'Boston Harbor Hotel', chain: 'Marriott', city: 'Boston', cityCode: 'BOS', stars: 4, address: '70 Rowes Wharf', amenities: ['WiFi', 'Gym', 'Restaurant'], basePrice: 275 },
{ id: 'HTL028', name: 'Atlanta Peachtree Plaza', chain: 'Westin', city: 'Atlanta', cityCode: 'ATL', stars: 4, address: '210 Peachtree St', amenities: ['WiFi', 'Pool', 'Gym'], basePrice: 185 },
{ id: 'HTL029', name: 'Denver Mountain View', chain: 'Hilton', city: 'Denver', cityCode: 'DEN', stars: 4, address: '1701 Broadway', amenities: ['WiFi', 'Gym'], basePrice: 195 },
{ id: 'HTL030', name: 'Phoenix Desert Resort', chain: 'JW Marriott', city: 'Phoenix', cityCode: 'PHX', stars: 5, address: '5350 E Marriott Dr', amenities: ['WiFi', 'Pool', 'Gym', 'Spa', 'Golf'], basePrice: 340 },
// More international cities
{ id: 'HTL031', name: 'Rome Colosseum View', chain: 'St. Regis', city: 'Rome', cityCode: 'FCO', stars: 5, address: 'Via Vittorio', amenities: ['WiFi', 'Breakfast', 'Gym', 'Spa'], basePrice: 450 },
{ id: 'HTL032', name: 'Barcelona Ramblas Hotel', chain: 'Independent', city: 'Barcelona', cityCode: 'BCN', stars: 4, address: 'La Rambla 45', amenities: ['WiFi', 'Breakfast'], basePrice: 210 },
{ id: 'HTL033', name: 'Amsterdam Canal House', chain: 'NH Hotels', city: 'Amsterdam', cityCode: 'AMS', stars: 4, address: 'Prinsengracht 100', amenities: ['WiFi', 'Breakfast'], basePrice: 240 },
{ id: 'HTL034', name: 'Hong Kong Harbor Plaza', chain: 'Mandarin Oriental', city: 'Hong Kong', cityCode: 'HKG', stars: 5, address: '5 Connaught Rd', amenities: ['WiFi', 'Pool', 'Gym', 'Spa'], basePrice: 490 },
{ id: 'HTL035', name: 'Bangkok Sukhumvit Suites', chain: 'Sofitel', city: 'Bangkok', cityCode: 'BKK', stars: 4, address: '189 Sukhumvit Rd', amenities: ['WiFi', 'Pool', 'Gym'], basePrice: 180 },
// US Regional coverage
{ id: 'HTL036', name: 'Nashville Music City Hotel', chain: 'Gaylord', city: 'Nashville', cityCode: 'BNA', stars: 4, address: '2800 Opryland Dr', amenities: ['WiFi', 'Pool', 'Gym'], basePrice: 205 },
{ id: 'HTL037', name: 'Austin Downtown', chain: 'Fairmont', city: 'Austin', cityCode: 'AUS', stars: 4, address: '101 Red River', amenities: ['WiFi', 'Pool', 'Gym'], basePrice: 220 },
{ id: 'HTL038', name: 'Portland Pearl District', chain: 'Kimpton', city: 'Portland', cityCode: 'PDX', stars: 4, address: '425 NW 9th Ave', amenities: ['WiFi', 'Gym', 'Restaurant'], basePrice: 215 },
{ id: 'HTL039', name: 'Philadelphia Historic Inn', chain: 'Independent', city: 'Philadelphia', cityCode: 'PHL', stars: 3, address: '1234 Market St', amenities: ['WiFi', 'Breakfast'], basePrice: 165 },
{ id: 'HTL040', name: 'Detroit Renaissance Center', chain: 'Marriott', city: 'Detroit', cityCode: 'DTW', stars: 4, address: '400 Renaissance Dr', amenities: ['WiFi', 'Gym'], basePrice: 170 },
// European additions
{ id: 'HTL041', name: 'Berlin Mitte Palace', chain: 'Adlon', city: 'Berlin', cityCode: 'BER', stars: 5, address: 'Unter den Linden 77', amenities: ['WiFi', 'Spa', 'Gym'], basePrice: 380 },
{ id: 'HTL042', name: 'Munich Marienplatz', chain: 'Bayerischer Hof', city: 'Munich', cityCode: 'MUC', stars: 5, address: 'Promenadeplatz 2', amenities: ['WiFi', 'Pool', 'Spa'], basePrice: 400 },
{ id: 'HTL043', name: 'Zurich Lake View', chain: 'Baur au Lac', city: 'Zurich', cityCode: 'ZRH', stars: 5, address: 'Talstrasse 1', amenities: ['WiFi', 'Spa', 'Restaurant'], basePrice: 520 },
{ id: 'HTL044', name: 'Vienna Imperial', chain: 'Imperial', city: 'Vienna', cityCode: 'VIE', stars: 5, address: 'Kärntner Ring 16', amenities: ['WiFi', 'Spa', 'Restaurant'], basePrice: 430 },
{ id: 'HTL045', name: 'Brussels Grand Place', chain: 'Amigo', city: 'Brussels', cityCode: 'BRU', stars: 5, address: 'Rue de l\'Amigo 1', amenities: ['WiFi', 'Restaurant'], basePrice: 350 },
// Asia Pacific additions
{ id: 'HTL046', name: 'Seoul Gangnam Suites', chain: 'Park Hyatt', city: 'Seoul', cityCode: 'ICN', stars: 5, address: '606 Teheran-ro', amenities: ['WiFi', 'Pool', 'Gym', 'Spa'], basePrice: 360 },
{ id: 'HTL047', name: 'Shanghai Bund Hotel', chain: 'Peninsula', city: 'Shanghai', cityCode: 'PVG', stars: 5, address: '32 The Bund', amenities: ['WiFi', 'Pool', 'Spa'], basePrice: 420 },
{ id: 'HTL048', name: 'Mumbai Marine Drive', chain: 'Taj', city: 'Mumbai', cityCode: 'BOM', stars: 5, address: 'Apollo Bunder', amenities: ['WiFi', 'Pool', 'Spa'], basePrice: 280 },
{ id: 'HTL049', name: 'Melbourne CBD Tower', chain: 'Crown', city: 'Melbourne', cityCode: 'MEL', stars: 5, address: '8 Whiteman St', amenities: ['WiFi', 'Pool', 'Gym'], basePrice: 330 },
{ id: 'HTL050', name: 'Osaka Namba Plaza', chain: 'Swissotel', city: 'Osaka', cityCode: 'KIX', stars: 4, address: '5-1-60 Namba', amenities: ['WiFi', 'Gym'], basePrice: 195 }
];
/**
* Get hotels by city code
*/
export function getHotelsByCity(cityCode: any) {
return hotels.filter(h => h.cityCode === cityCode);
}
/**
* Get hotel by ID
*/
export function getHotelById(hotelId: any) {
return hotels.find(h => h.id === hotelId);
}
/**
* Generate hotel pricing based on check-in date, nights, and base price
*/
export function generateHotelPrice(basePrice: any, nights: any, checkInDate: any) {
const date = new Date(checkInDate);
const dayOfWeek = date.getDay();
// Weekend premium (Friday-Saturday)
const weekendMultiplier = (dayOfWeek === 5 || dayOfWeek === 6) ? 1.3 : 1.0;
// Seasonal variation (simple month-based)
const month = date.getMonth();
const peakSeason = [5, 6, 7, 11]; // June, July, Aug, Dec
const seasonMultiplier = peakSeason.includes(month) ? 1.2 : 1.0;
const pricePerNight = Math.round(basePrice * weekendMultiplier * seasonMultiplier);
const totalPrice = pricePerNight * nights;
return {
pricePerNight,
nights,
totalPrice
};
}
export default hotels;

104
src/data/pnr.ts Normal file
View File

@@ -0,0 +1,104 @@
/**
* PNR (Passenger Name Record) generation utilities
* Format: TEST-{BASE32} (e.g., TEST-ABC123)
*/
const BASE32_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
/**
* Generate a unique PNR with TEST- prefix
* @param {string} sessionId - Session identifier for entropy
* @returns {string} PNR in format TEST-XXXXXX
*/
export function generatePNR(sessionId = '') {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000000);
// Combine session, timestamp, and random for uniqueness
const entropy = `${sessionId}${timestamp}${random}`;
// Generate base32 encoded string
const code = generateBase32(entropy, 6);
return `TEST-${code}`;
}
/**
* Generate base32 encoded string from input
* @param {string} input - Input string for entropy
* @param {number} length - Desired output length (default: 6)
* @returns {string} Base32 encoded string
*/
function generateBase32(input, length = 6) {
let result = '';
let hash = simpleHash(input);
for (let i = 0; i < length; i++) {
const index = hash % 32;
result += BASE32_CHARS[index];
hash = Math.floor(hash / 32) + (hash % 32);
// Add more entropy if hash gets too small
if (hash < 32) {
hash = simpleHash(result + Date.now());
}
}
return result;
}
/**
* Simple hash function for string input
* @param {string} str - Input string
* @returns {number} Hash value
*/
function simpleHash(str: any) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return Math.abs(hash);
}
/**
* Validate PNR format
* @param {string} pnr - PNR to validate
* @returns {boolean} True if valid format
*/
export function isValidPNR(pnr: any) {
return /^TEST-[A-Z0-9]{6}$/.test(pnr);
}
/**
* Extract session-scoped booking ID from PNR
* For display and tracking purposes
* @param {string} pnr - PNR code
* @returns {string} Booking ID (just the code part)
*/
export function extractBookingId(pnr: any) {
if (!isValidPNR(pnr)) {
return pnr;
}
return pnr.replace('TEST-', '');
}
/**
* Generate a unique segment ID
* @param {string} type - Segment type (flight, hotel, car)
* @param {number} index - Segment index
* @returns {string} Segment identifier
*/
export function generateSegmentId(type: any, index: any) {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000);
return `${type}-${index}-${timestamp}-${random}`;
}
export default {
generatePNR,
isValidPNR,
extractBookingId,
generateSegmentId
};