gtfs 4.18.3 → 4.18.5

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.
@@ -96,7 +96,8 @@ async function getConfig(argv2) {
96
96
  } catch (error) {
97
97
  if (error instanceof SyntaxError) {
98
98
  throw new Error(
99
- `Cannot parse configuration file. Check to ensure that it is valid JSON. Error: ${error.message}`
99
+ `Cannot parse configuration file. Check to ensure that it is valid JSON. Error: ${error.message}`,
100
+ { cause: error }
100
101
  );
101
102
  }
102
103
  throw error;
@@ -583,6 +584,61 @@ var serviceAlertInformedEntities = {
583
584
 
584
585
  // src/lib/db.ts
585
586
  import Database from "better-sqlite3";
587
+
588
+ // src/lib/errors.ts
589
+ var GtfsError = class extends Error {
590
+ code;
591
+ category;
592
+ isOperational;
593
+ statusCode;
594
+ details;
595
+ constructor(message, options) {
596
+ super(message, { cause: options.cause });
597
+ this.name = "GtfsError";
598
+ this.code = options.code;
599
+ this.category = options.category;
600
+ this.isOperational = options.isOperational ?? true;
601
+ this.statusCode = options.statusCode;
602
+ this.details = options.details;
603
+ }
604
+ };
605
+ function isGtfsError(error) {
606
+ if (!error || typeof error !== "object") {
607
+ return false;
608
+ }
609
+ const candidate = error;
610
+ return candidate.name === "GtfsError" && typeof candidate.message === "string" && typeof candidate.code === "string" && typeof candidate.category === "string" && typeof candidate.isOperational === "boolean";
611
+ }
612
+ function toGtfsError(error, fallback) {
613
+ if (isGtfsError(error)) {
614
+ return error;
615
+ }
616
+ return new GtfsError(fallback.message, {
617
+ ...fallback,
618
+ cause: error
619
+ });
620
+ }
621
+ function addImportError(report, error) {
622
+ report.errors.push(error);
623
+ report.errorCountsByCode[error.code] = (report.errorCountsByCode[error.code] ?? 0) + 1;
624
+ }
625
+ function formatGtfsError(error, options = { verbosity: "developer" }) {
626
+ if (!isGtfsError(error)) {
627
+ const message = error instanceof Error ? error.message : String(error);
628
+ return options.verbosity === "user" ? message : `UNKNOWN_ERROR: ${message}`;
629
+ }
630
+ if (options.verbosity === "user") {
631
+ return error.message;
632
+ }
633
+ return [
634
+ `${error.code}: ${error.message}`,
635
+ `category=${error.category}`,
636
+ error.statusCode !== void 0 ? `statusCode=${error.statusCode}` : null,
637
+ error.details ? `details=${JSON.stringify(error.details)}` : null
638
+ ].filter(Boolean).join(" | ");
639
+ }
640
+
641
+ // src/lib/db.ts
586
642
  var dbs = {};
587
643
  function setupDb(sqlitePath) {
588
644
  const db = new Database(untildify(sqlitePath));
@@ -611,11 +667,19 @@ function openDb(config = null) {
611
667
  return dbs[filename];
612
668
  }
613
669
  if (Object.keys(dbs).length > 1) {
614
- throw new Error(
615
- "Multiple databases open, please specify which one to use."
670
+ throw new GtfsError(
671
+ "Multiple databases open, please specify which one to use.",
672
+ {
673
+ code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
674
+ category: "database" /* DATABASE */,
675
+ details: { openDatabaseCount: Object.keys(dbs).length }
676
+ }
616
677
  );
617
678
  }
618
- throw new Error("Unable to find database connection.");
679
+ throw new GtfsError("Unable to find database connection.", {
680
+ code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
681
+ category: "database" /* DATABASE */
682
+ });
619
683
  }
620
684
 
621
685
  // src/lib/geojson-utils.ts
@@ -641,12 +705,21 @@ import sqlString from "sqlstring-sqlite";
641
705
  import Long from "long";
642
706
  function validateConfigForImport(config) {
643
707
  if (!config.agencies || config.agencies.length === 0) {
644
- throw new Error("No `agencies` specified in config");
708
+ throw new GtfsError("No `agencies` specified in config", {
709
+ code: "GTFS_CONFIG_INVALID" /* GTFS_CONFIG_INVALID */,
710
+ category: "config" /* CONFIG */,
711
+ details: { field: "agencies" }
712
+ });
645
713
  }
646
714
  for (const [index, agency2] of config.agencies.entries()) {
647
715
  if (!agency2.path && !agency2.url) {
648
- throw new Error(
649
- `No Agency \`url\` or \`path\` specified in config for agency index ${index}.`
716
+ throw new GtfsError(
717
+ `No Agency \`url\` or \`path\` specified in config for agency index ${index}.`,
718
+ {
719
+ code: "GTFS_CONFIG_INVALID" /* GTFS_CONFIG_INVALID */,
720
+ category: "config" /* CONFIG */,
721
+ details: { agencyIndex: index }
722
+ }
650
723
  );
651
724
  }
652
725
  }
@@ -743,7 +816,16 @@ async function fetchGtfsRealtimeData(type, task) {
743
816
  signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
744
817
  });
745
818
  if (response.status !== 200) {
746
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
819
+ throw new GtfsError(`HTTP ${response.status}: ${response.statusText}`, {
820
+ code: "GTFS_DOWNLOAD_HTTP" /* GTFS_DOWNLOAD_HTTP */,
821
+ category: "download" /* DOWNLOAD */,
822
+ statusCode: response.status,
823
+ details: {
824
+ url: urlConfig.url,
825
+ status: response.status,
826
+ statusText: response.statusText
827
+ }
828
+ });
747
829
  }
748
830
  const buffer = await response.arrayBuffer();
749
831
  const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
@@ -760,17 +842,27 @@ async function fetchGtfsRealtimeData(type, task) {
760
842
  });
761
843
  return feedMessage;
762
844
  } catch (error) {
763
- const errorMessage = error instanceof Error ? error.message : String(error);
845
+ const gtfsError = toGtfsError(error, {
846
+ message: error instanceof Error ? error.message : String(error),
847
+ code: "GTFS_DOWNLOAD_FAILED" /* GTFS_DOWNLOAD_FAILED */,
848
+ category: "download" /* DOWNLOAD */,
849
+ details: { type, url: urlConfig.url }
850
+ });
764
851
  if (attempt === MAX_RETRIES) {
765
852
  if (task.ignoreErrors) {
766
853
  task.logError(
767
- `Failed to fetch ${type} after ${MAX_RETRIES} attempts: ${errorMessage}`
854
+ `Failed to fetch ${type} after ${MAX_RETRIES} attempts: ${gtfsError.message}`
768
855
  );
856
+ if (task.report) {
857
+ addImportError(task.report, gtfsError);
858
+ }
769
859
  return null;
770
860
  }
771
- throw error;
861
+ throw gtfsError;
772
862
  }
773
- task.logWarning(`Attempt ${attempt} failed for ${type}: ${errorMessage}`);
863
+ task.logWarning(
864
+ `Attempt ${attempt} failed for ${type}: ${gtfsError.message}`
865
+ );
774
866
  await new Promise(
775
867
  (resolve) => setTimeout(resolve, RETRY_DELAY * attempt)
776
868
  );
@@ -977,8 +1069,9 @@ async function updateGtfsRealtime(initialConfig) {
977
1069
  );
978
1070
  removeExpiredRealtimeData(config);
979
1071
  await mapSeries(config.agencies, async (agency2) => {
1072
+ let task;
980
1073
  try {
981
- const task = {
1074
+ task = {
982
1075
  realtimeAlerts: agency2.realtimeAlerts,
983
1076
  realtimeTripUpdates: agency2.realtimeTripUpdates,
984
1077
  realtimeVehiclePositions: agency2.realtimeVehiclePositions,
@@ -994,11 +1087,19 @@ async function updateGtfsRealtime(initialConfig) {
994
1087
  };
995
1088
  await updateGtfsRealtimeData(task);
996
1089
  } catch (error) {
997
- const errorMessage = error instanceof Error ? error.message : String(error);
1090
+ const gtfsError = toGtfsError(error, {
1091
+ message: error instanceof Error ? error.message : String(error),
1092
+ code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
1093
+ category: "database" /* DATABASE */,
1094
+ details: { sqlitePath: task?.sqlitePath ?? config.sqlitePath }
1095
+ });
998
1096
  if (config.ignoreErrors) {
999
- logError(config)(errorMessage);
1097
+ logError(config)(formatGtfsError(gtfsError));
1098
+ if (task?.report) {
1099
+ addImportError(task.report, gtfsError);
1100
+ }
1000
1101
  } else {
1001
- throw error;
1102
+ throw gtfsError;
1002
1103
  }
1003
1104
  }
1004
1105
  });
@@ -1012,11 +1113,26 @@ async function updateGtfsRealtime(initialConfig) {
1012
1113
  );
1013
1114
  } catch (error) {
1014
1115
  if (error.code === "SQLITE_CANTOPEN") {
1015
- logError(config)(
1016
- `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
1116
+ const dbOpenError = new GtfsError(
1117
+ `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`,
1118
+ {
1119
+ code: "DB_OPEN_FAILED" /* DB_OPEN_FAILED */,
1120
+ category: "database" /* DATABASE */,
1121
+ details: {
1122
+ sqlitePath: config.sqlitePath,
1123
+ dbCode: error.code
1124
+ },
1125
+ cause: error
1126
+ }
1017
1127
  );
1128
+ logError(config)(dbOpenError.message);
1129
+ throw dbOpenError;
1018
1130
  }
1019
- throw error;
1131
+ throw toGtfsError(error, {
1132
+ message: error instanceof Error ? error.message : String(error),
1133
+ code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
1134
+ category: "database" /* DATABASE */
1135
+ });
1020
1136
  }
1021
1137
  }
1022
1138