158 lines
7.0 KiB
Markdown
158 lines
7.0 KiB
Markdown
# 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.
|
|
|
|
```typescript
|
|
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.
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
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.
|