apply linting rules

follow-up of 228c7253
This commit is contained in:
Kristjan ESPERANTO 2024-02-06 22:58:49 +01:00 committed by Jannis R
parent 228c72531b
commit 66d9fb5194
No known key found for this signature in database
GPG key ID: 0FE83946296A88A5
246 changed files with 12447 additions and 11915 deletions

View file

@ -7,7 +7,7 @@ const products = [
bitmasks: [16],
name: 'ACME Commuter Rail',
short: 'CR',
default: true
default: true,
},
{
id: 'metro',
@ -15,9 +15,9 @@ const products = [
bitmasks: [8],
name: 'Foo Bar Metro',
short: 'M',
default: true
}
]
default: true,
},
];
const transformReqBody = (body) => {
// get these from the recorded app requests
@ -25,8 +25,8 @@ const transformReqBody = (body) => {
// body.ver = …
// body.auth = { … }
// body.lang = …
return body
}
return body;
};
const insaProfile = {
// locale: …,
@ -37,9 +37,9 @@ const insaProfile = {
products: products,
trip: false,
radar: false
}
radar: false,
};
export {
insaProfile,
}
};

View file

@ -1,25 +1,27 @@
import {formatLocationIdentifier} from './location-identifier.js'
import {formatCoord} from './coord.js'
import {formatLocationIdentifier} from './location-identifier.js';
import {formatCoord} from './coord.js';
const formatAddress = (a) => {
if (a.type !== 'location' || !a.latitude || !a.longitude || !a.address) {
throw new TypeError('invalid address')
throw new TypeError('invalid address');
}
const data = {
A: '2', // address?
O: a.address,
X: formatCoord(a.longitude),
Y: formatCoord(a.latitude)
Y: formatCoord(a.latitude),
};
if (a.id) {
data.L = a.id;
}
if (a.id) data.L = a.id
return {
type: 'A', // address
name: a.address,
lid: formatLocationIdentifier(data)
}
}
lid: formatLocationIdentifier(data),
};
};
export {
formatAddress,
}
};

View file

@ -1,5 +1,5 @@
const formatCoord = x => Math.round(x * 1000000)
const formatCoord = x => Math.round(x * 1000000);
export {
formatCoord,
}
};

View file

@ -1,21 +1,24 @@
import {DateTime, IANAZone} from 'luxon'
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js'
import {DateTime, IANAZone} from 'luxon';
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js';
// todo: change to `(profile) => (when) => {}`
const formatDate = (profile, when) => {
let timezone
if (timezones.has(profile)) timezone = timezones.get(profile)
else {
timezone = new IANAZone(profile.timezone)
timezones.set(profile, timezone)
let timezone;
if (timezones.has(profile)) {
timezone = timezones.get(profile);
} else {
timezone = new IANAZone(profile.timezone);
timezones.set(profile, timezone);
}
return DateTime.fromMillis(+when, {
locale: profile.locale,
zone: timezone
}).toFormat('yyyyMMdd')
}
return DateTime
.fromMillis(Number(when), {
locale: profile.locale,
zone: timezone,
})
.toFormat('yyyyMMdd');
};
export {
formatDate,
}
};

View file

@ -1,12 +1,12 @@
const bike = {type: 'BC', mode: 'INC'}
const bike = {type: 'BC', mode: 'INC'};
const accessibility = {
none: {type: 'META', mode: 'INC', meta: 'notBarrierfree'},
partial: {type: 'META', mode: 'INC', meta: 'limitedBarrierfree'},
complete: {type: 'META', mode: 'INC', meta: 'completeBarrierfree'}
}
complete: {type: 'META', mode: 'INC', meta: 'completeBarrierfree'},
};
export {
bike,
accessibility,
}
};

View file

@ -3,10 +3,10 @@ const formatLinesReq = (ctx, query) => {
meth: 'LineMatch',
req: {
input: query,
}
}
}
},
};
};
export {
formatLinesReq,
}
};

View file

@ -1,8 +1,14 @@
const formatLocationFilter = (stops, addresses, poi) => {
if (stops && addresses && poi) return 'ALL'
return (stops ? 'S' : '') + (addresses ? 'A' : '') + (poi ? 'P' : '')
}
if (stops && addresses && poi) {
return 'ALL';
}
return (
(stops ? 'S' : '')
+ (addresses ? 'A' : '')
+ (poi ? 'P' : '')
);
};
export {
formatLocationFilter,
}
};

View file

@ -1,16 +1,18 @@
const sep = '@'
const sep = '@';
const formatLocationIdentifier = (data) => {
let str = ''
let str = '';
for (let key in data) {
if (!Object.prototype.hasOwnProperty.call(data, key)) continue
if (!Object.prototype.hasOwnProperty.call(data, key)) {
continue;
}
str += key + '=' + data[key] + sep // todo: escape, but how?
str += key + '=' + data[key] + sep; // todo: escape, but how?
}
return str
}
return str;
};
export {
formatLocationIdentifier,
}
};

View file

@ -1,17 +1,25 @@
const formatLocation = (profile, l, name = 'location') => {
if ('string' === typeof l) return profile.formatStation(l)
if ('string' === typeof l) {
return profile.formatStation(l);
}
if ('object' === typeof l && !Array.isArray(l)) {
if (l.type === 'station' || l.type === 'stop') {
return profile.formatStation(l.id)
return profile.formatStation(l.id);
}
if (l.poi) return profile.formatPoi(l)
if ('string' === typeof l.address) return profile.formatAddress(l)
if (!l.type) throw new TypeError(`missing ${name}.type`)
throw new TypeError(`invalid ${name}.type: ${l.type}`)
if (l.poi) {
return profile.formatPoi(l);
}
if ('string' === typeof l.address) {
return profile.formatAddress(l);
}
if (!l.type) {
throw new TypeError(`missing ${name}.type`);
}
throw new TypeError(`invalid ${name}.type: ${l.type}`);
}
throw new TypeError(name + ': valid station, address or poi required.')
}
throw new TypeError(name + ': valid station, address or poi required.');
};
export {
formatLocation,
}
};

View file

@ -1,5 +1,5 @@
const formatLocationsReq = (ctx, query) => {
const {profile, opt} = ctx
const {profile, opt} = ctx;
return {
cfg: {polyEnc: 'GPA'},
@ -7,14 +7,16 @@ const formatLocationsReq = (ctx, query) => {
req: {input: {
loc: {
type: profile.formatLocationFilter(opt.stops, opt.addresses, opt.poi),
name: opt.fuzzy ? query + '?' : query
name: opt.fuzzy
? query + '?'
: query,
},
maxLoc: opt.results,
field: 'S' // todo: what is this?
}}
}
}
field: 'S', // todo: what is this?
}},
};
};
export {
formatLocationsReq,
}
};

View file

@ -1,5 +1,5 @@
const formatNearbyReq = (ctx, location) => {
const {profile, opt} = ctx
const {profile, opt} = ctx;
return {
cfg: {polyEnc: 'GPA'},
@ -8,21 +8,21 @@ const formatNearbyReq = (ctx, location) => {
ring: {
cCrd: {
x: profile.formatCoord(location.longitude),
y: profile.formatCoord(location.latitude)
y: profile.formatCoord(location.latitude),
},
maxDist: opt.distance || -1,
minDist: 0
minDist: 0,
},
locFltrL: [
profile.formatProductsFilter(ctx, opt.products || {}),
],
getPOIs: !!opt.poi,
getStops: !!opt.stops,
maxLoc: opt.results
}
}
}
getPOIs: Boolean(opt.poi),
getStops: Boolean(opt.stops),
maxLoc: opt.results,
},
};
};
export {
formatNearbyReq,
}
};

View file

@ -1,10 +1,10 @@
import {formatLocationIdentifier} from './location-identifier.js'
import {formatCoord} from './coord.js'
import {formatLocationIdentifier} from './location-identifier.js';
import {formatCoord} from './coord.js';
const formatPoi = (p) => {
// todo: use Number.isFinite()!
if (p.type !== 'location' || !p.latitude || !p.longitude || !p.id || !p.name) {
throw new TypeError('invalid POI')
throw new TypeError('invalid POI');
}
return {
@ -15,11 +15,11 @@ const formatPoi = (p) => {
O: p.name,
L: p.id,
X: formatCoord(p.longitude),
Y: formatCoord(p.latitude)
})
}
}
Y: formatCoord(p.latitude),
}),
};
};
export {
formatPoi,
}
};

View file

@ -1,35 +1,45 @@
import isObj from 'lodash/isObject.js'
import isObj from 'lodash/isObject.js';
const hasProp = (o, k) => Object.prototype.hasOwnProperty.call(o, k)
const hasProp = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
const formatProductsFilter = (ctx, filter) => {
if (!isObj(filter)) throw new TypeError('products filter must be an object')
const {profile} = ctx
if (!isObj(filter)) {
throw new TypeError('products filter must be an object');
}
const {profile} = ctx;
const byProduct = {}
const defaultProducts = {}
const byProduct = {};
const defaultProducts = {};
for (let product of profile.products) {
byProduct[product.id] = product
defaultProducts[product.id] = product.default
byProduct[product.id] = product;
defaultProducts[product.id] = product.default;
}
filter = Object.assign({}, defaultProducts, filter)
filter = Object.assign({}, defaultProducts, filter);
let res = 0, products = 0
let res = 0, products = 0;
for (let product in filter) {
if (!hasProp(filter, product) || filter[product] !== true) continue
if (!byProduct[product]) throw new TypeError('unknown product ' + product)
products++
for (let bitmask of byProduct[product].bitmasks) res = res | bitmask
if (!hasProp(filter, product) || filter[product] !== true) {
continue;
}
if (!byProduct[product]) {
throw new TypeError('unknown product ' + product);
}
products++;
for (let bitmask of byProduct[product].bitmasks) {
res = res | bitmask;
}
}
if (products === 0) {
throw new Error('no products used');
}
if (products === 0) throw new Error('no products used')
return {
type: 'PROD',
mode: 'INC',
value: res + ''
}
}
value: String(res),
};
};
export {
formatProductsFilter,
}
};

View file

@ -1,5 +1,5 @@
const formatRadarReq = (ctx, north, west, south, east) => {
const {profile, opt} = ctx
const {profile, opt} = ctx;
return {
meth: 'JourneyGeoPos',
@ -14,16 +14,16 @@ const formatRadarReq = (ctx, north, west, south, east) => {
perStep: Math.round(opt.duration / Math.max(opt.frames, 1) * 1000),
ageOfReport: true, // todo: what is this?
jnyFltrL: [
profile.formatProductsFilter(ctx, opt.products || {})
profile.formatProductsFilter(ctx, opt.products || {}),
],
// todo: what is this? what about realtime?
// - CALC
// - CALC_REPORT (as seen in the INSA Young app)
trainPosMode: 'CALC',
}
}
}
},
};
};
export {
formatRadarReq,
}
};

View file

@ -1,22 +1,24 @@
const formatReachableFromReq = (ctx, address) => {
const {profile, opt} = ctx
const {profile, opt} = ctx;
return {
meth: 'LocGeoReach',
req: {
loc: profile.formatLocation(profile, address, 'address'),
maxDur: opt.maxDuration === null ? -1 : opt.maxDuration,
maxDur: opt.maxDuration === null
? -1
: opt.maxDuration,
maxChg: opt.maxTransfers,
date: profile.formatDate(profile, opt.when),
time: profile.formatTime(profile, opt.when),
period: 120, // todo: what is this?
jnyFltrL: [
profile.formatProductsFilter(ctx, opt.products || {})
]
}
}
}
profile.formatProductsFilter(ctx, opt.products || {}),
],
},
};
};
export {
formatReachableFromReq,
}
};

View file

@ -2,15 +2,15 @@ const formatRectangle = (profile, north, west, south, east) => {
return {
llCrd: {
x: profile.formatCoord(west),
y: profile.formatCoord(south)
y: profile.formatCoord(south),
},
urCrd: {
x: profile.formatCoord(east),
y: profile.formatCoord(north)
}
}
}
y: profile.formatCoord(north),
},
};
};
export {
formatRectangle,
}
};

View file

@ -1,25 +1,24 @@
const formatRefreshJourneyReq = (ctx, refreshToken) => {
// eslint-disable-next-line no-unused-vars
const {profile, opt} = ctx
const {profile, opt} = ctx;
const req = {
getIST: true, // todo: make an option
getPasslist: !!opt.stopovers,
getPolyline: !!opt.polylines,
getTariff: !!opt.tickets
}
getPasslist: Boolean(opt.stopovers),
getPolyline: Boolean(opt.polylines),
getTariff: Boolean(opt.tickets),
};
if (profile.refreshJourneyUseOutReconL) {
req.outReconL = [{ctx: refreshToken}]
req.outReconL = [{ctx: refreshToken}];
} else {
req.ctxRecon = refreshToken
req.ctxRecon = refreshToken;
}
return {
meth: 'Reconstruction',
req,
}
}
};
};
export {
formatRefreshJourneyReq,
}
};

View file

@ -1,34 +1,38 @@
const formatRemarksReq = (ctx) => {
const {profile, opt} = ctx
const {profile, opt} = ctx;
const himFltrL = []
const himFltrL = [];
// todo: https://github.com/marudor/BahnhofsAbfahrten/blob/95fef0217d01344642dd423457473fe9b8b6056e/src/types/HAFAS/index.ts#L76-L91
if (opt.products) {
himFltrL.push(profile.formatProductsFilter(ctx, opt.products))
himFltrL.push(profile.formatProductsFilter(ctx, opt.products));
}
const req = {
himFltrL,
};
if (profile.remarksGetPolyline) {
req.getPolyline = Boolean(opt.polylines);
}
if (profile.remarksGetPolyline) req.getPolyline = !!opt.polylines
// todo: stLoc, dirLoc
// todo: comp, dept, onlyHimId, onlyToday
// todo: dailyB, dailyE
// see https://github.com/marudor/BahnhofsAbfahrten/blob/46a74957d68edc15713112df44e1a25150f5a178/src/types/HAFAS/HimSearch.ts#L3-L18
if (opt.results !== null) req.maxNum = opt.results
if (opt.results !== null) {
req.maxNum = opt.results;
}
if (opt.from !== null) {
req.dateB = profile.formatDate(profile, opt.from)
req.timeB = profile.formatTime(profile, opt.from)
req.dateB = profile.formatDate(profile, opt.from);
req.timeB = profile.formatTime(profile, opt.from);
}
if (opt.to !== null) {
req.dateE = profile.formatDate(profile, opt.to)
req.timeE = profile.formatTime(profile, opt.to)
req.dateE = profile.formatDate(profile, opt.to);
req.timeE = profile.formatTime(profile, opt.to);
}
return {meth: 'HimSearch', req}
}
return {meth: 'HimSearch', req};
};
export {
formatRemarksReq,
}
};

View file

@ -1,11 +1,11 @@
const formatStationBoardReq = (ctx, station, type) => {
const {profile, opt} = ctx
const {profile, opt} = ctx;
const jnyFltrL = [
profile.formatProductsFilter(ctx, opt.products || {})
]
profile.formatProductsFilter(ctx, opt.products || {}),
];
if (opt.line !== null) {
jnyFltrL.push({type: 'LINEID', mode: 'INC', value: opt.line})
jnyFltrL.push({type: 'LINEID', mode: 'INC', value: opt.line});
}
const req = {
@ -13,22 +13,30 @@ const formatStationBoardReq = (ctx, station, type) => {
date: profile.formatDate(profile, opt.when),
time: profile.formatTime(profile, opt.when),
stbLoc: station,
dirLoc: opt.direction ? profile.formatStation(opt.direction) : undefined,
dirLoc: opt.direction
? profile.formatStation(opt.direction)
: undefined,
jnyFltrL,
dur: opt.duration
}
dur: opt.duration,
};
if (opt.results !== null) {
req.maxJny = opt.results === Infinity ? 10000 : opt.results
req.maxJny = opt.results === Infinity
? 10000
: opt.results;
}
if (profile.departuresGetPasslist) {
req.getPasslist = Boolean(opt.stopovers);
}
if (profile.departuresStbFltrEquiv) {
req.stbFltrEquiv = !opt.includeRelatedStations;
}
if (profile.departuresGetPasslist) req.getPasslist = !!opt.stopovers
if (profile.departuresStbFltrEquiv) req.stbFltrEquiv = !opt.includeRelatedStations
return {
meth: 'StationBoard',
req
}
}
req,
};
};
export {
formatStationBoardReq,
}
};

View file

@ -1,4 +1,4 @@
import {formatLocationIdentifier} from './location-identifier.js'
import {formatLocationIdentifier} from './location-identifier.js';
const formatStation = (id) => {
return {
@ -6,12 +6,12 @@ const formatStation = (id) => {
// todo: name necessary?
lid: formatLocationIdentifier({
A: '1', // station?
L: id
L: id,
// todo: `p` timestamp of when the ID was obtained
})
}
}
}),
};
};
export {
formatStation,
}
};

View file

@ -3,11 +3,11 @@ const formatStopReq = (ctx, stopRef) => {
// todo: there's also `StationDetails`, are there differences?
meth: 'LocDetails',
req: {
locL: [stopRef]
}
}
}
locL: [stopRef],
},
};
};
export {
formatStopReq,
}
};

View file

@ -1,21 +1,24 @@
import {DateTime, IANAZone} from 'luxon'
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js'
import {DateTime, IANAZone} from 'luxon';
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js';
// todo: change to `(profile) => (when) => {}`
const formatTime = (profile, when) => {
let timezone
if (timezones.has(profile)) timezone = timezones.get(profile)
else {
timezone = new IANAZone(profile.timezone)
timezones.set(profile, timezone)
let timezone;
if (timezones.has(profile)) {
timezone = timezones.get(profile);
} else {
timezone = new IANAZone(profile.timezone);
timezones.set(profile, timezone);
}
return DateTime.fromMillis(+when, {
locale: profile.locale,
zone: timezone
}).toFormat('HHmmss')
}
return DateTime
.fromMillis(Number(when), {
locale: profile.locale,
zone: timezone,
})
.toFormat('HHmmss');
};
export {
formatTime,
}
};

View file

@ -8,11 +8,11 @@ const formatTripReq = ({opt}, id) => {
// HAFAS apparently ignores the date in the trip ID and uses the `date` field.
// Thus, it will find a different trip if you pass the wrong date via `opt.when`.
// date: profile.formatDate(profile, opt.when),
getPolyline: !!opt.polyline
}
}
}
getPolyline: Boolean(opt.polyline),
},
};
};
export {
formatTripReq,
}
};

671
index.js

File diff suppressed because it is too large Load diff

View file

@ -1,60 +1,60 @@
import {request} from '../lib/request.js'
import {request} from '../lib/request.js';
import {formatStationBoardReq} from '../format/station-board-req.js'
import {formatLocationsReq} from '../format/locations-req.js'
import {formatStopReq} from '../format/stop-req.js'
import {formatNearbyReq} from '../format/nearby-req.js'
import {formatTripReq} from '../format/trip-req.js'
import {formatRadarReq} from '../format/radar-req.js'
import {formatReachableFromReq} from '../format/reachable-from-req.js'
import {formatRefreshJourneyReq} from '../format/refresh-journey-req.js'
import {formatRemarksReq} from '../format/remarks-req.js'
import {formatLinesReq} from '../format/lines-req.js'
import {formatStationBoardReq} from '../format/station-board-req.js';
import {formatLocationsReq} from '../format/locations-req.js';
import {formatStopReq} from '../format/stop-req.js';
import {formatNearbyReq} from '../format/nearby-req.js';
import {formatTripReq} from '../format/trip-req.js';
import {formatRadarReq} from '../format/radar-req.js';
import {formatReachableFromReq} from '../format/reachable-from-req.js';
import {formatRefreshJourneyReq} from '../format/refresh-journey-req.js';
import {formatRemarksReq} from '../format/remarks-req.js';
import {formatLinesReq} from '../format/lines-req.js';
import {parseDateTime} from '../parse/date-time.js'
import {parsePlatform} from '../parse/platform.js'
import {parseBitmask as parseProductsBitmask} from '../parse/products-bitmask.js'
import {parseIcon} from '../parse/icon.js'
import {parseWhen} from '../parse/when.js'
import {parsePrognosisType} from '../parse/prognosis-type.js'
import {parseScheduledDays} from '../parse/scheduled-days.js'
import {parseDeparture} from '../parse/departure.js'
import {parseArrival} from '../parse/arrival.js'
import {parseTrip} from '../parse/trip.js'
import {parseJourneyLeg} from '../parse/journey-leg.js'
import {parseJourney} from '../parse/journey.js'
import {parseLine} from '../parse/line.js'
import {parseLocation} from '../parse/location.js'
import {parseCommonData as parseCommon} from '../parse/common.js'
import {parsePolyline} from '../parse/polyline.js'
import {parseMovement} from '../parse/movement.js'
import {parseNearby} from '../parse/nearby.js'
import {parseOperator} from '../parse/operator.js'
import {parseHint} from '../parse/hint.js'
import {parseWarning} from '../parse/warning.js'
import {parseStopover} from '../parse/stopover.js'
import {parseDateTime} from '../parse/date-time.js';
import {parsePlatform} from '../parse/platform.js';
import {parseBitmask as parseProductsBitmask} from '../parse/products-bitmask.js';
import {parseIcon} from '../parse/icon.js';
import {parseWhen} from '../parse/when.js';
import {parsePrognosisType} from '../parse/prognosis-type.js';
import {parseScheduledDays} from '../parse/scheduled-days.js';
import {parseDeparture} from '../parse/departure.js';
import {parseArrival} from '../parse/arrival.js';
import {parseTrip} from '../parse/trip.js';
import {parseJourneyLeg} from '../parse/journey-leg.js';
import {parseJourney} from '../parse/journey.js';
import {parseLine} from '../parse/line.js';
import {parseLocation} from '../parse/location.js';
import {parseCommonData as parseCommon} from '../parse/common.js';
import {parsePolyline} from '../parse/polyline.js';
import {parseMovement} from '../parse/movement.js';
import {parseNearby} from '../parse/nearby.js';
import {parseOperator} from '../parse/operator.js';
import {parseHint} from '../parse/hint.js';
import {parseWarning} from '../parse/warning.js';
import {parseStopover} from '../parse/stopover.js';
import {formatAddress} from '../format/address.js'
import {formatCoord} from '../format/coord.js'
import {formatDate} from '../format/date.js'
import {formatLocationFilter} from '../format/location-filter.js'
import {formatProductsFilter} from '../format/products-filter.js'
import {formatPoi} from '../format/poi.js'
import {formatStation} from '../format/station.js'
import {formatTime} from '../format/time.js'
import {formatLocation} from '../format/location.js'
import {formatRectangle} from '../format/rectangle.js'
import * as filters from '../format/filters.js'
import {formatAddress} from '../format/address.js';
import {formatCoord} from '../format/coord.js';
import {formatDate} from '../format/date.js';
import {formatLocationFilter} from '../format/location-filter.js';
import {formatProductsFilter} from '../format/products-filter.js';
import {formatPoi} from '../format/poi.js';
import {formatStation} from '../format/station.js';
import {formatTime} from '../format/time.js';
import {formatLocation} from '../format/location.js';
import {formatRectangle} from '../format/rectangle.js';
import * as filters from '../format/filters.js';
const DEBUG = /(^|,)hafas-client(,|$)/.test(process.env.DEBUG || '')
const DEBUG = (/(^|,)hafas-client(,|$)/).test(process.env.DEBUG || '');
const logRequest = DEBUG
? (_, req, reqId) => console.error(req.body + '')
: () => {}
? (_, req, reqId) => console.error(String(req.body))
: () => {};
const logResponse = DEBUG
? (_, res, body, reqId) => console.error(body)
: () => {}
: () => {};
const id = (ctx, x) => x
const id = (ctx, x) => x;
const defaultProfile = {
request,
@ -115,7 +115,7 @@ const defaultProfile = {
formatRectangle,
filters,
journeysOutFrwd: true, // `journeys()` method: support for `outFrwd` field?
journeysOutFrwd: true, // `journeys()` method: support for `outFrwd` field?
// todo: https://github.com/KDE/kpublictransport/commit/c7c54304160d8f22eab0c91812a107aca82304b7
// `departures()` method: support for `getPasslist` field?
@ -133,8 +133,8 @@ const defaultProfile = {
// `remarks()` method: support for `getPolyline` field?
remarksGetPolyline: true, // `remarks()` method: support for `getPolyline` field?
lines: true,
}
};
export {
defaultProfile,
}
};

View file

@ -1,59 +1,59 @@
const ACCESS_DENIED = 'ACCESS_DENIED'
const INVALID_REQUEST = 'INVALID_REQUEST'
const NOT_FOUND = 'NOT_FOUND'
const SERVER_ERROR = 'SERVER_ERROR'
const ACCESS_DENIED = 'ACCESS_DENIED';
const INVALID_REQUEST = 'INVALID_REQUEST';
const NOT_FOUND = 'NOT_FOUND';
const SERVER_ERROR = 'SERVER_ERROR';
class HafasError extends Error {
constructor (cleanMessage, hafasCode, props) {
const msg = hafasCode
? hafasCode + ': ' + cleanMessage
: cleanMessage
super(msg)
: cleanMessage;
super(msg);
// generic props
this.isHafasError = true
this.isHafasError = true;
// error-specific props
this.code = null
this.code = null;
// By default, we take the blame, unless we know for sure.
this.isCausedByServer = false
this.hafasCode = hafasCode
Object.assign(this, props)
this.isCausedByServer = false;
this.hafasCode = hafasCode;
Object.assign(this, props);
return this
return this;
}
}
class HafasAccessDeniedError extends HafasError {
constructor (cleanMessage, hafasCode, props) {
super(cleanMessage, hafasCode, props)
this.code = ACCESS_DENIED
return this
super(cleanMessage, hafasCode, props);
this.code = ACCESS_DENIED;
return this;
}
}
class HafasInvalidRequestError extends HafasError {
constructor (cleanMessage, hafasCode, props) {
super(cleanMessage, hafasCode, props)
this.code = INVALID_REQUEST
return this
super(cleanMessage, hafasCode, props);
this.code = INVALID_REQUEST;
return this;
}
}
class HafasNotFoundError extends HafasError {
constructor (cleanMessage, hafasCode, props) {
super(cleanMessage, hafasCode, props)
this.code = NOT_FOUND
return this
super(cleanMessage, hafasCode, props);
this.code = NOT_FOUND;
return this;
}
}
class HafasServerError extends HafasError {
constructor (cleanMessage, hafasCode, props) {
super(cleanMessage, hafasCode, props)
this.code = SERVER_ERROR
this.isCausedByServer = true
return this
super(cleanMessage, hafasCode, props);
this.code = SERVER_ERROR;
this.isCausedByServer = true;
return this;
}
}
@ -285,8 +285,8 @@ const byErrorCode = Object.assign(Object.create(null), {
props: {
shouldRetry: true,
},
}
})
},
});
export {
ACCESS_DENIED,
@ -299,4 +299,4 @@ export {
HafasNotFoundError,
HafasServerError,
byErrorCode,
}
};

View file

@ -1,23 +1,23 @@
import objectScan from 'object-scan'
import objectScan from 'object-scan';
const createFindInTree = (needles) => {
const scanner = objectScan(needles, {
filterFn: ({ value, parents, matchedBy, context }) => {
filterFn: ({value, parents, matchedBy, context}) => {
matchedBy.forEach((needle) => {
context[needle].push([value, parents])
})
}
})
context[needle].push([value, parents]);
});
},
});
return (haystack) => {
const context = Object.create(null)
const context = Object.create(null);
needles.forEach((needle) => {
context[needle] = []
})
return scanner(haystack, context)
}
}
context[needle] = [];
});
return scanner(haystack, context);
};
};
export {
createFindInTree,
}
};

View file

@ -1,6 +1,6 @@
// hafas-client profile -> luxon.IANAZone
const luxonIANAZonesByProfile = new WeakMap()
const luxonIANAZonesByProfile = new WeakMap();
export {
luxonIANAZonesByProfile,
}
};

View file

@ -13,11 +13,11 @@ const parseHook = (oldParse, newParse) => {
return (ctx, ...args) => {
return newParse({
...ctx,
parsed: oldParse({...ctx, parsed: {}}, ...args)
}, ...args)
}
}
parsed: oldParse({...ctx, parsed: {}}, ...args),
}, ...args);
};
};
export {
parseHook,
}
};

View file

@ -1,105 +1,118 @@
import ProxyAgent from 'https-proxy-agent'
import {isIP} from 'net'
import {Agent as HttpsAgent} from 'https'
import roundRobin from '@derhuerst/round-robin-scheduler'
import {randomBytes} from 'crypto'
import createHash from 'create-hash'
import {Buffer} from 'node:buffer'
import {stringify} from 'qs'
import {Request, fetch} from 'cross-fetch'
import {parse as parseContentType} from 'content-type'
import {HafasError, byErrorCode} from './errors.js'
import ProxyAgent from 'https-proxy-agent';
import {isIP} from 'net';
import {Agent as HttpsAgent} from 'https';
import roundRobin from '@derhuerst/round-robin-scheduler';
import {randomBytes} from 'crypto';
import createHash from 'create-hash';
import {Buffer} from 'node:buffer';
import {stringify} from 'qs';
import {Request, fetch} from 'cross-fetch';
import {parse as parseContentType} from 'content-type';
import {HafasError, byErrorCode} from './errors.js';
const proxyAddress = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || null
const localAddresses = process.env.LOCAL_ADDRESS || null
const proxyAddress = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || null;
const localAddresses = process.env.LOCAL_ADDRESS || null;
if (proxyAddress && localAddresses) {
console.error('Both env vars HTTPS_PROXY/HTTP_PROXY and LOCAL_ADDRESS are not supported.')
process.exit(1)
console.error('Both env vars HTTPS_PROXY/HTTP_PROXY and LOCAL_ADDRESS are not supported.');
process.exit(1);
}
const plainAgent = new HttpsAgent({
keepAlive: true,
})
let getAgent = () => plainAgent
});
let getAgent = () => plainAgent;
if (proxyAddress) {
const agent = new ProxyAgent(proxyAddress, {
keepAlive: true,
keepAliveMsecs: 10 * 1000, // 10s
})
getAgent = () => agent
});
getAgent = () => agent;
} else if (localAddresses) {
const agents = process.env.LOCAL_ADDRESS.split(',')
.map((addr) => {
const family = isIP(addr)
if (family === 0) throw new Error('invalid local address:' + addr)
return new HttpsAgent({
localAddress: addr, family,
keepAlive: true,
})
})
const pool = roundRobin(agents)
getAgent = () => pool.get()
.map((addr) => {
const family = isIP(addr);
if (family === 0) {
throw new Error('invalid local address:' + addr);
}
return new HttpsAgent({
localAddress: addr, family,
keepAlive: true,
});
});
const pool = roundRobin(agents);
getAgent = () => pool.get();
}
const id = randomBytes(3).toString('hex')
const id = randomBytes(3)
.toString('hex');
const randomizeUserAgent = (userAgent) => {
let ua = userAgent
let ua = userAgent;
for (
let i = Math.round(5 + Math.random() * 5);
i < ua.length;
i += Math.round(5 + Math.random() * 5)
) {
ua = ua.slice(0, i) + id + ua.slice(i)
i += id.length
ua = ua.slice(0, i) + id + ua.slice(i);
i += id.length;
}
return ua
}
return ua;
};
const md5 = input => createHash('md5').update(input).digest()
const md5 = input => createHash('md5')
.update(input)
.digest();
const checkIfResponseIsOk = (_) => {
const {
body,
errProps: baseErrProps,
} = _
} = _;
const errProps = {
...baseErrProps,
};
if (body.id) {
errProps.hafasResponseId = body.id;
}
if (body.id) errProps.hafasResponseId = body.id
// Because we want more accurate stack traces, we don't construct the error here,
// but only return the constructor & error message.
const getError = (_) => {
// mutating here is ugly but pragmatic
if (_.errTxt) errProps.hafasMessage = _.errTxt
if (_.errTxtOut) errProps.hafasDescription = _.errTxtOut
if (_.errTxt) {
errProps.hafasMessage = _.errTxt;
}
if (_.errTxtOut) {
errProps.hafasDescription = _.errTxtOut;
}
if (_.err in byErrorCode) return byErrorCode[_.err]
if (_.err in byErrorCode) {
return byErrorCode[_.err];
}
return {
Error: HafasError,
message: body.errTxt || 'unknown error',
props: {},
}
}
};
};
if (body.err && body.err !== 'OK') {
const {Error: HafasError, message, props} = getError(body)
throw new HafasError(message, body.err, {...errProps, ...props})
const {Error: HafasError, message, props} = getError(body);
throw new HafasError(message, body.err, {...errProps, ...props});
}
if (!body.svcResL || !body.svcResL[0]) {
throw new HafasError('invalid/unsupported response structure', null, errProps)
throw new HafasError('invalid/unsupported response structure', null, errProps);
}
if (body.svcResL[0].err !== 'OK') {
const {Error: HafasError, message, props} = getError(body.svcResL[0])
throw new HafasError(message, body.svcResL[0].err, {...errProps, ...props})
const {Error: HafasError, message, props} = getError(body.svcResL[0]);
throw new HafasError(message, body.svcResL[0].err, {...errProps, ...props});
}
}
};
const request = async (ctx, userAgent, reqData) => {
const {profile, opt} = ctx
const {profile, opt} = ctx;
const rawReqBody = profile.transformReqBody(ctx, {
// todo: is it `eng` actually?
@ -111,7 +124,7 @@ const request = async (ctx, userAgent, reqData) => {
ext: profile.ext, // ?
ver: profile.ver, // HAFAS protocol version
auth: profile.auth, // static authentication
})
});
const req = profile.transformReq(ctx, {
agent: getAgent(),
@ -128,39 +141,40 @@ const request = async (ctx, userAgent, reqData) => {
'connection': 'keep-alive', // prevent excessive re-connecting
},
redirect: 'follow',
query: {}
})
query: {},
});
if (profile.addChecksum || profile.addMicMac) {
if (!Buffer.isBuffer(profile.salt) && 'string' !== typeof profile.salt) {
throw new TypeError('profile.salt must be a Buffer or a string.')
throw new TypeError('profile.salt must be a Buffer or a string.');
}
// Buffer.from(buf, 'hex') just returns buf
const salt = Buffer.from(profile.salt, 'hex')
const salt = Buffer.from(profile.salt, 'hex');
if (profile.addChecksum) {
const checksum = md5(Buffer.concat([
Buffer.from(req.body, 'utf8'),
salt,
]))
req.query.checksum = checksum.toString('hex')
]));
req.query.checksum = checksum.toString('hex');
}
if (profile.addMicMac) {
const mic = md5(Buffer.from(req.body, 'utf8'))
req.query.mic = mic.toString('hex')
const mic = md5(Buffer.from(req.body, 'utf8'));
req.query.mic = mic.toString('hex');
const micAsHex = Buffer.from(mic.toString('hex'), 'utf8')
const mac = md5(Buffer.concat([micAsHex, salt]))
req.query.mac = mac.toString('hex')
const micAsHex = Buffer.from(mic.toString('hex'), 'utf8');
const mac = md5(Buffer.concat([micAsHex, salt]));
req.query.mac = mac.toString('hex');
}
}
const reqId = randomBytes(3).toString('hex')
const url = profile.endpoint + '?' + stringify(req.query)
const fetchReq = new Request(url, req)
profile.logRequest(ctx, fetchReq, reqId)
const reqId = randomBytes(3)
.toString('hex');
const url = profile.endpoint + '?' + stringify(req.query);
const fetchReq = new Request(url, req);
profile.logRequest(ctx, fetchReq, reqId);
const res = await fetch(url, req)
const res = await fetch(url, req);
const errProps = {
// todo [breaking]: assign as non-enumerable property
@ -168,40 +182,40 @@ const request = async (ctx, userAgent, reqData) => {
// todo [breaking]: assign as non-enumerable property
response: res,
url,
}
};
if (!res.ok) {
// todo [breaking]: make this a FetchError or a HafasClientError?
const err = new Error(res.statusText)
Object.assign(err, errProps)
throw err
const err = new Error(res.statusText);
Object.assign(err, errProps);
throw err;
}
let cType = res.headers.get('content-type')
let cType = res.headers.get('content-type');
if (cType) {
const {type} = parseContentType(cType)
const {type} = parseContentType(cType);
if (type !== 'application/json') {
throw new HafasError('invalid/unsupported response content-type: ' + cType, null, errProps)
throw new HafasError('invalid/unsupported response content-type: ' + cType, null, errProps);
}
}
const body = await res.text()
profile.logResponse(ctx, res, body, reqId)
const body = await res.text();
profile.logResponse(ctx, res, body, reqId);
const b = JSON.parse(body)
const b = JSON.parse(body);
checkIfResponseIsOk({
body: b,
errProps,
})
});
const svcRes = b.svcResL[0].res
const svcRes = b.svcResL[0].res;
return {
res: svcRes,
common: profile.parseCommon({...ctx, res: svcRes}),
}
}
};
};
export {
checkIfResponseIsOk,
request,
}
};

View file

@ -1,47 +1,63 @@
const findById = (needle) => {
const needleStopId = needle.id
const needleStationId = needle.station ? needle.station.id : null
const needleStopId = needle.id;
const needleStationId = needle.station
? needle.station.id
: null;
return (stop) => {
if (needleStopId === stop.id) return true
const stationId = stop.station ? stop.station.id : null
if (needleStationId && stationId && needleStationId === stationId) return true
if (needleStopId === stop.id) {
return true;
}
const stationId = stop.station
? stop.station.id
: null;
if (needleStationId && stationId && needleStationId === stationId) {
return true;
}
// todo: `needleStationId === stop.id`? `needleStopId === stationId`?
return false
}
}
return false;
};
};
const sliceLeg = (leg, from, to) => {
if (!Array.isArray(leg.stopovers)) throw new Error('leg.stopovers must be an array.')
if (!Array.isArray(leg.stopovers)) {
throw new Error('leg.stopovers must be an array.');
}
const stops = leg.stopovers.map(st => st.stop)
const fromI = stops.findIndex(findById(from))
if (fromI === -1) throw new Error('from not found in stopovers')
const fromStopover = leg.stopovers[fromI]
const stops = leg.stopovers.map(st => st.stop);
const fromI = stops.findIndex(findById(from));
if (fromI === -1) {
throw new Error('from not found in stopovers');
}
const fromStopover = leg.stopovers[fromI];
const toI = stops.findIndex(findById(to))
if (toI === -1) throw new Error('to not found in stopovers')
const toStopover = leg.stopovers[toI]
const toI = stops.findIndex(findById(to));
if (toI === -1) {
throw new Error('to not found in stopovers');
}
const toStopover = leg.stopovers[toI];
if (fromI === 0 && toI === leg.stopovers.length - 1) return leg
const newLeg = Object.assign({}, leg)
newLeg.stopovers = leg.stopovers.slice(fromI, toI + 1)
if (fromI === 0 && toI === leg.stopovers.length - 1) {
return leg;
}
const newLeg = Object.assign({}, leg);
newLeg.stopovers = leg.stopovers.slice(fromI, toI + 1);
newLeg.origin = fromStopover.stop
newLeg.departure = fromStopover.departure
newLeg.departureDelay = fromStopover.departureDelay
newLeg.scheduledDeparture = fromStopover.scheduledDeparture
newLeg.departurePlatform = fromStopover.departurePlatform
newLeg.origin = fromStopover.stop;
newLeg.departure = fromStopover.departure;
newLeg.departureDelay = fromStopover.departureDelay;
newLeg.scheduledDeparture = fromStopover.scheduledDeparture;
newLeg.departurePlatform = fromStopover.departurePlatform;
newLeg.destination = toStopover.stop
newLeg.arrival = toStopover.arrival
newLeg.arrivalDelay = toStopover.arrivalDelay
newLeg.scheduledArrival = toStopover.scheduledArrival
newLeg.arrivalPlatform = toStopover.arrivalPlatform
newLeg.destination = toStopover.stop;
newLeg.arrival = toStopover.arrival;
newLeg.arrivalDelay = toStopover.arrivalDelay;
newLeg.scheduledArrival = toStopover.scheduledArrival;
newLeg.arrivalPlatform = toStopover.arrivalPlatform;
return newLeg
}
return newLeg;
};
export {
sliceLeg,
}
};

View file

@ -43,53 +43,55 @@ const types = {
formatStation: 'function',
formatTime: 'function',
formatLocation: 'function',
formatRectangle: 'function'
}
formatRectangle: 'function',
};
const validateProfile = (profile) => {
for (let key of Object.keys(types)) {
const type = types[key]
const type = types[key];
if (type === 'array') {
if (!Array.isArray(profile[key])) {
throw new TypeError(`profile.${key} must be an array.`)
throw new TypeError(`profile.${key} must be an array.`);
}
} else if (type !== typeof profile[key]) {
throw new TypeError(`profile.${key} must be a ${type}.`)
throw new TypeError(`profile.${key} must be a ${type}.`);
}
if (type === 'object' && profile[key] === null) {
throw new TypeError(`profile.${key} must not be null.`)
throw new TypeError(`profile.${key} must not be null.`);
}
}
if (!Array.isArray(profile.products)) {
throw new TypeError('profile.products must be an array.')
throw new TypeError('profile.products must be an array.');
}
if (profile.products.length === 0) {
throw new Error('profile.products is empty.');
}
if (profile.products.length === 0) throw new Error('profile.products is empty.')
for (let product of profile.products) {
if ('string' !== typeof product.id) {
throw new TypeError('profile.products[].id must be a string.')
throw new TypeError('profile.products[].id must be a string.');
}
if ('boolean' !== typeof product.default) {
throw new TypeError('profile.products[].default must be a boolean.')
throw new TypeError('profile.products[].default must be a boolean.');
}
if (!Array.isArray(product.bitmasks)) {
throw new TypeError(product.id + '.bitmasks must be an array.')
throw new TypeError(product.id + '.bitmasks must be an array.');
}
for (let bitmask of product.bitmasks) {
if ('number' !== typeof bitmask) {
throw new TypeError(product.id + '.bitmasks[] must be a number.')
throw new TypeError(product.id + '.bitmasks[] must be a number.');
}
}
}
if ('trip' in profile && 'boolean' !== typeof profile.trip) {
throw new Error('profile.trip must be a boolean.')
throw new Error('profile.trip must be a boolean.');
}
if ('journeyLeg' in profile) {
throw new Error('profile.journeyLeg has been removed. Use profile.trip.')
throw new Error('profile.journeyLeg has been removed. Use profile.trip.');
}
}
};
export {
validateProfile,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'regional-train',
@ -82,7 +82,7 @@ const products = [{
name: 'Fähre',
short: 'Fähre',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -98,8 +98,8 @@ const profile = {
reachableFrom: true,
remarks: true,
remarksGetPolyline: false,
}
};
export {
profile,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'bart',
@ -47,7 +47,7 @@ const products = [{
name: 'cable car',
short: 'cable car',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -62,8 +62,8 @@ const profile = {
reachableFrom: true,
refreshJourneyUseOutReconL: true,
}
};
export {
profile,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'ice',
@ -75,7 +75,7 @@ const products = [{
name: 'Autoverlad',
short: 'Autoverlad',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -89,8 +89,8 @@ const profile = {
radar: true,
refreshJourneyUseOutReconL: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,97 +1,99 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import {parseHook} from '../../lib/profile-hooks.js'
import {parseHook} from '../../lib/profile-hooks.js';
import {parseAndAddLocationDHID} from '../vbb/parse-loc-dhid.js'
import {parseAndAddLocationDHID} from '../vbb/parse-loc-dhid.js';
import {parseLocation as _parseLocation} from '../../parse/location.js'
import {parseArrival as _parseArrival} from '../../parse/arrival.js'
import {parseDeparture as _parseDeparture} from '../../parse/departure.js'
import {parseStopover as _parseStopover} from '../../parse/stopover.js'
import {parseJourneyLeg as _parseJourneyLeg} from '../../parse/journey-leg.js'
import {parseLocation as _parseLocation} from '../../parse/location.js';
import {parseArrival as _parseArrival} from '../../parse/arrival.js';
import {parseDeparture as _parseDeparture} from '../../parse/departure.js';
import {parseStopover as _parseStopover} from '../../parse/stopover.js';
import {parseJourneyLeg as _parseJourneyLeg} from '../../parse/journey-leg.js';
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
// todo: there's also a referenced icon `{"res":"occup_fig_{low,mid}"}`
const addOccupancy = (item, occupancyCodes) => {
const remIdx = (item.remarks || [])
.findIndex(r => r.code && occupancyCodes.has(r.code))
if (remIdx < 0) return;
const rem = item.remarks[remIdx]
.findIndex(r => r.code && occupancyCodes.has(r.code));
if (remIdx < 0) {
return;
}
const rem = item.remarks[remIdx];
item.occupancy = occupancyCodes.get(rem.code)
item.occupancy = occupancyCodes.get(rem.code);
item.remarks = [
...item.remarks.slice(0, remIdx),
...item.remarks.slice(remIdx + 1),
]
}
];
};
const stopoverOccupancyCodes = new Map([
['text.occup.loc.max.11', 'low'],
['text.occup.loc.max.12', 'medium'],
['text.occup.loc.max.13', 'high'],
])
]);
const journeyLegOccupancyCodes = new Map([
['text.occup.jny.max.11', 'low'],
['text.occup.jny.max.12', 'medium'],
['text.occup.jny.max.13', 'high'],
])
]);
const parseLocation = ({parsed}, l) => {
parseAndAddLocationDHID(parsed, l)
return parsed
}
parseAndAddLocationDHID(parsed, l);
return parsed;
};
// todo: S45, S46?
const ringbahnClockwise = /^ringbahn s\s?41$/i
const ringbahnAnticlockwise = /^ringbahn s\s?42$/i
const ringbahnClockwise = /^ringbahn s\s?41$/i;
const ringbahnAnticlockwise = /^ringbahn s\s?42$/i;
const parseDepartureRenameRingbahn = ({parsed}, dep) => {
if (parsed.line && parsed.line.product === 'suburban') {
const d = parsed.direction && parsed.direction.trim()
const d = parsed.direction && parsed.direction.trim();
if (ringbahnClockwise.test(d)) {
parsed.direction = 'Ringbahn S41 ⟳'
parsed.direction = 'Ringbahn S41 ⟳';
} else if (ringbahnAnticlockwise.test(d)) {
parsed.direction = 'Ringbahn S42 ⟲'
parsed.direction = 'Ringbahn S42 ⟲';
}
}
return parsed
}
return parsed;
};
const parseArrivalRenameRingbahn = ({parsed}, arr) => {
if (parsed.line && parsed.line.product === 'suburban') {
const p = parsed.provenance && parsed.provenance.trim()
const p = parsed.provenance && parsed.provenance.trim();
if (ringbahnClockwise.test(p)) {
parsed.provenance = 'Ringbahn S41 ⟳'
parsed.provenance = 'Ringbahn S41 ⟳';
} else if (ringbahnAnticlockwise.test(p)) {
parsed.provenance = 'Ringbahn S42 ⟲'
parsed.provenance = 'Ringbahn S42 ⟲';
}
}
return parsed
}
return parsed;
};
const parseArrDepWithOccupancy = ({parsed}, d) => {
addOccupancy(parsed, stopoverOccupancyCodes)
return parsed
}
addOccupancy(parsed, stopoverOccupancyCodes);
return parsed;
};
const parseStopoverWithOccupancy = ({parsed}, st, date) => {
addOccupancy(parsed, stopoverOccupancyCodes)
return parsed
}
addOccupancy(parsed, stopoverOccupancyCodes);
return parsed;
};
const parseJourneyLegWithBerlkönig = (ctx, leg, date) => {
if (leg.type === 'KISS') {
const icon = ctx.common.icons[leg.icoX]
const icon = ctx.common.icons[leg.icoX];
if (icon && icon.type === 'prod_berl') {
const res = _parseJourneyLeg(ctx, {
...leg, type: 'WALK'
}, date)
delete res.walking
...leg, type: 'WALK',
}, date);
delete res.walking;
const mcp = leg.dep.mcp || {}
const mcpData = mcp.mcpData || {}
const mcp = leg.dep.mcp || {};
const mcpData = mcp.mcpData || {};
// todo: mcp.lid
// todo: mcpData.occupancy, mcpData.type
// todo: journey.trfRes.bkgData
@ -102,33 +104,35 @@ const parseJourneyLegWithBerlkönig = (ctx, leg, date) => {
name: mcpData.providerName,
public: true,
mode: 'taxi',
product: 'berlkoenig'
product: 'berlkoenig',
// todo: operator
}
return res
};
return res;
}
}
return _parseJourneyLeg(ctx, leg, date)
}
return _parseJourneyLeg(ctx, leg, date);
};
const parseJourneyLegWithOccupancy = ({parsed}, leg, date) => {
if (leg.type === 'JNY') {
addOccupancy(parsed, journeyLegOccupancyCodes)
addOccupancy(parsed, journeyLegOccupancyCodes);
}
return parsed
}
return parsed;
};
// use the Berlkönig ride sharing service?
// todo: https://github.com/alexander-albers/tripkit/issues/26#issuecomment-825437320
const requestJourneysWithBerlkoenig = ({opt}, query) => {
if (('numF' in query) && opt.berlkoenig) {
if ('numF' in query && opt.berlkoenig) {
// todo: check if this is still true
throw new Error('The `berlkoenig` and `results` options are mutually exclusive.')
throw new Error('The `berlkoenig` and `results` options are mutually exclusive.');
}
query.jnyFltrL.push({type: 'GROUP', mode: 'INC', value: 'OEV'})
if (opt.berlkoenig) query.jnyFltrL.push({type: 'GROUP', mode: 'INC', value: 'BERLKOENIG'})
query.gisFltrL = [{meta: 'foot_speed_normal', type: 'M', mode: 'FB'}]
return query
}
query.jnyFltrL.push({type: 'GROUP', mode: 'INC', value: 'OEV'});
if (opt.berlkoenig) {
query.jnyFltrL.push({type: 'GROUP', mode: 'INC', value: 'BERLKOENIG'});
}
query.gisFltrL = [{meta: 'foot_speed_normal', type: 'M', mode: 'FB'}];
return query;
};
// todo: adapt/extend `vbb-parse-ticket` to support the BVG markup
@ -160,9 +164,9 @@ const profile = {
trip: true,
radar: true,
refreshJourney: true,
reachableFrom: true
}
reachableFrom: true,
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'subway',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -21,7 +21,7 @@ const products = [
bitmasks: [4],
name: 'Tram',
short: 'T',
default: true
default: true,
},
{
id: 'bus',
@ -29,7 +29,7 @@ const products = [
bitmasks: [8],
name: 'Bus',
short: 'B',
default: true
default: true,
},
{
id: 'ferry',
@ -37,7 +37,7 @@ const products = [
bitmasks: [16],
name: 'Fähre',
short: 'F',
default: true
default: true,
},
{
id: 'express',
@ -45,7 +45,7 @@ const products = [
bitmasks: [32],
name: 'IC/ICE',
short: 'E',
default: true
default: true,
},
{
id: 'regional',
@ -53,10 +53,10 @@ const products = [
bitmasks: [64],
name: 'RB/RE',
short: 'R',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -19,8 +19,8 @@ const profile = {
radar: true,
reachableFrom: true,
remarksGetPolyline: false,
}
};
export {
profile,
}
};

View file

@ -6,7 +6,7 @@ const products = [
bitmasks: [1, 2],
name: 'TGV, ICE, EuroCity',
short: 'TGV/ICE/EC',
default: true
default: true,
},
{
id: 'local-train',
@ -14,7 +14,7 @@ const products = [
bitmasks: [8, 16],
name: 'local trains',
short: 'local',
default: true
default: true,
},
{
id: 'tram',
@ -22,7 +22,7 @@ const products = [
bitmasks: [256],
name: 'tram',
short: 'tram',
default: true
default: true,
},
{
id: 'bus',
@ -30,7 +30,7 @@ const products = [
bitmasks: [32],
name: 'bus',
short: 'bus',
default: true
default: true,
},
{
id: 'gondola',
@ -38,10 +38,10 @@ const products = [
bitmasks: [512],
name: 'Fun', // taken from the horaires.cfl.lu website
short: 'Fun', // abbreviation for funicular?
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -19,8 +19,8 @@ const profile = {
refreshJourney: true,
reachableFrom: true,
remarks: true, // `.svcResL[0].res.msgL[]` is missing though 🤔
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [32],
name: 'MetroBus',
short: 'B',
default: true
default: true,
},
{
id: 'rapid',
@ -13,7 +13,7 @@ const products = [
bitmasks: [4096],
name: 'MetroRapid',
short: 'R',
default: true
default: true,
},
{
id: 'rail',
@ -21,10 +21,10 @@ const products = [
bitmasks: [8],
name: 'MetroRail',
short: 'M',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'bus',
@ -12,7 +12,7 @@ const products = [{
name: 'Bus',
short: 'Bus',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -25,8 +25,8 @@ const profile = {
trip: true,
reachableFrom: true,
radar: true,
}
};
export {
profile,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
// DB Busradar NRW app does not allow selecting specific modes of transport to filter results,
// so the bitmasks had to be determined by querying some stations and looking at the results..
@ -14,7 +14,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
default: true,
},
{
id: 'national',
@ -22,7 +22,7 @@ const products = [
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
default: true,
},
// todo: not always true when a station has RE stopping at it
// maybe something else?
@ -32,7 +32,7 @@ const products = [
bitmasks: [4],
name: 'Regionalexpress',
short: 'RE',
default: true
default: true,
},
// todo: also used for replacement service incl. S-Bahn replacement
{
@ -41,7 +41,7 @@ const products = [
bitmasks: [8],
name: 'Regionalzug',
short: 'RB/RE',
default: true
default: true,
},
{
id: 'suburban',
@ -49,7 +49,7 @@ const products = [
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -57,7 +57,7 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true
default: true,
},
{
id: 'ferry',
@ -65,7 +65,7 @@ const products = [
bitmasks: [64],
name: 'Ferry',
short: 'F',
default: true
default: true,
},
// todo: are `128` & `256` unused?
{
@ -74,9 +74,9 @@ const products = [
bitmasks: [512],
name: 'AnrufSammelTaxi',
short: 'AST',
default: true
}
]
default: true,
},
];
const profile = {
...baseProfile,
@ -91,8 +91,8 @@ const profile = {
radar: true,
remarks: true, // `.svcResL[0].res.msgL[]` is missing though 🤔
lines: false, // `.svcResL[0].res.lineL[]` is missing 🤔
}
};
export {
profile,
}
};

View file

@ -9,26 +9,31 @@ const ageGroup = {
CHILD: 15,
YOUNG: 27,
ADULT: 65,
SENIOR: Infinity
}
}
SENIOR: Infinity,
},
};
const ageGroupFromAge = (age) => {
const {upperBoundOf} = ageGroup
if (age < upperBoundOf.BABY)
return ageGroup.BABY
if (age < upperBoundOf.CHILD)
return ageGroup.CHILD
if (age < upperBoundOf.YOUNG)
return ageGroup.YOUNG
if (age < upperBoundOf.ADULT)
return ageGroup.ADULT
if (age < upperBoundOf.SENIOR)
return ageGroup.SENIOR
throw new TypeError(`Invalid age '${age}'`)
}
const {upperBoundOf} = ageGroup;
if (age < upperBoundOf.BABY) {
return ageGroup.BABY;
}
if (age < upperBoundOf.CHILD) {
return ageGroup.CHILD;
}
if (age < upperBoundOf.YOUNG) {
return ageGroup.YOUNG;
}
if (age < upperBoundOf.ADULT) {
return ageGroup.ADULT;
}
if (age < upperBoundOf.SENIOR) {
return ageGroup.SENIOR;
}
throw new TypeError(`Invalid age '${age}'`);
};
export {
ageGroup,
ageGroupFromAge,
}
};

View file

@ -1,70 +1,72 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import trim from 'lodash/trim.js'
import uniqBy from 'lodash/uniqBy.js'
import slugg from 'slugg'
import without from 'lodash/without.js'
import {parseHook} from '../../lib/profile-hooks.js'
import trim from 'lodash/trim.js';
import uniqBy from 'lodash/uniqBy.js';
import slugg from 'slugg';
import without from 'lodash/without.js';
import {parseHook} from '../../lib/profile-hooks.js';
import {parseJourney as _parseJourney} from '../../parse/journey.js'
import {parseJourneyLeg as _parseJourneyLeg} from '../../parse/journey-leg.js'
import {parseLine as _parseLine} from '../../parse/line.js'
import {parseArrival as _parseArrival} from '../../parse/arrival.js'
import {parseDeparture as _parseDeparture} from '../../parse/departure.js'
import {parseHint as _parseHint} from '../../parse/hint.js'
import {parseLocation as _parseLocation} from '../../parse/location.js'
import {formatStation as _formatStation} from '../../format/station.js'
import {bike} from '../../format/filters.js'
import {parseJourney as _parseJourney} from '../../parse/journey.js';
import {parseJourneyLeg as _parseJourneyLeg} from '../../parse/journey-leg.js';
import {parseLine as _parseLine} from '../../parse/line.js';
import {parseArrival as _parseArrival} from '../../parse/arrival.js';
import {parseDeparture as _parseDeparture} from '../../parse/departure.js';
import {parseHint as _parseHint} from '../../parse/hint.js';
import {parseLocation as _parseLocation} from '../../parse/location.js';
import {formatStation as _formatStation} from '../../format/station.js';
import {bike} from '../../format/filters.js';
const baseProfile = require('./base.json')
import {products} from './products.js'
import {formatLoyaltyCard} from './loyalty-cards.js'
import {ageGroup, ageGroupFromAge} from './ageGroup.js'
import {routingModes} from './routing-modes.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
import {formatLoyaltyCard} from './loyalty-cards.js';
import {ageGroup, ageGroupFromAge} from './ageGroup.js';
import {routingModes} from './routing-modes.js';
const transformReqBody = (ctx, body) => {
const req = body.svcReqL[0] || {}
const req = body.svcReqL[0] || {};
// see https://pastebin.com/qZ9WS3Cx
const rtMode = ('routingMode' in ctx.opt) ? ctx.opt.routingMode : routingModes.REALTIME
const rtMode = 'routingMode' in ctx.opt
? ctx.opt.routingMode
: routingModes.REALTIME;
req.cfg = {
...req.cfg,
rtMode,
}
};
return body
}
return body;
};
const transformReq = (ctx, req) => {
const body = JSON.parse(req.body)
const body = JSON.parse(req.body);
// stop() a.k.a. LocDetails seems broken with ver >1.16, all other methods work
if (body.svcReqL[0].meth === 'LocDetails') {
req.body = JSON.stringify({
...body,
ver: '1.16',
})
});
}
return req
}
return req;
};
const slices = (n, arr) => {
const initialState = {slices: [], count: Infinity}
const initialState = {slices: [], count: Infinity};
return arr.reduce(({slices, count}, item) => {
if (count >= n) {
slices.push([item])
count = 1
slices.push([item]);
count = 1;
} else {
slices[slices.length - 1].push(item)
count++
slices[slices.length - 1].push(item);
count++;
}
return {slices, count}
}, initialState).slices
}
return {slices, count};
}, initialState).slices;
};
const parseGrid = (g) => {
// todo: g.type, e.g. `S`
@ -78,13 +80,12 @@ const parseGrid = (g) => {
// iterative process.
return {
title: g.title,
rows: slices(g.nCols, g.itemL.map(item => (
Array.isArray(item.hints) && item.hints[0] ||
Array.isArray(item.remarkRefs) && item.remarkRefs[0] && item.remarkRefs[0].hint ||
{}
))),
}
}
rows: slices(g.nCols, g.itemL.map(item => Array.isArray(item.hints) && item.hints[0]
|| Array.isArray(item.remarkRefs) && item.remarkRefs[0] && item.remarkRefs[0].hint
|| {},
)),
};
};
const ausstattungKeys = Object.assign(Object.create(null), {
'3-s-zentrale': '3SZentrale',
@ -96,115 +97,142 @@ const ausstattungKeys = Object.assign(Object.create(null), {
'reisebedarf': 'travelShop',
'stufenfreier-zugang': 'stepFreeAccess',
'ein-umsteigehilfe': 'boardingAid',
'taxi-am-bahnhof': 'taxis'
})
'taxi-am-bahnhof': 'taxis',
});
const parseAusstattungVal = (val) => {
val = val.toLowerCase()
return val === 'ja' ? true : (val === 'nein' ? false : val)
}
val = val.toLowerCase();
return val === 'ja'
? true
: val === 'nein'
? false
: val;
};
const parseAusstattungGrid = (g) => {
// filter duplicate hint rows
const rows = uniqBy(g.rows, ([key, val]) => key + ':' + val)
const rows = uniqBy(g.rows, ([key, val]) => key + ':' + val);
const res = {}
Object.defineProperty(res, 'raw', {value: rows})
const res = {};
Object.defineProperty(res, 'raw', {value: rows});
for (let [key, val] of rows) {
key = ausstattungKeys[slugg(key)]
if (key) res[key] = parseAusstattungVal(val)
key = ausstattungKeys[slugg(key)];
if (key) {
res[key] = parseAusstattungVal(val);
}
}
return res
}
return res;
};
const parseReisezentrumÖffnungszeiten = (g) => {
const res = {}
for (const [dayOfWeek, val] of g.rows) res[dayOfWeek] = val
res.raw = g.rows
return res
}
const res = {};
for (const [dayOfWeek, val] of g.rows) {
res[dayOfWeek] = val;
}
res.raw = g.rows;
return res;
};
const parseLocWithDetails = ({parsed, common}, l) => {
if (!parsed) return parsed
if (parsed.type !== 'stop' && parsed.type !== 'station') return parsed
if (!parsed) {
return parsed;
}
if (parsed.type !== 'stop' && parsed.type !== 'station') {
return parsed;
}
if (Array.isArray(l.gridL)) {
const resolveCells = grid => ({
...grid,
rows: grid.rows.map(row => row.map(cell => cell && cell.text)),
})
});
let grids = l.gridL
.map(grid => parseGrid(grid, common))
.map(resolveCells)
.map(grid => parseGrid(grid, common))
.map(resolveCells);
const ausstattung = grids.find(g => slugg(g.title) === 'ausstattung')
const ausstattung = grids.find(g => slugg(g.title) === 'ausstattung');
if (ausstattung) {
parsed.facilities = parseAusstattungGrid(ausstattung)
parsed.facilities = parseAusstattungGrid(ausstattung);
}
const öffnungszeiten = grids.find(g => slugg(g.title) === 'offnungszeiten-reisezentrum')
const öffnungszeiten = grids.find(g => slugg(g.title) === 'offnungszeiten-reisezentrum');
if (öffnungszeiten) {
parsed.reisezentrumOpeningHours = parseReisezentrumÖffnungszeiten(öffnungszeiten)
parsed.reisezentrumOpeningHours = parseReisezentrumÖffnungszeiten(öffnungszeiten);
}
grids = without(grids, ausstattung, öffnungszeiten)
if (grids.length > 0) parsed.grids = grids
grids = without(grids, ausstattung, öffnungszeiten);
if (grids.length > 0) {
parsed.grids = grids;
}
}
return parsed
}
return parsed;
};
// https://www.bahn.de/p/view/service/buchung/auslastungsinformation.shtml
const loadFactors = []
loadFactors[1] = 'low-to-medium'
loadFactors[2] = 'high'
loadFactors[3] = 'very-high'
loadFactors[4] = 'exceptionally-high'
const loadFactors = [];
loadFactors[1] = 'low-to-medium';
loadFactors[2] = 'high';
loadFactors[3] = 'very-high';
loadFactors[4] = 'exceptionally-high';
const parseLoadFactor = (opt, tcocL, tcocX) => {
const cls = opt.firstClass ? 'FIRST' : 'SECOND'
const load = tcocX.map(i => tcocL[i]).find(lf => lf.c === cls)
return load && loadFactors[load.r] || null
}
const cls = opt.firstClass
? 'FIRST'
: 'SECOND';
const load = tcocX.map(i => tcocL[i])
.find(lf => lf.c === cls);
return load && loadFactors[load.r] || null;
};
const parseArrOrDepWithLoadFactor = ({parsed, res, opt}, d) => {
if (d.stbStop.dTrnCmpSX && Array.isArray(d.stbStop.dTrnCmpSX.tcocX)) {
const load = parseLoadFactor(opt, res.common.tcocL || [], d.stbStop.dTrnCmpSX.tcocX)
if (load) parsed.loadFactor = load
const load = parseLoadFactor(opt, res.common.tcocL || [], d.stbStop.dTrnCmpSX.tcocX);
if (load) {
parsed.loadFactor = load;
}
}
return parsed
}
return parsed;
};
const transformJourneysQuery = ({opt}, query) => {
const filters = query.jnyFltrL
if (opt.bike) filters.push(bike)
if (('age' in opt) && ('ageGroup' in opt)) {
throw new TypeError(`\
opt.age and opt.ageGroup are mutually exclusive.
Pass in just opt.age, and the age group will calculated automatically.`)
const filters = query.jnyFltrL;
if (opt.bike) {
filters.push(bike);
}
const tvlrAgeGroup = ('age' in opt) ? ageGroupFromAge(opt.age) : opt.ageGroup
if ('age' in opt && 'ageGroup' in opt) {
throw new TypeError(`\
opt.age and opt.ageGroup are mutually exclusive.
Pass in just opt.age, and the age group will calculated automatically.`);
}
const tvlrAgeGroup = 'age' in opt
? ageGroupFromAge(opt.age)
: opt.ageGroup;
query.trfReq = {
// todo: what are these?
// "directESuiteCall": true,
// "rType": "DB-PE",
jnyCl: opt.firstClass === true ? 1 : 2,
jnyCl: opt.firstClass === true
? 1
: 2,
// todo [breaking]: support multiple travelers
tvlrProf: [{
type: tvlrAgeGroup || ageGroup.ADULT,
...(('age' in opt) ? {age: opt.age} : {}),
...'age' in opt
? {age: opt.age}
: {},
redtnCard: opt.loyaltyCard
? formatLoyaltyCard(opt.loyaltyCard)
: null
: null,
}],
cType: 'PK'
}
cType: 'PK',
};
return query
}
return query;
};
// todo: fix this
// line: {
@ -219,19 +247,19 @@ Pass in just opt.age, and the age group will calculated automatically.`)
// }
const parseLineWithAdditionalName = ({parsed}, l) => {
if (l.nameS && ['bus', 'tram', 'ferry'].includes(l.product)) {
parsed.name = l.nameS
parsed.name = l.nameS;
}
if (l.addName) {
parsed.additionalName = parsed.name
parsed.name = l.addName
parsed.additionalName = parsed.name;
parsed.name = l.addName;
}
return parsed
}
return parsed;
};
// todo: sotRating, conSubscr, isSotCon, showARSLink, sotCtxt
// todo: conSubscr, showARSLink, useableTime
const parseJourneyWithPrice = ({parsed}, raw) => {
parsed.price = null
parsed.price = null;
// todo: find cheapest, find discounts
// todo: write a parser like vbb-parse-ticket
// {
@ -274,33 +302,35 @@ const parseJourneyWithPrice = ({parsed}, raw) => {
// }
// ]
if (
raw.trfRes &&
Array.isArray(raw.trfRes.fareSetL) &&
raw.trfRes.fareSetL[0] &&
Array.isArray(raw.trfRes.fareSetL[0].fareL) &&
raw.trfRes.fareSetL[0].fareL[0]
raw.trfRes
&& Array.isArray(raw.trfRes.fareSetL)
&& raw.trfRes.fareSetL[0]
&& Array.isArray(raw.trfRes.fareSetL[0].fareL)
&& raw.trfRes.fareSetL[0].fareL[0]
) {
const tariff = raw.trfRes.fareSetL[0].fareL[0]
const tariff = raw.trfRes.fareSetL[0].fareL[0];
if (tariff.price && tariff.price.amount >= 0) { // wat
parsed.price = {
amount: tariff.price.amount / 100,
currency: 'EUR',
hint: null
}
hint: null,
};
}
}
return parsed
}
return parsed;
};
const parseJourneyLegWithLoadFactor = ({parsed, res, opt}, raw) => {
const tcocX = raw.jny && raw.jny.dTrnCmpSX && raw.jny.dTrnCmpSX.tcocX
const tcocX = raw.jny && raw.jny.dTrnCmpSX && raw.jny.dTrnCmpSX.tcocX;
if (Array.isArray(tcocX) && Array.isArray(res.common.tcocL)) {
const load = parseLoadFactor(opt, res.common.tcocL, tcocX)
if (load) parsed.loadFactor = load
const load = parseLoadFactor(opt, res.common.tcocL, tcocX);
if (load) {
parsed.loadFactor = load;
}
}
return parsed
}
return parsed;
};
// todo:
// [ { type: 'hint',
@ -310,189 +340,189 @@ const hintsByCode = Object.assign(Object.create(null), {
fb: {
type: 'hint',
code: 'bicycle-conveyance',
summary: 'bicycles conveyed'
summary: 'bicycles conveyed',
},
fr: {
type: 'hint',
code: 'bicycle-conveyance-reservation',
summary: 'bicycles conveyed, subject to reservation'
summary: 'bicycles conveyed, subject to reservation',
},
nf: {
type: 'hint',
code: 'no-bicycle-conveyance',
summary: 'bicycles not conveyed'
summary: 'bicycles not conveyed',
},
k2: {
type: 'hint',
code: '2nd-class-only',
summary: '2. class only'
summary: '2. class only',
},
eh: {
type: 'hint',
code: 'boarding-ramp',
summary: 'vehicle-mounted boarding ramp available'
summary: 'vehicle-mounted boarding ramp available',
},
ro: {
type: 'hint',
code: 'wheelchairs-space',
summary: 'space for wheelchairs'
summary: 'space for wheelchairs',
},
oa: {
type: 'hint',
code: 'wheelchairs-space-reservation',
summary: 'space for wheelchairs, subject to reservation'
summary: 'space for wheelchairs, subject to reservation',
},
wv: {
type: 'hint',
code: 'wifi',
summary: 'WiFi available'
summary: 'WiFi available',
},
wi: {
type: 'hint',
code: 'wifi',
summary: 'WiFi available'
summary: 'WiFi available',
},
sn: {
type: 'hint',
code: 'snacks',
summary: 'snacks available for purchase'
summary: 'snacks available for purchase',
},
mb: {
type: 'hint',
code: 'snacks',
summary: 'snacks available for purchase'
summary: 'snacks available for purchase',
},
mp: {
type: 'hint',
code: 'snacks',
summary: 'snacks available for purchase at the seat'
summary: 'snacks available for purchase at the seat',
},
bf: {
type: 'hint',
code: 'barrier-free',
summary: 'barrier-free'
summary: 'barrier-free',
},
rg: {
type: 'hint',
code: 'barrier-free-vehicle',
summary: 'barrier-free vehicle'
summary: 'barrier-free vehicle',
},
bt: {
type: 'hint',
code: 'on-board-bistro',
summary: 'Bordbistro available'
summary: 'Bordbistro available',
},
br: {
type: 'hint',
code: 'on-board-restaurant',
summary: 'Bordrestaurant available'
summary: 'Bordrestaurant available',
},
ki: {
type: 'hint',
code: 'childrens-area',
summary: `children's area available`
summary: 'children\'s area available',
},
kk: {
type: 'hint',
code: 'parents-childrens-compartment',
summary: `parent-and-children compartment available`
summary: 'parent-and-children compartment available',
},
kr: {
type: 'hint',
code: 'kids-service',
summary: 'DB Kids Service available'
summary: 'DB Kids Service available',
},
ls: {
type: 'hint',
code: 'power-sockets',
summary: 'power sockets available'
summary: 'power sockets available',
},
ev: {
type: 'hint',
code: 'replacement-service',
summary: 'replacement service'
summary: 'replacement service',
},
kl: {
type: 'hint',
code: 'air-conditioned',
summary: 'air-conditioned vehicle'
summary: 'air-conditioned vehicle',
},
r0: {
type: 'hint',
code: 'upward-escalator',
summary: 'upward escalator'
summary: 'upward escalator',
},
au: {
type: 'hint',
code: 'elevator',
summary: 'elevator available'
summary: 'elevator available',
},
ck: {
type: 'hint',
code: 'komfort-checkin',
summary: 'Komfort-Checkin available'
summary: 'Komfort-Checkin available',
},
it: {
type: 'hint',
code: 'ice-sprinter',
summary: 'ICE Sprinter service'
summary: 'ICE Sprinter service',
},
rp: {
type: 'hint',
code: 'compulsory-reservation',
summary: 'compulsory seat reservation'
summary: 'compulsory seat reservation',
},
rm: {
type: 'hint',
code: 'optional-reservation',
summary: 'optional seat reservation'
summary: 'optional seat reservation',
},
scl: {
type: 'hint',
code: 'all-2nd-class-seats-reserved',
summary: 'all 2nd class seats reserved'
summary: 'all 2nd class seats reserved',
},
acl: {
type: 'hint',
code: 'all-seats-reserved',
summary: 'all seats reserved'
summary: 'all seats reserved',
},
sk: {
type: 'hint',
code: 'oversize-luggage-forbidden',
summary: 'oversize luggage not allowed'
summary: 'oversize luggage not allowed',
},
hu: {
type: 'hint',
code: 'animals-forbidden',
summary: 'animals not allowed, except guide dogs'
summary: 'animals not allowed, except guide dogs',
},
ik: {
type: 'hint',
code: 'baby-cot-required',
summary: 'baby cot/child seat required'
summary: 'baby cot/child seat required',
},
ee: {
type: 'hint',
code: 'on-board-entertainment',
summary: 'on-board entertainment available'
summary: 'on-board entertainment available',
},
toilet: {
type: 'hint',
code: 'toilet',
summary: 'toilet available'
summary: 'toilet available',
},
oc: {
type: 'hint',
code: 'wheelchair-accessible-toilet',
summary: 'wheelchair-accessible toilet available'
summary: 'wheelchair-accessible toilet available',
},
iz: {
type: 'hint',
code: 'intercity-2',
summary: 'Intercity 2'
}
})
summary: 'Intercity 2',
},
});
const codesByText = Object.assign(Object.create(null), {
'journey cancelled': 'journey-cancelled', // todo: German variant
@ -501,34 +531,39 @@ const codesByText = Object.assign(Object.create(null), {
'signalstörung': 'signal-failure',
'additional stop': 'additional-stopover', // todo: German variant
'platform change': 'changed platform', // todo: use dash, German variant
})
});
const parseHintByCode = ({parsed}, raw) => {
// plain-text hints used e.g. for stop metadata
if (raw.type === 'K') {
return {type: 'hint', text: raw.txtN}
return {type: 'hint', text: raw.txtN};
}
if (raw.type === 'A') {
const hint = hintsByCode[raw.code && raw.code.trim().toLowerCase()]
const hint = hintsByCode[raw.code && raw.code.trim()
.toLowerCase()];
if (hint) {
return Object.assign({text: raw.txtN}, hint)
return Object.assign({text: raw.txtN}, hint);
}
}
if (parsed && raw.txtN) {
const text = trim(raw.txtN.toLowerCase(), ' ()')
if (codesByText[text]) parsed.code = codesByText[text]
const text = trim(raw.txtN.toLowerCase(), ' ()');
if (codesByText[text]) {
parsed.code = codesByText[text];
}
}
return parsed
}
return parsed;
};
const isIBNR = /^\d{6,}$/
const isIBNR = /^\d{6,}$/;
const formatStation = (id) => {
if (!isIBNR.test(id)) throw new Error('station ID must be an IBNR.')
return _formatStation(id)
}
if (!isIBNR.test(id)) {
throw new Error('station ID must be an IBNR.');
}
return _formatStation(id);
};
// todo: find option for absolute number of results
@ -560,8 +595,8 @@ const profile = {
radar: true,
reachableFrom: true,
lines: false, // `.svcResL[0].res.lineL[]` is missing 🤔
}
};
export {
profile,
}
};

View file

@ -6,24 +6,46 @@ const c = {
HALBTAXABO: Symbol('HalbtaxAbo'),
VOORDEELURENABO: Symbol('Voordeelurenabo'),
SHCARD: Symbol('SH-Card'),
GENERALABONNEMENT: Symbol('General-Abonnement')
}
GENERALABONNEMENT: Symbol('General-Abonnement'),
};
// see https://gist.github.com/juliuste/202bb04f450a79f8fa12a2ec3abcd72d
const formatLoyaltyCard = (data) => {
if (data.type === c.BAHNCARD) {
if (data.discount === 25) return data.class === 1 ? 1 : 2
if (data.discount === 50) return data.class === 1 ? 3 : 4
if (data.discount === 25) {
return data.class === 1
? 1
: 2;
}
if (data.discount === 50) {
return data.class === 1
? 3
: 4;
}
}
if (data.type === c.VORTEILSCARD) return 9
if (data.type === c.HALBTAXABO) return data.railplus ? 10 : 11
if (data.type === c.VOORDEELURENABO) return data.railplus ? 12 : 13
if (data.type === c.SHCARD) return 14
if (data.type === c.GENERALABONNEMENT) return 15
return 0
}
if (data.type === c.VORTEILSCARD) {
return 9;
}
if (data.type === c.HALBTAXABO) {
return data.railplus
? 10
: 11;
}
if (data.type === c.VOORDEELURENABO) {
return data.railplus
? 12
: 13;
}
if (data.type === c.SHCARD) {
return 14;
}
if (data.type === c.GENERALABONNEMENT) {
return 15;
}
return 0;
};
export {
c as data,
formatLoyaltyCard,
}
};

View file

@ -6,7 +6,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
default: true,
},
{
id: 'national',
@ -14,7 +14,7 @@ const products = [
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
default: true,
},
{
id: 'regionalExpress',
@ -22,7 +22,7 @@ const products = [
bitmasks: [4],
name: 'RegionalExpress & InterRegio',
short: 'RE/IR',
default: true
default: true,
},
{
id: 'regional',
@ -30,7 +30,7 @@ const products = [
bitmasks: [8],
name: 'Regio',
short: 'RB',
default: true
default: true,
},
{
id: 'suburban',
@ -38,7 +38,7 @@ const products = [
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -46,7 +46,7 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'B',
default: true
default: true,
},
{
id: 'ferry',
@ -54,7 +54,7 @@ const products = [
bitmasks: [64],
name: 'Ferry',
short: 'F',
default: true
default: true,
},
{
id: 'subway',
@ -62,7 +62,7 @@ const products = [
bitmasks: [128],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -70,7 +70,7 @@ const products = [
bitmasks: [256],
name: 'Tram',
short: 'T',
default: true
default: true,
},
{
id: 'taxi',
@ -78,10 +78,10 @@ const products = [
bitmasks: [512],
name: 'Group Taxi',
short: 'Taxi',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -6,8 +6,8 @@ const routingModes = {
REALTIME: 'REALTIME',
SERVER_DEFAULT: 'SERVER_DEFAULT',
HYBRID: 'HYBRID',
}
};
export {
routingModes,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -17,8 +17,8 @@ const profile = {
radar: true,
refreshJourneyUseOutReconL: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
default: true,
},
{
id: 'national',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
default: true,
},
{
id: 'regional',
@ -21,7 +21,7 @@ const products = [
bitmasks: [8],
name: 'RegionalExpress & RegionalBahn',
short: 'RE/RB',
default: true
default: true,
},
{
id: 'suburban',
@ -29,7 +29,7 @@ const products = [
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'tram',
@ -37,7 +37,7 @@ const products = [
bitmasks: [32],
name: 'Tram',
short: 'T',
default: true
default: true,
},
{
id: 'bus',
@ -45,7 +45,7 @@ const products = [
bitmasks: [64, 128],
name: 'Bus',
short: 'B',
default: true
default: true,
},
{
id: 'tourismTrain',
@ -53,10 +53,10 @@ const products = [
bitmasks: [256],
name: 'Tourism Train',
short: 'TT',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -17,8 +17,8 @@ const profile = {
trip: true,
radar: true,
refreshJourney: true,
}
};
export {
profile,
}
};

View file

@ -6,7 +6,7 @@ const products = [
bitmasks: [1, 16],
name: 'Bus',
short: 'Bus',
default: true // the other `bus` has `false`
default: true, // the other `bus` has `false`
},
{
id: 'express-train',
@ -14,7 +14,7 @@ const products = [
bitmasks: [2],
name: 'High-speed train',
short: 'Train',
default: false
default: false,
},
{
id: 'regional-train',
@ -22,7 +22,7 @@ const products = [
bitmasks: [4],
name: 'Regional train',
short: 'Train',
default: false
default: false,
},
{
id: 'local-train',
@ -30,7 +30,7 @@ const products = [
bitmasks: [8],
name: 'Nahverkehrszug',
short: 'Zug',
default: true
default: true,
},
{
id: 'ferry',
@ -38,7 +38,7 @@ const products = [
bitmasks: [32],
name: 'Ferry',
short: 'Ferry',
default: false
default: false,
},
{
id: 'subway',
@ -46,7 +46,7 @@ const products = [
bitmasks: [64],
name: 'Subway',
short: 'Subway',
default: false
default: false,
},
{
id: 'tram',
@ -54,7 +54,7 @@ const products = [
bitmasks: [128],
name: 'Tram',
short: 'Tram',
default: false
default: false,
},
{
id: 'on-demand',
@ -62,10 +62,10 @@ const products = [
bitmasks: [256],
name: 'On-demand traffic',
short: 'on demand',
default: false
}
]
default: false,
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -19,8 +19,8 @@ const profile = {
refreshJourney: false, // fails with `CGI_READ_FAILED`
trip: true,
radar: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [2],
name: 'InterCity',
short: 'IC',
default: true
default: true,
},
// todo: 4
{
@ -14,7 +14,7 @@ const products = [
bitmasks: [8],
name: 'Commuter',
short: 'Commuter',
default: true
default: true,
},
{
id: 'suburban',
@ -22,7 +22,7 @@ const products = [
bitmasks: [16],
name: 'Dublin Area Rapid Transit',
short: 'DART',
default: true
default: true,
},
// todo: 32
{
@ -31,10 +31,10 @@ const products = [
bitmasks: [64],
name: 'LUAS Tram',
short: 'LUAS',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,11 +1,11 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import {readFileSync} from 'fs'
import {Agent} from 'https'
const baseProfile = require('./base.json')
import {readFileSync} from 'fs';
import {Agent} from 'https';
const baseProfile = require('./base.json');
const products = [{
id: 'train-and-s-bahn',
@ -77,14 +77,14 @@ const products = [{
name: 'Anrufsammeltaxi',
short: 'AST',
default: true,
}]
}];
// `fahrplan.ivb.at:443` doesn't provide the necessary CA certificate chain for
// Node.js to trust the certificate, so we manually add it.
// todo: fix this properly, e.g. by letting them know
const ca = readFileSync(new URL('./digicert-tls-rsa-sha256-2020-ca1.crt.pem', import.meta.url).pathname)
const agent = new Agent({ca})
const transformReq = (ctx, req) => ({...req, agent})
const ca = readFileSync(new URL('./digicert-tls-rsa-sha256-2020-ca1.crt.pem', import.meta.url).pathname);
const agent = new Agent({ca});
const transformReq = (ctx, req) => ({...req, agent});
const profile = {
...baseProfile,
@ -99,8 +99,8 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,11 +1,11 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import {readFileSync} from 'fs'
import {Agent} from 'https'
const baseProfile = require('./base.json')
import {readFileSync} from 'fs';
import {Agent} from 'https';
const baseProfile = require('./base.json');
const products = [{
id: 'stadtbahn',
@ -49,14 +49,14 @@ const products = [{
name: 'Fernverkehr',
short: 'Fernverkehr',
default: true,
}]
}];
// `auskunft.kvb.koeln:443` doesn't provide the necessary CA certificate chain for
// Node.js to trust the certificate, so we manually add it.
// todo: fix this properly, e.g. by letting them know
const ca = readFileSync(new URL('./thawte-rsa-ca-2018.pem', import.meta.url).pathname)
const agent = new Agent({ca})
const transformReq = (ctx, req) => ({...req, agent})
const ca = readFileSync(new URL('./thawte-rsa-ca-2018.pem', import.meta.url).pathname);
const agent = new Agent({ca});
const transformReq = (ctx, req) => ({...req, agent});
const profile = {
...baseProfile,
@ -69,8 +69,8 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -18,8 +18,8 @@ const profile = {
reachableFrom: true,
refreshJourneyUseOutReconL: true,
remarks: true,
}
};
export {
profile,
}
};

View file

@ -72,9 +72,9 @@ const products = [
name: 'EC/IC',
short: 'EC/IC',
default: true,
}
]
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -18,8 +18,8 @@ const profile = {
reachableFrom: true,
refreshJourneyUseOutReconL: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'local train (TGV/ICE)',
short: 'TGV/ICE',
default: true
default: true,
},
{
id: 'national-train',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2, 4],
name: 'national train (IC/RE/IRE)',
short: 'IC/RE/IRE',
default: true
default: true,
},
{
id: 'local-train',
@ -21,7 +21,7 @@ const products = [
bitmasks: [8],
name: 'local train (RB/TER)',
short: 'RB/TER',
default: true
default: true,
},
{
id: 'bus',
@ -29,7 +29,7 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true
default: true,
},
{
id: 'tram',
@ -37,10 +37,10 @@ const products = [
bitmasks: [256],
name: 'Tram',
short: 'Tram',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,73 +1,77 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import {parseHook} from '../../lib/profile-hooks.js'
import {parseHook} from '../../lib/profile-hooks.js';
import {parseLocation as _parseLocation} from '../../parse/location.js'
import {parseJourney as _parseJourney} from '../../parse/journey.js'
import {parseMovement as _parseMovement} from '../../parse/movement.js'
const baseProfile = require('./base.json')
import {products} from './products.js'
import {parseLocation as _parseLocation} from '../../parse/location.js';
import {parseJourney as _parseJourney} from '../../parse/journey.js';
import {parseMovement as _parseMovement} from '../../parse/movement.js';
const baseProfile = require('./base.json');
import {products} from './products.js';
// todo: journey prices
const fixLocation = ({parsed}, l) => {
// weird fix for empty lines, e.g. IC/EC at Flensburg Hbf
if (parsed.lines) {
parsed.lines = parsed.lines.filter(x => x.id && x.name)
parsed.lines = parsed.lines.filter(x => x.id && x.name);
}
// remove leading zeroes, todo
if (parsed.id && parsed.id.length > 0) {
parsed.id = parsed.id.replace(/^0+/, '')
parsed.id = parsed.id.replace(/^0+/, '');
}
return parsed
}
return parsed;
};
const parseJourneyWithTickets = ({parsed}, j) => {
if (
j.trfRes &&
Array.isArray(j.trfRes.fareSetL) &&
j.trfRes.fareSetL.length > 0
j.trfRes
&& Array.isArray(j.trfRes.fareSetL)
&& j.trfRes.fareSetL.length > 0
) {
parsed.tickets = []
parsed.tickets = [];
for (let t of j.trfRes.fareSetL) {
const tariff = t.desc
if (!tariff || !Array.isArray(t.fareL)) continue
const tariff = t.desc;
if (!tariff || !Array.isArray(t.fareL)) {
continue;
}
for (let v of t.fareL) {
const variant = v.name
if(!variant) continue
const variant = v.name;
if (!variant) {
continue;
}
const ticket = {
name: [tariff, variant].join(' - '),
tariff,
variant
}
variant,
};
if (v.prc && Number.isInteger(v.prc) && v.cur) {
ticket.amount = v.prc/100
ticket.currency = v.cur
ticket.amount = v.prc / 100;
ticket.currency = v.cur;
} else {
ticket.amount = null
ticket.hint = 'No pricing information available.'
ticket.amount = null;
ticket.hint = 'No pricing information available.';
}
parsed.tickets.push(ticket)
parsed.tickets.push(ticket);
}
}
}
return parsed
}
return parsed;
};
const fixMovement = ({parsed}, m) => {
// filter out empty nextStopovers entries
parsed.nextStopovers = parsed.nextStopovers.filter((f) => {
return f.stop !== null || f.arrival !== null || f.departure !== null
})
return parsed
}
return f.stop !== null || f.arrival !== null || f.departure !== null;
});
return parsed;
};
const profile = {
...baseProfile,
@ -84,8 +88,8 @@ const profile = {
trip: true,
radar: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'High-speed rail',
short: 'ICE/HSR',
default: true
default: true,
},
{
id: 'national',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
default: true,
},
{ // todo: also includes EN?
id: 'interregional',
@ -21,7 +21,7 @@ const products = [
bitmasks: [4],
name: 'Interregional',
short: 'IR',
default: true
default: true,
},
{
id: 'regional',
@ -29,7 +29,7 @@ const products = [
bitmasks: [8],
name: 'Regional & RegionalExpress',
short: 'RB/RE',
default: true
default: true,
},
{
id: 'suburban',
@ -37,7 +37,7 @@ const products = [
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -45,7 +45,7 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'B',
default: true
default: true,
},
{
id: 'ferry',
@ -53,7 +53,7 @@ const products = [
bitmasks: [64],
name: 'Ferry',
short: 'F',
default: true
default: true,
},
{
id: 'subway',
@ -61,7 +61,7 @@ const products = [
bitmasks: [128],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -69,7 +69,7 @@ const products = [
bitmasks: [256],
name: 'Tram',
short: 'T',
default: true
default: true,
},
{
id: 'onCall',
@ -77,10 +77,10 @@ const products = [
bitmasks: [512],
name: 'On-call transit',
short: 'on-call',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -17,8 +17,8 @@ const profile = {
trip: true,
radar: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -6,7 +6,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
default: true,
},
{
id: 'national',
@ -14,7 +14,7 @@ const products = [
bitmasks: [2],
name: 'EuroCity/InterCity',
short: 'EC/IC',
default: true
default: true,
},
{
id: 'regional',
@ -22,7 +22,7 @@ const products = [
bitmasks: [4],
name: 'Regionalzug',
short: 'RE/RB',
default: true
default: true,
},
{
id: 'regiotram',
@ -30,7 +30,7 @@ const products = [
bitmasks: [1024, 16, 8], // it is `1048` actually
name: 'RegioTram',
short: 'RegioTram',
default: true
default: true,
},
{
id: 'tram',
@ -38,7 +38,7 @@ const products = [
bitmasks: [4, 32],
name: 'Tram',
short: 'Tram',
default: true
default: true,
},
{
id: 'bus',
@ -46,7 +46,7 @@ const products = [
bitmasks: [128, 64], // it is `192` actually
name: 'Bus',
short: 'Bus',
default: true
default: true,
},
{
id: 'on-call',
@ -54,10 +54,10 @@ const products = [
bitmasks: [512],
name: 'AnrufSammelTaxi',
short: 'Sammeltaxi',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,50 +1,52 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
// todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L5
// todo: https://gist.github.com/anonymous/a5fc856bc80ae7364721943243f934f4#file-haf_config_base-properties-L47-L234
import {parseHook} from '../../lib/profile-hooks.js'
import {parseHook} from '../../lib/profile-hooks.js';
import {parseLocation as _parseLocation} from '../../parse/location.js'
import {parseMovement as _parseMovement} from '../../parse/movement.js'
const baseProfile = require('./base.json')
import {products} from './products.js'
import {parseLocation as _parseLocation} from '../../parse/location.js';
import {parseMovement as _parseMovement} from '../../parse/movement.js';
const baseProfile = require('./base.json');
import {products} from './products.js';
// ÖBB has some 'stations' **in austria** with no departures/products,
// like station entrances, that are actually POIs.
const fixWeirdPOIs = ({parsed}) => {
if (
(parsed.type === 'station' || parsed.type === 'stop') &&
!parsed.products &&
parsed.name &&
parsed.id && parsed.id.length !== 7
(parsed.type === 'station' || parsed.type === 'stop')
&& !parsed.products
&& parsed.name
&& parsed.id && parsed.id.length !== 7
) {
return Object.assign({
type: 'location',
id: parsed.id,
poi: true,
name: parsed.name
}, parsed.location)
name: parsed.name,
}, parsed.location);
}
return parsed
}
return parsed;
};
const fixMovement = ({parsed}, m) => {
// filter out POIs
// todo: make use of them, as some of them specify fare zones
parsed.nextStopovers = parsed.nextStopovers.filter(st => {
let s = st.stop || {}
if (s.station) s = s.station
return s.type === 'stop' || s.type === 'station'
})
let s = st.stop || {};
if (s.station) {
s = s.station;
}
return s.type === 'stop' || s.type === 'station';
});
parsed.frames = parsed.frames.filter((f) => {
return f.origin.type !== 'location' && f.destination.type !== 'location'
})
return parsed
}
return f.origin.type !== 'location' && f.destination.type !== 'location';
});
return parsed;
};
const profile = {
...baseProfile,
@ -62,8 +64,8 @@ const profile = {
radar: true,
reachableFrom: true,
// lines: false, // `.svcResL[0].res.lineL[]` is missing 🤔
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress & RailJet',
short: 'ICE/RJ',
default: true
default: true,
},
{
id: 'national',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2, 4],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
default: true,
},
{
id: 'interregional',
@ -21,7 +21,7 @@ const products = [
bitmasks: [8, 4096],
name: 'Durchgangszug & EuroNight',
short: 'D/EN',
default: true
default: true,
},
{
id: 'regional',
@ -29,7 +29,7 @@ const products = [
bitmasks: [16],
name: 'Regional & RegionalExpress',
short: 'R/REX',
default: true
default: true,
},
{
id: 'suburban',
@ -37,7 +37,7 @@ const products = [
bitmasks: [32],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -45,7 +45,7 @@ const products = [
bitmasks: [64],
name: 'Bus',
short: 'B',
default: true
default: true,
},
{
id: 'ferry',
@ -53,7 +53,7 @@ const products = [
bitmasks: [128],
name: 'Ferry',
short: 'F',
default: true
default: true,
},
{
id: 'subway',
@ -61,7 +61,7 @@ const products = [
bitmasks: [256],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -69,7 +69,7 @@ const products = [
bitmasks: [512],
name: 'Tram',
short: 'T',
default: true
default: true,
},
{
id: 'onCall',
@ -77,10 +77,10 @@ const products = [
bitmasks: [2048],
name: 'on-call transit, lifts, etc',
short: 'on-call/lift',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'train-and-s-bahn',
@ -75,7 +75,7 @@ const products = [{
name: 'Anrufsammeltaxi',
short: 'AST',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -96,8 +96,8 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,20 +1,20 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import {parseHook} from '../../lib/profile-hooks.js'
import {parseHook} from '../../lib/profile-hooks.js';
import {parseLocation} from '../../parse/location.js'
const baseProfile = require('./base.json')
import {products} from './products.js'
import {parseLocation} from '../../parse/location.js';
const baseProfile = require('./base.json');
import {products} from './products.js';
const trimStopName = ({parsed}, l) => {
if (parsed.type === 'stop' || parsed.type === 'station' && parsed.name) {
parsed.name = parsed.name.replace(/(^-|-$)/g, '')
parsed.name = parsed.name.replace(/(^-|-$)/g, '');
}
return parsed
}
return parsed;
};
const profile = {
...baseProfile,
@ -30,8 +30,8 @@ const profile = {
refreshJourney: false,
reachableFrom: true,
remarks: false, // seems like ver >= 1.20 is required
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1, 2],
name: 'ExpressInterCity & ExpressInterCity Premium & InterCityExpress',
short: 'EIC/EIP/ICE',
default: true
default: true,
},
{
id: 'long-distance-train',
@ -13,7 +13,7 @@ const products = [
bitmasks: [4],
name: 'InterCity & Twoje Linie Kolejowe & EuroCity & EuroNight',
short: 'IC/TLK/EC/EN',
default: true
default: true,
},
{
id: 'regional-train',
@ -21,7 +21,7 @@ const products = [
bitmasks: [8],
name: 'Regional',
short: 'R',
default: true
default: true,
},
{
id: 'bus',
@ -29,10 +29,10 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'B',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -16,8 +16,8 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
}
};
export {
profile,
}
};

View file

@ -38,9 +38,9 @@ const products = [
name: 'S-Tog A/B/Bx/C/E/F/H',
short: 'S',
default: true,
}
]
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -18,8 +18,8 @@ const profile = {
radar: true,
refreshJourney: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress/Fernzug',
short: 'ICE',
default: true
default: true,
},
{
id: 'long-distance-train',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2],
name: 'EuroCity/InterCity/EuroNight/InterRegio',
short: 'EC/IC/EN/IR',
default: true
default: true,
},
{
id: 'regiona-train',
@ -21,7 +21,7 @@ const products = [
bitmasks: [4],
name: 'RegionalExpress/Regionalbahn',
short: 'RE/RB',
default: true
default: true,
},
{
id: 's-bahn',
@ -29,7 +29,7 @@ const products = [
bitmasks: [8],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'u-bahn',
@ -37,7 +37,7 @@ const products = [
bitmasks: [16],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -45,7 +45,7 @@ const products = [
bitmasks: [32],
name: 'Straßenbahn',
short: 'Tram',
default: true
default: true,
},
{
id: 'bus',
@ -53,7 +53,7 @@ const products = [
bitmasks: [64, 128],
name: 'Bus',
short: 'Bus',
default: true
default: true,
},
{
id: 'watercraft',
@ -61,7 +61,7 @@ const products = [
bitmasks: [256],
name: 'Schiff',
short: 'Schiff',
default: true
default: true,
},
{
id: 'ast',
@ -69,7 +69,7 @@ const products = [
bitmasks: [512],
name: 'Anruf-Sammel-Taxi',
short: 'AST',
default: true
default: true,
},
{
id: 'cable-car',
@ -77,11 +77,11 @@ const products = [
bitmasks: [1024],
name: 'Seilbahn',
short: 'Seilbahn',
default: true
}
default: true,
},
// todo: remaining bitmask `1015`
]
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -17,8 +17,8 @@ const profile = {
radar: true,
reachableFrom: true,
refreshJourneyUseOutReconL: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
default: true,
},
{
id: 'ic-ec',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
default: true,
},
{
id: 'long-distance-train',
@ -21,7 +21,7 @@ const products = [
bitmasks: [4],
name: 'InterRegio/high-speed train',
short: 'IR/other',
default: true
default: true,
},
{
id: 'regional-train', // todo: rename
@ -29,7 +29,7 @@ const products = [
bitmasks: [8],
name: 'regional train',
short: 'RE/RB',
default: true
default: true,
},
{
id: 's-bahn',
@ -37,7 +37,7 @@ const products = [
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -45,7 +45,7 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'B',
default: true
default: true,
},
{
id: 'ferry',
@ -53,7 +53,7 @@ const products = [
bitmasks: [64],
name: 'Schiff',
short: 'F',
default: true
default: true,
},
{
id: 'u-bahn',
@ -61,7 +61,7 @@ const products = [
bitmasks: [128],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -69,7 +69,7 @@ const products = [
bitmasks: [256],
name: 'Tram',
short: 'T',
default: true
default: true,
},
{
id: 'on-call',
@ -77,10 +77,10 @@ const products = [
bitmasks: [512],
name: 'Taxi/on-call vehicle',
short: 'AST',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,19 +1,19 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import {parseHook} from '../../lib/profile-hooks.js'
import {parseHook} from '../../lib/profile-hooks.js';
import {parseMovement as _parseMovement} from '../../parse/movement.js'
const baseProfile = require('./base.json')
import {products} from './products.js'
import {parseMovement as _parseMovement} from '../../parse/movement.js';
const baseProfile = require('./base.json');
import {products} from './products.js';
const fixMovement = ({parsed}, m) => {
// filter out empty stopovers
parsed.nextStopovers = parsed.nextStopovers.filter(st => !!st.stop)
return parsed
}
parsed.nextStopovers = parsed.nextStopovers.filter(st => Boolean(st.stop));
return parsed;
};
const profile = {
...baseProfile,
@ -29,9 +29,9 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
radar: true,
reachableFrom: true
}
reachableFrom: true,
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [8192],
name: 'Hochgeschwindigkeitszug',
short: 'ICE',
default: true
default: true,
},
{
id: 'national',
@ -13,7 +13,7 @@ const products = [
bitmasks: [4096],
name: 'InterCity & EuroCity',
short: 'IC/EC',
default: true
default: true,
},
{
id: 'interregional',
@ -21,7 +21,7 @@ const products = [
bitmasks: [2048],
name: 'InterRegio',
short: 'IR',
default: true
default: true,
},
{
id: 'regional',
@ -29,7 +29,7 @@ const products = [
bitmasks: [1024],
name: 'Regionalzug',
short: 'RB ?', // todo
default: true
default: true,
},
{
id: 'suburban',
@ -37,7 +37,7 @@ const products = [
bitmasks: [512],
name: 'S-Bahn',
short: 'S-Bahn',
default: true
default: true,
},
{
id: 'subway',
@ -45,7 +45,7 @@ const products = [
bitmasks: [256],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'saarbahn',
@ -53,7 +53,7 @@ const products = [
bitmasks: [128],
name: 'Saarbahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -61,7 +61,7 @@ const products = [
bitmasks: [64],
name: 'Bus',
short: 'Bus',
default: true
default: true,
},
{
id: 'watercraft',
@ -69,7 +69,7 @@ const products = [
bitmasks: [32], // todo: correct?
name: 'Schiff',
short: 'Schiff',
default: true
default: true,
},
{
id: 'onCall',
@ -77,7 +77,7 @@ const products = [
bitmasks: [16],
name: 'Anruf-Sammel-Taxi',
short: 'AST',
default: true
default: true,
},
{
id: 'school-bus',
@ -85,11 +85,11 @@ const products = [
bitmasks: [8],
name: 'Schulbus',
short: 'Schulbus',
default: true
}
default: true,
},
// todo: `1`, `2`, `4` bitmasks?
]
];
export {
products,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'train-and-s-bahn',
@ -75,7 +75,7 @@ const products = [{
name: 'Anrufsammeltaxi',
short: 'AST',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -89,8 +89,8 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -19,9 +19,9 @@ const profile = {
trip: true,
radar: true,
refreshJourney: true,
reachableFrom: true
}
reachableFrom: true,
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
default: true,
},
{
id: 'ic-ec',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2],
name: 'InterCity/EuroCity',
short: 'IC/EC',
default: true
default: true,
},
{
id: 'ir-d',
@ -21,7 +21,7 @@ const products = [
bitmasks: [4],
name: 'Interregio/Schnellzug',
short: 'IRE',
default: true
default: true,
},
{
id: 'region',
@ -29,7 +29,7 @@ const products = [
bitmasks: [8],
name: 'Regio- und Nahverkehr',
short: 'RE/RB',
default: true
default: true,
},
{
id: 'sbahn',
@ -37,7 +37,7 @@ const products = [
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -45,7 +45,7 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true
default: true,
},
// todo: 64
{
@ -54,7 +54,7 @@ const products = [
bitmasks: [128],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -62,7 +62,7 @@ const products = [
bitmasks: [256],
name: 'Straßenbahn',
short: 'Tram',
default: true
default: true,
},
{
id: 'on-call',
@ -70,10 +70,10 @@ const products = [
bitmasks: [512],
name: 'Anrufsammeltaxi',
short: 'Sammeltaxi',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,51 +1,57 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import {readFileSync} from 'fs'
import {Agent} from 'https'
import {strictEqual as eql} from 'assert'
import {parseHook} from '../../lib/profile-hooks.js'
import {parseLine} from '../../parse/line.js'
const baseProfile = require('./base.json')
import {products} from './products.js'
import {readFileSync} from 'fs';
import {Agent} from 'https';
import {strictEqual as eql} from 'assert';
import {parseHook} from '../../lib/profile-hooks.js';
import {parseLine} from '../../parse/line.js';
const baseProfile = require('./base.json');
import {products} from './products.js';
// `www.belgianrail.be:443` doesn't provide the necessary CA certificate
// chain for Node.js to trust the certificate, so we manually add it.
// todo: fix this properly, e.g. by letting them know
const ca = readFileSync(new URL('./digicert-sha2-secure-server-ca.crt.pem', import.meta.url).pathname)
const agent = new Agent({ca})
const transformReq = (ctx, req) => ({...req, agent})
const ca = readFileSync(new URL('./digicert-sha2-secure-server-ca.crt.pem', import.meta.url).pathname);
const agent = new Agent({ca});
const transformReq = (ctx, req) => ({...req, agent});
// todo: this is ugly
const lineNameWithoutFahrtNr = ({parsed}) => {
const {name, fahrtNr} = parsed
if (!name || !fahrtNr || !/s\d/i.test(name)) return parsed
const i = name.indexOf(fahrtNr)
if (i < 0) return parsed
const {name, fahrtNr} = parsed;
if (!name || !fahrtNr || !(/s\d/i).test(name)) {
return parsed;
}
const i = name.indexOf(fahrtNr);
if (i < 0) {
return parsed;
}
if (
/\s/.test(name[i - 1] || '') && // space before
name.length === i + fahrtNr.length // nothing behind
) return {
...parsed,
name: name.slice(0, i - 1) + name.slice(i + fahrtNr.length + 1),
(/\s/).test(name[i - 1] || '') // space before
&& name.length === i + fahrtNr.length // nothing behind
) {
return {
...parsed,
name: name.slice(0, i - 1) + name.slice(i + fahrtNr.length + 1),
};
}
return parsed
}
return parsed;
};
eql(lineNameWithoutFahrtNr({
parsed: {name: 'THA 123', fahrtNr: '123'}
}).name, 'THA 123')
parsed: {name: 'THA 123', fahrtNr: '123'},
}).name, 'THA 123');
eql(lineNameWithoutFahrtNr({
parsed: {name: 'S1 123', fahrtNr: '123'}
}).name, 'S1')
parsed: {name: 'S1 123', fahrtNr: '123'},
}).name, 'S1');
eql(lineNameWithoutFahrtNr({
parsed: {name: 'S1-123', fahrtNr: '123'}
}).name, 'S1-123')
parsed: {name: 'S1-123', fahrtNr: '123'},
}).name, 'S1-123');
eql(lineNameWithoutFahrtNr({
parsed: {name: 'S1 123a', fahrtNr: '123'}
}).name, 'S1 123a')
parsed: {name: 'S1 123a', fahrtNr: '123'},
}).name, 'S1 123a');
const profile = {
...baseProfile,
@ -62,8 +68,8 @@ const profile = {
refreshJourney: true,
radar: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -6,7 +6,7 @@ const products = [ // todo: 2, 8, 32, 128
bitmasks: [1],
name: 'high-speed train',
short: 'HST',
default: true
default: true,
},
{
id: 'intercity-p',
@ -14,7 +14,7 @@ const products = [ // todo: 2, 8, 32, 128
bitmasks: [4],
name: 'InterCity/Peak',
short: 'IC/P',
default: true
default: true,
},
{
id: 's-train',
@ -22,7 +22,7 @@ const products = [ // todo: 2, 8, 32, 128
bitmasks: [16],
name: 'S-train',
short: 'S',
default: true
default: true,
},
{
id: 'local-train',
@ -30,7 +30,7 @@ const products = [ // todo: 2, 8, 32, 128
bitmasks: [64],
name: 'local train',
short: 'L',
default: true
default: true,
},
{
id: 'metro',
@ -38,7 +38,7 @@ const products = [ // todo: 2, 8, 32, 128
bitmasks: [256],
name: 'Metro',
short: 'M',
default: true
default: true,
},
{
id: 'bus',
@ -46,7 +46,7 @@ const products = [ // todo: 2, 8, 32, 128
bitmasks: [512],
name: 'bus',
short: 'bus',
default: true
default: true,
},
{
id: 'tram',
@ -54,10 +54,10 @@ const products = [ // todo: 2, 8, 32, 128
bitmasks: [1024],
name: 'tram',
short: 'tram',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'train-and-s-bahn',
@ -75,7 +75,7 @@ const products = [{
name: 'Anrufsammeltaxi',
short: 'AST',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -87,8 +87,8 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -17,8 +17,8 @@ const profile = {
refreshJourney: true,
reachableFrom: true,
refreshJourneyUseOutReconL: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1, 2],
name: 'Bahn & S-Bahn',
short: 'S/Zug',
default: true
default: true,
},
{
id: 'u-bahn',
@ -13,7 +13,7 @@ const products = [
bitmasks: [4],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'strassenbahn',
@ -21,7 +21,7 @@ const products = [
bitmasks: [16],
name: 'Strassenbahn',
short: 'Str',
default: true
default: true,
},
{
id: 'fernbus',
@ -29,7 +29,7 @@ const products = [
bitmasks: [32],
name: 'Fernbus',
short: 'Bus',
default: true
default: true,
},
{
id: 'regionalbus',
@ -37,7 +37,7 @@ const products = [
bitmasks: [64],
name: 'Regionalbus',
short: 'Bus',
default: true
default: true,
},
{
id: 'stadtbus',
@ -45,7 +45,7 @@ const products = [
bitmasks: [128],
name: 'Stadtbus',
short: 'Bus',
default: true
default: true,
},
{
id: 'seilbahn-zahnradbahn',
@ -53,7 +53,7 @@ const products = [
bitmasks: [256],
name: 'Seil-/Zahnradbahn',
short: 'Seil-/Zahnradbahn',
default: true
default: true,
},
{
id: 'schiff',
@ -61,10 +61,10 @@ const products = [
bitmasks: [512],
name: 'Schiff',
short: 'F',
default: true
default: true,
},
]
];
export {
products,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'tgv',
@ -68,7 +68,7 @@ const products = [{
name: 'Tram',
short: 'Tram',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -82,8 +82,8 @@ const profile = {
radar: true,
refreshJourneyUseOutReconL: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,38 +1,40 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
import {parseHook} from '../../lib/profile-hooks.js'
import {parseHook} from '../../lib/profile-hooks.js';
import {parseAndAddLocationDHID} from './parse-loc-dhid.js'
import {parseLine as _parseLine} from '../../parse/line.js'
import {parseLocation as _parseLocation} from '../../parse/location.js'
import {parseJourney as _parseJourney} from '../../parse/journey.js'
import {parseDeparture as _parseDeparture} from '../../parse/departure.js'
import {parseAndAddLocationDHID} from './parse-loc-dhid.js';
import {parseLine as _parseLine} from '../../parse/line.js';
import {parseLocation as _parseLocation} from '../../parse/location.js';
import {parseJourney as _parseJourney} from '../../parse/journey.js';
import {parseDeparture as _parseDeparture} from '../../parse/departure.js';
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const parseLineWithShortName = ({parsed}, p) => {
parsed.name = p.name.replace(/^(bus|tram)\s+/i, '')
return parsed
}
parsed.name = p.name.replace(/^(bus|tram)\s+/i, '');
return parsed;
};
const parseLocation = ({parsed}, l) => {
parseAndAddLocationDHID(parsed, l)
return parsed
}
parseAndAddLocationDHID(parsed, l);
return parsed;
};
// todo: move this to parse/tickets.js?
const parseJourneyWithTickets = ({parsed}, j) => {
if (
j.trfRes &&
Array.isArray(j.trfRes.fareSetL)
j.trfRes
&& Array.isArray(j.trfRes.fareSetL)
) {
parsed.tickets = j.trfRes.fareSetL
.map((s) => {
if (!Array.isArray(s.fareL) || s.fareL.length === 0) return null
if (!Array.isArray(s.fareL) || s.fareL.length === 0) {
return null;
}
return {
name: s.name,
description: s.desc,
@ -41,30 +43,30 @@ const parseJourneyWithTickets = ({parsed}, j) => {
name: f.name,
price: f.price,
})),
}
};
})
.filter(set => !!set)
.filter(set => Boolean(set));
// todo: j.trfRes.totalPrice
// todo: j.trfRes.msgL
}
return parsed
}
return parsed;
};
const ringbahnClockwise = /^ringbahn s\s?41$/i
const ringbahnAnticlockwise = /^ringbahn s\s?42$/i
const ringbahnClockwise = /^ringbahn s\s?41$/i;
const ringbahnAnticlockwise = /^ringbahn s\s?42$/i;
const parseDepartureRenameRingbahn = ({parsed}) => {
if (parsed.line && parsed.line.product === 'suburban') {
const d = parsed.direction && parsed.direction.trim()
const d = parsed.direction && parsed.direction.trim();
if (ringbahnClockwise.test(d)) {
parsed.direction = 'Ringbahn S41 ⟳'
parsed.direction = 'Ringbahn S41 ⟳';
} else if (ringbahnAnticlockwise.test(d)) {
parsed.direction = 'Ringbahn S42 ⟲'
parsed.direction = 'Ringbahn S42 ⟲';
}
}
return parsed
}
return parsed;
};
const profile = {
...baseProfile,
@ -83,8 +85,8 @@ const profile = {
trip: true,
radar: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,19 +1,23 @@
const dhidPrefix = 'A×'
const dhidPrefix = 'A×';
const parseAndAddLocationDHID = (loc, l) => {
if (!Array.isArray(l.gidL)) return;
if (!Array.isArray(l.gidL)) {
return;
}
const dhidGid = l.gidL.find(gid => gid.slice(0, dhidPrefix.length) === dhidPrefix)
if (!dhidGid) return;
const dhid = dhidGid.slice(dhidPrefix.length)
const dhidGid = l.gidL.find(gid => gid.slice(0, dhidPrefix.length) === dhidPrefix);
if (!dhidGid) {
return;
}
const dhid = dhidGid.slice(dhidPrefix.length);
// It seems that the DHID of the parent station is being used, not of the stop.
// if (!loc.ids) loc.ids = {}
// loc.ids.dhid = dhid
// todo: use loc.ids.stationDHID instead?
loc.stationDHID = dhid
}
loc.stationDHID = dhid;
};
export {
parseAndAddLocationDHID,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'subway',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -21,7 +21,7 @@ const products = [
bitmasks: [4],
name: 'Tram',
short: 'T',
default: true
default: true,
},
{
id: 'bus',
@ -29,7 +29,7 @@ const products = [
bitmasks: [8],
name: 'Bus',
short: 'B',
default: true
default: true,
},
{
id: 'ferry',
@ -37,7 +37,7 @@ const products = [
bitmasks: [16],
name: 'Fähre',
short: 'F',
default: true
default: true,
},
{
id: 'express',
@ -45,7 +45,7 @@ const products = [
bitmasks: [32],
name: 'IC/ICE',
short: 'E',
default: true
default: true,
},
{
id: 'regional',
@ -53,10 +53,10 @@ const products = [
bitmasks: [64],
name: 'RB/RE',
short: 'R',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -21,8 +21,8 @@ const profile = {
radar: true,
reachableFrom: true,
refreshJourneyUseOutReconL: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
default: true,
},
{
id: 'national-train',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2, 4],
name: 'InterCity, EuroCity, CityNightLine, InterRegio',
short: 'IC/EC/CNL/IR',
default: true
default: true,
},
{
id: 'local-train',
@ -21,7 +21,7 @@ const products = [
bitmasks: [8],
name: 'Nahverkehr',
short: 'Nahv.',
default: true
default: true,
},
{
id: 'suburban',
@ -29,7 +29,7 @@ const products = [
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -37,7 +37,7 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true
default: true,
},
{
id: 'watercraft',
@ -45,7 +45,7 @@ const products = [
bitmasks: [64],
name: 'Schiff',
short: 'Schiff',
default: true
default: true,
},
{
id: 'subway',
@ -53,7 +53,7 @@ const products = [
bitmasks: [128],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -61,7 +61,7 @@ const products = [
bitmasks: [256],
name: 'Tram',
short: 'Tram',
default: true
default: true,
},
{
id: 'dial-a-ride',
@ -69,10 +69,10 @@ const products = [
bitmasks: [256],
name: 'Anrufverkehr',
short: 'AST',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{ // todo: what is `8`?
id: 'trains',
@ -75,7 +75,7 @@ const products = [{ // todo: what is `8`?
name: 'Anrufsammeltaxi',
short: 'AST',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -88,8 +88,8 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -21,8 +21,8 @@ const profile = {
trip: true,
reachableFrom: true,
remarks: false, // seems like ver >= 1.20 is required
}
};
export {
profile,
}
};

View file

@ -6,7 +6,7 @@ const products = [
bitmasks: [1, 2, 4],
name: 'long-distance train',
short: 'ICE/IC/EC',
default: true
default: true,
},
{
id: 'regional-train',
@ -15,7 +15,7 @@ const products = [
bitmasks: [8, 16],
name: 'regional train',
short: 'RE/RB',
default: true
default: true,
},
{
id: 'tram',
@ -23,7 +23,7 @@ const products = [
bitmasks: [32],
name: 'tram',
short: 'tram',
default: true
default: true,
},
// todo: what are `64` & `128`?
{
@ -32,10 +32,10 @@ const products = [
bitmasks: [256],
name: 'bus',
short: 'bus',
default: true
}
]
default: true,
},
];
export {
products,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'train-and-s-bahn',
@ -75,7 +75,7 @@ const products = [{
name: 'Anrufsammeltaxi',
short: 'AST',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -96,8 +96,8 @@ const profile = {
refreshJourneyUseOutReconL: true,
trip: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,9 +1,9 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
const baseProfile = require('./base.json');
const products = [{
id: 'ice',
@ -75,7 +75,7 @@ const products = [{
name: 'Anrufverkehr',
short: 'AST',
default: true,
}]
}];
const profile = {
...baseProfile,
@ -89,8 +89,8 @@ const profile = {
radar: true,
refreshJourneyUseOutReconL: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -18,8 +18,8 @@ const profile = {
reachableFrom: true,
refreshJourney: true,
refreshJourneyUseOutReconL: true,
}
};
export {
profile,
}
};

View file

@ -56,8 +56,8 @@ const products = [
short: 'ICE/IC/EC/EN',
default: false,
},
]
];
export {
products,
}
};

View file

@ -1,10 +1,10 @@
// todo: use import assertions once they're supported by Node.js & ESLint
// https://github.com/tc39/proposal-import-assertions
import {createRequire} from 'module'
const require = createRequire(import.meta.url)
import {createRequire} from 'module';
const require = createRequire(import.meta.url);
const baseProfile = require('./base.json')
import {products} from './products.js'
const baseProfile = require('./base.json');
import {products} from './products.js';
const profile = {
...baseProfile,
@ -21,8 +21,8 @@ const profile = {
trip: true,
radar: true,
reachableFrom: true,
}
};
export {
profile,
}
};

View file

@ -5,7 +5,7 @@ const products = [
bitmasks: [1],
name: 'InterCityExpress',
short: 'ICE',
default: true
default: true,
},
{
id: 'national',
@ -13,7 +13,7 @@ const products = [
bitmasks: [2],
name: 'Fernzug',
short: 'IC/EC/CNL',
default: true
default: true,
},
{
id: 'regionalExpress',
@ -21,7 +21,7 @@ const products = [
bitmasks: [4],
name: 'RegionalExpress & InterRegio',
short: 'RE/IR',
default: true
default: true,
},
{
id: 'regional',
@ -29,7 +29,7 @@ const products = [
bitmasks: [8],
name: 'Nahverhehr',
short: 'NV',
default: true
default: true,
},
{
id: 'suburban',
@ -37,7 +37,7 @@ const products = [
bitmasks: [16],
name: 'S-Bahn',
short: 'S',
default: true
default: true,
},
{
id: 'bus',
@ -45,7 +45,7 @@ const products = [
bitmasks: [32],
name: 'Bus',
short: 'Bus',
default: true
default: true,
},
{
id: 'ferry',
@ -53,7 +53,7 @@ const products = [
bitmasks: [64],
name: 'Schiff',
short: 'F',
default: true
default: true,
},
{
id: 'subway',
@ -61,7 +61,7 @@ const products = [
bitmasks: [128],
name: 'U-Bahn',
short: 'U',
default: true
default: true,
},
{
id: 'tram',
@ -69,7 +69,7 @@ const products = [
bitmasks: [256],
name: 'Straßen-/Stadtbahn',
short: 'T',
default: true
default: true,
},
{
id: 'anrufSammelTaxi',
@ -77,10 +77,10 @@ const products = [
bitmasks: [512],
name: 'Anruf-Sammel-Taxi',
short: 'AST',
default: true
default: true,
},
]
];
export {
products,
}
};

Some files were not shown because too many files have changed in this diff Show more