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
JavaScript
"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;