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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,16 +1,18 @@
const sep = '@' const sep = '@';
const formatLocationIdentifier = (data) => { const formatLocationIdentifier = (data) => {
let str = '' let str = '';
for (let key in data) { 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?
} }
return str str += key + '=' + data[key] + sep; // todo: escape, but how?
} }
return str;
};
export { export {
formatLocationIdentifier, formatLocationIdentifier,
} };

View file

@ -1,17 +1,25 @@
const formatLocation = (profile, l, name = 'location') => { 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 ('object' === typeof l && !Array.isArray(l)) {
if (l.type === 'station' || l.type === 'stop') { 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 (l.poi) {
if ('string' === typeof l.address) return profile.formatAddress(l) return profile.formatPoi(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.') 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.');
};
export { export {
formatLocation, formatLocation,
} };

View file

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

View file

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

View file

@ -1,10 +1,10 @@
import {formatLocationIdentifier} from './location-identifier.js' import {formatLocationIdentifier} from './location-identifier.js';
import {formatCoord} from './coord.js' import {formatCoord} from './coord.js';
const formatPoi = (p) => { const formatPoi = (p) => {
// todo: use Number.isFinite()! // todo: use Number.isFinite()!
if (p.type !== 'location' || !p.latitude || !p.longitude || !p.id || !p.name) { if (p.type !== 'location' || !p.latitude || !p.longitude || !p.id || !p.name) {
throw new TypeError('invalid POI') throw new TypeError('invalid POI');
} }
return { return {
@ -15,11 +15,11 @@ const formatPoi = (p) => {
O: p.name, O: p.name,
L: p.id, L: p.id,
X: formatCoord(p.longitude), X: formatCoord(p.longitude),
Y: formatCoord(p.latitude) Y: formatCoord(p.latitude),
}) }),
} };
} };
export { export {
formatPoi, 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) => { const formatProductsFilter = (ctx, filter) => {
if (!isObj(filter)) throw new TypeError('products filter must be an object') if (!isObj(filter)) {
const {profile} = ctx throw new TypeError('products filter must be an object');
}
const {profile} = ctx;
const byProduct = {} const byProduct = {};
const defaultProducts = {} const defaultProducts = {};
for (let product of profile.products) { for (let product of profile.products) {
byProduct[product.id] = product byProduct[product.id] = product;
defaultProducts[product.id] = product.default 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) { for (let product in filter) {
if (!hasProp(filter, product) || filter[product] !== true) continue if (!hasProp(filter, product) || filter[product] !== true) {
if (!byProduct[product]) throw new TypeError('unknown product ' + product) continue;
products++ }
for (let bitmask of byProduct[product].bitmasks) res = res | bitmask 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 { return {
type: 'PROD', type: 'PROD',
mode: 'INC', mode: 'INC',
value: res + '' value: String(res),
} };
} };
export { export {
formatProductsFilter, formatProductsFilter,
} };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,21 +1,24 @@
import {DateTime, IANAZone} from 'luxon' import {DateTime, IANAZone} from 'luxon';
import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js' import {luxonIANAZonesByProfile as timezones} from '../lib/luxon-timezones.js';
// todo: change to `(profile) => (when) => {}` // todo: change to `(profile) => (when) => {}`
const formatTime = (profile, when) => { const formatTime = (profile, when) => {
let timezone let timezone;
if (timezones.has(profile)) timezone = timezones.get(profile) if (timezones.has(profile)) {
else { timezone = timezones.get(profile);
timezone = new IANAZone(profile.timezone) } else {
timezones.set(profile, timezone) timezone = new IANAZone(profile.timezone);
timezones.set(profile, timezone);
} }
return DateTime.fromMillis(+when, { return DateTime
.fromMillis(Number(when), {
locale: profile.locale, locale: profile.locale,
zone: timezone zone: timezone,
}).toFormat('HHmmss') })
} .toFormat('HHmmss');
};
export { export {
formatTime, 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. // 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`. // Thus, it will find a different trip if you pass the wrong date via `opt.when`.
// date: profile.formatDate(profile, opt.when), // date: profile.formatDate(profile, opt.when),
getPolyline: !!opt.polyline getPolyline: Boolean(opt.polyline),
} },
} };
} };
export { export {
formatTripReq, formatTripReq,
} };

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,47 +1,63 @@
const findById = (needle) => { const findById = (needle) => {
const needleStopId = needle.id const needleStopId = needle.id;
const needleStationId = needle.station ? needle.station.id : null const needleStationId = needle.station
? needle.station.id
: null;
return (stop) => { return (stop) => {
if (needleStopId === stop.id) return true if (needleStopId === stop.id) {
const stationId = stop.station ? stop.station.id : null return true;
if (needleStationId && stationId && needleStationId === stationId) return true }
const stationId = stop.station
? stop.station.id
: null;
if (needleStationId && stationId && needleStationId === stationId) {
return true;
}
// todo: `needleStationId === stop.id`? `needleStopId === stationId`? // todo: `needleStationId === stop.id`? `needleStopId === stationId`?
return false return false;
} };
} };
const sliceLeg = (leg, from, to) => { 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 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)
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
return newLeg
} }
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];
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.destination = toStopover.stop;
newLeg.arrival = toStopover.arrival;
newLeg.arrivalDelay = toStopover.arrivalDelay;
newLeg.scheduledArrival = toStopover.scheduledArrival;
newLeg.arrivalPlatform = toStopover.arrivalPlatform;
return newLeg;
};
export { export {
sliceLeg, sliceLeg,
} };

View file

@ -43,53 +43,55 @@ const types = {
formatStation: 'function', formatStation: 'function',
formatTime: 'function', formatTime: 'function',
formatLocation: 'function', formatLocation: 'function',
formatRectangle: 'function' formatRectangle: 'function',
} };
const validateProfile = (profile) => { const validateProfile = (profile) => {
for (let key of Object.keys(types)) { for (let key of Object.keys(types)) {
const type = types[key] const type = types[key];
if (type === 'array') { if (type === 'array') {
if (!Array.isArray(profile[key])) { 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]) { } 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) { 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)) { 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) { for (let product of profile.products) {
if ('string' !== typeof product.id) { 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) { 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)) { 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) { for (let bitmask of product.bitmasks) {
if ('number' !== typeof bitmask) { 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) { 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) { 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 { export {
validateProfile, validateProfile,
} };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,26 +9,31 @@ const ageGroup = {
CHILD: 15, CHILD: 15,
YOUNG: 27, YOUNG: 27,
ADULT: 65, ADULT: 65,
SENIOR: Infinity SENIOR: Infinity,
} },
} };
const ageGroupFromAge = (age) => { const ageGroupFromAge = (age) => {
const {upperBoundOf} = ageGroup const {upperBoundOf} = ageGroup;
if (age < upperBoundOf.BABY) if (age < upperBoundOf.BABY) {
return ageGroup.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}'`)
} }
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 { export {
ageGroup, ageGroup,
ageGroupFromAge, ageGroupFromAge,
} };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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