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
236 lines (235 loc) • 12.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findClosestWithBikes = exports.sortByDistance = exports.findElectricBikes = exports.getPropertyValue = exports.LINE_ORDER = exports.SEVERITY_MAPPING = exports.LINE_COLORS = exports.getLineDisplayName = exports.getLineAriaLabel = exports.hasNightService = exports.isNormalService = exports.getLineStatusSummary = exports.sortLinesBySeverityAndOrder = exports.getAccessibleSeverityLabel = exports.getSeverityClasses = exports.getSeverityCategory = exports.getLineCssProps = exports.getLineColor = exports.TflErrorHandler = exports.TflConfigError = exports.TflTimeoutError = exports.TflValidationError = exports.TflNetworkError = exports.TflHttpError = exports.TflError = exports.severityDescriptions = exports.severityByMode = exports.DIRECTIONS = exports.SERVICE_TYPES = exports.MODES = exports.LINE_IDS = void 0;
const tfl_1 = require("./generated/tfl");
const line_1 = require("./line");
const accidentStats_1 = require("./accidentStats");
const airQuality_1 = require("./airQuality");
const bikePoint_1 = require("./bikePoint");
const cabwise_1 = require("./cabwise");
const journey_1 = require("./journey");
const stopPoint_1 = require("./stopPoint");
const mode_1 = require("./mode");
const road_1 = require("./road");
const Meta_1 = require("./generated/meta/Meta");
const Line_1 = require("./generated/meta/Line");
const errors_1 = require("./errors");
Object.defineProperty(exports, "TflError", { enumerable: true, get: function () { return errors_1.TflError; } });
Object.defineProperty(exports, "TflHttpError", { enumerable: true, get: function () { return errors_1.TflHttpError; } });
Object.defineProperty(exports, "TflNetworkError", { enumerable: true, get: function () { return errors_1.TflNetworkError; } });
Object.defineProperty(exports, "TflValidationError", { enumerable: true, get: function () { return errors_1.TflValidationError; } });
Object.defineProperty(exports, "TflTimeoutError", { enumerable: true, get: function () { return errors_1.TflTimeoutError; } });
Object.defineProperty(exports, "TflConfigError", { enumerable: true, get: function () { return errors_1.TflConfigError; } });
Object.defineProperty(exports, "TflErrorHandler", { enumerable: true, get: function () { return errors_1.TflErrorHandler; } });
// Create mode metadata from the generated Modes data
const modeMetadata = Meta_1.Modes.reduce((acc, mode) => {
acc[mode.modeName] = {
isTflService: mode.isTflService,
isFarePaying: mode.isFarePaying,
isScheduledService: mode.isScheduledService
};
return acc;
}, {});
// Build line IDs from generated Lines data
const buildLineIds = () => {
const lineIds = {
tube: {},
dlr: {},
overground: {},
tram: {},
bus: {}
};
Line_1.Lines.forEach(line => {
const modeName = line.modeName;
if (modeName === 'tube') {
lineIds.tube[line.name.toUpperCase()] = line.id;
}
else if (modeName === 'dlr') {
lineIds.dlr[line.name.toUpperCase()] = line.id;
}
else if (modeName === 'overground') {
lineIds.overground[line.name.toUpperCase()] = line.id;
}
else if (modeName === 'tram') {
lineIds.tram[line.name.toUpperCase()] = line.id;
}
else if (modeName === 'bus') {
lineIds.bus[line.name.toUpperCase()] = line.id;
}
});
return lineIds;
};
// Build severity by mode from generated Severity data
const 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;
};
// Build severity descriptions from generated Severity data
const buildSeverityDescriptions = () => {
const descriptions = new Set();
Meta_1.Severity.forEach(severity => {
descriptions.add(severity.description);
});
return Array.from(descriptions).sort();
};
// Tfl Line IDs - built from generated data
const LINE_IDS = buildLineIds();
exports.LINE_IDS = LINE_IDS;
// Tfl Transport Modes - from generated data
const MODES = modeMetadata;
exports.MODES = MODES;
// Tfl Service Types - from generated data
const SERVICE_TYPES = {
REGULAR: 'Regular',
NIGHT: 'Night'
};
exports.SERVICE_TYPES = SERVICE_TYPES;
// Tfl Direction Types
const DIRECTIONS = {
INBOUND: 'inbound',
OUTBOUND: 'outbound',
ALL: 'all'
};
exports.DIRECTIONS = DIRECTIONS;
// Build severity by mode and descriptions
const severityByMode = buildSeverityByMode();
exports.severityByMode = severityByMode;
const severityDescriptions = buildSeverityDescriptions();
exports.severityDescriptions = severityDescriptions;
class TflClient {
constructor(config) {
/**
* Generate a unique request ID for tracking
*/
this.generateRequestId = () => {
return `tfl_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
};
const appId = config?.appId || process.env.TFL_APP_ID;
const appKey = config?.appKey || process.env.TFL_APP_KEY;
if (!appId || !appKey) {
throw new errors_1.TflConfigError("Missing TFL API credentials. Please either:\n" +
"1. Create a .env file in the root directory with:\n" +
" TFL_APP_ID=your-app-id\n" +
" TFL_APP_KEY=your-app-key\n" +
"2. Or pass the credentials directly:\n" +
" new TflClient({ appId: 'your-app-id', appKey: 'your-app-key' })\n" +
"You can get these credentials by registering at https://api-portal.tfl.gov.uk/", 'credentials');
}
this.config = {
appId,
appKey,
timeout: config?.timeout || 30000,
maxRetries: config?.maxRetries || 3,
retryDelay: config?.retryDelay || 1000
};
this.api = new tfl_1.Api({
baseUrl: "https://api.tfl.gov.uk",
customFetch: (input, init) => {
const url = new URL(input.toString());
url.searchParams.set("app_id", this.config.appId);
url.searchParams.set("app_key", this.config.appKey);
// Add timeout to the request
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
return fetch(url, {
...init,
signal: controller.signal
}).finally(() => clearTimeout(timeoutId));
}
});
// Initialize core transport services
this.line = new line_1.Line(this.api);
this.stopPoint = new stopPoint_1.StopPoint(this.api);
this.journey = new journey_1.Journey(this.api);
this.mode = new mode_1.Mode(this.api);
this.road = new road_1.Road(this.api);
this.bikePoint = new bikePoint_1.BikePoint(this.api);
// Initialize additional services
this.accidentStats = new accidentStats_1.AccidentStats(this.api);
this.airQuality = new airQuality_1.AirQuality(this.api);
this.cabwise = new cabwise_1.Cabwise(this.api);
}
/**
* Execute an API call with error handling and retry logic
*/
async executeWithRetry(apiCall, _context) {
const requestId = this.generateRequestId();
let lastError;
for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
try {
return await apiCall();
}
catch (error) {
const tflError = errors_1.TflErrorHandler.handleApiError(error, undefined, requestId);
lastError = tflError;
// Don't retry on the last attempt
if (attempt === this.config.maxRetries) {
break;
}
// Check if error is retryable
if (!errors_1.TflErrorHandler.isRetryableError(tflError)) {
break;
}
// Calculate retry delay
const delay = errors_1.TflErrorHandler.getRetryDelay(tflError, attempt, this.config.retryDelay);
// Wait before retrying
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
/**
* Get client configuration
*/
getConfig() {
return this.config;
}
}
exports.default = TflClient;
// Export UI utilities for building user interfaces
__exportStar(require("./utils/ui"), exports);
// Explicitly export key UI functions for better documentation clarity
var ui_1 = require("./utils/ui");
Object.defineProperty(exports, "getLineColor", { enumerable: true, get: function () { return ui_1.getLineColor; } });
Object.defineProperty(exports, "getLineCssProps", { enumerable: true, get: function () { return ui_1.getLineCssProps; } });
Object.defineProperty(exports, "getSeverityCategory", { enumerable: true, get: function () { return ui_1.getSeverityCategory; } });
Object.defineProperty(exports, "getSeverityClasses", { enumerable: true, get: function () { return ui_1.getSeverityClasses; } });
Object.defineProperty(exports, "getAccessibleSeverityLabel", { enumerable: true, get: function () { return ui_1.getAccessibleSeverityLabel; } });
Object.defineProperty(exports, "sortLinesBySeverityAndOrder", { enumerable: true, get: function () { return ui_1.sortLinesBySeverityAndOrder; } });
Object.defineProperty(exports, "getLineStatusSummary", { enumerable: true, get: function () { return ui_1.getLineStatusSummary; } });
Object.defineProperty(exports, "isNormalService", { enumerable: true, get: function () { return ui_1.isNormalService; } });
Object.defineProperty(exports, "hasNightService", { enumerable: true, get: function () { return ui_1.hasNightService; } });
Object.defineProperty(exports, "getLineAriaLabel", { enumerable: true, get: function () { return ui_1.getLineAriaLabel; } });
Object.defineProperty(exports, "getLineDisplayName", { enumerable: true, get: function () { return ui_1.getLineDisplayName; } });
Object.defineProperty(exports, "LINE_COLORS", { enumerable: true, get: function () { return ui_1.LINE_COLORS; } });
Object.defineProperty(exports, "SEVERITY_MAPPING", { enumerable: true, get: function () { return ui_1.SEVERITY_MAPPING; } });
Object.defineProperty(exports, "LINE_ORDER", { enumerable: true, get: function () { return ui_1.LINE_ORDER; } });
// Export bike point utilities for working with bike point data
var bikePoint_2 = require("./utils/bikePoint");
Object.defineProperty(exports, "getPropertyValue", { enumerable: true, get: function () { return bikePoint_2.getPropertyValue; } });
Object.defineProperty(exports, "findElectricBikes", { enumerable: true, get: function () { return bikePoint_2.findElectricBikes; } });
Object.defineProperty(exports, "sortByDistance", { enumerable: true, get: function () { return bikePoint_2.sortByDistance; } });
Object.defineProperty(exports, "findClosestWithBikes", { enumerable: true, get: function () { return bikePoint_2.findClosestWithBikes; } });