UNPKG

tfl-ts

Version:

🚇 Fully-typed TypeScript client for Transport for London (TfL) API • Zero dependencies • Auto-generated types • Real-time arrivals • Journey planning • Universal compatibility

531 lines (530 loc) • 23.1 kB
"use strict"; // this code is not generated, please edit to add additional features Object.defineProperty(exports, "__esModule", { value: true }); exports.modeMetadata = exports.StopPoint = void 0; const batchRequest_1 = require("./utils/batchRequest"); const stripTypes_1 = require("./utils/stripTypes"); // 🚨 ALWAYS use generated metadata (never hardcode!) const Meta_1 = require("./generated/meta/Meta"); // Import raw data from generated meta files const StopPoint_1 = require("./generated/meta/StopPoint"); Object.defineProperty(exports, "modeMetadata", { enumerable: true, get: function () { return StopPoint_1.modeMetadata; } }); /** * StopPoint class for interacting with TfL StopPoint API endpoints * @example * // Get stop point information * const stopInfo = await client.stopPoint.get({ id: '940GZZLUOXC' }); * * // Search for stops * const results = await client.stopPoint.search({ * query: "Oxford Circus", * modes: ['tube', 'bus'] * }); * * // Get arrivals for a stop * const arrivals = await client.stopPoint.getArrivals({ ids: ['940GZZLUOXC'] }); * * // Access static metadata (no HTTP request) * const categories = client.stopPoint.STOP_POINT_CATEGORIES; * const stopTypes = client.stopPoint.STOP_POINT_TYPES; * const modeNames = client.stopPoint.MODE_NAMES; // For validation */ class StopPoint { constructor(api) { this.api = api; // 🚨 ALWAYS use generated metadata (never hardcode!) /** Available transport modes (static, no HTTP request needed) */ this.MODE_NAMES = Meta_1.Modes.map(m => m.modeName); /** Service types (static, no HTTP request needed) */ this.SERVICE_TYPES = Meta_1.ServiceTypes; /** Disruption categories (static, no HTTP request needed) */ this.DISRUPTION_CATEGORIES = Meta_1.DisruptionCategories; /** Place types (static, no HTTP request needed) */ this.PLACE_TYPES = Meta_1.PlaceTypes; /** Search providers (static, no HTTP request needed) */ this.SEARCH_PROVIDERS = Meta_1.SearchProviders; /** Sort options (static, no HTTP request needed) */ this.SORT_OPTIONS = Meta_1.Sorts; /** Stop types (static, no HTTP request needed) */ this.STOP_TYPES = Meta_1.StopTypes; /** Categories (static, no HTTP request needed) */ this.CATEGORIES = Meta_1.Categories.map(c => c.category); /** All severity levels (static, no HTTP request needed) */ this.ALL_SEVERITY = Meta_1.Severity; /** Available stop point categories (static, no HTTP request needed) */ this.STOP_POINT_CATEGORIES = [ 'Accessibility', 'AirQuality', 'BikePoint', 'CarPark', 'CycleSuperhighway', 'Disruption', 'JourneyPlanner', 'Line', 'Mode', 'Place', 'Route', 'StopPoint', 'Train', 'Tube' ]; /** Available stop point types (static, no HTTP request needed) */ this.STOP_POINT_TYPES = [ 'NaptanMetroStation', 'NaptanRailStation', 'NaptanBusCoachStation', 'NaptanPublicBusCoachTram', 'NaptanAccessibleArea', 'NaptanFlexibleZone' ]; /** TfL service modes (static, no HTTP request needed) */ this.TFL_SERVICE_MODES = [ 'tube', 'bus', 'dlr', 'overground', 'elizabeth-line', 'river-bus', 'cable-car', 'cycle-hire' ]; /** Fare paying modes (static, no HTTP request needed) */ this.FARE_PAYING_MODES = [ 'tube', 'bus', 'dlr', 'overground', 'elizabeth-line', 'river-bus', 'cable-car', 'coach', 'cycle-hire', 'national-rail' ]; /** Scheduled service modes (static, no HTTP request needed) */ this.SCHEDULED_SERVICE_MODES = [ 'tube', 'bus', 'dlr', 'overground', 'elizabeth-line', 'river-bus', 'cable-car', 'coach', 'national-rail' ]; /** Mode metadata (static, no HTTP request needed) */ this.MODE_METADATA = StopPoint_1.modeMetadata; // 🚨 Build derived metadata from generated data /** Severity levels grouped by mode (static, no HTTP request needed) */ this.SEVERITY_BY_MODE = this.buildSeverityByMode(); /** Severity descriptions (static, no HTTP request needed) */ this.SEVERITY_DESCRIPTIONS = this.buildSeverityDescriptions(); /** Categories with their available keys (static, no HTTP request needed) */ this.CATEGORIES_WITH_KEYS = this.buildCategoriesWithKeys(); this.batchRequest = new batchRequest_1.BatchRequest(api); } /** * Builds severity levels grouped by mode from generated data * @returns Record mapping mode names to their severity levels * @example * const tubeSeverity = client.stopPoint.SEVERITY_BY_MODE.tube; * // Returns: [{ level: 10, description: 'Good Service' }, ...] */ buildSeverityByMode() { const severityMap = {}; Meta_1.Severity.forEach(severity => { if (!severityMap[severity.modeName]) { severityMap[severity.modeName] = []; } severityMap[severity.modeName].push({ level: severity.severityLevel, description: severity.description }); }); return severityMap; } /** * Builds severity descriptions from generated data * @returns Record mapping severity levels to descriptions * @example * const description = client.stopPoint.SEVERITY_DESCRIPTIONS[10]; * // Returns: 'Good Service' */ buildSeverityDescriptions() { const descriptions = {}; Meta_1.Severity.forEach(severity => { descriptions[severity.severityLevel] = severity.description; }); return descriptions; } /** * Builds categories with their available keys from generated data * @returns Record mapping category names to their available keys * @example * const facilityKeys = client.stopPoint.CATEGORIES_WITH_KEYS.Facility; * // Returns: ['Lifts', 'Boarding Ramp', 'Cash Machines', ...] */ buildCategoriesWithKeys() { const categoriesMap = {}; Meta_1.Categories.forEach(category => { categoriesMap[category.category] = category.availableKeys; }); return categoriesMap; } /** * Gets the list of available StopPoint additional information categories * @returns Promise resolving to an array of stop point categories * @example * const categories = await client.stopPoint.getCategories(); */ async getCategories() { return this.api.stopPoint.stopPointMetaCategories() .then(response => (0, stripTypes_1.stripTypeFields)(response.data)); } /** * Gets the list of available StopPoint types * @returns Promise resolving to an array of stop point types * @example * const types = await client.stopPoint.getTypes(); */ async getTypes() { return this.api.stopPoint.stopPointMetaStopTypes() .then(response => response.data); } /** * Gets the list of available StopPoint modes * @returns Promise resolving to an array of stop point modes * @example * const modes = await client.stopPoint.getModes(); */ async getModes() { return this.api.stopPoint.stopPointMetaModes() .then(response => (0, stripTypes_1.stripTypeFields)(response.data)); } async get(input) { // Handle single ID if (typeof input === 'string') { return this.batchRequest.processBatch([input], async (chunk) => this.api.stopPoint.stopPointGet({ ids: chunk }) .then(response => (0, stripTypes_1.stripTypeFields)(response.data[0]))); } // Handle array of IDs if (Array.isArray(input)) { if (!input.length) { throw new Error('Stop point ID(s) are required'); } return this.batchRequest.processBatch(input, async (chunk) => this.api.stopPoint.stopPointGet({ ids: chunk }) .then(response => (0, stripTypes_1.stripTypeFields)(response.data))); } // Handle options object (original behavior) const { stopPointIds } = input; if (!stopPointIds?.length) { throw new Error('Stop point ID(s) are required'); } return this.batchRequest.processBatch(stopPointIds, async (chunk) => this.api.stopPoint.stopPointGet({ ids: chunk }) .then(response => (0, stripTypes_1.stripTypeFields)(response.data, input.keepTflTypes))); } /** * Get a list of places corresponding to a given id and place types * @param id - Stop point ID * @param placeTypes - Array of place types * @returns Promise resolving to an array of places * @example * const places = await client.stopPoint.getPlaces('940GZZLUOXC', ['NaptanMetroStation']); * * // Validate place types before making API calls * const userInput = ['NaptanMetroStation', 'InvalidType']; * const validPlaceTypes = userInput.filter(type => client.stopPoint.PLACE_TYPES.includes(type)); * if (validPlaceTypes.length !== userInput.length) { * throw new Error(`Invalid place types: ${userInput.filter(type => !client.stopPoint.PLACE_TYPES.includes(type)).join(', ')}`); * } */ async getPlaces(id, placeTypes) { return this.api.stopPoint.stopPointGet2({ id, placeTypes }) .then(response => (0, stripTypes_1.stripTypeFields)(response.data)); } /** * Gets all the Crowding data for the StopPointId, plus crowding data for a given line and optionally a particular direction * @param options - Query options for crowding data * @returns Promise resolving to an array of stop points with crowding data * @example * const crowding = await client.stopPoint.getCrowding({ * id: '940GZZLUOXC', * line: 'central', * direction: 'inbound' * }); */ async getCrowding(options) { const { id, line, direction, keepTflTypes } = options; return this.api.stopPoint.stopPointCrowding({ id, line, direction: direction || 'all' }).then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Gets all stop points of a given type * @param types - Array of stop point types * @returns Promise resolving to an array of stop points * @example * const metroStations = await client.stopPoint.getByType(['NaptanMetroStation']); * * // Validate stop types before making API calls * const userInput = ['NaptanMetroStation', 'InvalidType']; * const validStopTypes = userInput.filter(type => client.stopPoint.STOP_TYPES.includes(type)); * if (validStopTypes.length !== userInput.length) { * throw new Error(`Invalid stop types: ${userInput.filter(type => !client.stopPoint.STOP_TYPES.includes(type)).join(', ')}`); * } */ async getByType(types) { return this.api.stopPoint.stopPointGetByType(types) .then(response => (0, stripTypes_1.stripTypeFields)(response.data)); } /** * Gets all the stop points of given type(s) with a page number * @param types - Array of stop point types * @param page - Page number * @returns Promise resolving to an array of stop points * @example * const metroStations = await client.stopPoint.getByTypeWithPagination(['NaptanMetroStation'], 1); */ async getByTypeWithPagination(types, page) { return this.api.stopPoint.stopPointGetByTypeWithPagination(types, page) .then(response => (0, stripTypes_1.stripTypeFields)(response.data)); } /** * Gets the service types for a given stoppoint * @param options - Query options for service types * @returns Promise resolving to an array of line service types * @example * const serviceTypes = await client.stopPoint.getServiceTypes({ * id: '940GZZLUOXC', * lineIds: ['central', 'victoria'] * }); */ async getServiceTypes(options) { return this.api.stopPoint.stopPointGetServiceTypes(options) .then(response => (0, stripTypes_1.stripTypeFields)(response.data, options.keepTflTypes)); } /** * Gets the list of arrival predictions for the given stop point id * @param options - Query options for arrivals * @returns Promise resolving to an array of arrival predictions * @example * const arrivals = await client.stopPoint.getArrivals({ * stopPointIds: ['940GZZLUOXC'], * sortBy: 'timeToStation' * }); */ async getArrivals(options) { const { stopPointIds, sortBy, sortOrder, keepTflTypes } = options; const arrivals = await this.batchRequest.processBatch(stopPointIds, async (chunk) => Promise.all(chunk.map(id => this.api.stopPoint.stopPointArrivals(id) .then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)))).then(results => results.flat())); if (!sortBy) { return arrivals; } const multiplier = sortOrder === 'desc' ? -1 : 1; return arrivals.sort((a, b) => { let aValue, bValue; switch (sortBy) { case 'timeToStation': aValue = a.timeToStation || 0; bValue = b.timeToStation || 0; break; case 'lineName': aValue = a.lineName || ''; bValue = b.lineName || ''; break; case 'destinationName': aValue = a.destinationName || ''; bValue = b.destinationName || ''; break; default: return 0; } if (typeof aValue === 'string' && typeof bValue === 'string') { return aValue.localeCompare(bValue) * multiplier; } return (aValue - bValue) * multiplier; }); } /** * Gets the list of arrival and departure predictions for the given stop point id (overground, Elizabeth line and thameslink only) * @param options - Query options for arrival departures * @returns Promise resolving to an array of arrival departure predictions * @example * const arrivals = await client.stopPoint.getArrivalDepartures({ * id: '940GZZLULBG', * lineIds: ['london-overground'] * }); */ async getArrivalDepartures(options) { const { id, lineIds, keepTflTypes } = options; return this.api.stopPoint.stopPointArrivalDepartures({ id, lineIds }) .then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Gets Stopoints that are reachable from a station/line combination * @param options - Query options for reachable stops * @returns Promise resolving to an array of reachable stop points * @example * const reachable = await client.stopPoint.getReachableFrom({ * id: '940GZZLUOXC', * lineId: 'central', * serviceTypes: ['Regular'] * }); */ async getReachableFrom(options) { const { id, lineId, serviceTypes, keepTflTypes } = options; return this.api.stopPoint.stopPointReachableFrom({ id, lineId, serviceTypes: serviceTypes }).then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Returns the route sections for all the lines that service the given stop point ids * @param options - Query options for route sections * @returns Promise resolving to an array of stop point route sections * @example * const routes = await client.stopPoint.getRoute({ * id: '940GZZLUOXC', * serviceTypes: ['Regular'] * }); */ async getRoute(options) { const { id, serviceTypes, keepTflTypes } = options; return this.api.stopPoint.stopPointRoute({ id, serviceTypes: serviceTypes }).then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Gets a distinct list of disrupted stop points for the given modes * @param modes - Array of transport modes * @param options - Additional options * @returns Promise resolving to an array of disrupted points * @example * const disruptions = await client.stopPoint.getDisruptionByMode(['tube', 'dlr'], { * includeRouteBlockedStops: true * }); * * // Validate modes before making API calls * const userInput = ['tube', 'invalid-mode']; * const validModes = userInput.filter(mode => client.stopPoint.MODE_NAMES.includes(mode)); * if (validModes.length !== userInput.length) { * throw new Error(`Invalid modes: ${userInput.filter(mode => !client.stopPoint.MODE_NAMES.includes(mode)).join(', ')}`); * } */ async getDisruptionByMode(modes, options) { return this.api.stopPoint.stopPointDisruptionByMode({ modes, ...options }) .then(response => (0, stripTypes_1.stripTypeFields)(response.data, options?.keepTflTypes)); } /** * Gets all disruptions for the specified StopPointId, plus disruptions for any child Naptan records it may have * @param options - Query options for disruptions * @returns Promise resolving to an array of disrupted points * @example * const disruptions = await client.stopPoint.getDisruption({ * stopPointIds: ['940GZZLUOXC', '940GZZLUVIC'], * getFamily: true * }); */ async getDisruption(options) { const { stopPointIds, getFamily, includeRouteBlockedStops, flattenResponse, keepTflTypes } = options; return this.api.stopPoint.stopPointDisruption({ ids: stopPointIds, getFamily, includeRouteBlockedStops, flattenResponse }).then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Returns the canonical direction, "inbound" or "outbound", for a given pair of stop point Ids * @param options - Query options for direction * @returns Promise resolving to direction string * @example * const direction = await client.stopPoint.getDirection({ * id: '940GZZLUOXC', * toStopPointId: '940GZZLUVIC', * lineId: 'central' * }); */ async getDirection(options) { const { id, toStopPointId, lineId, keepTflTypes } = options; return this.api.stopPoint.stopPointDirection({ id, toStopPointId, lineId }) .then(response => response.data); } /** * Gets a list of StopPoints within radius by the specified criteria * @param options - Query options for geo location search * @returns Promise resolving to stop points response * @example * const stops = await client.stopPoint.getByGeoPoint({ * lat: 51.5074, * lon: -0.1278, * radius: 500, * modes: ['tube', 'bus'] * }); */ async getByGeoPoint(options) { const { lat, lon, radius, useStopPointHierarchy, categories, returnLines, stoptypes, keepTflTypes } = options; return this.api.stopPoint.stopPointGetByGeoPoint({ locationLat: lat, locationLon: lon, radius, useStopPointHierarchy, categories: categories || [], returnLines, stopTypes: stoptypes || [] }).then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Gets a list of StopPoints filtered by the modes available at that StopPoint * @param options - Query options for mode-based search * @returns Promise resolving to stop points response * @example * const tubeStops = await client.stopPoint.getByMode({ * modes: ['tube'], * page: 1 * }); */ async getByMode(options) { const { modes, page, keepTflTypes } = options; return this.api.stopPoint.stopPointGetByMode({ modes, page }).then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Search StopPoints by their common name, or their 5-digit Countdown Bus Stop Code * @param options - Query options for search * @returns Promise resolving to search response with extended properties * @example * const results = await client.stopPoint.search({ * query: "Oxford Circus", * modes: ['tube', 'bus'], * lineIds: ['central', 'victoria'] * }); * * // Access extended properties that are actually returned by the API * console.log(results.matches?.[0]?.stationName); // "Oxford Circus" * console.log(results.matches?.[0]?.platformName); // "Westbound - Platform 1" */ async search(options) { const { query, modes, maxResults, lineIds, tflOperatedNationalRailStationsOnly, faresOnly, includeHubs, keepTflTypes } = options; return this.api.stopPoint.stopPointSearch({ query, modes, maxResults, lines: lineIds, tflOperatedNationalRailStationsOnly, faresOnly, includeHubs }).then(response => { const data = response.data; const sanitized = JSON.parse(JSON.stringify(data)); return (0, stripTypes_1.stripTypeFields)(sanitized, keepTflTypes); }); } /** * Gets a StopPoint for a given sms code * @param options - Query options for SMS lookup * @returns Promise resolving to system object * @example * const stop = await client.stopPoint.getBySms({ * id: '73241', * output: 'web' * }); */ async getBySms(options) { const { id, output, keepTflTypes } = options; return this.api.stopPoint.stopPointGetBySms({ id, output }) .then(response => (0, stripTypes_1.stripTypeFields)(response.data, keepTflTypes)); } /** * Gets a list of taxi ranks corresponding to the given stop point id * @param stopPointId - Stop point ID * @returns Promise resolving to an array of places * @example * const taxiRanks = await client.stopPoint.getTaxiRanks('940GZZLUOXC'); */ async getTaxiRanks(stopPointId) { return this.api.stopPoint.stopPointGetTaxiRanksByIds(stopPointId) .then(response => (0, stripTypes_1.stripTypeFields)(response.data)); } /** * Get car parks corresponding to the given stop point id * @param stopPointId - Stop point ID * @returns Promise resolving to an array of places * @example * const carParks = await client.stopPoint.getCarParks('940GZZLUOXC'); */ async getCarParks(stopPointId) { return this.api.stopPoint.stopPointGetCarParksById(stopPointId) .then(response => (0, stripTypes_1.stripTypeFields)(response.data)); } } exports.StopPoint = StopPoint;