/** * routing.worker.ts — runs route computation off the UI thread. * ---------------------------------------------------------------------- * The main thread posts a RouteJob; this worker runs `routeWithProgress` * (which yields periodically and reports progress) and posts back: * * { type: 'progress', pct: number } * { type: 'done', result: RouteResult, token: number } * { type: 'error', message: string, token: number } * * A `token` distinguishes runs so a stale/obsolete in-flight job can be * ignored by the pe; worker stays single-flight per post. */ import { routeWithProgress } from './routing'; import type { RouteGraph, AvoidRule, RouteResult } from './routing'; export interface RouteJob { type: 'route'; token: number; graph: RouteGraph; pois: Array<{ type: 'Feature'; properties: Record; geometry: { type: string; coordinates: number[] } }>; from: [number, number]; to: [number, number]; avoid: AvoidRule[]; /** Approximate fraction to send as progress during the search. */ onEvery?: number; } type WorkerReply = | { type: 'progress'; pct: number; token: number } | { type: 'done'; result: RouteResult; token: number } | { type: 'error'; message: string; token: number }; self.onmessage = (e: MessageEvent) => { const job = e.data; if (!job || job.type !== 'route') return; const { token, graph, pois, from, to, avoid } = job; const send = (msg: WorkerReply) => { (self as unknown as { postMessage: (m: WorkerReply, transfer?: Transferable[]) => void; }).postMessage(msg); }; void routeWithProgress(graph, from, to, pois as GeoJSON.Feature[], avoid, (pct) => { send({ type: 'progress', pct, token }); }) .then((result) => send({ type: 'done', result, token })) .catch((err) => send({ type: 'error', message: err instanceof Error ? err.message : String(err), token }) ); };