gtfs-to-html 2.12.5 → 2.12.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app/index.js CHANGED
@@ -306,6 +306,30 @@ function formatTripNameForCSV(trip, timetable) {
306
306
  return tripName;
307
307
  }
308
308
 
309
+ // src/lib/errors.ts
310
+ import { GtfsErrorCategory, isGtfsError } from "gtfs";
311
+ var GtfsToHtmlError = class extends Error {
312
+ code;
313
+ category;
314
+ isOperational;
315
+ details;
316
+ constructor(message, options) {
317
+ super(message, { cause: options.cause });
318
+ this.name = "GtfsToHtmlError";
319
+ this.code = options.code;
320
+ this.category = options.category;
321
+ this.isOperational = options.isOperational ?? true;
322
+ this.details = options.details;
323
+ }
324
+ };
325
+ function isGtfsToHtmlError(error) {
326
+ if (!error || typeof error !== "object") {
327
+ return false;
328
+ }
329
+ const candidate = error;
330
+ return candidate.name === "GtfsToHtmlError" && typeof candidate.message === "string" && typeof candidate.code === "string" && typeof candidate.category === "string" && typeof candidate.isOperational === "boolean";
331
+ }
332
+
309
333
  // src/lib/file-utils.ts
310
334
  var homeDirectory = homedir();
311
335
  function getPathToThisModuleFolder() {
@@ -385,7 +409,7 @@ import { featureCollection, round } from "@turf/helpers";
385
409
  import { clearLine, cursorTo } from "readline";
386
410
  import { noop } from "lodash-es";
387
411
  import * as colors from "yoctocolors";
388
- import { getAgencies, getFeedInfo } from "gtfs";
412
+ import { getAgencies, getFeedInfo, isGtfsError as isGtfsError2, formatGtfsError } from "gtfs";
389
413
  import Table from "cli-table";
390
414
  function logWarning(config2) {
391
415
  if (config2.logFunction) {
@@ -480,7 +504,7 @@ function getAgencyGeoJSON(config2) {
480
504
  // package.json
481
505
  var package_default = {
482
506
  name: "gtfs-to-html",
483
- version: "2.12.5",
507
+ version: "2.12.6",
484
508
  private: false,
485
509
  description: "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
486
510
  keywords: [
@@ -537,21 +561,21 @@ var package_default = {
537
561
  archiver: "^7.0.1",
538
562
  "cli-table": "^0.3.11",
539
563
  "css.escape": "^1.5.1",
540
- "csv-stringify": "^6.6.0",
564
+ "csv-stringify": "^6.7.0",
541
565
  express: "^5.2.1",
542
- gtfs: "^4.18.3",
566
+ gtfs: "^4.18.4",
543
567
  "gtfs-realtime-pbf-js-module": "^1.0.0",
544
568
  "js-beautify": "^1.15.4",
545
569
  "lodash-es": "^4.17.23",
546
- "maplibre-gl": "^5.20.1",
547
- marked: "^17.0.4",
570
+ "maplibre-gl": "^5.21.0",
571
+ marked: "^17.0.5",
548
572
  moment: "^2.30.1",
549
573
  pbf: "^4.0.1",
550
574
  "pretty-error": "^4.0.0",
551
575
  pug: "^3.0.4",
552
- puppeteer: "^24.39.1",
553
- "sanitize-filename": "^1.6.3",
554
- "sanitize-html": "^2.17.1",
576
+ puppeteer: "^24.40.0",
577
+ "sanitize-filename": "^1.6.4",
578
+ "sanitize-html": "^2.17.2",
555
579
  sqlstring: "^2.3.3",
556
580
  toposort: "^2.0.2",
557
581
  yargs: "^18.0.0",
@@ -561,10 +585,8 @@ var package_default = {
561
585
  "@types/archiver": "^7.0.0",
562
586
  "@types/cli-table": "^0.3.4",
563
587
  "@types/express": "^5.0.6",
564
- "@types/insane": "^1.0.0",
565
588
  "@types/js-beautify": "^1.14.3",
566
589
  "@types/lodash-es": "^4.17.12",
567
- "@types/morgan": "^1.9.10",
568
590
  "@types/node": "^25",
569
591
  "@types/pug": "^2.0.10",
570
592
  "@types/sanitize-html": "^2.16.1",
@@ -1146,8 +1168,17 @@ var getStopsForTimetable = (timetable, config2) => {
1146
1168
  stop_id: stopId
1147
1169
  });
1148
1170
  if (stops.length === 0) {
1149
- throw new Error(
1150
- `No stop found found for stop_id=${stopId} in timetable_id=${timetable.timetable_id}`
1171
+ throw new GtfsToHtmlError(
1172
+ `No stop found found for stop_id=${stopId} in timetable_id=${timetable.timetable_id}`,
1173
+ {
1174
+ code: "GTFS_TO_HTML_QUERY_RESULT_NOT_FOUND" /* QUERY_RESULT_NOT_FOUND */,
1175
+ category: "query" /* QUERY */,
1176
+ details: {
1177
+ entity: "stop",
1178
+ stopId,
1179
+ timetableId: timetable.timetable_id
1180
+ }
1181
+ }
1151
1182
  );
1152
1183
  }
1153
1184
  const stop = {
@@ -1182,7 +1213,14 @@ var getCalendarsFromConfig = (config2) => {
1182
1213
  const whereClauses = [];
1183
1214
  if (config2.endDate) {
1184
1215
  if (!moment2(config2.endDate).isValid()) {
1185
- throw new Error(`Invalid endDate=${config2.endDate} in config.json`);
1216
+ throw new GtfsToHtmlError(
1217
+ `Invalid endDate=${config2.endDate} in config.json`,
1218
+ {
1219
+ code: "GTFS_TO_HTML_CONFIG_DATE_INVALID" /* CONFIG_DATE_INVALID */,
1220
+ category: "config" /* CONFIG */,
1221
+ details: { field: "endDate", value: config2.endDate }
1222
+ }
1223
+ );
1186
1224
  }
1187
1225
  whereClauses.push(
1188
1226
  `start_date <= ${sqlString.escape(moment2(config2.endDate).format("YYYYMMDD"))}`
@@ -1190,7 +1228,14 @@ var getCalendarsFromConfig = (config2) => {
1190
1228
  }
1191
1229
  if (config2.startDate) {
1192
1230
  if (!moment2(config2.startDate).isValid()) {
1193
- throw new Error(`Invalid startDate=${config2.startDate} in config.json`);
1231
+ throw new GtfsToHtmlError(
1232
+ `Invalid startDate=${config2.startDate} in config.json`,
1233
+ {
1234
+ code: "GTFS_TO_HTML_CONFIG_DATE_INVALID" /* CONFIG_DATE_INVALID */,
1235
+ category: "config" /* CONFIG */,
1236
+ details: { field: "startDate", value: config2.startDate }
1237
+ }
1238
+ );
1194
1239
  }
1195
1240
  whereClauses.push(
1196
1241
  `end_date >= ${sqlString.escape(moment2(config2.startDate).format("YYYYMMDD"))}`
@@ -1215,16 +1260,34 @@ var getCalendarsFromTimetable = (timetable) => {
1215
1260
  const whereClauses = [];
1216
1261
  if (timetable.end_date) {
1217
1262
  if (!moment2(timetable.end_date, "YYYYMMDD", true).isValid()) {
1218
- throw new Error(
1219
- `Invalid end_date=${timetable.end_date} for timetable_id=${timetable.timetable_id}`
1263
+ throw new GtfsToHtmlError(
1264
+ `Invalid end_date=${timetable.end_date} for timetable_id=${timetable.timetable_id}`,
1265
+ {
1266
+ code: "GTFS_TO_HTML_QUERY_INVALID" /* QUERY_INVALID */,
1267
+ category: "validation" /* VALIDATION */,
1268
+ details: {
1269
+ field: "end_date",
1270
+ value: timetable.end_date,
1271
+ timetableId: timetable.timetable_id
1272
+ }
1273
+ }
1220
1274
  );
1221
1275
  }
1222
1276
  whereClauses.push(`start_date <= ${sqlString.escape(timetable.end_date)}`);
1223
1277
  }
1224
1278
  if (timetable.start_date) {
1225
1279
  if (!moment2(timetable.start_date, "YYYYMMDD", true).isValid()) {
1226
- throw new Error(
1227
- `Invalid start_date=${timetable.start_date} for timetable_id=${timetable.timetable_id}`
1280
+ throw new GtfsToHtmlError(
1281
+ `Invalid start_date=${timetable.start_date} for timetable_id=${timetable.timetable_id}`,
1282
+ {
1283
+ code: "GTFS_TO_HTML_QUERY_INVALID" /* QUERY_INVALID */,
1284
+ category: "validation" /* VALIDATION */,
1285
+ details: {
1286
+ field: "start_date",
1287
+ value: timetable.start_date,
1288
+ timetableId: timetable.timetable_id
1289
+ }
1290
+ }
1228
1291
  );
1229
1292
  }
1230
1293
  whereClauses.push(`end_date >= ${sqlString.escape(timetable.start_date)}`);
@@ -1269,7 +1332,11 @@ var getAllStationStopIds = (stopId) => {
1269
1332
  stop_id: stopId
1270
1333
  });
1271
1334
  if (stops.length === 0) {
1272
- throw new Error(`No stop found for stop_id=${stopId}`);
1335
+ throw new GtfsToHtmlError(`No stop found for stop_id=${stopId}`, {
1336
+ code: "GTFS_TO_HTML_QUERY_RESULT_NOT_FOUND" /* QUERY_RESULT_NOT_FOUND */,
1337
+ category: "query" /* QUERY */,
1338
+ details: { entity: "stop", stopId }
1339
+ });
1273
1340
  }
1274
1341
  const stop = stops[0];
1275
1342
  if (isNullOrEmpty(stop.parent_station)) {
@@ -1303,8 +1370,13 @@ var getTripsWithSameBlock = (trip, timetable) => {
1303
1370
  [["stop_sequence", "ASC"]]
1304
1371
  );
1305
1372
  if (stopTimes.length === 0) {
1306
- throw new Error(
1307
- `No stoptimes found found for trip_id=${blockTrip.trip_id}`
1373
+ throw new GtfsToHtmlError(
1374
+ `No stoptimes found found for trip_id=${blockTrip.trip_id}`,
1375
+ {
1376
+ code: "GTFS_TO_HTML_QUERY_RESULT_NOT_FOUND" /* QUERY_RESULT_NOT_FOUND */,
1377
+ category: "query" /* QUERY */,
1378
+ details: { entity: "stoptime", tripId: blockTrip.trip_id }
1379
+ }
1308
1380
  );
1309
1381
  }
1310
1382
  blockTrip.firstStoptime = first(stopTimes);
@@ -1645,8 +1717,18 @@ var getDataForTimetablePageById = (timetablePageId) => {
1645
1717
  );
1646
1718
  const uniqueTripDirections = uniqBy2(trips, (trip) => trip.direction_id);
1647
1719
  if (uniqueTripDirections.length === 0) {
1648
- throw new Error(
1649
- `No trips found for timetable_page_id=${timetablePageId} route_id=${routeId} direction_id=${directionId}`
1720
+ throw new GtfsToHtmlError(
1721
+ `No trips found for timetable_page_id=${timetablePageId} route_id=${routeId} direction_id=${directionId}`,
1722
+ {
1723
+ code: "GTFS_TO_HTML_QUERY_RESULT_NOT_FOUND" /* QUERY_RESULT_NOT_FOUND */,
1724
+ category: "query" /* QUERY */,
1725
+ details: {
1726
+ entity: "trip",
1727
+ timetablePageId,
1728
+ routeId,
1729
+ directionId
1730
+ }
1731
+ }
1650
1732
  );
1651
1733
  }
1652
1734
  if (/^[01]*$/.test(calendarCode ?? "")) {
@@ -1676,8 +1758,13 @@ var getTimetablePageById = (timetablePageId, config2) => {
1676
1758
  getTimetables()
1677
1759
  );
1678
1760
  if (timetablePages.length > 1) {
1679
- throw new Error(
1680
- `Multiple timetable_pages found for timetable_page_id=${timetablePageId}`
1761
+ throw new GtfsToHtmlError(
1762
+ `Multiple timetable_pages found for timetable_page_id=${timetablePageId}`,
1763
+ {
1764
+ code: "GTFS_TO_HTML_QUERY_RESULT_AMBIGUOUS" /* QUERY_RESULT_AMBIGUOUS */,
1765
+ category: "query" /* QUERY */,
1766
+ details: { entity: "timetable_page", timetablePageId }
1767
+ }
1681
1768
  );
1682
1769
  }
1683
1770
  if (timetablePages.length === 1) {
@@ -1700,8 +1787,13 @@ var getTimetablePageById = (timetablePageId, config2) => {
1700
1787
  (timetable2) => timetable2.timetable_id === timetablePageId
1701
1788
  );
1702
1789
  if (timetablePageTimetables.length === 0) {
1703
- throw new Error(
1704
- `No timetable found for timetable_page_id=${timetablePageId}`
1790
+ throw new GtfsToHtmlError(
1791
+ `No timetable found for timetable_page_id=${timetablePageId}`,
1792
+ {
1793
+ code: "GTFS_TO_HTML_QUERY_RESULT_NOT_FOUND" /* QUERY_RESULT_NOT_FOUND */,
1794
+ category: "query" /* QUERY */,
1795
+ details: { entity: "timetable", timetablePageId }
1796
+ }
1705
1797
  );
1706
1798
  }
1707
1799
  return createTimetablePage({
@@ -1715,8 +1807,13 @@ var getTimetablePageById = (timetablePageId, config2) => {
1715
1807
  route_short_name: timetablePageId.split("_")[1]
1716
1808
  });
1717
1809
  if (routes.length === 0) {
1718
- throw new Error(
1719
- `No route found for timetable_page_id=${timetablePageId}`
1810
+ throw new GtfsToHtmlError(
1811
+ `No route found for timetable_page_id=${timetablePageId}`,
1812
+ {
1813
+ code: "GTFS_TO_HTML_QUERY_RESULT_NOT_FOUND" /* QUERY_RESULT_NOT_FOUND */,
1814
+ category: "query" /* QUERY */,
1815
+ details: { entity: "route", timetablePageId }
1816
+ }
1720
1817
  );
1721
1818
  }
1722
1819
  const { calendars: calendars2, calendarDates: calendarDates2 } = getCalendarsFromConfig(config2);
@@ -1907,7 +2004,11 @@ function generateTimetableHTML(timetablePage, config2) {
1907
2004
  function generateOverviewHTML(timetablePages, config2) {
1908
2005
  const agencies = getAgencies2();
1909
2006
  if (agencies.length === 0) {
1910
- throw new Error("No agencies found");
2007
+ throw new GtfsToHtmlError("No agencies found", {
2008
+ code: "GTFS_TO_HTML_QUERY_RESULT_NOT_FOUND" /* QUERY_RESULT_NOT_FOUND */,
2009
+ category: "query" /* QUERY */,
2010
+ details: { entity: "agency" }
2011
+ });
1911
2012
  }
1912
2013
  const geojson = config2.showMap ? getAgencyGeoJSON(config2) : void 0;
1913
2014
  const templateVars = {
@@ -2287,7 +2388,15 @@ try {
2287
2388
  console.error(
2288
2389
  `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists and run gtfs-to-html to import GTFS before running this app.`
2289
2390
  );
2290
- throw error;
2391
+ throw new GtfsToHtmlError(
2392
+ `Unable to open sqlite database "${config.sqlitePath}"`,
2393
+ {
2394
+ code: "GTFS_TO_HTML_DATABASE_OPEN_FAILED" /* DATABASE_OPEN_FAILED */,
2395
+ category: "database" /* DATABASE */,
2396
+ details: { sqlitePath: config.sqlitePath, dbCode: error?.code },
2397
+ cause: error
2398
+ }
2399
+ );
2291
2400
  }
2292
2401
  app.set("views", getPathToViewsFolder(config));
2293
2402
  app.set("view engine", "pug");
@@ -2371,7 +2480,7 @@ app.get("/timetables/:timetablePageId", async (req, res, next) => {
2371
2480
  const html = await generateTimetableHTML(timetablePage, config);
2372
2481
  res.send(html);
2373
2482
  } catch (error) {
2374
- if (error?.message.startsWith("No timetable found")) {
2483
+ if (isGtfsToHtmlError(error) && error.code === "GTFS_TO_HTML_QUERY_RESULT_NOT_FOUND" /* QUERY_RESULT_NOT_FOUND */) {
2375
2484
  res.status(404).send("Timetable page not found");
2376
2485
  return;
2377
2486
  }