import type { RoutingMode } from "./osm-tags.js"; // ─── Job data types ─────────────────────────────────────────────────────────── export interface DownloadPbfJobData { type: "download-pbf"; citySlug: string; geofabrikUrl: string; expectedBytes?: number; } export interface ExtractPoisJobData { type: "extract-pois"; citySlug: string; pbfPath: string; /** Optional bounding box [minLng, minLat, maxLng, maxLat] to clip PBF before tag-filtering */ bbox?: [number, number, number, number]; } export interface GenerateGridJobData { type: "generate-grid"; citySlug: string; resolutionM: number; } export interface ComputeScoresJobData { type: "compute-scores"; citySlug: string; modes: RoutingMode[]; thresholds: number[]; /** Set after compute-routing children are dispatched (internal two-phase state). */ routingDispatched?: boolean; /** When true, ingest-boris-ni is dispatched in Phase 1 to run alongside routing jobs. */ ingestBorisNi?: boolean; /** When true, ingest-boris-hb is dispatched in Phase 1 to run alongside routing jobs. */ ingestBorisHb?: boolean; } export interface ComputeRoutingJobData { type: "compute-routing"; citySlug: string; mode: "walking" | "cycling" | "driving"; category: string; } export interface ComputeTransitJobData { type: "compute-transit"; citySlug: string; } export interface BuildValhallaJobData { type: "build-valhalla"; /** City being added/updated. Absent for removal-only rebuilds. */ citySlug?: string; pbfPath?: string; /** Optional bounding box [minLng, minLat, maxLng, maxLat] to clip PBF before building routing tiles */ bbox?: [number, number, number, number]; /** Slugs to drop from the global routing tile set before rebuilding */ removeSlugs?: string[]; } export interface RefreshCityJobData { type: "refresh-city"; citySlug: string; geofabrikUrl: string; resolutionM?: number; /** Monotonically increasing counter incremented at each trigger; used in jobIds * to prevent completed-job deduplication on re-runs. */ iter: number; /** ID of the compute-scores job enqueued for this refresh; set after flow.add(). */ computeScoresJobId?: string; } export interface IngestBorisNiJobData { type: "ingest-boris-ni"; citySlug: string; } export interface IngestBorisHbJobData { type: "ingest-boris-hb"; citySlug: string; } export interface DownloadGtfsDeJobData { type: "download-gtfs-de"; url: string; /** Re-download even if data already exists */ force?: boolean; /** * Per-city bounding boxes [minLng, minLat, maxLng, maxLat] used to clip the * GTFS feed after extraction. A stop is kept when it falls inside ANY of the * bboxes (each already padded by a small buffer in refresh-city.ts). * When absent the full feed is kept. */ bboxes?: [number, number, number, number][]; } export type PipelineJobData = | DownloadPbfJobData | ExtractPoisJobData | GenerateGridJobData | ComputeScoresJobData | ComputeRoutingJobData | BuildValhallaJobData | RefreshCityJobData | IngestBorisNiJobData | IngestBorisHbJobData | DownloadGtfsDeJobData | ComputeTransitJobData; // ─── Job options (BullMQ-compatible plain objects) ──────────────────────────── export const JOB_OPTIONS: Record = { "compute-transit": { attempts: 1, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "download-gtfs-de": { attempts: 2, backoff: { type: "fixed", delay: 10000 }, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "compute-routing": { attempts: 2, backoff: { type: "fixed", delay: 3000 }, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "download-pbf": { attempts: 2, backoff: { type: "fixed", delay: 5000 }, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "extract-pois": { attempts: 1, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "generate-grid": { attempts: 1, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "compute-scores": { attempts: 1, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "build-valhalla": { attempts: 3, backoff: { type: "fixed", delay: 60_000 }, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "refresh-city": { attempts: 1, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "ingest-boris-ni": { attempts: 2, backoff: { type: "fixed", delay: 5000 }, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, "ingest-boris-hb": { attempts: 2, backoff: { type: "fixed", delay: 5000 }, removeOnComplete: { age: 86400 * 7 }, removeOnFail: { age: 86400 * 30 }, }, };