← back to Charge And Explore
backend/src/providers/provider.ts
83 lines
// Provider-adapter interfaces (TypeScript port of the Swift protocols).
//
// The whole external-data strategy hinges on this seam: NO provider secret ever
// reaches the iOS binary — the app calls only the first-party backend, and the
// backend fans out to whichever charger/place/routing provider is licensed.
// Swapping Apple MapKit -> Google Places, or NREL -> HERE, must not require a
// product rewrite. Every normalized record MUST keep its provenance.
import type { GeoPoint, PlaceCategory } from "../core/types.ts";
/** Field-level provenance carried on every canonical value (legal requirement). */
export interface Provenance {
providerId: string;
providerRecordId: string;
sourceRetrievedAt: string; // ISO-8601
sourceEffectiveAt?: string;
lastVerifiedAt?: string;
confidence: number; // 0–1
licenseCode: string;
attributionText: string;
redistributionAllowed: boolean;
cacheExpiresAt?: string;
}
export interface ProviderStation {
id: string;
name: string;
location: GeoPoint;
maxKw?: number;
stallCount?: number;
network?: string;
provenance: Provenance;
}
export interface ProviderPlace {
id: string;
name: string;
location: GeoPoint;
categories: PlaceCategory[];
provenance: Provenance;
}
export interface RouteCorridor {
polyline: GeoPoint[];
bufferMeters: number;
}
export interface ChargingStationProvider {
stationsNear(point: GeoPoint, radiusMeters: number): Promise<ProviderStation[]>;
stationsAlong(corridor: RouteCorridor): Promise<ProviderStation[]>;
}
export interface PlacesProvider {
placesNear(
point: GeoPoint,
categories: Set<PlaceCategory>,
radiusMeters: number,
): Promise<ProviderPlace[]>;
}
export interface RouteRequest {
origin: GeoPoint;
destination: GeoPoint;
}
export interface RouteResult {
corridor: RouteCorridor;
distanceMeters: number;
durationMinutes: number;
}
export interface WalkingRoute {
durationMinutes: number;
distanceMeters: number;
/** True when a real pedestrian path exists (not a freeway-severed straight line). */
isRoutable: boolean;
}
export interface RoutingProvider {
route(request: RouteRequest): Promise<RouteResult>;
walkingRoute(from: GeoPoint, to: GeoPoint): Promise<WalkingRoute>;
}