UNPKG

ojp-sdk

Version:

OJP (Open Journey Planner) Javascript SDK

1,570 lines (1,549 loc) 86.2 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.23.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 mapNsTags = JSON.parse(JSON.stringify(OJP_Types.OpenAPI_Dependencies.MapNS_Tags)); const isOJPv1 = xmlConfig.ojpVersion === "1.0"; if (isOJPv1) { const keysToOverride = Object.keys(OJP_Types.OpenAPI_Dependencies.MapLegacyOverrideNS_Tags); keysToOverride.forEach((key) => { mapNsTags[key] = OJP_Types.OpenAPI_Dependencies.MapLegacyOverrideNS_Tags[key]; }); } 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 = mapNsTags[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; } /** * Serializes a JavaScript object to XML format * * @param obj - The object to serialize * @param wrapperNodeName - The name of the root XML element * @returns XML string representation of the object * * @example * ```typescript * const serializer = new XmlSerializer(); * const xml = serializer.serialize({ * firstName: "John", * lastName: "Doe" * }, "person"); * // Returns: '<person><firstName>John</firstName><lastName>Doe</lastName></person>' * ``` * */ serialize(obj, wrapperNodeName) { const xml = buildXML(obj, wrapperNodeName, this.xmlConfig); return xml; } /** * Transforms a tag name to camelCase format for XML serialization * * This method handles special cases like OJP prefixes and preserves uppercase-only tags. * It converts hyphenated or underscored names to camelCase while ensuring the first * character is lowercase. * * @param tagName - The original tag name to transform * @returns Transformed tag name in camelCase format * * @example * ```typescript * // Transform various tag name formats * XmlSerializer.transformTagName('OJP_Feature'); // Returns: 'OJP_Feature' * XmlSerializer.transformTagName('FIRST_NAME'); // Returns: 'FIRST_NAME' * XmlSerializer.transformTagName('first-name'); // Returns: 'firstName' * XmlSerializer.transformTagName('user_name'); // Returns: 'userName' * ``` */ 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 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; } /** * Creates a new instance of the request class with a mock request XML string * * This method is used for testing purposes to simulate API requests with predefined XML payloads. * When this mock is set, the request will use the provided XML instead of building a new one. * * @param mockText - The XML string to use as the mock request * @returns A new instance of the request class with the mock set * * @example * ```typescript * const mockRequest = LocationInformationRequest.initWithRequestMock('<OJP>...</OJP>'); * ``` * * @group Initialization */ static initWithRequestMock(mockText) { const instance = this.Default(); instance.mockRequestXML = mockText; return instance; } /** * Creates a new instance of the request class with a mock response XML string * * This method is used for testing purposes to simulate API responses with predefined XML payloads. * When this mock is set, the request will use the provided XML response instead of making actual API calls. * * @param mockText - The XML string to use as the mock response * @returns A new instance of the request class with the mock set * * @example * ```typescript * const mockRequest = LocationInformationRequest.initWithResponseMock('<OJP>...</OJP>'); * ``` * * @group Initialization */ 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 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 { /** * The payload object that gets serialized to XML for the request * * @see {@link https://vdvde.github.io/OJP/develop/documentation-tables/ojp.html#type_ojp__OJPLocationInformationRequestStructure OJP LocationInformationRequest XSD Schema} */ payload; constructor(restrictions) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), initialInput: void 0, placeRef: void 0, restrictions }; } static DefaultRestrictionParams() { const restrictionParams = { type: [], numberOfResults: void 0, modes: void 0, includePtModes: true }; return restrictionParams; } updateRestrictions(placeTypes, numberOfResults) { if (!this.payload.restrictions) { return; } if (placeTypes.length > 0) { this.payload.restrictions.type = placeTypes; } if (numberOfResults !== null) { this.payload.restrictions.numberOfResults = numberOfResults; } } /** * Used by BaseRequest methods (i.e. `initWithRequestMock`, `initWithResponseMock` * @hidden */ static Default() { const restrictions = _LocationInformationRequest.DefaultRestrictionParams(); const request = new _LocationInformationRequest(restrictions); return request; } /** * Inits LIR with location name * * @param name string, location name * @param placeTypes `OJP_Types.PlaceTypeEnum` "stop" | "address" | "poi" | "location" | "topographicPlace" * @param numberOfResults maximum number of results to return (default: 10) * * @group Initialization */ static initWithLocationName(name, placeTypes = [], numberOfResults = 10) { const request = _LocationInformationRequest.Default(); request.payload.initialInput = { name }; if (request.payload.restrictions) { request.updateRestrictions(placeTypes, numberOfResults); } return request; } /** * Inits LIR with place ref or literal coords * * @param placeRefOrCoords place reference or coordinates string (e.g. `ch:1:sloid:7000:4:7` or `46.94857,7.43683` (latitude, longitude in WGS84)) * @param numberOfResults maximum number of results to return (default: 10) * * @group Initialization */ static initWithPlaceRef(placeRefOrCoords, numberOfResults = 10) { const request = _LocationInformationRequest.Default(); request.payload.placeRef = PlaceRef.initWithPlaceRefsOrCoords(placeRefOrCoords); if (request.payload.restrictions) { request.updateRestrictions(["stop"], numberOfResults); } return request; } /** * Inits LIR with bounding box * * @param bboxData bounding box data as string ("minLon,minLat,maxLon,maxLat") or array [minLon, minLat, maxLon, maxLat] * @param placeTypes `OJP_Types.PlaceTypeEnum` "stop" | "address" | "poi" | "location" | "topographicPlace" * @param numberOfResults maximum number of results to return (default: 10) * * @group Initialization */ 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(placeTypes, numberOfResults); } return request; } /** * Builds the XML request string for the LIR * * @param language The language to use for the request (e.g. "en", "de") * @param requestorRef The requestor reference identifier * @param xmlConfig XML configuration options for building the request, default {@link DefaultXML_Config} OJP 2.0 * @returns A formatted XML string representing the Location Information Request */ buildRequestXML(language, requestorRef, xmlConfig = DefaultXML_Config) { 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 { /** * The payload object that gets serialized to XML for the request * * @see {@link https://vdvde.github.io/OJP/develop/documentation-tables/ojp.html#type_ojp__OJPStopEventRequestStructure OJP StopEventRequest XSD Schema} */ payload; constructor(location, params = void 0) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), location, params }; } /** * Used by BaseRequest methods (i.e. `initWithRequestMock`, `initWithResponseMock` * @hidden */ 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; } /** * Creates a new StopEventRequest with the given place reference and date * * @param placeRefS The stop point reference ID (e.g. "8507000" for Bern) * @param date The date and time for the stop event request (defaults to current date/time) * @returns A new StopEventRequest instance * * @example * ```typescript * const request = StopEventRequest.initWithPlaceRefAndDate('8507000'); * const response = await request.fetch(sdk); * ``` * * @group Initialization */ 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; } /** * Builds the XML request string for the SER * * @param language The language to use for the request (e.g. "en", "de") * @param requestorRef The requestor reference identifier * @param xmlConfig XML configuration options for building the request, default {@link DefaultXML_Config} OJP 2.0 * @returns A formatted XML string representing the Location Information 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 { /** * The payload object that gets serialized to XML for the request * * @see {@link https://vdvde.github.io/OJP/develop/documentation-tables/ojp.html#type_ojp__OJPTripInfoRequestStructure OJP TripInfoRequest XSD Schema} */ payload; constructor(journeyRef, operatingDayRef, params) { super(); this.payload = { requestTimestamp: RequestHelpers.computeRequestTimestamp(), journeyRef, operatingDayRef, params }; } /** * Used by BaseRequest methods (i.e. `initWithRequestMock`, `initWithResponseMock` * @hidden */ static Default() { const request = new _TripInfoRequest("n/a", "n/a", SharedTripInfoRequest.DefaultRequestParams()); return request; } /** * Creates a new TripInfoRequest with the given journey reference and date * * @param journeyRef The journey reference ID (e.g. "ch:1:sjyid:100001:2179-001") * @param journeyDate The date of the journey (defaults to current date) * @returns A new TripInfoRequest instance * * @example * ```typescript * const request = TripInfoRequest.initWithJourneyRef('ch:1:sjyid:100001:2179-001'); * const response = await request.fetch(sdk); * ``` * * @group Initialization */ 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; } /** * Enables track projection in the request payload * * This method modifies the request to include track projection data in the response. * When enabled, the response will contain additional information about the vehicle's * track and movement patterns. * * @example * ```typescript * const request = TripInfoRequest.initWithJourneyRef('ch:1:sjyid:100001:2179-001'); * request.enableTrackProjection(); * const response = await request.fetch(sdk); * ``` * * @group Request Payload Modification */ enableTrackProjection() { if (this.payload.params) { this.payload.params.includeTrackProjection = true; } } /** * Builds the XML request string for the TIR * * @param language The language to use for the request (e.g. "en", "de") * @param requestorRef The requestor reference identifier * @param xmlConfig XML configuration options for building the request, default {@link DefaultXML_Config} OJP 2.0 * @returns A formatted XML string representing the Location Information Request */ 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 { /** * The payload object that gets serialized to XML for the request * * @see {@link https://vdvde.github.io/OJP/develop/documentation-tables/ojp.html#type_ojp__OJPTripRefineRequestStructure OJP TripRefineRequest XSD Schema} */ 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 BaseRequest methods (i.e. `initWithRequestMock`, `initWithResponseMock` * @hidden */ static Default() { const fakeTripResult = {}; const params = _TripRefineRequest.DefaultRequestParams(); const request = new _TripRefineRequest(fakeTripResult, params); return request; } /** * Creates a new TripRefineRequest with the given Trip OJP XSD Schema * * @param trip the trip to be refined as Trip OJP XSD Schema * * @group Initialization */ static initWithTrip(trip) { const tripResult = { id: trip.id, trip }; const params = _TripRefineRequest.DefaultRequestParams(); const request = new _TripRefineRequest(tripResult, params); return request; } /** * Builds the XML request string for the TIR * * @param language The language to use for the request (e.g. "en", "de") * @param requestorRef The requestor reference identifier * @param xmlConfig XML configuration options for building the request, default {@link DefaultXML_Config} OJP 2.0 * @returns A formatted XML string representing the Location Information 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 { /** * The payload object that gets serialized to XML for the request * * @see {@link https://vdvde.github.io/OJP/develop/documentation-tables/ojp.html#type_ojp__OJPTripRequestStructure OJP TripRequest XSD Schema} */ 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 BaseRequest methods (i.e. `initWithRequestMock`, `initWithResponseMock` * @hidden */ 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; } /** * Initializes a TripRequest object with either place references or coordinates. * * @param originPlaceRefS - The origin place reference string - place reference or coordinate literals (lat, lon WGS84) * @param destinationPlaceRefS - The destination place reference string - place reference or coordinate literals (lat, lon WGS84) * * @group Initialization */ 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; } /** * Initializes a TripRequest object with Place OJP schema objects * * @param origin - The origin place * @param destination - The destination place * * @group Initialization */ static initWithPlaces(origin, destination) { const originPlaceRefS = origin.asStopPlaceRefOrCoords(); const destinationPlaceRefS = destination.asStopPlaceRefOrCoords(); const request = _TripRequest.initWithPlaceRefsOrCoords(originPlaceRefS, destinationPlaceRefS); return request; } /** * Updates arrival at destination datetime in the request payload * * @group Request Payload Modification */ setArrivalDatetime(newDatetime = /* @__PURE__ */ new Date()) { delete this.payload.origin.depArrTime; this.payload.destination.depArrTime = newDatetime.toISOString(); } /** * Updates departure from origin datetime in the request payload * * @group Request Payload Modification */ setDepartureDatetime(newDatetime = /* @__PURE__ */ new Date()) { delete this.payload.destination.depArrTime; this.payload.origin.depArrTime = newDatetime.toISOString(); } /** * @group Request Payload Modification */ 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: [] } ]; } } /** * @group Request Payload Modification */ disableLinkProkection() { if (!this.payload.params) { return; } this.payload.params.includeLegProjection = false; } /** * @group Request Payload Modification */ enableLinkProkection() { if (!this.payload.params) { return; } this.payload.params.includeLegProjection = true; } /** * @group Request Payload Modification */ setCarRequest() { if (!this.payload.params) { return; } this.payload.params.numberOfResults = 0; this.payload.params.modeAndModeOfOperationFilter = [ { ptMode: [], personalMode: [], railSubmode: "vehicleTunnelTransportRailService", waterSubmode: "localCarFerry" } ]; } /** * @group Request Payload Modification */ 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]; } } /** * see https://vdvde.github.io/OJP/develop/documentation-tables/siri.html#type_siri__RailSubmodesOfTransportEnumeration * * @group Request Payload Modification */ 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; } /** * @group Request Payload Modification */ setNumberOfResults(resultsNo) { if (!this.payload.params) { return; } this.payload.params.numberOfResults = resultsNo ?? void 0; } /** * @group Request Payload Modification */ setNumberOfResultsAfter(resultsNo) { if (!this.payload.params) { return; } this.payload.params.numberOfResultsAfter = resultsNo; } /** * @group Request Payload Modification */ setNumberOfResultsBefore(resultsNo) { if (!this.payload.params) { return; } this.payload.params.numberOfResultsBefore = resultsNo; } setEndpointDurationDistanceRestrictions(placeContext, transportMode, minDuration, maxDuration, minDistance, maxDistance) { if (minDuration === null && maxDuration === null && minDistance === null && maxDistance === null) { return; } const transportOption = { itModeAndModeOfOperation: { personalMode: transportMode, personalModeOfOperation: ["own"] } };