{"version":3,"file":"create-position-tracker.cjs","names":[],"sources":["../../src/geo/create-position-tracker.ts"],"sourcesContent":["import { haversineKm } from \"./distance\";\nimport type { TrackPoint } from \"./types\";\n\n/** Lifecycle status of a {@link PositionTracker}. */\nexport type TrackerStatus = \"idle\" | \"tracking\" | \"error\";\n\n/** Options for {@link createPositionTracker}. */\nexport interface CreatePositionTrackerOptions {\n    /**\n     * Discard a new sample when it is closer than this (in km) to the last\n     * kept point — filters GPS jitter while standing still. Default: `0.005`\n     * (5 meters). Pass `0` to keep every sample.\n     */\n    minDistanceKm?: number;\n    /**\n     * Cap the retained trajectory to the most recent N points (older points are\n     * dropped, but their distance stays counted). Default: `Infinity`.\n     */\n    maxPoints?: number;\n    /** Forwarded to `navigator.geolocation.watchPosition`. */\n    positionOptions?: PositionOptions;\n    /** Called with the full trajectory each time a point is added. */\n    onUpdate?: (points: readonly TrackPoint[]) => void;\n    /** Called on a `GeolocationPositionError`. */\n    onError?: (error: GeolocationPositionError) => void;\n    /** Called whenever the status changes. */\n    onStatusChange?: (status: TrackerStatus) => void;\n}\n\n/** Imperative controller returned by {@link createPositionTracker}. */\nexport interface PositionTracker {\n    /** Begin watching position. No-op if already tracking. */\n    start: () => void;\n    /** Stop watching. Retains the recorded trajectory. */\n    stop: () => void;\n    /** Stop and discard the recorded trajectory. */\n    clear: () => void;\n    /** Snapshot of the recorded trajectory (newest last). */\n    readonly points: readonly TrackPoint[];\n    /** Total distance of the recorded trajectory in kilometers. */\n    readonly distanceKm: number;\n    /** Current tracker status. */\n    readonly status: TrackerStatus;\n}\n\n/**\n * Record a live GPS trajectory from `navigator.geolocation.watchPosition`.\n * Framework-free — the {@link usePositionTracker} hook wraps this for React.\n *\n * Jitter while stationary is filtered by `minDistanceKm`; distance is\n * accumulated across the *full* run even when `maxPoints` trims the retained\n * array. 100% browser-side: no network, no external service.\n *\n * @param options - Filtering, retention and callbacks.\n * @returns A controller with `start`/`stop`/`clear` and live getters.\n *\n * @example\n * const tracker = createPositionTracker({\n *   minDistanceKm: 0.01,\n *   onUpdate: (pts) => console.log(pts.length, \"points\"),\n * });\n * tracker.start();\n * // …later\n * tracker.stop();\n * console.log(tracker.distanceKm);\n */\nexport function createPositionTracker(options: CreatePositionTrackerOptions = {}): PositionTracker {\n    const {\n        minDistanceKm = 0.005,\n        maxPoints = Infinity,\n        positionOptions,\n        onUpdate,\n        onError,\n        onStatusChange,\n    } = options;\n\n    let points: TrackPoint[] = [];\n    let distanceKm = 0;\n    let lastKept: TrackPoint | null = null;\n    let watchId: number | null = null;\n    let status: TrackerStatus = \"idle\";\n\n    function setStatus(next: TrackerStatus): void {\n        if (status === next) return;\n        status = next;\n        onStatusChange?.(next);\n    }\n\n    function handlePosition(position: GeolocationPosition): void {\n        const point: TrackPoint = {\n            latitude: position.coords.latitude,\n            longitude: position.coords.longitude,\n            timestamp: position.timestamp,\n            accuracy: position.coords.accuracy,\n        };\n\n        if (lastKept) {\n            const step = haversineKm(lastKept, point);\n            if (step < minDistanceKm) return;\n            distanceKm += step;\n        }\n\n        lastKept = point;\n        points.push(point);\n        if (points.length > maxPoints) {\n            points = points.slice(points.length - maxPoints);\n        }\n        onUpdate?.(points);\n    }\n\n    function handleError(error: GeolocationPositionError): void {\n        setStatus(\"error\");\n        onError?.(error);\n    }\n\n    function start(): void {\n        if (watchId !== null) return;\n        if (typeof navigator === \"undefined\" || !navigator.geolocation) {\n            setStatus(\"error\");\n            return;\n        }\n        setStatus(\"tracking\");\n        watchId = navigator.geolocation.watchPosition(handlePosition, handleError, positionOptions);\n    }\n\n    function stop(): void {\n        if (watchId !== null && typeof navigator !== \"undefined\" && navigator.geolocation) {\n            navigator.geolocation.clearWatch(watchId);\n        }\n        watchId = null;\n        setStatus(\"idle\");\n    }\n\n    function clear(): void {\n        stop();\n        points = [];\n        distanceKm = 0;\n        lastKept = null;\n        onUpdate?.(points);\n    }\n\n    return {\n        start,\n        stop,\n        clear,\n        get points() {\n            return points;\n        },\n        get distanceKm() {\n            return distanceKm;\n        },\n        get status() {\n            return status;\n        },\n    };\n}\n"],"mappings":"kCAkEA,SAAgB,EAAsB,EAAwC,CAAC,EAAoB,CAC/F,GAAM,CACF,gBAAgB,KAChB,YAAY,IACZ,kBACA,WACA,UACA,kBACA,EAEA,EAAuB,CAAC,EACxB,EAAa,EACb,EAA8B,KAC9B,EAAyB,KACzB,EAAwB,OAE5B,SAAS,EAAU,EAA2B,CACtC,IAAW,IACf,EAAS,EACT,IAAiB,CAAI,EACzB,CAEA,SAAS,EAAe,EAAqC,CACzD,IAAM,EAAoB,CACtB,SAAU,EAAS,OAAO,SAC1B,UAAW,EAAS,OAAO,UAC3B,UAAW,EAAS,UACpB,SAAU,EAAS,OAAO,QAC9B,EAEA,GAAI,EAAU,CACV,IAAM,EAAO,EAAA,YAAY,EAAU,CAAK,EACxC,GAAI,EAAO,EAAe,OAC1B,GAAc,CAClB,CAEA,EAAW,EACX,EAAO,KAAK,CAAK,EACb,EAAO,OAAS,IAChB,EAAS,EAAO,MAAM,EAAO,OAAS,CAAS,GAEnD,IAAW,CAAM,CACrB,CAEA,SAAS,EAAY,EAAuC,CACxD,EAAU,OAAO,EACjB,IAAU,CAAK,CACnB,CAEA,SAAS,GAAc,CACf,OAAY,KAChB,IAAI,OAAO,UAAc,KAAe,CAAC,UAAU,YAAa,CAC5D,EAAU,OAAO,EACjB,MACJ,CACA,EAAU,UAAU,EACpB,EAAU,UAAU,YAAY,cAAc,EAAgB,EAAa,CAAe,CAF1F,CAGJ,CAEA,SAAS,GAAa,CACd,IAAY,MAAQ,OAAO,UAAc,KAAe,UAAU,aAClE,UAAU,YAAY,WAAW,CAAO,EAE5C,EAAU,KACV,EAAU,MAAM,CACpB,CAEA,SAAS,GAAc,CACnB,EAAK,EACL,EAAS,CAAC,EACV,EAAa,EACb,EAAW,KACX,IAAW,CAAM,CACrB,CAEA,MAAO,CACH,QACA,OACA,QACA,IAAI,QAAS,CACT,OAAO,CACX,EACA,IAAI,YAAa,CACb,OAAO,CACX,EACA,IAAI,QAAS,CACT,OAAO,CACX,CACJ,CACJ"}