gtfs 4.18.3 → 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.
- package/dist/bin/gtfs-export.js +31 -3
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +377 -72
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +133 -18
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.d.ts +73 -1
- package/dist/index.js +499 -91
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: ${
|
|
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
|
|
860
|
+
throw gtfsError;
|
|
772
861
|
}
|
|
773
|
-
task.logWarning(
|
|
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
|
-
|
|
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
|
|
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)(
|
|
1096
|
+
logError(config)(formatGtfsError(gtfsError));
|
|
1097
|
+
if (task?.report) {
|
|
1098
|
+
addImportError(task.report, gtfsError);
|
|
1099
|
+
}
|
|
1000
1100
|
} else {
|
|
1001
|
-
throw
|
|
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
|
-
|
|
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
|
|