UNPKG

ojp-sdk-next

Version:

OJP (Open Journey Planner) Javascript SDK

1,578 lines (1,557 loc) 63.8 kB
// src/helpers/xml/builder.ts import * as OJP_Types from "ojp-shared-types"; import { XMLBuilder } from "fast-xml-parser"; // src/constants.ts var SDK_VERSION = "0.21.4"; var mapNS = { "ojp": "http://www.vdv.de/ojp", "siri": "http://www.siri.org.uk/siri" }; var DefaultXML_Config = { ojpVersion: "2.0", defaultNS: "ojp", mapNS }; var XML_BuilderConfigOJPv1 = { ojpVersion: "1.0", defaultNS: "siri", mapNS }; var XML_ParserConfigOJPv1 = { ojpVersion: "1.0", defaultNS: "ojp", mapNS }; // src/helpers/xml/builder.ts function transformKeys(obj, path = [], callback) { if (obj !== null && typeof obj === "object" && !Array.isArray(obj)) { for (const key in obj) { if (key.includes("@_")) { delete obj[key]; } } } return Object.entries(obj).reduce((acc, [key, value]) => { const newKey = callback(key, value, path); const newPath = path.concat([newKey]); acc[newKey] = (() => { if (value instanceof Object) { if (Array.isArray(value)) { value.forEach((el, idx) => { if (el instanceof Object) { value[idx] = transformKeys(el, newPath, callback); } }); } else { return transformKeys(value, newPath, callback); } } return value; })(); return acc; }, {}); } function buildRootXML(obj, xmlConfig = DefaultXML_Config, callbackTransformedObj = null) { const wrapperNodeName = "OJP"; const rootXML = buildXML(obj, wrapperNodeName, xmlConfig, ((objTransformed) => { const rootKeys = Object.keys(objTransformed); if (typeof objTransformed === "object" && rootKeys.length === 1) { const rootKeyParts = rootKeys[0].split(":"); if (rootKeyParts.length === 2) { const oldKey = rootKeys[0]; const newKey = rootKeyParts[1]; objTransformed[newKey] = objTransformed[oldKey]; delete objTransformed[oldKey]; } } if (callbackTransformedObj) { callbackTransformedObj(objTransformed); } })); const wrapperRootXML_Lines = [ '<?xml version="1.0" encoding="utf-8"?>', rootXML ]; const wrapperRootXML = wrapperRootXML_Lines.join("\n"); return wrapperRootXML; } function buildXML(obj, wrapperNodeName = "OJP", xmlConfig = DefaultXML_Config, callbackTransformedObj = null) { const objCopy = JSON.parse(JSON.stringify(obj)); const objTransformed = transformKeys(objCopy, [wrapperNodeName], (key, value, path) => { let newKey = key.charAt(0).toUpperCase() + key.slice(1); const parentKey = path.at(-1) ?? null; if (parentKey !== null) { const tagNS_Key = parentKey.replace(/^.*:/, "") + "." + newKey; const tagNS = (() => { const tagNSConfig = OJP_Types.OpenAPI_Dependencies.MapNS_Tags[tagNS_Key] ?? "ojp"; if (xmlConfig.defaultNS === tagNSConfig) { return ""; } return tagNSConfig + ":"; })(); if (tagNS !== null) { newKey = tagNS + newKey; } } return newKey; }); if (callbackTransformedObj) { callbackTransformedObj(objTransformed); } const xmlParts = []; const isRootNode = wrapperNodeName === "OJP"; if (isRootNode) { const xmlAttrs = []; for (const ns in xmlConfig.mapNS) { const url = xmlConfig.mapNS[ns]; const attrNS = ns === xmlConfig.defaultNS ? "xmlns" : "xmlns:" + ns; const xmlAttr = attrNS + '="' + url + '"'; xmlAttrs.push(xmlAttr); } const xmlVersionAttr = 'version="' + xmlConfig.ojpVersion + '"'; xmlAttrs.push(xmlVersionAttr); xmlParts.push("<OJP " + xmlAttrs.join(" ") + ">"); } else { xmlParts.push("<" + wrapperNodeName + ">"); } const builder = new XMLBuilder({ format: true, ignoreAttributes: false, suppressEmptyNode: true }); xmlParts.push(builder.build(objTransformed)); xmlParts.push("</" + wrapperNodeName + ">"); const xmlS = xmlParts.join("\n"); return xmlS; } // src/helpers/xml/parser.ts import * as OJP_Types2 from "ojp-shared-types"; import { XMLParser } from "fast-xml-parser"; // src/models/xml-serializer.ts var XmlSerializer = class { xmlConfig; constructor(xmlConfig = DefaultXML_Config) { this.xmlConfig = xmlConfig; } serialize(obj, wrapperNodeName) { const xml = buildXML(obj, wrapperNodeName, this.xmlConfig); return xml; } static transformTagName(tagName) { if (tagName.startsWith("OJP")) { return tagName; } if (tagName.toUpperCase() === tagName) { return tagName; } let newTagName = tagName.replace(/[-_](.)/g, (_, char) => char.toUpperCase()); newTagName = newTagName.replace(/^([A-Z])/, (match) => match.toLowerCase()); return newTagName; } }; // src/helpers/xml/parser.ts var mapArrayTags = Object.assign({}, OJP_Types2.OpenAPI_Dependencies.MapArrayTags); var mapLegacyArrayTags = Object.assign({}, OJP_Types2.OpenAPI_Dependencies.MapArrayTags); for (const key in OJP_Types2.OpenAPI_Dependencies.MapLegacyArrayTags) { mapLegacyArrayTags[key] = OJP_Types2.OpenAPI_Dependencies.MapLegacyArrayTags[key]; } function computeMapParentArrayTags(mapArrayTags2) { const mapParentArrayTags = {}; for (const key in mapArrayTags2) { const keyParts = key.split("."); if (keyParts.length !== 2) { console.error("invalid OpenAPI_Dependencies.MapArrayTags key: " + key); continue; } const parentTagName = keyParts[0]; const childTagName = keyParts[1]; if (!(parentTagName in mapParentArrayTags)) { mapParentArrayTags[parentTagName] = []; } mapParentArrayTags[parentTagName].push(childTagName); } return mapParentArrayTags; } var MapParentArrayTags = computeMapParentArrayTags(mapArrayTags); var MapLegacyParentArrayTags = computeMapParentArrayTags(mapLegacyArrayTags); function transformJsonInPlace(root, ojpVersion) { const hashTextKey = "#text"; const isOJP_v2 = ojpVersion === "2.0"; const mapParentArrayTags = isOJP_v2 ? MapParentArrayTags : MapLegacyParentArrayTags; function isHashKeyObject(v) { if (typeof v !== "object") { return false; } if (Array.isArray(v)) { return false; } const hasKey = hashTextKey in v; return hasKey; } function normalizeValue(value, path) { if (path.length < 2) { return value; } const pathSuffix = path.slice(-2).join("."); if (pathSuffix in OJP_Types2.OpenAPI_Dependencies.MapStringValues && typeof value !== "string") { return String(value); } return value; } function visit(node, path) { const currentNodeKey = path.at(-1); if (Array.isArray(node)) { for (let i = 0; i < node.length; i++) { visit(node[i], path); } return; } if (node && typeof node === "object") { const rec = node; const keys = Object.keys(rec); for (const key of keys) { let value = rec[key]; const valuePath = [...path, key]; if (isHashKeyObject(value)) { const inner = value; rec[key] = inner[hashTextKey]; for (const innerKey of Object.keys(inner)) { if (innerKey === hashTextKey) { continue; } const flatKey = `${key}.${innerKey}`; rec[flatKey] = inner[innerKey]; } continue; } if (Array.isArray(value) && value.length > 0 && value.every(isHashKeyObject)) { const arr = value; const basePath = valuePath; rec[key] = arr.map((o) => normalizeValue(o[hashTextKey], basePath)); const extraKeys = /* @__PURE__ */ new Set(); for (const o of arr) { for (const k of Object.keys(o)) { if (k !== hashTextKey) { extraKeys.add(k); } } } for (const extraKey of extraKeys) { const flatKey = `${key}.${extraKey}`; rec[flatKey] = arr.map((o) => o[extraKey]); } continue; } value = normalizeValue(value, valuePath); rec[key] = value; visit(value, valuePath); } if (currentNodeKey !== void 0) { const expectedPropAsArray = mapParentArrayTags[currentNodeKey] ?? []; expectedPropAsArray.forEach((prop) => { if (!(prop in rec)) { rec[prop] = []; } }); } } } visit(root, []); } var transformTagNameHandler = (tagName) => { return XmlSerializer.transformTagName(tagName); }; var isArrayHandler = (tagName, jPath, ojpVersion) => { const isOJP_v2 = ojpVersion === "2.0"; const targetMapArrayTags = isOJP_v2 ? mapArrayTags : mapLegacyArrayTags; const jPathParts = jPath.split("."); if (jPathParts.length >= 2) { const pathPart = jPathParts.slice(-2).join("."); if (pathPart in targetMapArrayTags) { return true; } } return false; }; function parseXML(xml, ojpVersion) { const parser = new XMLParser({ ignoreAttributes: false, removeNSPrefix: true, transformTagName: transformTagNameHandler, isArray: (tagName, jPath) => { return isArrayHandler(tagName, jPath, ojpVersion); } // parseTagValue: false, }); const response = parser.parse(xml); transformJsonInPlace(response, ojpVersion); return response; } // src/models/geoposition.ts var GeoPosition = class { longitude; latitude; properties; constructor(geoPositionArg, optionalLatitude = null) { const invalidCoords = [Infinity, Infinity]; const coords = (() => { if (typeof geoPositionArg === "number" && isNaN(geoPositionArg)) { return invalidCoords; } if (typeof geoPositionArg === "number" && optionalLatitude !== null) { const longitude = geoPositionArg; const latitude = optionalLatitude; return [longitude, latitude]; } if (typeof geoPositionArg === "string") { const stringParts = geoPositionArg.split(","); if (stringParts.length < 2) { return invalidCoords; } const longitude = parseFloat(stringParts[1]); const latitude = parseFloat(stringParts[0]); return [longitude, latitude]; } if (Array.isArray(geoPositionArg) && geoPositionArg.length > 1) { return geoPositionArg; } if (typeof geoPositionArg === "object") { const geoPositionObj = geoPositionArg; if (geoPositionObj.hasOwnProperty("longitude") && geoPositionObj.hasOwnProperty("latitude")) { const longitude = geoPositionArg.longitude; const latitude = geoPositionArg.latitude; return [Number(longitude), Number(latitude)]; } } return invalidCoords; })(); this.longitude = parseFloat(coords[0].toFixed(6)); this.latitude = parseFloat(coords[1].toFixed(6)); this.properties = {}; } isValid() { return this.longitude !== Infinity && this.latitude !== Infinity; } // From https://stackoverflow.com/a/27943 distanceFrom(pointB) { const R = 6371; const dLat = (pointB.latitude - this.latitude) * Math.PI / 180; const dLon = (pointB.longitude - this.longitude) * Math.PI / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(this.latitude * Math.PI / 180) * Math.cos(pointB.latitude * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const d = R * c; const dMeters = Math.round(d * 1e3); return dMeters; } asLatLngString(roundCoords = true) { let s = ""; if (roundCoords) { s = this.latitude.toFixed(6) + "," + this.longitude.toFixed(6); } else { s = this.latitude + "," + this.longitude; } return s; } // For Mapbox LngLat constructs asLngLat() { const coords = [this.longitude, this.latitude]; return coords; } asGeoPositionSchema() { const schema = { longitude: this.longitude, latitude: this.latitude }; return schema; } }; // src/models/ojp.ts var PlaceRef = class _PlaceRef { stopPointRef; stopPlaceRef; geoPosition; name; constructor(name) { this.stopPointRef = void 0; this.stopPlaceRef = void 0; this.geoPosition = void 0; this.name = name; } // TODO - introduce a PlaceRefOrCoordsLike type that handles // - string (currently implemented) // - PlaceRef (and /or Place) // - GeoPosition (and /or GeoPositionLike) static initWithPlaceRefsOrCoords(placeRefOrCoords, nameS = null) { const geoPosition = new GeoPosition(placeRefOrCoords); if (geoPosition.isValid()) { const nameText = nameS ?? geoPosition.asLatLngString(); const placeRef = new _PlaceRef({ text: nameText }); placeRef.geoPosition = geoPosition; return placeRef; } else { const name = { text: nameS ?? "n/a" }; const placeRef = new _PlaceRef(name); placeRef.stopPlaceRef = placeRefOrCoords; return placeRef; } } asOJPv1Schema() { const legacyPlaceRef = { stopPointRef: this.stopPointRef, stopPlaceRef: this.stopPlaceRef, geoPosition: this.geoPosition, locationName: this.name }; return legacyPlaceRef; } }; var Trip = class _Trip { id; duration; startTime; endTime; transfers; leg; cancelled; delayed; deviation; infeasible; unplanned; constructor(id, duration, startTime, endTime, transfers, leg, cancelled, delayed, deviation, infeasible, unplanned) { this.id = id; this.duration = duration; this.startTime = startTime; this.endTime = endTime; this.transfers = transfers; this.leg = leg; this.cancelled = cancelled; this.delayed = delayed; this.deviation = deviation; this.infeasible = infeasible; this.unplanned = unplanned; } static initWithTripXML(rawXML) { const parentTagName = "TripResult"; const parsedTrip = parseXML(rawXML, "2.0"); const trip = new _Trip( parsedTrip.trip.id, parsedTrip.trip.duration, parsedTrip.trip.startTime, parsedTrip.trip.endTime, parsedTrip.trip.transfers, parsedTrip.trip.leg, parsedTrip.trip.cancelled, parsedTrip.trip.delayed, parsedTrip.trip.deviation, parsedTrip.trip.infeasible, parsedTrip.trip.unplanned ); return trip; } }; var Place = class _Place { stopPoint; stopPlace; topographicPlace; pointOfInterest; address; name; geoPosition; mode; attribute; placeType; constructor(stopPoint, stopPlace, topographicPlace, pointOfInterest, address, name, geoPosition, mode, attribute) { this.stopPoint = stopPoint; this.stopPlace = stopPlace; this.topographicPlace = topographicPlace; this.pointOfInterest = pointOfInterest; this.address = address; this.name = name; this.geoPosition = geoPosition; this.mode = mode; this.attribute = attribute; this.placeType = geoPosition.isValid() ? "location" : null; if (stopPoint || stopPlace) { this.placeType = "stop"; } if (topographicPlace) { this.placeType = "topographicPlace"; } if (pointOfInterest) { this.placeType = "poi"; } if (address) { this.placeType = "address"; } } static initWithXMLSchema(placeSchema) { const geoPosition = new GeoPosition(placeSchema.geoPosition); const place = new _Place(placeSchema.stopPoint, placeSchema.stopPlace, placeSchema.topographicPlace, placeSchema.pointOfInterest, placeSchema.address, placeSchema.name, geoPosition, placeSchema.mode, placeSchema.attribute); return place; } static initWithCoords(geoPositionArg, optionalLatitude = null) { const geoPosition = new GeoPosition(geoPositionArg, optionalLatitude); const name = { text: geoPosition.latitude + "," + geoPosition.longitude }; const place = new _Place(void 0, void 0, void 0, void 0, void 0, name, geoPosition, [], []); return place; } static Empty() { const name = { text: "n/a Empty" }; const geoPosition = new GeoPosition("0,0"); const place = new _Place(void 0, void 0, void 0, void 0, void 0, name, geoPosition, [], []); return place; } findClosestPlace(otherPlaces) { const geoPositionA = this.geoPosition; let closestPlace = null; otherPlaces.forEach((locationB) => { const geoPositionB = locationB.geoPosition; if (geoPositionB === null) { return; } const dAB = geoPositionA.distanceFrom(geoPositionB); if (closestPlace === null || dAB < closestPlace.distance) { closestPlace = { object: locationB, distance: dAB }; } }); return closestPlace; } // used by TR // TODO - logic should be added to Place.initWithPlaceRefsOrCoords asStopPlaceRefOrCoords() { const stopPlaceRef = this.stopPlace?.stopPlaceRef ?? null; if (stopPlaceRef !== null) { return stopPlaceRef; } const coordsS = this.geoPosition.asLatLngString(); return coordsS; } }; var PlaceResult = class _PlaceResult { place; complete; probability; constructor(place, complete, probability) { this.place = place; this.complete = complete; this.probability = probability; } static initWithXMLSchema(placeResultSchema) { const place = Place.initWithXMLSchema(placeResultSchema.place); const placeResult = new _PlaceResult(place, placeResultSchema.complete, placeResultSchema.probability); return placeResult; } static initWithXML(nodeXML) { const parentTagName = "PlaceResult"; const parsedObj = parseXML(nodeXML, "2.0"); const placeSchema = parsedObj.placeResult.place; const place = Place.initWithXMLSchema(placeSchema); const placeResult = new _PlaceResult(place, parsedObj.placeResult.complete, parsedObj.placeResult.probability); return placeResult; } }; var StopEventResult = class _StopEventResult { id; stopEvent; constructor(id, stopEvent) { this.id = id; this.stopEvent = stopEvent; } static initWithXML(nodeXML) { const parentTagName = "StopEventResult"; const parsedObj = parseXML(nodeXML, "2.0"); const result = new _StopEventResult(parsedObj.stopEventResult.id, parsedObj.stopEventResult.stopEvent); return result; } }; // src/helpers/request-helpers.ts import axios, { AxiosHeaders } from "axios"; var RequestHelpers = class _RequestHelpers { static computeRequestTimestamp() { const now = /* @__PURE__ */ new Date(); const requestTimestamp = now.toISOString(); return requestTimestamp; } static async computeResponse(request, sdk, xmlConfig) { const requestXML = (() => { if (request.mockRequestXML) { return request.mockRequestXML; } const xml = request.buildRequestXML(sdk.language, sdk.requestorRef, xmlConfig); return xml; })(); request.requestInfo.requestDateTime = /* @__PURE__ */ new Date(); request.requestInfo.requestXML = requestXML; const responseXML = await (async () => { if (request.mockResponseXML) { return request.mockResponseXML; } const xml = await _RequestHelpers.fetchResponseXML(requestXML, sdk.httpConfig); return xml; })(); request.requestInfo.responseDateTime = /* @__PURE__ */ new Date(); request.requestInfo.responseXML = responseXML; return responseXML; } static async fetchResponseXML(requestXML, httpConfig) { const headers = new AxiosHeaders(); headers.set("Accept", "application/xml"); headers.set("Content-Type", "application/xml"); if (httpConfig.authToken !== null) { headers.set("Authorization", "Bearer " + httpConfig.authToken); } const requestConfig = { method: "POST", url: httpConfig.url, headers }; if (requestConfig.method === "POST") { requestConfig.data = requestXML; } const response = await axios.request(requestConfig); const responseXML = response.data; return responseXML; } }; // src/versions/current/requests/base.ts var BaseRequest = class { requestInfo; mockRequestXML; mockResponseXML; constructor() { const now = /* @__PURE__ */ new Date(); this.requestInfo = { requestDateTime: null, requestXML: null, responseDateTime: null, responseXML: null, parseDateTime: null }; this.mockRequestXML = null; this.mockResponseXML = null; } static initWithRequestMock(mockText) { const instance = this.Default(); instance.mockRequestXML = mockText; return instance; } static initWithResponseMock(mockText) { const instance = this.Default(); instance.mockResponseXML = mockText; return instance; } async fetchResponse(sdk) { const response = await this._fetchResponse(sdk); return response; } }; // src/versions/current/requests/lir.shared.ts var SharedLocationInformationRequest = class extends BaseRequest { static DefaultRestrictionParams() { const restrictionParams = { type: [], numberOfResults: void 0, modes: void 0, includePtModes: true }; return restrictionParams; } updateRestrictions(restrictions, placeTypes, numberOfResults) { if (placeTypes.length > 0) { restrictions.type = placeTypes; } if (numberOfResults !== null) { restrictions.numberOfResults = numberOfResults; } } static computeGeoRestriction(bboxData) { const bboxDataParts = (() => { if (Array.isArray(bboxData)) { return bboxData; } return bboxData.split(",").map((el) => Number(el)); })(); if (bboxDataParts.length !== 4) { return null; } const minLongitude = bboxDataParts[0]; const minLatitude = bboxDataParts[1]; const maxLongitude = bboxDataParts[2]; const maxLatitude = bboxDataParts[3]; const geoRestrictionsSchema = { rectangle: { upperLeft: { longitude: minLongitude, latitude: maxLatitude }, lowerRight: { longitude: maxLongitude, latitude: minLatitude } } }; return geoRestrictionsSchema; } }; // src/versions/current/requests/lir.ts var LocationInformationRequest = class _LocationInformationRequest extends SharedLocationInformationRequest { payload; constructor(restrictions) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), initialInput: void 0, placeRef: void 0, restrictions }; } static Default() { const restrictions = SharedLocationInformationRequest.DefaultRestrictionParams(); const request = new _LocationInformationRequest(restrictions); return request; } static initWithLocationName(name, placeTypes = [], numberOfResults = 10) { const request = _LocationInformationRequest.Default(); request.payload.initialInput = { name }; if (request.payload.restrictions) { request.updateRestrictions(request.payload.restrictions, placeTypes, numberOfResults); } return request; } static initWithPlaceRef(placeRefOrCoords, numberOfResults = 10) { const request = _LocationInformationRequest.Default(); request.payload.placeRef = PlaceRef.initWithPlaceRefsOrCoords(placeRefOrCoords); if (request.payload.restrictions) { request.updateRestrictions(request.payload.restrictions, ["stop"], numberOfResults); } return request; } static initWithBBOX(bboxData, placeTypes = [], numberOfResults = 10) { const request = _LocationInformationRequest.Default(); const geoRestriction = this.computeGeoRestriction(bboxData); if (geoRestriction) { request.payload.initialInput = { name: void 0, geoRestriction }; } if (request.payload.restrictions) { request.updateRestrictions(request.payload.restrictions, placeTypes, numberOfResults); } return request; } buildRequestXML(language, requestorRef, xmlConfig) { this.payload.requestTimestamp = RequestHelpers.computeRequestTimestamp(); const requestOJP = { OJPRequest: { serviceRequest: { serviceRequestContext: { language }, requestTimestamp: this.payload.requestTimestamp, requestorRef, OJPLocationInformationRequest: this.payload } } }; const xmlS = buildRootXML(requestOJP, xmlConfig); return xmlS; } async _fetchResponse(sdk) { const xmlConfig = sdk.version === "2.0" ? DefaultXML_Config : XML_BuilderConfigOJPv1; const responseXML = await RequestHelpers.computeResponse(this, sdk, xmlConfig); try { const parsedObj = parseXML(responseXML, sdk.version); const response = parsedObj.OJP.OJPResponse.serviceDelivery.OJPLocationInformationDelivery; if (response === void 0) { console.log(responseXML); throw new Error("Parse error"); } return { ok: true, value: response }; } catch (error) { return { ok: false, error: error instanceof Error ? error : new Error("Unknown error") }; } } }; // src/versions/current/requests/ser.shared.ts var SharedStopEventRequest = class extends BaseRequest { static DefaultRequestParams(version = "2.0") { const params = { includeAllRestrictedLines: void 0, // this works only with OJP v2 numberOfResults: 10, stopEventType: "departure", includePreviousCalls: true, includeOnwardCalls: true, useRealtimeData: "explanatory" }; if (version === "2.0") { params.includeAllRestrictedLines = true; } return params; } }; // src/versions/current/requests/ser.ts var StopEventRequest = class _StopEventRequest extends SharedStopEventRequest { payload; constructor(location, params = void 0) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), location, params }; } // Used by Base.initWithRequestMock / initWithResponseMock static Default() { const date = /* @__PURE__ */ new Date(); const location = { placeRef: { stopPointRef: "8507000", name: { text: "n/a" } }, depArrTime: date.toISOString() }; const requestParams = SharedStopEventRequest.DefaultRequestParams(); const request = new _StopEventRequest(location, requestParams); return request; } static initWithPlaceRefAndDate(placeRefS, date = /* @__PURE__ */ new Date()) { const location = { placeRef: { stopPointRef: placeRefS, name: { text: "n/a" } }, depArrTime: date.toISOString() }; const params = SharedStopEventRequest.DefaultRequestParams(); const request = new _StopEventRequest(location, params); return request; } buildRequestXML(language, requestorRef, xmlConfig) { this.payload.requestTimestamp = RequestHelpers.computeRequestTimestamp(); const requestOJP = { OJPRequest: { serviceRequest: { serviceRequestContext: { language }, requestTimestamp: this.payload.requestTimestamp, requestorRef, OJPStopEventRequest: this.payload } } }; const xmlS = buildRootXML(requestOJP, xmlConfig); return xmlS; } async _fetchResponse(sdk) { const xmlConfig = sdk.version === "2.0" ? DefaultXML_Config : XML_BuilderConfigOJPv1; const responseXML = await RequestHelpers.computeResponse(this, sdk, xmlConfig); try { const parsedObj = parseXML(responseXML, sdk.version); const response = parsedObj.OJP.OJPResponse.serviceDelivery.OJPStopEventDelivery; if (response === void 0) { console.log(responseXML); throw new Error("Parse error"); } return { ok: true, value: response }; } catch (error) { return { ok: false, error: error instanceof Error ? error : new Error("Unknown error") }; } } }; // src/helpers/date-helpers.ts var DateHelpers = class _DateHelpers { // 2021-06-03 21:38:04 static formatDate(d) { const date_parts = [ d.getFullYear(), "-", ("00" + (d.getMonth() + 1)).slice(-2), "-", ("00" + d.getDate()).slice(-2), " ", ("00" + d.getHours()).slice(-2), ":", ("00" + d.getMinutes()).slice(-2), ":", ("00" + d.getSeconds()).slice(-2) ]; return date_parts.join(""); } // 21:38 static formatTimeHHMM(d = /* @__PURE__ */ new Date()) { const dateFormatted = _DateHelpers.formatDate(d); return dateFormatted.substring(11, 16); } static computeDelayMinutes(timetableTimeS, estimatedTimeS) { if (estimatedTimeS === null) { return null; } const timetableTime = typeof timetableTimeS === "string" ? new Date(timetableTimeS) : timetableTimeS; const estimatedTime = typeof estimatedTimeS === "string" ? new Date(estimatedTimeS) : estimatedTimeS; const dateDiffSeconds = (estimatedTime.getTime() - timetableTime.getTime()) / 1e3; const delayMinutes = Math.floor(dateDiffSeconds / 60); return delayMinutes; } }; // src/versions/current/requests/tir.shared.ts var SharedTripInfoRequest = class extends BaseRequest { static DefaultRequestParams() { const params = { includeCalls: true, includeService: true, includeTrackProjection: false, includePlacesContext: true, includeSituationsContext: true }; return params; } }; // src/versions/current/requests/tir.ts var TripInfoRequest = class _TripInfoRequest extends SharedTripInfoRequest { payload; constructor(journeyRef, operatingDayRef, params) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), journeyRef, operatingDayRef, params }; } // Used by Base.initWithRequestMock / initWithResponseMock static Default() { const request = new _TripInfoRequest("n/a", "n/a", SharedTripInfoRequest.DefaultRequestParams()); return request; } static initWithJourneyRef(journeyRef, journeyDate = /* @__PURE__ */ new Date()) { const operatingDayRef = DateHelpers.formatDate(journeyDate).substring(0, 10); const params = _TripInfoRequest.DefaultRequestParams(); const request = new _TripInfoRequest(journeyRef, operatingDayRef, params); return request; } enableTrackProjection() { if (this.payload.params) { this.payload.params.includeTrackProjection = true; } } buildRequestXML(language, requestorRef, xmlConfig) { this.payload.requestTimestamp = RequestHelpers.computeRequestTimestamp(); const requestOJP = { OJPRequest: { serviceRequest: { serviceRequestContext: { language }, requestTimestamp: this.payload.requestTimestamp, requestorRef, OJPTripInfoRequest: this.payload } } }; const xmlS = buildRootXML(requestOJP, xmlConfig); return xmlS; } async _fetchResponse(sdk) { const xmlConfig = sdk.version === "2.0" ? DefaultXML_Config : XML_BuilderConfigOJPv1; const responseXML = await RequestHelpers.computeResponse(this, sdk, xmlConfig); try { const parsedObj = parseXML(responseXML, sdk.version); const response = parsedObj.OJP.OJPResponse.serviceDelivery.OJPTripInfoDelivery; if (response === void 0) { console.log(responseXML); throw new Error("Parse error"); } return { ok: true, value: response }; } catch (error) { return { ok: false, error: error instanceof Error ? error : new Error("Unknown error") }; } } }; // src/versions/current/requests/trr.ts var TripRefineRequest = class _TripRefineRequest extends BaseRequest { payload; constructor(tripResult, refineParams) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), refineParams, tripResult }; } static DefaultRequestParams() { const params = { includeLegProjection: true, includeTurnDescription: true, includeIntermediateStops: true }; return params; } // Used by Base.initWithRequestMock / initWithResponseMock static Default() { const fakeTripResult = {}; const params = _TripRefineRequest.DefaultRequestParams(); const request = new _TripRefineRequest(fakeTripResult, params); return request; } static initWithTrip(trip) { const tripResult = { id: trip.id, trip }; const params = _TripRefineRequest.DefaultRequestParams(); const request = new _TripRefineRequest(tripResult, params); return request; } buildRequestXML(language, requestorRef, xmlConfig) { this.payload.requestTimestamp = RequestHelpers.computeRequestTimestamp(); const requestOJP = { OJPRequest: { serviceRequest: { serviceRequestContext: { language }, requestTimestamp: this.payload.requestTimestamp, requestorRef, OJPTripRefineRequest: this.payload } } }; const xmlS = buildRootXML(requestOJP, xmlConfig); return xmlS; } async _fetchResponse(sdk) { const xmlConfig = sdk.version === "2.0" ? DefaultXML_Config : XML_BuilderConfigOJPv1; const responseXML = await RequestHelpers.computeResponse(this, sdk, xmlConfig); try { const parsedObj = parseXML(responseXML, sdk.version); const response = parsedObj.OJP.OJPResponse.serviceDelivery.OJPTripRefineDelivery; if (response === void 0) { console.log(responseXML); throw new Error("Parse error"); } return { ok: true, value: response }; } catch (error) { return { ok: false, error: error instanceof Error ? error : new Error("Unknown error") }; } } }; // src/versions/current/requests/tr.shared.ts var SharedTripRequest = class extends BaseRequest { }; // src/versions/current/requests/tr.ts var TripRequest = class _TripRequest extends SharedTripRequest { payload; constructor(origin, destination, via = [], params = null) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), origin, destination, via, params: params ??= {} }; } static DefaultRequestParams() { const requestParams = { modeAndModeOfOperationFilter: [], numberOfResults: 5, numberOfResultsBefore: void 0, numberOfResultsAfter: void 0, useRealtimeData: "explanatory", includeAllRestrictedLines: true, includeTrackSections: true, includeLegProjection: false, includeIntermediateStops: true }; return requestParams; } // Used by Base.initWithRequestMock / initWithResponseMock static Default() { const date = /* @__PURE__ */ new Date(); const origin = { placeRef: PlaceRef.initWithPlaceRefsOrCoords("8503000", "Z\xFCrich"), depArrTime: date.toISOString(), individualTransportOption: [] }; const destination = { placeRef: PlaceRef.initWithPlaceRefsOrCoords("8507000", "Bern"), individualTransportOption: [] }; const params = _TripRequest.DefaultRequestParams(); const request = new _TripRequest(origin, destination, [], params); return request; } static initWithPlaceRefsOrCoords(originPlaceRefS, destinationPlaceRefS) { const origin = { placeRef: PlaceRef.initWithPlaceRefsOrCoords(originPlaceRefS), individualTransportOption: [] }; const destination = { placeRef: PlaceRef.initWithPlaceRefsOrCoords(destinationPlaceRefS), individualTransportOption: [] }; const params = _TripRequest.DefaultRequestParams(); const request = new _TripRequest(origin, destination, [], params); request.setDepartureDatetime(); return request; } static initWithPlaces(origin, destination) { const originPlaceRefS = origin.asStopPlaceRefOrCoords(); const destinationPlaceRefS = destination.asStopPlaceRefOrCoords(); const request = _TripRequest.initWithPlaceRefsOrCoords(originPlaceRefS, destinationPlaceRefS); return request; } setArrivalDatetime(newDatetime = /* @__PURE__ */ new Date()) { delete this.payload.origin.depArrTime; this.payload.destination.depArrTime = newDatetime.toISOString(); } setDepartureDatetime(newDatetime = /* @__PURE__ */ new Date()) { delete this.payload.destination.depArrTime; this.payload.origin.depArrTime = newDatetime.toISOString(); } setPublicTransportRequest(motFilter = null) { if (!this.payload.params) { return; } this.payload.params.modeAndModeOfOperationFilter = void 0; if (motFilter !== null && motFilter.length > 0) { this.payload.params.modeAndModeOfOperationFilter = [ { exclude: false, ptMode: motFilter, personalMode: [] } ]; } } disableLinkProkection() { if (!this.payload.params) { return; } this.payload.params.includeLegProjection = false; } enableLinkProkection() { if (!this.payload.params) { return; } this.payload.params.includeLegProjection = true; } setCarRequest() { if (!this.payload.params) { return; } this.payload.params.numberOfResults = 0; this.payload.params.modeAndModeOfOperationFilter = [ { ptMode: [], personalMode: [], railSubmode: "vehicleTunnelTransportRailService", waterSubmode: "localCarFerry" } ]; } setMaxDurationWalkingTime(maxDurationMinutes = void 0, endpointType = "both") { if (!maxDurationMinutes) { maxDurationMinutes = 30; } const maxDuration = "PT" + maxDurationMinutes + "M"; const individualTransportOption = { maxDuration, itModeAndModeOfOperation: { personalMode: "foot", personalModeOfOperation: ["own"] } }; if (endpointType === "origin" || endpointType === "both") { this.payload.origin.individualTransportOption = [individualTransportOption]; } if (endpointType === "destination" || endpointType === "both") { this.payload.destination.individualTransportOption = [individualTransportOption]; } } // https://vdvde.github.io/OJP/develop/documentation-tables/siri.html#type_siri__RailSubmodesOfTransportEnumeration setRailSubmodes(railSubmodes) { if (!Array.isArray(railSubmodes)) { railSubmodes = [railSubmodes]; } if (!this.payload.params) { return; } this.payload.params.modeAndModeOfOperationFilter = []; const modeFilters = []; railSubmodes.forEach((railSubmode) => { const modeFilter = { exclude: false, ptMode: [], personalMode: [], railSubmode }; modeFilters.push(modeFilter); }); this.payload.params.modeAndModeOfOperationFilter = modeFilters; } setNumberOfResults(resultsNo) { if (!this.payload.params) { return; } this.payload.params.numberOfResults = resultsNo ?? void 0; } setNumberOfResultsAfter(resultsNo) { if (!this.payload.params) { return; } this.payload.params.numberOfResultsAfter = resultsNo; } setNumberOfResultsBefore(resultsNo) { if (!this.payload.params) { return; } this.payload.params.numberOfResultsBefore = resultsNo; } setEndpointDurationDistanceRestrictions(placeContext, minDuration, maxDuration, minDistance, maxDistance) { if (minDuration === null && maxDuration === null && minDistance === null && maxDistance === null) { return; } const transportOption = { itModeAndModeOfOperation: { personalMode: "foot", personalModeOfOperation: ["own"] } }; if (minDuration !== null) { transportOption.minDuration = "PT" + minDuration + "M"; } if (maxDuration !== null) { transportOption.maxDuration = "PT" + maxDuration + "M"; } if (minDistance !== null) { transportOption.minDistance = minDistance; } if (maxDistance !== null) { transportOption.maxDistance = maxDistance; } placeContext.individualTransportOption = [transportOption]; } setOriginDurationDistanceRestrictions(minDuration, maxDuration, minDistance, maxDistance) { const placeContext = this.payload.origin; this.setEndpointDurationDistanceRestrictions(placeContext, minDuration, maxDuration, minDistance, maxDistance); } setDestinationDurationDistanceRestrictions(minDuration, maxDuration, minDistance, maxDistance) { const placeContext = this.payload.destination; this.setEndpointDurationDistanceRestrictions(placeContext, minDuration, maxDuration, minDistance, maxDistance); } setWalkSpeedDeviation(walkSpeedPercent) { if (!this.payload.params) { return; } this.payload.params.walkSpeed = walkSpeedPercent; } setViaPlace(place, dwellTime) { const placeRefS = place.asStopPlaceRefOrCoords(); const placeRef = PlaceRef.initWithPlaceRefsOrCoords(placeRefS); const viaPointSchema = { viaPoint: placeRef }; if (dwellTime !== null) { const dwellTimeS = "PT" + dwellTime.toString() + "M"; viaPointSchema.dwellTime = dwellTimeS; } this.payload.via = [viaPointSchema]; } buildRequestXML(language, requestorRef, xmlConfig) { this.payload.requestTimestamp = RequestHelpers.computeRequestTimestamp(); const requestOJP = { OJPRequest: { serviceRequest: { serviceRequestContext: { language }, requestTimestamp: this.payload.requestTimestamp, requestorRef, OJPTripRequest: this.payload } } }; const xmlS = buildRootXML(requestOJP, xmlConfig); return xmlS; } async _fetchResponse(sdk) { const xmlConfig = sdk.version === "2.0" ? DefaultXML_Config : XML_BuilderConfigOJPv1; const responseXML = await RequestHelpers.computeResponse(this, sdk, xmlConfig); try { const parsedObj = parseXML(responseXML, sdk.version); const response = parsedObj.OJP.OJPResponse.serviceDelivery.OJPTripDelivery; if (response === void 0) { console.log(responseXML); throw new Error("Parse error"); } return { ok: true, value: response }; } catch (error) { return { ok: false, error: error instanceof Error ? error : new Error("Unknown error") }; } } }; // src/versions/legacy/v1/requests/fr.ts var OJPv1_FareRequest = class _OJPv1_FareRequest extends BaseRequest { payload; constructor(items) { super(); this.payload = items; } static DefaultRequestParams() { const params = { fareAuthorityFilter: ["ch:1:NOVA"], passengerCategory: ["Adult"], travelClass: "second", traveller: [ { age: 25, passengerCategory: "Adult", entitlementProducts: { entitlementProduct: [ { fareAuthorityRef: "ch:1:NOVA", entitlementProductRef: "HTA", entitlementProductName: "Halbtax-Abonnement" } ] } } ] }; return params; } // Used by Base.initWithRequestMock / initWithResponseMock static Default() { const request = new _OJPv1_FareRequest([]); return request; } static cleanTripForFareRequest(trip) { trip.tripLeg.forEach((leg) => { if (leg.continuousLeg) { leg.continuousLeg = { legStart: { locationName: leg.continuousLeg.legStart.locationName }, legEnd: { locationName: leg.continuousLeg.legEnd.locationName }, service: { personalMode: "foot", personalModeOfOperation: "own" }, duration: leg.continuousLeg.duration }; } if (leg.transferLeg) { leg.transferLeg = { transferType: leg.transferLeg.transferType, legStart: { locationName: leg.transferLeg.legStart.locationName }, legEnd: { locationName: leg.transferLeg.legEnd.locationName }, duration: leg.transferLeg.duration }; } if (leg.timedLeg) { const newLegIntermediates = leg.timedLeg.legIntermediates.map((el) => { const newLeg = { stopPointRef: el.stopPointRef, stopPointName: el.stopPointName, serviceArrival: el.serviceArrival, serviceDeparture: el.serviceDeparture }; return newLeg; }); leg.timedLeg = { legBoard: { stopPointRef: leg.timedLeg.legBoard.stopPointRef, stopPointName: leg.timedLeg.legBoard.stopPointName, serviceDeparture: leg.timedLeg.legBoard.serviceDeparture }, legIntermediates: newLegIntermediates, legAlight: { stopPointRef: leg.timedLeg.legAlight.stopPointRef, stopPointName: leg.timedLeg.legAlight.stopPointName, serviceArrival: leg.timedLeg.legAlight.serviceArrival }, service: { operatingDayRef: leg.timedLeg.service.operatingDayRef, journeyRef: leg.timedLeg.service.journeyRef, lineRef: leg.timedLeg.service.lineRef, directionRef: leg.timedLeg.service.directionRef, mode: leg.timedLeg.service.mode, publishedLineName: leg.timedLeg.service.publishedLineName, attribute: leg.timedLeg.service.attribute, operatorRef: leg.timedLeg.service.operatorRef } }; } }); } static initWithOJPv1Trips(trips) { trips.map((tripV1) => { _OJPv1_FareRequest.cleanTripForFareRequest(tripV1); }); const now = /* @__PURE__ */ new Date(); const requestTimestamp = now.toISOString(); const fareRequests = []; trips.forEach((trip) => { const fareRequest = { requestTimestamp, tripFareRequest: { trip }, params: _OJPv1_FareRequest.DefaultRequestParams() }; fareRequests.push(fareRequest); }); const request = new _OJPv1_FareRequest(fareRequests); return request; } buildRequestXML(language, requestorRef, xmlConfig) { if (xmlConfig.ojpVersion !== "1.0") { throw new Error("FareRequest can be consructed only with OJPv1 XML_Config"); } const requestTimestamp = RequestHelpers.computeRequestTimestamp(); this.payload.forEach((fareRequestPayload) => { fareRequestPayload.requestTimestamp = requestTimestamp; }); const requestOJP = { OJPRequest: { serviceRequest: { serviceRequestContext: { language }, requestTimestamp, requestorRef, OJPFareRequest: this.payload } } }; const xmlS = buildRootXML(requestOJP, xmlConfig, ((objTransformed) => { const siriPrefix = xmlConfig.defaultNS !== "siri" ? "siri:" : ""; const ojpPrefix = xmlConfig.defaultNS !== "ojp" ? "ojp:" : ""; const fareRequests = objTransformed[siriPrefix + "OJPRequest"][siriPrefix + "ServiceRequest"][ojpPrefix + "OJPFareRequest"]; fareRequests.forEach((fareRequest) => { const trip = fareRequest[ojpPrefix + "TripFareRequest"][ojpPrefix + "Trip"]; trip[ojpPrefix + "TripLeg"].forEach((leg) => { if (ojpPrefix + "TimedLeg" in leg) { const service = leg[ojpPrefix + "TimedLeg"][ojpPrefix + "Service"]; if (siriPrefix + "OperatorRef" in service) { service[ojpPrefix + "OperatorRef"] = service[siriPrefix + "OperatorRef"]; delete service[siriPrefix + "OperatorRef"]; } } }); }); })); return xmlS; } async _fetchResponse(sdk) { const xmlConfig = sdk.version === "2.0" ? DefaultXML_Config : XML_BuilderConfigOJPv1; const responseXML = await RequestHelpers.computeResponse(this, sdk, xmlConfig); try { const parsedObj = parseXML(responseXML, sdk.version); const response = parsedObj.OJP.OJPResponse.serviceDelivery.OJPFareDelivery; if (response === void 0) { console.log(responseXML); throw new Error("Parse error"); } return { ok: true, value: response }; } catch (error) { return { ok: false, error: error instanceof Error ? error : new Error("Unknown error") }; } } }; // src/versions/legacy/v1/requests/lir.ts var OJPv1_LocationInformationRequest = class _OJPv1_LocationInformationRequest extends SharedLocationInformationRequest { payload; constructor(restrictions) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), initialInput: void 0, placeRef: void 0, restrictions }; } static Default() { const restrictions = SharedLocationInformationRequest.DefaultRestrictionParams(); const request = new _OJPv1_LocationInformationRequest(restrictions); return request; } static initWithLocationName(name, placeTypes = [], numberOfResults = 10) { const request = _OJPv1_LocationInformationRequest.Default(); request.payload.initialInput = { locationName: name }; if (request.payload.restrictions) { request.updateRestrictions(request.payload.restrictions, placeTypes, numberOfResults); } return request; } static initWithPlaceRef(placeRefOrCoords, numberOfResults = 10) { const request = _OJPv1_LocationInformationRequest.Default(); const placeRef = PlaceRef.initWithPlaceRefsOrCoords(placeRefOrCoords); request.payload.placeRef = placeRef.asOJPv1Schema(); if (request.payload.restrictions) { request.updateRestrictions(request.payload.restrictions, ["s