import type { Coord, Driver, Place } from '../types';
export type Role = 'rider' | 'driver';
export type DeviceProfile = {
id: string;
role: Role;
name: string;
car?: string;
plate?: string;
rating?: number;
};
export type QuoteRequest = {
pickup: Coord;
destination: Place;
distanceMeters: number;
durationSeconds: number;
};
* Upfront prices for one trip, one per ride type, set by dispatch. Booking a ride uses
* the quote's price for that type, as long as the quote hasn't expired.
*/
export type Quote = {
id: string;
destinationId: string;
surge: number;
expiresAt: number;
fares: Record<string, number>;
};
export type RateCard = {
baseFare: number;
perMile: number;
perMinute: number;
bookingFee: number;
minimumFare: number;
};
* Surge: each zone's price multiplier, from recent ride requests (demand) against the
* available drivers there (supply). Recomputed at most every `intervalMs` per zone.
* multiplier = 1 + (demand / supply − threshold) × sensitivity, rounded down to `step`,
* between 1 and `maxMultiplier`.
*/
export type SurgeConfig = {
enabled: boolean;
zoneSizeDegrees: number;
intervalMs: number;
demandWindowMs: number;
threshold: number;
sensitivity: number;
step: number;
maxMultiplier: number;
};
export type Market = { name: string; center: Coord; radiusKm: number };
export type PricingConfig = {
markets: Record<string, Market>;
defaultMarket: string;
rateCards: Record<string, Record<string, RateCard>>;
surge: SurgeConfig;
driverShare: number;
quoteTtlMs: number;
};
export type PricingOverrides = {
markets?: Record<string, Market>;
defaultMarket?: string;
rateCards?: Record<string, Record<string, Partial<RateCard>>>;
surge?: Partial<SurgeConfig>;
driverShare?: number;
quoteTtlMs?: number;
};
* How a ride's price auction runs. Prices are multiples of the rider's quoted fare:
* round n suggests quote × (startMultiplier + (n − 1) × stepMultiplier), never above
* quote × maxMultiplier. When the round at the cap finds no taker, the ride is unmatched.
*/
export type AuctionPolicy = {
startMultiplier: number;
stepMultiplier: number;
maxMultiplier: number | null;
roundDurationMs: number;
};
* auction – a round is open: drivers are being offered the current price
* matched – a driver accepted; they're on the way / on trip
* completed – the driver finished the trip
* unmatched – the round at the price cap ended with no taker
* cancelled – the rider cancelled during the auction
*/
export type RideStatus = 'auction' | 'matched' | 'completed' | 'unmatched' | 'cancelled';
export type RiderRideView = {
id: string;
status: RideStatus;
destination: Place;
rideTypeId: string;
distanceMeters: number;
durationSeconds: number;
quotedFare: number;
round: number;
price: number;
closesAt: number;
fare: number | null;
driver: Driver | null;
driverFinishingTrip: boolean;
completedAt: string | null;
};
export type DriverOfferView = {
rideId: string;
riderName: string;
destination: Place;
rideTypeId: string;
distanceMeters: number;
durationSeconds: number;
round: number;
price: number;
earnings: number;
closesAt: number;
declined: boolean;
};
export type DriverJobView = {
rideId: string;
riderName: string;
destination: Place;
fare: number;
earnings: number;
};
export type DriverResult =
| { rideId: string; outcome: 'lost' | 'cancelled' | 'expired' }
| { rideId: string; outcome: 'completed'; earnings: number };
export type Snapshot =
| { role: 'rider'; ride: RiderRideView | null; quote: Quote | null }
| {
role: 'driver';
driving: boolean;
offers: DriverOfferView[];
job: DriverJobView | null;
nextJob: DriverJobView | null;
lastResult: DriverResult | null;
};
export type ClientMessage =
| { type: 'hello'; device: DeviceProfile }
| { type: 'requestQuote'; request: QuoteRequest }
| { type: 'requestRide'; quoteId: string; rideTypeId: string }
| { type: 'cancelRide' }
| { type: 'ackRide' }
| { type: 'accept'; rideId: string; round: number }
| { type: 'decline'; rideId: string; round: number }
| { type: 'completeTrip'; rideId: string }
| { type: 'setDriving'; driving: boolean }
| { type: 'setPosition'; position: Coord };
export type ServerMessage = { type: 'snapshot'; snapshot: Snapshot } | { type: 'error'; message: string };