gtfs 4.18.2 → 4.18.4

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