UNPKG

gtfs-realtime

Version:
190 lines (184 loc) 5.26 kB
#!/usr/bin/env node // src/bin/gtfs-realtime.ts import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import PrettyError from "pretty-error"; // src/lib/log-utils.ts import { clearLine, cursorTo } from "node:readline"; import { noop } from "lodash-es"; import * as colors from "yoctocolors"; function log(config) { if (config.verbose === false) { return noop; } if (config.logFunction) { return config.logFunction; } return (text, overwrite) => { if (overwrite === true && process.stdout.isTTY) { clearLine(process.stdout, 0); cursorTo(process.stdout, 0); } else { process.stdout.write("\n"); } process.stdout.write(text); }; } function logError(config) { if (config.logFunction) { return config.logFunction; } return (text) => { process.stdout.write(` ${formatError(text)} `); }; } function formatError(error) { const messageText = error instanceof Error ? error.message : error; const errorMessage = `${colors.underline("Error")}: ${messageText.replace( "Error: ", "" )}`; return colors.red(errorMessage); } // src/lib/gtfs-realtime.ts import { writeFile } from "node:fs/promises"; import GtfsRealtimeBindings from "gtfs-realtime-bindings"; // src/lib/utils.ts import { dirname } from "node:path"; import { access, mkdir } from "node:fs/promises"; import { existsSync } from "node:fs"; import untildify from "untildify"; import { fromPairs } from "lodash-es"; async function prepDirectory(outputPath) { if (!outputPath) { return; } const folderPath = dirname(outputPath); try { await access(folderPath); } catch (error) { try { await mkdir(folderPath, { recursive: true }); } catch (error2) { if (error2?.code === "ENOENT") { throw new Error( `Unable to write to ${folderPath}. Try running this command from a writable directory.` ); } throw error2; } } if (existsSync(outputPath)) { throw new Error(`File already exists: ${outputPath}`); } } var formatHeaders = (headers) => { if (!headers) { return {}; } return fromPairs( headers.map((header) => { const parts = header.split(":"); if (parts.length === 1) { return [parts[0].trim(), ""]; } return [parts[0].trim(), parts.slice(1).join(":").trim()]; }) ); }; var formatFilename = (gtfsRealtimeType, outputPath) => { const isoDate = (/* @__PURE__ */ new Date()).toISOString(); const filepath = outputPath ? untildify(outputPath) : `gtfs-realtime-${gtfsRealtimeType}-${isoDate}.json`; return filepath; }; var determineGtfsRealtimeType = (feed) => { const hasTripUpdate = feed.entity.some((entity) => entity.tripUpdate); if (hasTripUpdate) { return "tripupdate"; } const hasVehiclePosition = feed.entity.some( (entity) => entity.vehicle && entity.vehicle.position ); if (hasVehiclePosition) { return "vehicleposition"; } const hasAlert = feed.entity.some((entity) => entity.alert); if (hasAlert) { return "alert"; } return "unknown"; }; // src/lib/gtfs-realtime.ts var gtfsRealtime = async (config) => { const log2 = log(config); const logError2 = logError(config); log2(`Downloading GTFS-Realtime from ${config.url}`); const response = await fetch(config.url, { headers: formatHeaders(config.header) }); if (!response.ok) { const error = new Error( `${response.url}: ${response.status} ${response.statusText}` ); logError2(error.message); throw error; } const buffer = await response.arrayBuffer(); const feed = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode( new Uint8Array(buffer) ); const gtfsRealtimeType = determineGtfsRealtimeType(feed); const filepath = formatFilename(gtfsRealtimeType, config.output); await prepDirectory(filepath); await writeFile(filepath, JSON.stringify(feed, null, 2)); log2(`GTFS-Realtime saved as JSON to ${filepath} `); }; var gtfs_realtime_default = gtfsRealtime; // src/bin/gtfs-realtime.ts var pe = new PrettyError(); var argv = yargs(hideBin(process.argv)).usage("Usage: $0 <url>").help().version().command( "$0 <url>", "Fetch GTFS-Realtime data and convert to JSON", (yargs2) => { yargs2.positional("url", { describe: "GTFS-Realtime URL", type: "string" }).option("H", { alias: "header", array: true, describe: "HTTP headers to be included in the request for GTFS-Realtime data (optional)", type: "string" }).option("o", { alias: "output", describe: "Path to output file (optional)", type: "string" }).option("s", { alias: "silent", describe: "Hide all output", type: "boolean" }); } ).parseSync(); var handleError = (error = new Error("Unknown Error")) => { process.stdout.write(` ${formatError(error)} `); console.error(pe.render(error)); process.exit(1); }; var setupImport = async () => { if (argv.url === void 0 || argv.url === null) { return handleError(new Error("URL is required")); } const config = { url: argv.url.toString(), ...argv }; await gtfs_realtime_default(config); process.exit(); }; setupImport().catch(handleError); //# sourceMappingURL=gtfs-realtime.js.map