Files
time_to_leave/docs/API_REFERENCE.md
T
2026-05-18 15:01:53 +02:00

7.0 KiB

Core & API Client Reference

This reference covers the two shared packages: @timetoleave/core and @timetoleave/api-client.

@timetoleave/core

The core package contains shared types, defaults, pure utilities, HAFAS parsing, and journey scoring. It has no first-party dependency on either app.

Main Types

Type Description
Event Locally stored app event with eventTime: Date.
CalendarEvent API-safe calendar event with eventTime: string.
Station HAFAS station identity with optional coordinates.
Journey Parsed transit journey with scheduled/real departure and arrival, delay, platform, changes, train labels, and cancellation state.
BikeRoute, BikeStep OSRM bicycle route summary and step data.
WalkRoute, WalkStep OSRM foot route summary and step data.
CountdownInfo Countdown label, color key, and urgency flag.
ReminderSettings Buffer, notification, walking, and bike visibility settings.
GeocodeResult Nominatim-style coordinate result.
NearbyStop, WienerLinien* Vienna stop and departure response shapes.
CalendarAccountType, CalendarSourceInfo, SelectableCalendar Mobile native-calendar selection metadata.

Defaults

packages/core/src/defaults.ts exports the shared fallback origin:

  • DEFAULT_ORIGIN_ADDRESS: Goethegasse 36, 2340 Moedling
  • DEFAULT_ORIGIN_LAT: 48.0806926
  • DEFAULT_ORIGIN_LNG: 16.2908052
  • DEFAULT_ORIGIN_STATION_NAME: Mödling Bahnhof
  • DEFAULT_ORIGIN_STATION_EXT_ID: 1231701
  • DEFAULT_ORIGIN_STATION: assembled Station

HAFAS Time Utilities

HAFAS date/time strings are Vienna-local values. Use these helpers for every HAFAS request/response conversion.

import { hafasDateTime, parseHafasTime } from "@timetoleave/core";

const parsed = parseHafasTime("20260518", "143000");
const outbound = hafasDateTime(new Date());
Function Description
parseHafasTime(dateStr, timeStr) Converts HAFAS YYYYMMDD and HHMMSS strings into a UTC Date, including CET/CEST transition handling.
hafasDateTime(date) Converts a JavaScript Date into HAFAS date/time strings in Europe/Vienna.
getTimezoneOffsetMinutes(instant, tz) Internal helper intended for whole-hour zones such as Europe/Vienna.
getDateTimeParts(instant, tz) Extracts timezone-local date parts through Intl.DateTimeFormat.

Journey Parsing and Scoring

Function Description
parseHafasJourneys(json, hafasDate, queryDate) Converts HAFAS outConL responses into Journey[], including real-time delay, platform, trains, changes, and cancellations.
rankJourneys(journeys, targetArrivalTime, finalLegDurationMs?) Scores journeys by arrival fit, transfer count, duration, and cancellation penalty.

Countdown, Status, and Formatting

Function Description
calculateCountdown(targetDate) Returns a countdown label and color: red for now/past, orange within 10 minutes, yellow within 30, green within 60, blue beyond 60.
getLeaveStatus(event, journeys) Returns No journey data, All journeys cancelled, Departure missed, Delayed +N min, Leave now, or On time.
StatusUtils.checkServerStatus(url) Performs a timeout-bound HEAD request and returns boolean availability.
formatTime(date) Austrian local HH:mm.
formatDate(date) Austrian local date with weekday.
formatDateTime(date) Austrian local date and time.
formatDuration(seconds) Human-readable duration such as 1h 05min or 45min.
formatDistance(meters) Meters below 1 km, one-decimal kilometers above.

@timetoleave/api-client

ApiClient is a small client for the web backend proxy. In the web app, an empty base URL means same-origin. In mobile, set EXPO_PUBLIC_API_BASE_URL or pass a deployed backend URL.

import { ApiClient } from "@timetoleave/api-client";

const api = new ApiClient("https://timetoleave.app");

The constructor accepts either a string or a string array. When an array is provided, the client tries the next base URL for network failures and unavailable statuses such as 408, 429, 502, 503, and 504.

Health

getHealth(): Promise<{ status: "ok"; uptime: number }>

Calls /api/health. The current route returns { ok, ts, version }, so callers should keep this method's legacy type in mind until it is aligned with the route payload.

Geocoding

geocode(name: string, countrycodes?: string): Promise<GeocodeResult[]>
reverseGeocode(lat: number, lng: number): Promise<GeocodeResult | null>

geocode() calls /api/geocode and wraps the first result in an array. reverseGeocode() calls /api/geocode/reverse; the current web app does not define that route, so it returns null for non-OK responses.

Calendar

fetchCalendar(url: string, days?: number): Promise<CalendarEvent[]>
parseCalendarIcs(content: string): Promise<CalendarEvent[]>

fetchCalendar() imports a remote allowed ICS URL through /api/calendar. parseCalendarIcs() posts raw ICS text to /api/calendar/parse.

Google Calendar sync is currently implemented in the web UI and backend routes, not as a dedicated ApiClient method.

HAFAS

hafasRequest<T = Record<string, unknown>>(body: unknown): Promise<T>
searchStation(query: string): Promise<Station[]>
findStationByExtId(extId: string): Promise<Station | null>
findNearestStationByCoords(lat: number, lng: number): Promise<Station | null>
searchJourneys(
  fromStationExtId: string,
  toStationExtId: string,
  date: Date,
  options?: { arriveBy?: boolean },
): Promise<Journey[]>

hafasRequest() posts a validated HAFAS body to /api/hafas. The server only allows TripSearch and LocMatch.

searchJourneys() builds a TripSearch, converts the requested date with hafasDateTime(), and parses the response with parseHafasJourneys(). When arriveBy is true and no journeys are returned, it retries with a two-hour backward fallback window.

Routing

getBikeRoute(fromLat, fromLng, toLat, toLng): Promise<BikeRoute>
getWalkRoute(fromLat, fromLng, toLat, toLng): Promise<WalkRoute>

Both methods call OSRM-backed proxy routes and return route summaries plus turn-by-turn steps.

Wiener Linien

findNearbyStops(lat: number, lng: number, radius?: number): Promise<NearbyStop[]>
monitorStops(stopIds: string[]): Promise<WienerLinienDeparture[]>

findNearbyStops() calls /api/wienerlinien/stops. monitorStops() sends repeated stopIds query parameters to /api/wienerlinien/monitor and returns flattened departure rows.

Backend Contract Notes

  • /api/health currently returns { ok, ts, version }.
  • /api/geocode returns a single GeocodeResult, while ApiClient.geocode() wraps it in an array for existing callers.
  • /api/hafas POST caps TripSearch.numF to 5 in the current implementation.
  • /api/calendar/parse has a small body limit intended for direct uploaded text parsing.
  • Remote calendar URL import is allow-list based and rejects redirects.