UNPKG

gbfs-system

Version:

A Node.js package that enables real-time data retrieval from GBFS (General Bikeshare Feed Specification) using the auto-discovery URLs provided by MobilityData. It allows developers to easily access and utilize live bikeshare system information, station i

210 lines 8.05 kB
import axios from "axios"; /** * Get realtime feed data for a given GBFS (General Bikeshare Feed Specification) compliant system. * The feed must be already provided by the operator. exp : station_information, station_status, system_info, etc. * Visit MobiltyData website or github repo for more informations : https://github.com/MobilityData/gbfs */ export class Gbfs { autoDiscoveryURL; preferredFeedLanguage; gbfsData; gbfsSupportedLanguages; /** * Instanciate the Gbfs class. * @param autoDiscoveryURL - The URL of the GBFS auto-discovery endpoint. * @param preferredFeedLanguage - [Optional] The preferred language code for fetching GBFS data. * @returns A new instance of the Gbfs class. */ constructor(autoDiscoveryURL, preferredFeedLanguage) { this.autoDiscoveryURL = autoDiscoveryURL; this.preferredFeedLanguage = preferredFeedLanguage; } /** * Create an instance of Gbfs and retrieve all found feeds URLs * @param {string} autoDiscoveryURL - The URL of the GBFS system. * That URL can be found in the operator API documentation, or by checking the MobilityData maintained system.csv. * The {Systems} module of this library also provide a way to get the autoDiscoveryURL for a registered operator. * @param {string} preferredFeedLanguage - [Optional] - The language code you want to get the feeds data from. * Some operators provide their gbfs system in multiple languages depending on their public's needs. * @ */ static async initialize(autoDiscoveryURL, preferredFeedLanguage) { const instance = new Gbfs(autoDiscoveryURL, preferredFeedLanguage); await instance.ensureInitialized(); return instance; } /** * Get the current defined preferred feed language. * @param language - The language code to set as the preferred feed language. */ set setPreferredFeedLanguage(language) { this.preferredFeedLanguage = language; } /** * Getter for the preferred feed language. * * @returns The currently set preferred language code. */ get getPreferredFeedLanguage() { return this.preferredFeedLanguage; } /** * Get all supported feed languages. * @returns An array of all feeds languages found for the current gbfs system. // ['en', 'fr'] */ getSupportedLanguages() { return this.gbfsSupportedLanguages; } /** * Checks if a given language is supported by the GBFS system. * @param language - The language code to check for support. exp: 'en' * @returns True if the language is supported. */ isLanguageSupported(language) { return this.gbfsSupportedLanguages?.includes(language) ?? false; } /** * Ensures the GBFS data is properly initialized and all required data stored. * @returns {void} */ async ensureInitialized() { if (this.gbfsData && this.gbfsSupportedLanguages) return; try { const { data } = await axios.get(this.autoDiscoveryURL, { headers: { "Content-Type": "application/json" }, }); this.gbfsData = data.data; this.gbfsSupportedLanguages = Object.keys(this.gbfsData); } catch (error) { throw new Error(`Failed to initialize URLs: ${error}`); } } /** * Finds the URL of a specific feed. * @param {string} feedName - The name of the feed to find the URL for. * @returns The URL of the specified feed or undefined if not found. * If there's no defined preferred feed language, it uses the first found feed object to process. */ async findFeedUrl(feedName) { try { await this.ensureInitialized(); if (this.preferredFeedLanguage && !this.gbfsSupportedLanguages.includes(this.preferredFeedLanguage)) { throw new Error(`The specified feed language '${this.preferredFeedLanguage}' doesn't exist in that gbfs system. The available languages are : ${this.gbfsSupportedLanguages.join(", ")}`); } const feeds = this.preferredFeedLanguage ? this.gbfsData[this.preferredFeedLanguage].feeds : Object.values(this.gbfsData)[0].feeds; return feeds?.find((feed) => feed.name === feedName)?.url; } catch (error) { throw error; } } /** * Fetches data from a specified URL. * @param {string} url - The URL to fetch data from. * @returns {Promise<unknown>} The data fetched from the specified URL. */ async fetchData(url) { try { const { data } = await axios.get(url, { headers: { "Content-Type": "application/json" }, }); return data; } catch (error) { throw new Error(`Failed to retrieve data from ${url}: ${error}`); } } /** * Gets data for a specified feed. * @param {string} feedName - The name of the feed to get data for. * @returns {Promise<unknown>} The data for the specified feed. */ async getFeedData(feedName) { const feedURL = await this.findFeedUrl(feedName); return this.fetchData(feedURL); } async stationInfo(stationId) { try { const { data: { stations }, } = (await this.getFeedData("station_information")); if (stationId) { const station = stations.find((station) => station.station_id === stationId); if (!station) { throw new Error(`Station with ID ${stationId} not found.`); } return station; } return stations; } catch (error) { throw error; } } async stationStatus(stationId) { try { const { data: { stations }, } = (await this.getFeedData("station_status")); if (stationId) { const station = stations.find((station) => station.station_id === stationId); if (!station) { throw new Error(`Station with ID ${stationId} not found.`); } return station; } return stations; } catch (error) { throw error; } } /** * Fetches system_information feed data. * @returns The system_information feed data as an object. */ async systemInfo() { try { const { data } = (await this.getFeedData("system_information")); if (!data.system_id) throw new Error("Unable to retrieve the system informations"); return data; } catch (error) { throw error; } } async stationUnified(stationId) { try { let stationInfoData; let stationStatusData; if (typeof stationId === 'string') { stationInfoData = await this.stationInfo(stationId); stationStatusData = await this.stationStatus(stationId); return { ...stationStatusData, ...stationInfoData, }; } else { stationInfoData = await this.stationInfo(); stationStatusData = await this.stationStatus(); const combinedData = stationInfoData.map((info) => { const status = Array.isArray(stationStatusData) ? stationStatusData.find((status) => status.station_id === info.station_id) : stationStatusData; return { ...status, ...info, }; }); return combinedData; } } catch (error) { throw error; } } } //# sourceMappingURL=Gbfs.js.map