2024-02-06 22:58:49 +01:00
import isObj from 'lodash/isObject.js' ;
2024-12-17 19:41:00 +00:00
import distance from 'gps-distance' ;
2024-12-18 00:21:10 +00:00
import readStations from 'db-hafas-stations' ;
2016-06-22 01:39:04 +02:00
2024-02-06 22:58:49 +01:00
import { defaultProfile } from './lib/default-profile.js' ;
import { validateProfile } from './lib/validate-profile.js' ;
2016-06-22 02:09:02 +02:00
2023-03-14 21:00:20 +01:00
// background info: https://github.com/public-transport/hafas-client/issues/286
const FORBIDDEN _USER _AGENTS = [
'my-awesome-program' , // previously used in readme.md, p/*/readme.md & docs/*.md
'hafas-client-example' , // previously used in p/*/example.js
'link-to-your-project-or-email' , // now used throughout
2025-01-09 12:28:42 +00:00
'db-vendo-client' ,
2024-02-06 22:58:49 +01:00
] ;
2023-03-14 21:00:20 +01:00
2024-02-06 22:58:49 +01:00
const isNonEmptyString = str => 'string' === typeof str && str . length > 0 ;
2018-02-28 16:09:23 +01:00
2018-08-25 01:52:46 +02:00
const validateLocation = ( loc , name = 'location' ) => {
if ( ! isObj ( loc ) ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( name + ' must be an object.' ) ;
2018-08-25 01:52:46 +02:00
} else if ( loc . type !== 'location' ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'invalid location object.' ) ;
2018-08-25 01:52:46 +02:00
} else if ( 'number' !== typeof loc . latitude ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( name + '.latitude must be a number.' ) ;
2018-08-25 01:52:46 +02:00
} else if ( 'number' !== typeof loc . longitude ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( name + '.longitude must be a number.' ) ;
2018-08-25 01:52:46 +02:00
}
2024-02-06 22:58:49 +01:00
} ;
2018-08-25 01:52:46 +02:00
2025-01-11 20:52:14 +00:00
const loadEnrichedStationData = ( profile ) => new Promise ( ( resolve , reject ) => {
2024-12-18 00:21:10 +00:00
const items = { } ;
2024-12-18 00:08:52 +00:00
readStations . full ( )
2024-12-18 00:21:10 +00:00
. on ( 'data' , ( station ) => {
items [ station . id ] = station ;
2025-02-09 00:46:21 +01:00
items [ station . name ] = station ;
2024-12-18 00:21:10 +00:00
} )
. once ( 'end' , ( ) => {
2025-01-11 20:52:14 +00:00
if ( profile . DEBUG ) {
console . log ( 'Loaded station index.' ) ;
}
2024-12-18 00:21:10 +00:00
resolve ( items ) ;
} )
. once ( 'error' , ( err ) => {
reject ( err ) ;
} ) ;
2024-12-18 00:08:52 +00:00
} ) ;
2025-02-13 22:16:22 +00:00
const applyEnrichedStationData = async ( ctx , shouldLoadEnrichedStationData ) => {
const { profile , common } = ctx ;
if ( shouldLoadEnrichedStationData && ! common . locations ) {
const locations = await loadEnrichedStationData ( profile ) ;
common . locations = locations ;
}
} ;
2019-09-28 22:15:22 +02:00
const createClient = ( profile , userAgent , opt = { } ) => {
2024-02-06 22:58:49 +01:00
profile = Object . assign ( { } , defaultProfile , profile ) ;
validateProfile ( profile ) ;
2024-12-18 00:08:52 +00:00
const common = { } ;
2025-02-13 22:16:22 +00:00
let shouldLoadEnrichedStationData = false ;
if ( typeof opt . enrichStations === 'function' ) {
profile . enrichStation = opt . enrichStations ;
} else if ( opt . enrichStations !== false ) {
shouldLoadEnrichedStationData = true ;
2024-12-18 00:08:52 +00:00
}
2017-10-03 17:36:42 +02:00
2018-07-19 21:50:20 +02:00
if ( 'string' !== typeof userAgent ) {
2019-02-13 17:55:57 +01:00
throw new TypeError ( 'userAgent must be a string' ) ;
2018-07-19 21:50:20 +02:00
}
2023-03-14 21:00:20 +01:00
if ( FORBIDDEN _USER _AGENTS . includes ( userAgent . toLowerCase ( ) ) ) {
2024-12-11 23:51:58 +00:00
throw new TypeError ( ` userAgent should tell the API operators how to contact you. If you have copied " ${ userAgent } " value from the documentation, please adapt it. ` ) ;
2023-03-14 21:00:20 +01:00
}
2018-07-19 21:50:20 +02:00
2021-12-29 21:11:07 +01:00
const _stationBoard = async ( station , type , resultsField , parse , opt = { } ) => {
2025-02-13 22:16:22 +00:00
await applyEnrichedStationData ( { profile , common } , shouldLoadEnrichedStationData ) ;
2024-12-07 22:46:04 +00:00
if ( isObj ( station ) && station . id ) {
station = station . id ;
} else if ( 'string' !== typeof station ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'station must be an object or a string.' ) ;
}
2017-11-12 18:06:16 +01:00
2018-06-26 15:49:50 +02:00
if ( 'string' !== typeof type || ! type ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'type must be a non-empty string.' ) ;
2018-06-26 15:49:50 +02:00
}
2024-02-06 22:58:49 +01:00
if ( ! profile . departuresGetPasslist && 'stopovers' in opt ) {
throw new Error ( 'opt.stopovers is not supported by this endpoint' ) ;
2018-12-30 15:21:04 +01:00
}
2025-01-14 22:16:09 +00:00
if ( ! profile . departuresStbFltrEquiv && 'includeRelatedStations' in opt ) { // TODO artificially filter?
2024-02-06 22:58:49 +01:00
throw new Error ( 'opt.includeRelatedStations is not supported by this endpoint' ) ;
2018-12-30 15:21:04 +01:00
}
2017-11-12 18:06:16 +01:00
opt = Object . assign ( {
2019-10-28 17:42:33 +01:00
// todo: for arrivals(), this is actually a station it *has already* stopped by
direction : null , // only show departures stopping by this station
2020-03-09 21:59:17 +01:00
line : null , // filter by line ID
2018-06-28 13:00:33 +02:00
duration : 10 , // show departures for the next n minutes
2020-03-25 21:54:38 +01:00
results : null , // max. number of results; `null` means "whatever HAFAS wants"
2020-03-18 20:04:39 +01:00
subStops : true , // parse & expose sub-stops of stations?
entrances : true , // parse & expose entrances of stops/stations?
2019-01-23 13:03:01 +08:00
linesOfStops : false , // parse & expose lines at the stop/station?
2018-07-10 18:59:54 +02:00
remarks : true , // parse & expose hints & warnings?
2018-12-27 20:17:23 +01:00
stopovers : false , // fetch & parse previous/next stopovers?
2018-07-10 18:59:54 +02:00
// departures at related stations
// e.g. those that belong together on the metro map.
2024-02-06 22:58:49 +01:00
includeRelatedStations : true ,
} , opt ) ;
opt . when = new Date ( opt . when || Date . now ( ) ) ;
if ( Number . isNaN ( Number ( opt . when ) ) ) {
throw new Error ( 'opt.when is invalid' ) ;
}
2019-10-20 01:47:09 +02:00
2024-12-07 22:46:04 +00:00
const req = profile . formatStationBoardReq ( { profile , opt } , station , resultsField ) ;
2019-10-31 20:08:56 +01:00
2024-12-18 00:21:10 +00:00
const { res } = await profile . request ( { profile , opt } , userAgent , req ) ;
2019-10-20 01:47:09 +02:00
2024-02-06 22:58:49 +01:00
const ctx = { profile , opt , common , res } ;
2025-02-13 20:09:18 +00:00
let results = ( res [ resultsField ] || res . items || res . bahnhofstafelAbfahrtPositionen || res . bahnhofstafelAnkunftPositionen || res . entries )
2024-12-21 23:04:05 +00:00
. map ( res => parse ( ctx , res ) ) ; // TODO sort?, slice
2021-12-29 21:11:07 +01:00
2025-02-13 20:09:18 +00:00
if ( ! opt . includeRelatedStations ) {
results = results . filter ( r => ! r . stop ? . id || r . stop . id == station ) ;
}
if ( opt . direction ) {
results = results . filter ( r => ! r . nextStopovers || r . nextStopovers . find ( s => s . stop ? . id == opt . direction || s . stop ? . name == opt . direction ) ) ;
}
2021-12-29 21:11:07 +01:00
return {
[ resultsField ] : results ,
2024-12-07 22:46:04 +00:00
realtimeDataUpdatedAt : null , // TODO
2024-02-06 22:58:49 +01:00
} ;
} ;
2017-11-12 18:06:16 +01:00
2021-12-29 18:53:50 +01:00
const departures = async ( station , opt = { } ) => {
2024-02-06 22:58:49 +01:00
return await _stationBoard ( station , 'DEP' , 'departures' , profile . parseDeparture , opt ) ;
} ;
2021-12-29 18:53:50 +01:00
const arrivals = async ( station , opt = { } ) => {
2024-02-06 22:58:49 +01:00
return await _stationBoard ( station , 'ARR' , 'arrivals' , profile . parseArrival , opt ) ;
} ;
2018-06-26 17:11:25 +02:00
2021-12-29 18:53:50 +01:00
const journeys = async ( from , to , opt = { } ) => {
2025-02-13 22:16:22 +00:00
await applyEnrichedStationData ( { profile , common } , shouldLoadEnrichedStationData ) ;
2024-02-06 22:58:49 +01:00
if ( 'earlierThan' in opt && 'laterThan' in opt ) {
throw new TypeError ( 'opt.earlierThan and opt.laterThan are mutually exclusive.' ) ;
2018-05-30 16:15:26 +02:00
}
2024-02-06 22:58:49 +01:00
if ( 'departure' in opt && 'arrival' in opt ) {
throw new TypeError ( 'opt.departure and opt.arrival are mutually exclusive.' ) ;
2018-03-04 19:53:53 +01:00
}
2024-02-06 22:58:49 +01:00
let journeysRef = null ;
2018-03-04 19:53:53 +01:00
if ( 'earlierThan' in opt ) {
if ( ! isNonEmptyString ( opt . earlierThan ) ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'opt.earlierThan must be a non-empty string.' ) ;
2018-03-04 19:53:53 +01:00
}
2024-02-06 22:58:49 +01:00
if ( 'departure' in opt || 'arrival' in opt ) {
throw new TypeError ( 'opt.earlierThan and opt.departure/opt.arrival are mutually exclusive.' ) ;
2018-03-04 19:53:53 +01:00
}
2024-02-06 22:58:49 +01:00
journeysRef = opt . earlierThan ;
2018-03-04 19:53:53 +01:00
}
if ( 'laterThan' in opt ) {
if ( ! isNonEmptyString ( opt . laterThan ) ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'opt.laterThan must be a non-empty string.' ) ;
2018-03-04 19:53:53 +01:00
}
2024-02-06 22:58:49 +01:00
if ( 'departure' in opt || 'arrival' in opt ) {
throw new TypeError ( 'opt.laterThan and opt.departure/opt.arrival are mutually exclusive.' ) ;
2018-03-04 19:53:53 +01:00
}
2024-02-06 22:58:49 +01:00
journeysRef = opt . laterThan ;
2018-03-04 19:53:53 +01:00
}
2017-11-12 20:02:32 +01:00
opt = Object . assign ( {
2020-02-27 15:57:54 +01:00
results : null , // number of journeys – `null` means "whatever HAFAS returns"
2017-11-12 20:02:32 +01:00
via : null , // let journeys pass this station?
2018-06-13 20:39:33 +02:00
stopovers : false , // return stations on the way?
2024-12-07 18:29:16 +00:00
transfers : null , // maximum nr of transfers
2017-11-12 20:02:32 +01:00
transferTime : 0 , // minimum time for a single transfer in minutes
// todo: does this work with every endpoint?
accessibility : 'none' , // 'none', 'partial' or 'complete'
bike : false , // only bike-friendly journeys
2018-10-25 16:11:19 +02:00
walkingSpeed : 'normal' , // 'slow', 'normal', 'fast'
2018-06-25 18:33:22 +02:00
// Consider walking to nearby stations at the beginning of a journey?
2018-07-23 20:42:22 +02:00
startWithWalking : true ,
2020-05-21 17:31:51 +02:00
tickets : false , // return tickets?
polylines : false , // return leg shapes?
2020-03-18 20:04:39 +01:00
subStops : true , // parse & expose sub-stops of stations?
entrances : true , // parse & expose entrances of stops/stations?
2020-05-21 17:31:51 +02:00
remarks : true , // parse & expose hints & warnings?
2022-10-24 17:30:05 +02:00
scheduledDays : false , // parse & expose dates each journey is valid on?
2024-02-06 22:58:49 +01:00
} , opt ) ;
2018-05-28 20:34:24 +02:00
if ( opt . when !== undefined ) {
2024-02-06 22:58:49 +01:00
throw new Error ( 'opt.when is not supported anymore. Use opt.departure/opt.arrival.' ) ;
2018-05-28 20:34:24 +02:00
}
2024-02-06 22:58:49 +01:00
let when = new Date ( ) , outFrwd = true ;
2018-05-28 20:34:24 +02:00
if ( opt . departure !== undefined && opt . departure !== null ) {
2024-02-06 22:58:49 +01:00
when = new Date ( opt . departure ) ;
if ( Number . isNaN ( Number ( when ) ) ) {
throw new TypeError ( 'opt.departure is invalid' ) ;
}
2018-05-28 20:34:24 +02:00
} else if ( opt . arrival !== undefined && opt . arrival !== null ) {
2019-12-29 22:16:45 +01:00
if ( ! profile . journeysOutFrwd ) {
2024-02-06 22:58:49 +01:00
throw new Error ( 'opt.arrival is unsupported' ) ;
}
when = new Date ( opt . arrival ) ;
if ( Number . isNaN ( Number ( when ) ) ) {
throw new TypeError ( 'opt.arrival is invalid' ) ;
2019-12-29 22:16:45 +01:00
}
2024-02-06 22:58:49 +01:00
outFrwd = false ;
2018-05-28 20:34:24 +02:00
}
2017-12-18 20:01:12 +01:00
2024-12-21 15:51:25 +00:00
const req = profile . formatJourneysReq ( { profile , opt } , from , to , when , outFrwd , journeysRef ) ;
2024-12-18 00:21:10 +00:00
const { res } = await profile . request ( { profile , opt } , userAgent , req ) ;
2024-02-06 22:58:49 +01:00
const ctx = { profile , opt , common , res } ;
2024-12-21 23:04:05 +00:00
const verbindungen = Number . isInteger ( opt . results ) ? res . verbindungen . slice ( 0 , opt . results ) : res . verbindungen ;
2024-12-17 19:54:14 +00:00
const journeys = verbindungen
2024-02-06 22:58:49 +01:00
. map ( j => profile . parseJourney ( ctx , j ) ) ;
2020-07-20 16:17:31 +02:00
2021-12-29 18:53:50 +01:00
return {
2025-01-03 10:57:24 +00:00
earlierRef : res . verbindungReference ? . earlier || res . frueherContext || null ,
laterRef : res . verbindungReference ? . later || res . spaeterContext || null ,
2021-12-29 18:53:50 +01:00
journeys ,
2024-12-08 21:42:57 +00:00
realtimeDataUpdatedAt : null , // TODO
2024-02-06 22:58:49 +01:00
} ;
} ;
2017-11-12 20:02:32 +01:00
2021-12-29 18:53:50 +01:00
const refreshJourney = async ( refreshToken , opt = { } ) => {
2025-02-13 22:16:22 +00:00
await applyEnrichedStationData ( { profile , common } , shouldLoadEnrichedStationData ) ;
2018-07-24 17:37:36 +02:00
if ( 'string' !== typeof refreshToken || ! refreshToken ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'refreshToken must be a non-empty string.' ) ;
2018-07-24 17:37:36 +02:00
}
opt = Object . assign ( {
stopovers : false , // return stations on the way?
tickets : false , // return tickets?
2021-01-26 22:10:44 +01:00
polylines : false , // return leg shapes? (not supported by all endpoints)
2020-03-18 20:04:39 +01:00
subStops : true , // parse & expose sub-stops of stations?
entrances : true , // parse & expose entrances of stops/stations?
2022-10-24 17:30:05 +02:00
remarks : true , // parse & expose hints & warnings?
scheduledDays : false , // parse & expose dates the journey is valid on?
2024-02-06 22:58:49 +01:00
} , opt ) ;
2018-07-24 17:37:36 +02:00
2024-02-06 22:58:49 +01:00
const req = profile . formatRefreshJourneyReq ( { profile , opt } , refreshToken ) ;
2020-02-07 02:31:51 +01:00
2024-12-18 00:21:10 +00:00
const { res } = await profile . request ( { profile , opt } , userAgent , req ) ;
2024-02-06 22:58:49 +01:00
const ctx = { profile , opt , common , res } ;
2021-12-29 18:53:50 +01:00
return {
2025-01-03 10:57:24 +00:00
journey : profile . parseJourney ( ctx , res . verbindungen && res . verbindungen [ 0 ] || res ) ,
2024-12-11 23:51:58 +00:00
realtimeDataUpdatedAt : null , // TODO
2024-02-06 22:58:49 +01:00
} ;
} ;
2017-11-12 20:02:32 +01:00
2021-12-29 18:53:50 +01:00
const locations = async ( query , opt = { } ) => {
2025-02-13 22:16:22 +00:00
await applyEnrichedStationData ( { profile , common } , shouldLoadEnrichedStationData ) ;
2018-03-04 19:53:53 +01:00
if ( ! isNonEmptyString ( query ) ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'query must be a non-empty string.' ) ;
2018-02-28 16:09:23 +01:00
}
2017-11-12 20:19:33 +01:00
opt = Object . assign ( {
fuzzy : true , // find only exact matches?
2018-12-07 17:00:24 +01:00
results : 5 , // how many search results?
2019-01-23 12:58:05 +08:00
stops : true , // return stops/stations?
2017-11-12 20:19:33 +01:00
addresses : true ,
2018-06-28 13:00:33 +02:00
poi : true , // points of interest
2020-03-18 20:04:39 +01:00
subStops : true , // parse & expose sub-stops of stations?
entrances : true , // parse & expose entrances of stops/stations?
2024-02-06 22:58:49 +01:00
linesOfStops : false , // parse & expose lines at each stop/station?
} , opt ) ;
const req = profile . formatLocationsReq ( { profile , opt } , query ) ;
2019-10-31 20:08:56 +01:00
2024-12-18 00:21:10 +00:00
const { res } = await profile . request ( { profile , opt } , userAgent , req ) ;
2019-10-20 01:47:09 +02:00
2024-02-06 22:58:49 +01:00
const ctx = { profile , opt , common , res } ;
2024-12-21 23:04:05 +00:00
const results = res . map ( loc => profile . parseLocation ( ctx , loc ) ) ;
return Number . isInteger ( opt . results )
? results . slice ( 0 , opt . results )
: results ;
2024-02-06 22:58:49 +01:00
} ;
2017-11-12 20:19:33 +01:00
2025-01-08 23:02:57 +00:00
const stop = async ( stop , opt = { } ) => {
2025-02-13 22:16:22 +00:00
await applyEnrichedStationData ( { profile , common } , shouldLoadEnrichedStationData ) ;
2025-01-08 23:02:57 +00:00
if ( isObj ( stop ) && stop . id ) {
stop = stop . id ;
} else if ( 'string' !== typeof stop ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'stop must be an object or a string.' ) ;
}
2018-01-26 17:08:07 +01:00
2018-06-28 13:00:33 +02:00
opt = Object . assign ( {
2020-05-21 17:31:51 +02:00
linesOfStops : false , // parse & expose lines at the stop/station?
2020-03-18 20:04:39 +01:00
subStops : true , // parse & expose sub-stops of stations?
entrances : true , // parse & expose entrances of stops/stations?
2020-05-21 17:31:51 +02:00
remarks : true , // parse & expose hints & warnings?
2024-02-06 22:58:49 +01:00
} , opt ) ;
2019-10-31 20:08:56 +01:00
2024-02-06 22:58:49 +01:00
const req = profile . formatStopReq ( { profile , opt } , stop ) ;
2019-10-31 20:08:56 +01:00
2024-12-18 00:21:10 +00:00
const { res } = await profile . request ( { profile , opt } , userAgent , req ) ;
2024-02-06 22:58:49 +01:00
const ctx = { profile , opt , res , common } ;
2025-01-08 23:02:57 +00:00
return profile . parseStop ( ctx , res , stop ) ;
2024-02-06 22:58:49 +01:00
} ;
2018-01-26 17:08:07 +01:00
2021-12-29 18:53:50 +01:00
const nearby = async ( location , opt = { } ) => {
2025-02-13 22:16:22 +00:00
await applyEnrichedStationData ( { profile , common } , shouldLoadEnrichedStationData ) ;
2024-02-06 22:58:49 +01:00
validateLocation ( location , 'location' ) ;
2018-01-05 14:53:03 +01:00
2017-11-12 20:29:57 +01:00
opt = Object . assign ( {
results : 8 , // maximum number of results
distance : null , // maximum walking distance in meters
poi : false , // return points of interest?
2019-01-23 12:58:05 +08:00
stops : true , // return stops/stations?
2020-03-18 20:04:39 +01:00
subStops : true , // parse & expose sub-stops of stations?
entrances : true , // parse & expose entrances of stops/stations?
2024-02-06 22:58:49 +01:00
linesOfStops : false , // parse & expose lines at each stop/station?
} , opt ) ;
2017-11-12 20:29:57 +01:00
2024-02-06 22:58:49 +01:00
const req = profile . formatNearbyReq ( { profile , opt } , location ) ;
2024-12-18 00:21:10 +00:00
const { res } = await profile . request ( { profile , opt } , userAgent , req ) ;
2019-10-31 20:08:56 +01:00
2024-02-06 22:58:49 +01:00
const ctx = { profile , opt , common , res } ;
2024-12-17 19:41:00 +00:00
const results = res . map ( loc => {
const res = profile . parseLocation ( ctx , loc ) ;
if ( res . latitude || res . location ? . latitude ) {
res . distance = Math . round ( distance ( location . latitude , location . longitude , res . latitude || res . location ? . latitude , res . longitude || res . location ? . longitude ) * 1000 ) ;
}
return res ;
} ) ;
2021-12-29 18:53:50 +01:00
return Number . isInteger ( opt . results )
? results . slice ( 0 , opt . results )
2024-02-06 22:58:49 +01:00
: results ;
} ;
2017-11-12 20:29:57 +01:00
2021-12-29 21:33:42 +01:00
const trip = async ( id , opt = { } ) => {
2025-02-13 22:16:22 +00:00
await applyEnrichedStationData ( { profile , common } , shouldLoadEnrichedStationData ) ;
2018-06-29 14:58:43 +02:00
if ( ! isNonEmptyString ( id ) ) {
2024-02-06 22:58:49 +01:00
throw new TypeError ( 'id must be a non-empty string.' ) ;
2018-02-28 16:09:23 +01:00
}
2017-12-17 20:33:04 +01:00
opt = Object . assign ( {
2018-06-13 20:39:33 +02:00
stopovers : true , // return stations on the way?
2018-07-16 11:35:47 +02:00
polyline : false , // return a track shape?
2020-03-18 20:04:39 +01:00
subStops : true , // parse & expose sub-stops of stations?
entrances : true , // parse & expose entrances of stops/stations?
2022-11-16 15:18:12 +01:00
remarks : true , // parse & expose hints & warnings?
scheduledDays : false , // parse & expose dates trip is valid on?
2024-02-06 22:58:49 +01:00
} , opt ) ;
2017-11-20 15:43:13 +01:00
2024-02-06 22:58:49 +01:00
const req = profile . formatTripReq ( { profile , opt } , id ) ;
2019-10-31 20:08:56 +01:00
2024-12-18 00:21:10 +00:00
const { res } = await profile . request ( { profile , opt } , userAgent , req ) ;
2024-02-06 22:58:49 +01:00
const ctx = { profile , opt , common , res } ;
2021-12-29 18:53:50 +01:00
2025-01-10 18:55:14 +00:00
const trip = profile . parseTrip ( ctx , res , id ) ;
2021-12-29 21:23:28 +01:00
return {
trip ,
2024-12-18 01:16:57 +00:00
realtimeDataUpdatedAt : null , // TODO
2024-02-06 22:58:49 +01:00
} ;
} ;
2017-11-20 15:43:13 +01:00
2021-10-21 23:00:44 +02:00
// todo [breaking]: rename to trips()?
2024-12-21 15:51:25 +00:00
const tripsByName = async ( _lineNameOrFahrtNr = '*' , _opt = { } ) => {
2025-02-13 22:16:22 +00:00
await applyEnrichedStationData ( { profile , common } , shouldLoadEnrichedStationData ) ;
2024-12-21 15:51:25 +00:00
throw new Error ( 'not implemented' ) ;
2024-02-06 22:58:49 +01:00
} ;
2019-12-17 14:12:35 +01:00
2020-03-09 19:47:29 +01:00
const client = {
departures ,
arrivals ,
journeys ,
locations ,
stop ,
nearby ,
2024-02-06 22:58:49 +01:00
} ;
if ( profile . trip ) {
client . trip = trip ;
}
if ( profile . refreshJourney ) {
client . refreshJourney = refreshJourney ;
}
if ( profile . tripsByName ) {
client . tripsByName = tripsByName ;
}
Object . defineProperty ( client , 'profile' , { value : profile } ) ;
return client ;
} ;
2016-06-22 01:39:59 +02:00
2022-05-07 16:17:37 +02:00
export {
createClient ,
2025-02-13 22:16:22 +00:00
loadEnrichedStationData ,
2024-02-06 22:58:49 +01:00
} ;