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
174 lines (165 loc) • 5.35 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const SWAGGER_URL = 'https://api.tfl.gov.uk/swagger/docs/v1';
// Fetch Swagger JSON
async function fetchSwagger() {
try {
const response = await fetch(SWAGGER_URL);
if (!response.ok) {
throw new Error(`Failed to fetch Swagger JSON: ${response.statusText}`);
}
return await response.json();
}
catch (error) {
console.error('Error fetching Swagger JSON:', error);
process.exit(1);
}
}
// Enhanced data extraction
function extractRelevantData(swaggerJson) {
const { paths, definitions } = swaggerJson;
const extractedData = {
endpoints: {},
definitions: {}
};
const endpointsToExtract = [
'/Line/Meta/Modes',
'/Line/Meta/Severity',
'/Line/Meta/DisruptionCategories',
'/Line/Meta/ServiceTypes',
'/Line/{ids}',
'/Line/Mode/{modes}'
];
// Extract endpoint data
for (const endpoint of endpointsToExtract) {
if (paths[endpoint]) {
extractedData.endpoints[endpoint] = paths[endpoint].get;
// Extract referenced definitions
const responses = paths[endpoint].get.responses;
for (const response of Object.values(responses)) {
if (response.schema?.$ref) {
const defName = response.schema.$ref.split('/').pop();
if (defName && definitions[defName]) {
extractedData.definitions[defName] = definitions[defName];
}
}
}
}
}
return extractedData;
}
// Enhanced type generation
function generateTypeDefinitions(data) {
let typeDefinitions = `// Generated TypeScript definitions for Tfl API
// Generated on ${new Date().toISOString()}
export interface TflResponse {
httpStatusCode?: number;
httpStatus?: string;
timestamp?: string;
}
`;
// Generate types for transport modes
typeDefinitions += `export type TransportMode =
| 'tube'
| 'bus'
| 'river-bus'
| 'overground'
| 'dlr'
| 'tram'
| 'elizabeth-line';
`;
// Generate Line Status types
typeDefinitions += `export interface LineStatus extends TflResponse {
id: string;
name: string;
modeName: TransportMode;
disruptions: Disruption[];
created: string;
modified: string;
lineStatuses: StatusDetail[];
}
export interface Disruption {
category: string;
categoryDescription: string;
description: string;
created: string;
affectedRoutes: Route[];
}
export interface StatusDetail {
statusSeverity: number;
statusSeverityDescription: string;
reason?: string;
validityPeriods: ValidityPeriod[];
}
export interface ValidityPeriod {
fromDate: string;
toDate: string;
isNow: boolean;
}
export interface Route {
id: string;
lineId: string;
routeSectionNaptanEntrySequence: number[];
name: string;
}
`;
// Generate types from extracted definitions
for (const [defName, definition] of Object.entries(data.definitions)) {
typeDefinitions += generateInterfaceFromDefinition(defName, definition);
}
return typeDefinitions;
}
function generateInterfaceFromDefinition(name, definition) {
if (!definition.properties)
return '';
let interfaceStr = `export interface ${name} {\n`;
for (const [propName, prop] of Object.entries(definition.properties)) {
const isRequired = definition.required?.includes(propName);
const typeStr = getTypeFromProperty(prop);
interfaceStr += ` ${propName}${isRequired ? '' : '?'}: ${typeStr};\n`;
}
interfaceStr += '}\n\n';
return interfaceStr;
}
function getTypeFromProperty(prop) {
if (prop.$ref) {
return prop.$ref.split('/').pop() || 'any';
}
switch (prop.type) {
case 'string':
return prop.enum ? prop.enum.map((e) => `'${e}'`).join(' | ') : 'string';
case 'integer':
case 'number':
return 'number';
case 'boolean':
return 'boolean';
case 'array':
const itemType = getTypeFromProperty(prop.items);
return `${itemType}[]`;
default:
return 'any';
}
}
// Save formatted JSON and type definitions
function saveOutput(data) {
// Save processed Swagger JSON
const jsonOutputPath = path_1.default.join(process.cwd(), 'src/swagger_processed.json');
fs_1.default.writeFileSync(jsonOutputPath, JSON.stringify(data, null, 2));
console.log('✅ Processed Swagger JSON saved to:', jsonOutputPath);
// Generate and save type definitions
const typeDefinitions = generateTypeDefinitions(data);
const typesOutputPath = path_1.default.join(process.cwd(), 'src/types.ts');
fs_1.default.writeFileSync(typesOutputPath, typeDefinitions);
console.log('✅ Type definitions generated at:', typesOutputPath);
}
// Main Execution
(async function main() {
const swaggerJson = await fetchSwagger();
const extractedData = extractRelevantData(swaggerJson);
saveOutput(extractedData);
})();