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 MoedlingDEFAULT_ORIGIN_LAT:48.0806926DEFAULT_ORIGIN_LNG:16.2908052DEFAULT_ORIGIN_STATION_NAME:Mödling BahnhofDEFAULT_ORIGIN_STATION_EXT_ID:1231701DEFAULT_ORIGIN_STATION: assembledStation
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/healthcurrently returns{ ok, ts, version }./api/geocodereturns a singleGeocodeResult, whileApiClient.geocode()wraps it in an array for existing callers./api/hafasPOST capsTripSearch.numFto 5 in the current implementation./api/calendar/parsehas a small body limit intended for direct uploaded text parsing.- Remote calendar URL import is allow-list based and rejects redirects.