@drfrost/bods-js
Version:
JavaScript client for the UK's Bus Open Data Service (BODS) API
208 lines (203 loc) ⢠6.76 kB
JavaScript
/**
* BODS CLI Tool
*
* Simple command-line interface for testing BODS API functionality
*
* Usage:
* bun run cli.ts --help
* bun run cli.ts timetables --noc SCMN
* bun run cli.ts fares --area manchester
* bun run cli.ts vehicles --operator SCMN
* bun run cli.ts disruptions
*/
import { BODSClient, UK_CITIES } from './index.js';
const API_KEY = process.env.BODS_API_KEY;
if (!API_KEY) {
console.error('ā Please set BODS_API_KEY environment variable');
console.log('Example: BODS_API_KEY=your-api-key bun run cli.ts --help');
process.exit(1);
}
const client = new BODSClient({ apiKey: API_KEY });
function showHelp() {
console.log(`
š BODS CLI Tool
Usage: bun run cli.ts <command> [options]
Commands:
timetables Search timetables
fares Search fares
vehicles Get vehicle locations
disruptions Get service disruptions
test Test API connectivity
Options:
--noc <codes> National Operator Codes (comma-separated)
--area <city> Predefined city area (manchester, london, etc.)
--bbox <coords> Custom bounding box (minLng,minLat,maxLng,maxLat)
--line <ref> Line reference
--limit <num> Limit results (default: 10)
--help Show this help
Examples:
bun run cli.ts timetables --noc SCMN --limit 5
bun run cli.ts fares --area manchester
bun run cli.ts vehicles --noc SCMN --area liverpool
bun run cli.ts disruptions
`);
}
async function handleTimetables(args) {
console.log('š Searching timetables...\n');
const params = {
limit: parseInt(args.limit) || 10
};
if (args.noc) {
params.noc = args.noc.split(',');
}
try {
const result = await client.timetables.search(params);
console.log(`Found ${result.count} timetables (showing ${result.results.length}):\n`);
result.results.forEach((timetable, i) => {
console.log(`${i + 1}. ${timetable.operatorName}`);
console.log(` Name: ${timetable.name}`);
console.log(` Lines: ${timetable.lines.join(', ')}`);
console.log(` Quality: ${timetable.dqRag} (${timetable.dqScore})`);
console.log(` Status: ${timetable.status}`);
console.log('');
});
}
catch (error) {
console.error('ā Error:', error);
}
}
async function handleFares(args) {
console.log('š Searching fares...\n');
const params = {
limit: parseInt(args.limit) || 10
};
if (args.noc) {
params.noc = args.noc.split(',');
}
if (args.area && args.area.toUpperCase() in UK_CITIES) {
params.boundingBox = UK_CITIES[args.area.toUpperCase()];
}
else if (args.bbox) {
params.boundingBox = args.bbox.split(',').map(Number);
}
try {
const result = await client.fares.search(params);
console.log(`Found ${result.count} fare datasets (showing ${result.results.length}):\n`);
result.results.forEach((fare, i) => {
console.log(`${i + 1}. ${fare.operatorName}`);
console.log(` Name: ${fare.name}`);
console.log(` Fare zones: ${fare.numOfFareZones}`);
console.log(` Products: ${fare.numOfFareProducts}`);
console.log(` Status: ${fare.status}`);
console.log('');
});
}
catch (error) {
console.error('ā Error:', error);
}
}
async function handleVehicles(args) {
console.log('š Getting vehicle locations...\n');
const params = {};
if (args.noc) {
params.operatorRef = args.noc.split(',');
}
if (args.line) {
params.lineRef = args.line;
}
if (args.area && args.area.toUpperCase() in UK_CITIES) {
params.boundingBox = UK_CITIES[args.area.toUpperCase()];
}
else if (args.bbox) {
params.boundingBox = args.bbox.split(',').map(Number);
}
try {
const result = await client.avl.getSIRIVM(params);
console.log(`SIRI-VM Data received: ${result.xmlData.length} characters`);
console.log(`First 500 characters:\n${result.xmlData.substring(0, 500)}...`);
}
catch (error) {
console.error('ā Error:', error);
}
}
async function handleDisruptions() {
console.log('š Getting service disruptions...\n');
try {
const parsed = await client.disruptions.getCurrentParsed();
console.log(`Found ${parsed.length} disruptions:\n`);
parsed.forEach((disruption, i) => {
console.log(`${i + 1}. ${disruption.planned ? '[PLANNED]' : '[UNPLANNED]'} ${disruption.participantRef}`);
console.log(` Summary: ${disruption.summary}`);
console.log(` Severity: ${disruption.severity}`);
if (disruption.startTime) {
console.log(` Start: ${new Date(disruption.startTime).toLocaleString()}`);
}
console.log('');
});
}
catch (error) {
console.error('ā Error:', error);
}
}
async function handleTest() {
console.log('š Testing API connectivity...\n');
try {
const isConnected = await client.testConnection();
console.log(`API Status: ${isConnected ? 'ā
Connected' : 'ā Failed'}`);
if (isConnected) {
const config = client.getConfig();
console.log(`Base URL: ${config.baseUrl}`);
console.log(`Timeout: ${config.timeout}ms`);
}
}
catch (error) {
console.error('ā Error:', error);
}
}
function parseArgs() {
const argv = process.argv.slice(2);
const command = argv[0] || 'help';
const args = {};
for (let i = 1; i < argv.length; i += 2) {
const arg = argv[i];
if (arg?.startsWith('--')) {
const key = arg.substring(2);
const value = argv[i + 1];
args[key] = value;
}
}
return { command, args };
}
async function main() {
const { command, args } = parseArgs();
if (command === 'help' || args.help) {
showHelp();
return;
}
console.log('š BODS CLI Tool\n');
switch (command) {
case 'timetables':
await handleTimetables(args);
break;
case 'fares':
await handleFares(args);
break;
case 'vehicles':
await handleVehicles(args);
break;
case 'disruptions':
await handleDisruptions();
break;
case 'test':
await handleTest();
break;
default:
console.error(`ā Unknown command: ${command}`);
showHelp();
process.exit(1);
}
}
if (import.meta.main) {
main().catch(console.error);
}