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.
- package/dist/bin/gtfs-export.js +33 -4
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +381 -74
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +135 -19
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.d.ts +73 -1
- package/dist/index.js +501 -92
- package/dist/index.js.map +1 -1
- package/package.json +15 -15
package/dist/index.js
CHANGED
|
@@ -4064,7 +4064,8 @@ async function unzip(zipfilePath, exportPath) {
|
|
|
4064
4064
|
await zip.close();
|
|
4065
4065
|
} catch (error) {
|
|
4066
4066
|
throw new Error(
|
|
4067
|
-
`Failed to extract zip file: ${error instanceof Error ? error.message : "Unknown error"}
|
|
4067
|
+
`Failed to extract zip file: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
4068
|
+
{ cause: error }
|
|
4068
4069
|
);
|
|
4069
4070
|
}
|
|
4070
4071
|
}
|
|
@@ -4078,6 +4079,104 @@ function untildify(pathWithTilde) {
|
|
|
4078
4079
|
return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
|
|
4079
4080
|
}
|
|
4080
4081
|
|
|
4082
|
+
// src/lib/errors.ts
|
|
4083
|
+
var GtfsErrorCategory = /* @__PURE__ */ ((GtfsErrorCategory2) => {
|
|
4084
|
+
GtfsErrorCategory2["CONFIG"] = "config";
|
|
4085
|
+
GtfsErrorCategory2["DOWNLOAD"] = "download";
|
|
4086
|
+
GtfsErrorCategory2["ZIP"] = "zip";
|
|
4087
|
+
GtfsErrorCategory2["VALIDATION"] = "validation";
|
|
4088
|
+
GtfsErrorCategory2["DATABASE"] = "database";
|
|
4089
|
+
GtfsErrorCategory2["PARSE"] = "parse";
|
|
4090
|
+
GtfsErrorCategory2["QUERY"] = "query";
|
|
4091
|
+
GtfsErrorCategory2["INTERNAL"] = "internal";
|
|
4092
|
+
return GtfsErrorCategory2;
|
|
4093
|
+
})(GtfsErrorCategory || {});
|
|
4094
|
+
var GtfsErrorCode = /* @__PURE__ */ ((GtfsErrorCode2) => {
|
|
4095
|
+
GtfsErrorCode2["GTFS_DOWNLOAD_HTTP"] = "GTFS_DOWNLOAD_HTTP";
|
|
4096
|
+
GtfsErrorCode2["GTFS_DOWNLOAD_FAILED"] = "GTFS_DOWNLOAD_FAILED";
|
|
4097
|
+
GtfsErrorCode2["GTFS_ZIP_INVALID"] = "GTFS_ZIP_INVALID";
|
|
4098
|
+
GtfsErrorCode2["GTFS_REQUIRED_FIELD_MISSING"] = "GTFS_REQUIRED_FIELD_MISSING";
|
|
4099
|
+
GtfsErrorCode2["GTFS_INVALID_DATE"] = "GTFS_INVALID_DATE";
|
|
4100
|
+
GtfsErrorCode2["GTFS_CONFIG_INVALID"] = "GTFS_CONFIG_INVALID";
|
|
4101
|
+
GtfsErrorCode2["DB_OPEN_FAILED"] = "DB_OPEN_FAILED";
|
|
4102
|
+
GtfsErrorCode2["GTFS_DB_OPERATION_FAILED"] = "GTFS_DB_OPERATION_FAILED";
|
|
4103
|
+
GtfsErrorCode2["GTFS_JSON_INVALID"] = "GTFS_JSON_INVALID";
|
|
4104
|
+
GtfsErrorCode2["GTFS_UNSUPPORTED_FILE_TYPE"] = "GTFS_UNSUPPORTED_FILE_TYPE";
|
|
4105
|
+
GtfsErrorCode2["GTFS_CSV_PARSE_FAILED"] = "GTFS_CSV_PARSE_FAILED";
|
|
4106
|
+
GtfsErrorCode2["GTFS_QUERY_INVALID"] = "GTFS_QUERY_INVALID";
|
|
4107
|
+
return GtfsErrorCode2;
|
|
4108
|
+
})(GtfsErrorCode || {});
|
|
4109
|
+
var GtfsWarningCode = /* @__PURE__ */ ((GtfsWarningCode2) => {
|
|
4110
|
+
GtfsWarningCode2["GTFS_DUPLICATE_PRIMARY_KEY"] = "GTFS_DUPLICATE_PRIMARY_KEY";
|
|
4111
|
+
return GtfsWarningCode2;
|
|
4112
|
+
})(GtfsWarningCode || {});
|
|
4113
|
+
var GtfsError = class extends Error {
|
|
4114
|
+
code;
|
|
4115
|
+
category;
|
|
4116
|
+
isOperational;
|
|
4117
|
+
statusCode;
|
|
4118
|
+
details;
|
|
4119
|
+
constructor(message, options) {
|
|
4120
|
+
super(message, { cause: options.cause });
|
|
4121
|
+
this.name = "GtfsError";
|
|
4122
|
+
this.code = options.code;
|
|
4123
|
+
this.category = options.category;
|
|
4124
|
+
this.isOperational = options.isOperational ?? true;
|
|
4125
|
+
this.statusCode = options.statusCode;
|
|
4126
|
+
this.details = options.details;
|
|
4127
|
+
}
|
|
4128
|
+
};
|
|
4129
|
+
function isGtfsError(error) {
|
|
4130
|
+
if (!error || typeof error !== "object") {
|
|
4131
|
+
return false;
|
|
4132
|
+
}
|
|
4133
|
+
const candidate = error;
|
|
4134
|
+
return candidate.name === "GtfsError" && typeof candidate.message === "string" && typeof candidate.code === "string" && typeof candidate.category === "string" && typeof candidate.isOperational === "boolean";
|
|
4135
|
+
}
|
|
4136
|
+
function isGtfsValidationError(error) {
|
|
4137
|
+
return isGtfsError(error) && error.category === "validation" /* VALIDATION */;
|
|
4138
|
+
}
|
|
4139
|
+
function toGtfsError(error, fallback) {
|
|
4140
|
+
if (isGtfsError(error)) {
|
|
4141
|
+
return error;
|
|
4142
|
+
}
|
|
4143
|
+
return new GtfsError(fallback.message, {
|
|
4144
|
+
...fallback,
|
|
4145
|
+
cause: error
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
function createImportReport() {
|
|
4149
|
+
return {
|
|
4150
|
+
errors: [],
|
|
4151
|
+
warnings: [],
|
|
4152
|
+
errorCountsByCode: {},
|
|
4153
|
+
warningCountsByCode: {}
|
|
4154
|
+
};
|
|
4155
|
+
}
|
|
4156
|
+
function addImportError(report, error) {
|
|
4157
|
+
report.errors.push(error);
|
|
4158
|
+
report.errorCountsByCode[error.code] = (report.errorCountsByCode[error.code] ?? 0) + 1;
|
|
4159
|
+
}
|
|
4160
|
+
function addImportWarning(report, warning) {
|
|
4161
|
+
report.warnings.push(warning);
|
|
4162
|
+
report.warningCountsByCode[warning.code] = (report.warningCountsByCode[warning.code] ?? 0) + 1;
|
|
4163
|
+
}
|
|
4164
|
+
function formatGtfsError(error, options = { verbosity: "developer" }) {
|
|
4165
|
+
if (!isGtfsError(error)) {
|
|
4166
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4167
|
+
return options.verbosity === "user" ? message : `UNKNOWN_ERROR: ${message}`;
|
|
4168
|
+
}
|
|
4169
|
+
if (options.verbosity === "user") {
|
|
4170
|
+
return error.message;
|
|
4171
|
+
}
|
|
4172
|
+
return [
|
|
4173
|
+
`${error.code}: ${error.message}`,
|
|
4174
|
+
`category=${error.category}`,
|
|
4175
|
+
error.statusCode !== void 0 ? `statusCode=${error.statusCode}` : null,
|
|
4176
|
+
error.details ? `details=${JSON.stringify(error.details)}` : null
|
|
4177
|
+
].filter(Boolean).join(" | ");
|
|
4178
|
+
}
|
|
4179
|
+
|
|
4081
4180
|
// src/lib/db.ts
|
|
4082
4181
|
var dbs = {};
|
|
4083
4182
|
function setupDb(sqlitePath) {
|
|
@@ -4107,22 +4206,39 @@ function openDb(config = null) {
|
|
|
4107
4206
|
return dbs[filename];
|
|
4108
4207
|
}
|
|
4109
4208
|
if (Object.keys(dbs).length > 1) {
|
|
4110
|
-
throw new
|
|
4111
|
-
"Multiple databases open, please specify which one to use."
|
|
4209
|
+
throw new GtfsError(
|
|
4210
|
+
"Multiple databases open, please specify which one to use.",
|
|
4211
|
+
{
|
|
4212
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
4213
|
+
category: "database" /* DATABASE */,
|
|
4214
|
+
details: { openDatabaseCount: Object.keys(dbs).length }
|
|
4215
|
+
}
|
|
4112
4216
|
);
|
|
4113
4217
|
}
|
|
4114
|
-
throw new
|
|
4218
|
+
throw new GtfsError("Unable to find database connection.", {
|
|
4219
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
4220
|
+
category: "database" /* DATABASE */
|
|
4221
|
+
});
|
|
4115
4222
|
}
|
|
4116
4223
|
function closeDb(db = null) {
|
|
4117
4224
|
if (Object.keys(dbs).length === 0) {
|
|
4118
|
-
throw new
|
|
4119
|
-
"No database connection. Call `openDb(config)` before using any methods."
|
|
4225
|
+
throw new GtfsError(
|
|
4226
|
+
"No database connection. Call `openDb(config)` before using any methods.",
|
|
4227
|
+
{
|
|
4228
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
4229
|
+
category: "database" /* DATABASE */
|
|
4230
|
+
}
|
|
4120
4231
|
);
|
|
4121
4232
|
}
|
|
4122
4233
|
if (!db) {
|
|
4123
4234
|
if (Object.keys(dbs).length > 1) {
|
|
4124
|
-
throw new
|
|
4125
|
-
"Multiple database connections. Pass the db you want to close as a parameter to `closeDb`."
|
|
4235
|
+
throw new GtfsError(
|
|
4236
|
+
"Multiple database connections. Pass the db you want to close as a parameter to `closeDb`.",
|
|
4237
|
+
{
|
|
4238
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
4239
|
+
category: "database" /* DATABASE */,
|
|
4240
|
+
details: { openDatabaseCount: Object.keys(dbs).length }
|
|
4241
|
+
}
|
|
4126
4242
|
);
|
|
4127
4243
|
}
|
|
4128
4244
|
db = dbs[Object.keys(dbs)[0]];
|
|
@@ -4132,14 +4248,23 @@ function closeDb(db = null) {
|
|
|
4132
4248
|
}
|
|
4133
4249
|
function deleteDb(db = null) {
|
|
4134
4250
|
if (Object.keys(dbs).length === 0) {
|
|
4135
|
-
throw new
|
|
4136
|
-
"No database connection. Call `openDb(config)` before using any methods."
|
|
4251
|
+
throw new GtfsError(
|
|
4252
|
+
"No database connection. Call `openDb(config)` before using any methods.",
|
|
4253
|
+
{
|
|
4254
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
4255
|
+
category: "database" /* DATABASE */
|
|
4256
|
+
}
|
|
4137
4257
|
);
|
|
4138
4258
|
}
|
|
4139
4259
|
if (!db) {
|
|
4140
4260
|
if (Object.keys(dbs).length > 1) {
|
|
4141
|
-
throw new
|
|
4142
|
-
"Multiple database connections. Pass the db you want to delete as a parameter to `deleteDb`."
|
|
4261
|
+
throw new GtfsError(
|
|
4262
|
+
"Multiple database connections. Pass the db you want to delete as a parameter to `deleteDb`.",
|
|
4263
|
+
{
|
|
4264
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
4265
|
+
category: "database" /* DATABASE */,
|
|
4266
|
+
details: { openDatabaseCount: Object.keys(dbs).length }
|
|
4267
|
+
}
|
|
4143
4268
|
);
|
|
4144
4269
|
}
|
|
4145
4270
|
db = dbs[Object.keys(dbs)[0]];
|
|
@@ -4278,12 +4403,21 @@ import sqlString from "sqlstring-sqlite";
|
|
|
4278
4403
|
import Long from "long";
|
|
4279
4404
|
function validateConfigForImport(config) {
|
|
4280
4405
|
if (!config.agencies || config.agencies.length === 0) {
|
|
4281
|
-
throw new
|
|
4406
|
+
throw new GtfsError("No `agencies` specified in config", {
|
|
4407
|
+
code: "GTFS_CONFIG_INVALID" /* GTFS_CONFIG_INVALID */,
|
|
4408
|
+
category: "config" /* CONFIG */,
|
|
4409
|
+
details: { field: "agencies" }
|
|
4410
|
+
});
|
|
4282
4411
|
}
|
|
4283
4412
|
for (const [index, agency2] of config.agencies.entries()) {
|
|
4284
4413
|
if (!agency2.path && !agency2.url) {
|
|
4285
|
-
throw new
|
|
4286
|
-
`No Agency \`url\` or \`path\` specified in config for agency index ${index}
|
|
4414
|
+
throw new GtfsError(
|
|
4415
|
+
`No Agency \`url\` or \`path\` specified in config for agency index ${index}.`,
|
|
4416
|
+
{
|
|
4417
|
+
code: "GTFS_CONFIG_INVALID" /* GTFS_CONFIG_INVALID */,
|
|
4418
|
+
category: "config" /* CONFIG */,
|
|
4419
|
+
details: { agencyIndex: index }
|
|
4420
|
+
}
|
|
4287
4421
|
);
|
|
4288
4422
|
}
|
|
4289
4423
|
}
|
|
@@ -4354,7 +4488,11 @@ function formatWhereClauseBoundingBox(latitudeDegree, longitudeDegree, boundingB
|
|
|
4354
4488
|
const lat = Number(latitudeDegree);
|
|
4355
4489
|
const lon = Number(longitudeDegree);
|
|
4356
4490
|
if (isNaN(lat) || isNaN(lon) || lat < -90 || lat > 90 || lon < -180 || lon > 180) {
|
|
4357
|
-
throw new
|
|
4491
|
+
throw new GtfsError("Invalid latitude or longitude values", {
|
|
4492
|
+
code: "GTFS_QUERY_INVALID" /* GTFS_QUERY_INVALID */,
|
|
4493
|
+
category: "query" /* QUERY */,
|
|
4494
|
+
details: { latitudeDegree, longitudeDegree, boundingBoxSideMeters }
|
|
4495
|
+
});
|
|
4358
4496
|
}
|
|
4359
4497
|
const latitudeRadian = degree2radian(lat);
|
|
4360
4498
|
const radiusFromLatitude = Math.cos(latitudeRadian) * EARTH_RADIUS_METERS;
|
|
@@ -4410,14 +4548,22 @@ function getDayOfWeekFromDate(date) {
|
|
|
4410
4548
|
"saturday"
|
|
4411
4549
|
];
|
|
4412
4550
|
if (!Number.isInteger(date) || date.toString().length !== 8) {
|
|
4413
|
-
throw new
|
|
4551
|
+
throw new GtfsError("Date must be in YYYYMMDD format", {
|
|
4552
|
+
code: "GTFS_INVALID_DATE" /* GTFS_INVALID_DATE */,
|
|
4553
|
+
category: "validation" /* VALIDATION */,
|
|
4554
|
+
details: { value: date }
|
|
4555
|
+
});
|
|
4414
4556
|
}
|
|
4415
4557
|
const year = Math.floor(date / 1e4);
|
|
4416
4558
|
const month = Math.floor(date % 1e4 / 100);
|
|
4417
4559
|
const day = date % 100;
|
|
4418
4560
|
const dateObj = new Date(year, month - 1, day);
|
|
4419
4561
|
if (dateObj.toString() === "Invalid Date") {
|
|
4420
|
-
throw new
|
|
4562
|
+
throw new GtfsError("Invalid date", {
|
|
4563
|
+
code: "GTFS_INVALID_DATE" /* GTFS_INVALID_DATE */,
|
|
4564
|
+
category: "validation" /* VALIDATION */,
|
|
4565
|
+
details: { value: date }
|
|
4566
|
+
});
|
|
4421
4567
|
}
|
|
4422
4568
|
return DAYS_OF_WEEK[dateObj.getDay()];
|
|
4423
4569
|
}
|
|
@@ -4504,7 +4650,16 @@ async function fetchGtfsRealtimeData(type, task) {
|
|
|
4504
4650
|
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
4505
4651
|
});
|
|
4506
4652
|
if (response.status !== 200) {
|
|
4507
|
-
throw new
|
|
4653
|
+
throw new GtfsError(`HTTP ${response.status}: ${response.statusText}`, {
|
|
4654
|
+
code: "GTFS_DOWNLOAD_HTTP" /* GTFS_DOWNLOAD_HTTP */,
|
|
4655
|
+
category: "download" /* DOWNLOAD */,
|
|
4656
|
+
statusCode: response.status,
|
|
4657
|
+
details: {
|
|
4658
|
+
url: urlConfig.url,
|
|
4659
|
+
status: response.status,
|
|
4660
|
+
statusText: response.statusText
|
|
4661
|
+
}
|
|
4662
|
+
});
|
|
4508
4663
|
}
|
|
4509
4664
|
const buffer = await response.arrayBuffer();
|
|
4510
4665
|
const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
|
|
@@ -4521,17 +4676,27 @@ async function fetchGtfsRealtimeData(type, task) {
|
|
|
4521
4676
|
});
|
|
4522
4677
|
return feedMessage;
|
|
4523
4678
|
} catch (error) {
|
|
4524
|
-
const
|
|
4679
|
+
const gtfsError = toGtfsError(error, {
|
|
4680
|
+
message: error instanceof Error ? error.message : String(error),
|
|
4681
|
+
code: "GTFS_DOWNLOAD_FAILED" /* GTFS_DOWNLOAD_FAILED */,
|
|
4682
|
+
category: "download" /* DOWNLOAD */,
|
|
4683
|
+
details: { type, url: urlConfig.url }
|
|
4684
|
+
});
|
|
4525
4685
|
if (attempt === MAX_RETRIES) {
|
|
4526
4686
|
if (task.ignoreErrors) {
|
|
4527
4687
|
task.logError(
|
|
4528
|
-
`Failed to fetch ${type} after ${MAX_RETRIES} attempts: ${
|
|
4688
|
+
`Failed to fetch ${type} after ${MAX_RETRIES} attempts: ${gtfsError.message}`
|
|
4529
4689
|
);
|
|
4690
|
+
if (task.report) {
|
|
4691
|
+
addImportError(task.report, gtfsError);
|
|
4692
|
+
}
|
|
4530
4693
|
return null;
|
|
4531
4694
|
}
|
|
4532
|
-
throw
|
|
4695
|
+
throw gtfsError;
|
|
4533
4696
|
}
|
|
4534
|
-
task.logWarning(
|
|
4697
|
+
task.logWarning(
|
|
4698
|
+
`Attempt ${attempt} failed for ${type}: ${gtfsError.message}`
|
|
4699
|
+
);
|
|
4535
4700
|
await new Promise(
|
|
4536
4701
|
(resolve) => setTimeout(resolve, RETRY_DELAY * attempt)
|
|
4537
4702
|
);
|
|
@@ -4738,8 +4903,9 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4738
4903
|
);
|
|
4739
4904
|
removeExpiredRealtimeData(config);
|
|
4740
4905
|
await mapSeries(config.agencies, async (agency2) => {
|
|
4906
|
+
let task;
|
|
4741
4907
|
try {
|
|
4742
|
-
|
|
4908
|
+
task = {
|
|
4743
4909
|
realtimeAlerts: agency2.realtimeAlerts,
|
|
4744
4910
|
realtimeTripUpdates: agency2.realtimeTripUpdates,
|
|
4745
4911
|
realtimeVehiclePositions: agency2.realtimeVehiclePositions,
|
|
@@ -4755,11 +4921,19 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4755
4921
|
};
|
|
4756
4922
|
await updateGtfsRealtimeData(task);
|
|
4757
4923
|
} catch (error) {
|
|
4758
|
-
const
|
|
4924
|
+
const gtfsError = toGtfsError(error, {
|
|
4925
|
+
message: error instanceof Error ? error.message : String(error),
|
|
4926
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
4927
|
+
category: "database" /* DATABASE */,
|
|
4928
|
+
details: { sqlitePath: task?.sqlitePath ?? config.sqlitePath }
|
|
4929
|
+
});
|
|
4759
4930
|
if (config.ignoreErrors) {
|
|
4760
|
-
logError(config)(
|
|
4931
|
+
logError(config)(formatGtfsError(gtfsError));
|
|
4932
|
+
if (task?.report) {
|
|
4933
|
+
addImportError(task.report, gtfsError);
|
|
4934
|
+
}
|
|
4761
4935
|
} else {
|
|
4762
|
-
throw
|
|
4936
|
+
throw gtfsError;
|
|
4763
4937
|
}
|
|
4764
4938
|
}
|
|
4765
4939
|
});
|
|
@@ -4773,42 +4947,88 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4773
4947
|
);
|
|
4774
4948
|
} catch (error) {
|
|
4775
4949
|
if (error.code === "SQLITE_CANTOPEN") {
|
|
4776
|
-
|
|
4777
|
-
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json
|
|
4950
|
+
const dbOpenError = new GtfsError(
|
|
4951
|
+
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`,
|
|
4952
|
+
{
|
|
4953
|
+
code: "DB_OPEN_FAILED" /* DB_OPEN_FAILED */,
|
|
4954
|
+
category: "database" /* DATABASE */,
|
|
4955
|
+
details: {
|
|
4956
|
+
sqlitePath: config.sqlitePath,
|
|
4957
|
+
dbCode: error.code
|
|
4958
|
+
},
|
|
4959
|
+
cause: error
|
|
4960
|
+
}
|
|
4778
4961
|
);
|
|
4962
|
+
logError(config)(dbOpenError.message);
|
|
4963
|
+
throw dbOpenError;
|
|
4779
4964
|
}
|
|
4780
|
-
throw error
|
|
4965
|
+
throw toGtfsError(error, {
|
|
4966
|
+
message: error instanceof Error ? error.message : String(error),
|
|
4967
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
4968
|
+
category: "database" /* DATABASE */
|
|
4969
|
+
});
|
|
4781
4970
|
}
|
|
4782
4971
|
}
|
|
4783
4972
|
|
|
4784
4973
|
// src/lib/import-gtfs.ts
|
|
4974
|
+
function reportTaskError(task, error) {
|
|
4975
|
+
if (task.report) {
|
|
4976
|
+
addImportError(task.report, error);
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4785
4979
|
var getTextFiles = async (folderPath) => {
|
|
4786
4980
|
const files = await readdir(folderPath);
|
|
4787
4981
|
return files.filter((filename) => filename.slice(-3) === "txt");
|
|
4788
4982
|
};
|
|
4789
4983
|
var downloadGtfsFiles = async (task) => {
|
|
4790
4984
|
if (!task.url) {
|
|
4791
|
-
throw new
|
|
4985
|
+
throw new GtfsError("No `url` specified in config", {
|
|
4986
|
+
code: "GTFS_CONFIG_INVALID" /* GTFS_CONFIG_INVALID */,
|
|
4987
|
+
category: "config" /* CONFIG */
|
|
4988
|
+
});
|
|
4792
4989
|
}
|
|
4793
4990
|
task.log(`Downloading GTFS from ${task.url}`);
|
|
4794
4991
|
task.path = `${task.downloadDir}/gtfs.zip`;
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4992
|
+
try {
|
|
4993
|
+
const response = await fetch(task.url, {
|
|
4994
|
+
method: "GET",
|
|
4995
|
+
headers: task.headers || {},
|
|
4996
|
+
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
4997
|
+
});
|
|
4998
|
+
if (response.status !== 200) {
|
|
4999
|
+
throw new GtfsError(
|
|
5000
|
+
`Unable to download GTFS from ${task.url}. Got status ${response.status}.`,
|
|
5001
|
+
{
|
|
5002
|
+
code: "GTFS_DOWNLOAD_HTTP" /* GTFS_DOWNLOAD_HTTP */,
|
|
5003
|
+
category: "download" /* DOWNLOAD */,
|
|
5004
|
+
statusCode: response.status,
|
|
5005
|
+
details: {
|
|
5006
|
+
url: task.url,
|
|
5007
|
+
status: response.status,
|
|
5008
|
+
statusText: response.statusText
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
);
|
|
5012
|
+
}
|
|
5013
|
+
const buffer = await response.arrayBuffer();
|
|
5014
|
+
await writeFile(task.path, Buffer.from(buffer));
|
|
5015
|
+
task.log("Download successful");
|
|
5016
|
+
} catch (error) {
|
|
5017
|
+
throw toGtfsError(error, {
|
|
5018
|
+
message: `Unable to download GTFS from ${task.url}.`,
|
|
5019
|
+
code: "GTFS_DOWNLOAD_FAILED" /* GTFS_DOWNLOAD_FAILED */,
|
|
5020
|
+
category: "download" /* DOWNLOAD */,
|
|
5021
|
+
details: { url: task.url }
|
|
5022
|
+
});
|
|
4804
5023
|
}
|
|
4805
|
-
const buffer = await response.arrayBuffer();
|
|
4806
|
-
await writeFile(task.path, Buffer.from(buffer));
|
|
4807
|
-
task.log("Download successful");
|
|
4808
5024
|
};
|
|
4809
5025
|
var extractGtfsFiles = async (task) => {
|
|
4810
5026
|
if (!task.path) {
|
|
4811
|
-
throw new
|
|
5027
|
+
throw new GtfsError("No `path` specified in config", {
|
|
5028
|
+
code: "GTFS_CONFIG_INVALID" /* GTFS_CONFIG_INVALID */,
|
|
5029
|
+
category: "config" /* CONFIG */,
|
|
5030
|
+
details: { field: "path" }
|
|
5031
|
+
});
|
|
4812
5032
|
}
|
|
4813
5033
|
const gtfsPath = untildify(task.path);
|
|
4814
5034
|
task.log(`Importing static GTFS from ${task.path}\r`);
|
|
@@ -4820,19 +5040,34 @@ var extractGtfsFiles = async (task) => {
|
|
|
4820
5040
|
const files = await readdir(task.downloadDir);
|
|
4821
5041
|
const folders = files.filter((filename) => !["__MACOSX"].includes(filename)).map((filename) => path2.join(task.downloadDir, filename)).filter((source) => lstatSync(source).isDirectory());
|
|
4822
5042
|
if (folders.length > 1) {
|
|
4823
|
-
throw new
|
|
4824
|
-
`More than one subfolder found in zip file at \`${task.path}\`. Ensure that .txt files are in the top level of the zip file, or in a single subdirectory
|
|
5043
|
+
throw new GtfsError(
|
|
5044
|
+
`More than one subfolder found in zip file at \`${task.path}\`. Ensure that .txt files are in the top level of the zip file, or in a single subdirectory.`,
|
|
5045
|
+
{
|
|
5046
|
+
code: "GTFS_ZIP_INVALID" /* GTFS_ZIP_INVALID */,
|
|
5047
|
+
category: "zip" /* ZIP */,
|
|
5048
|
+
details: { path: task.path, folderCount: folders.length }
|
|
5049
|
+
}
|
|
4825
5050
|
);
|
|
4826
5051
|
} else if (folders.length === 0) {
|
|
4827
|
-
throw new
|
|
4828
|
-
`No .txt files found in \`${task.path}\`. Ensure that .txt files are in the top level of the zip file, or in a single subdirectory
|
|
5052
|
+
throw new GtfsError(
|
|
5053
|
+
`No .txt files found in \`${task.path}\`. Ensure that .txt files are in the top level of the zip file, or in a single subdirectory.`,
|
|
5054
|
+
{
|
|
5055
|
+
code: "GTFS_ZIP_INVALID" /* GTFS_ZIP_INVALID */,
|
|
5056
|
+
category: "zip" /* ZIP */,
|
|
5057
|
+
details: { path: task.path }
|
|
5058
|
+
}
|
|
4829
5059
|
);
|
|
4830
5060
|
}
|
|
4831
5061
|
const subfolderName = folders[0];
|
|
4832
5062
|
const directoryTextFiles = await getTextFiles(subfolderName);
|
|
4833
5063
|
if (directoryTextFiles.length === 0) {
|
|
4834
|
-
throw new
|
|
4835
|
-
`No .txt files found in \`${task.path}\`. Ensure that .txt files are in the top level of the zip file, or in a single subdirectory
|
|
5064
|
+
throw new GtfsError(
|
|
5065
|
+
`No .txt files found in \`${task.path}\`. Ensure that .txt files are in the top level of the zip file, or in a single subdirectory.`,
|
|
5066
|
+
{
|
|
5067
|
+
code: "GTFS_ZIP_INVALID" /* GTFS_ZIP_INVALID */,
|
|
5068
|
+
category: "zip" /* ZIP */,
|
|
5069
|
+
details: { path: task.path, subfolderName }
|
|
5070
|
+
}
|
|
4836
5071
|
);
|
|
4837
5072
|
}
|
|
4838
5073
|
await Promise.all(
|
|
@@ -4845,15 +5080,27 @@ var extractGtfsFiles = async (task) => {
|
|
|
4845
5080
|
);
|
|
4846
5081
|
}
|
|
4847
5082
|
} catch (error) {
|
|
4848
|
-
|
|
4849
|
-
|
|
5083
|
+
const wrappedError = toGtfsError(error, {
|
|
5084
|
+
message: `Unable to unzip file ${task.path}`,
|
|
5085
|
+
code: "GTFS_ZIP_INVALID" /* GTFS_ZIP_INVALID */,
|
|
5086
|
+
category: "zip" /* ZIP */,
|
|
5087
|
+
details: { path: task.path }
|
|
5088
|
+
});
|
|
5089
|
+
task.logError(formatGtfsError(wrappedError));
|
|
5090
|
+
throw wrappedError;
|
|
4850
5091
|
}
|
|
4851
5092
|
} else {
|
|
4852
5093
|
try {
|
|
4853
5094
|
await cp(gtfsPath, task.downloadDir, { recursive: true });
|
|
4854
|
-
} catch {
|
|
4855
|
-
throw new
|
|
4856
|
-
`Unable to load files from path \`${gtfsPath}\` defined in configuration. Verify that path exists and contains GTFS files
|
|
5095
|
+
} catch (error) {
|
|
5096
|
+
throw new GtfsError(
|
|
5097
|
+
`Unable to load files from path \`${gtfsPath}\` defined in configuration. Verify that path exists and contains GTFS files.`,
|
|
5098
|
+
{
|
|
5099
|
+
code: "GTFS_DOWNLOAD_FAILED" /* GTFS_DOWNLOAD_FAILED */,
|
|
5100
|
+
category: "download" /* DOWNLOAD */,
|
|
5101
|
+
details: { path: gtfsPath },
|
|
5102
|
+
cause: error
|
|
5103
|
+
}
|
|
4857
5104
|
);
|
|
4858
5105
|
}
|
|
4859
5106
|
}
|
|
@@ -4948,8 +5195,17 @@ var formatGtfsLine = (line, model, totalLineCount) => {
|
|
|
4948
5195
|
if (value === "" || value === void 0 || value === null) {
|
|
4949
5196
|
formattedLine[name] = null;
|
|
4950
5197
|
if (required) {
|
|
4951
|
-
throw new
|
|
4952
|
-
`Missing required value in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}
|
|
5198
|
+
throw new GtfsError(
|
|
5199
|
+
`Missing required value in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}.`,
|
|
5200
|
+
{
|
|
5201
|
+
code: "GTFS_REQUIRED_FIELD_MISSING" /* GTFS_REQUIRED_FIELD_MISSING */,
|
|
5202
|
+
category: "validation" /* VALIDATION */,
|
|
5203
|
+
details: {
|
|
5204
|
+
file: `${filenameBase}.${filenameExtension}`,
|
|
5205
|
+
line: lineNumber,
|
|
5206
|
+
column: name
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
4953
5209
|
);
|
|
4954
5210
|
}
|
|
4955
5211
|
continue;
|
|
@@ -4957,8 +5213,18 @@ var formatGtfsLine = (line, model, totalLineCount) => {
|
|
|
4957
5213
|
if (type === "date") {
|
|
4958
5214
|
value = value?.toString().replace(/-/g, "");
|
|
4959
5215
|
if (value.length !== 8) {
|
|
4960
|
-
throw new
|
|
4961
|
-
`Invalid date in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}
|
|
5216
|
+
throw new GtfsError(
|
|
5217
|
+
`Invalid date in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}.`,
|
|
5218
|
+
{
|
|
5219
|
+
code: "GTFS_INVALID_DATE" /* GTFS_INVALID_DATE */,
|
|
5220
|
+
category: "validation" /* VALIDATION */,
|
|
5221
|
+
details: {
|
|
5222
|
+
file: `${filenameBase}.${filenameExtension}`,
|
|
5223
|
+
line: lineNumber,
|
|
5224
|
+
column: name,
|
|
5225
|
+
value
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
4962
5228
|
);
|
|
4963
5229
|
}
|
|
4964
5230
|
} else if (type === "time") {
|
|
@@ -5030,11 +5296,32 @@ var importGtfsFiles = async (db, task) => {
|
|
|
5030
5296
|
task.logWarning(
|
|
5031
5297
|
`Duplicate values for primary key (${primaryColumns.map((column) => column.name).join(", ")}) found in ${filename}. Set the \`ignoreDuplicates\` option to true in config.json to ignore this error`
|
|
5032
5298
|
);
|
|
5299
|
+
if (task.report) {
|
|
5300
|
+
addImportWarning(task.report, {
|
|
5301
|
+
code: "GTFS_DUPLICATE_PRIMARY_KEY" /* GTFS_DUPLICATE_PRIMARY_KEY */,
|
|
5302
|
+
message: `Duplicate values for primary key found in ${filename}.`,
|
|
5303
|
+
details: {
|
|
5304
|
+
file: filename,
|
|
5305
|
+
line: Number(rowNumber) + 1,
|
|
5306
|
+
columns: primaryColumns.map((column) => column.name)
|
|
5307
|
+
}
|
|
5308
|
+
});
|
|
5309
|
+
}
|
|
5033
5310
|
}
|
|
5034
5311
|
task.logWarning(
|
|
5035
5312
|
`Check ${filename} for invalid data on line ${rowNumber + 1}.`
|
|
5036
5313
|
);
|
|
5037
|
-
throw error
|
|
5314
|
+
throw toGtfsError(error, {
|
|
5315
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5316
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
5317
|
+
category: "database" /* DATABASE */,
|
|
5318
|
+
details: {
|
|
5319
|
+
file: filename,
|
|
5320
|
+
line: Number(rowNumber) + 1,
|
|
5321
|
+
sqlitePath: task.sqlitePath,
|
|
5322
|
+
dbCode: error.code
|
|
5323
|
+
}
|
|
5324
|
+
});
|
|
5038
5325
|
}
|
|
5039
5326
|
}
|
|
5040
5327
|
});
|
|
@@ -5063,12 +5350,20 @@ var importGtfsFiles = async (db, task) => {
|
|
|
5063
5350
|
}
|
|
5064
5351
|
}
|
|
5065
5352
|
} catch (error) {
|
|
5353
|
+
const gtfsError = toGtfsError(error, {
|
|
5354
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5355
|
+
code: "GTFS_CSV_PARSE_FAILED" /* GTFS_CSV_PARSE_FAILED */,
|
|
5356
|
+
category: "parse" /* PARSE */,
|
|
5357
|
+
details: { file: filename }
|
|
5358
|
+
});
|
|
5066
5359
|
if (task.ignoreErrors) {
|
|
5067
|
-
|
|
5068
|
-
task.logError(
|
|
5360
|
+
reportTaskError(task, gtfsError);
|
|
5361
|
+
task.logError(
|
|
5362
|
+
`Error processing ${filename}: ${gtfsError.message}`
|
|
5363
|
+
);
|
|
5069
5364
|
resolve();
|
|
5070
5365
|
} else {
|
|
5071
|
-
reject(
|
|
5366
|
+
reject(gtfsError);
|
|
5072
5367
|
}
|
|
5073
5368
|
}
|
|
5074
5369
|
});
|
|
@@ -5078,15 +5373,21 @@ var importGtfsFiles = async (db, task) => {
|
|
|
5078
5373
|
try {
|
|
5079
5374
|
insertLines(lines);
|
|
5080
5375
|
} catch (error) {
|
|
5376
|
+
const gtfsError = toGtfsError(error, {
|
|
5377
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5378
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
5379
|
+
category: "database" /* DATABASE */,
|
|
5380
|
+
details: { file: filename, sqlitePath: task.sqlitePath }
|
|
5381
|
+
});
|
|
5081
5382
|
if (task.ignoreErrors) {
|
|
5082
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5083
5383
|
task.logError(
|
|
5084
|
-
`Error inserting data for ${filename}: ${
|
|
5384
|
+
`Error inserting data for ${filename}: ${gtfsError.message}`
|
|
5085
5385
|
);
|
|
5386
|
+
reportTaskError(task, gtfsError);
|
|
5086
5387
|
resolve();
|
|
5087
5388
|
return;
|
|
5088
5389
|
} else {
|
|
5089
|
-
reject(
|
|
5390
|
+
reject(gtfsError);
|
|
5090
5391
|
return;
|
|
5091
5392
|
}
|
|
5092
5393
|
}
|
|
@@ -5097,22 +5398,38 @@ var importGtfsFiles = async (db, task) => {
|
|
|
5097
5398
|
);
|
|
5098
5399
|
resolve();
|
|
5099
5400
|
} catch (error) {
|
|
5401
|
+
const gtfsError = toGtfsError(error, {
|
|
5402
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5403
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
5404
|
+
category: "database" /* DATABASE */,
|
|
5405
|
+
details: { file: filename, sqlitePath: task.sqlitePath }
|
|
5406
|
+
});
|
|
5100
5407
|
if (task.ignoreErrors) {
|
|
5101
|
-
|
|
5102
|
-
|
|
5408
|
+
task.logError(
|
|
5409
|
+
`Error finalizing ${filename}: ${gtfsError.message}`
|
|
5410
|
+
);
|
|
5411
|
+
reportTaskError(task, gtfsError);
|
|
5103
5412
|
resolve();
|
|
5104
5413
|
} else {
|
|
5105
|
-
reject(
|
|
5414
|
+
reject(gtfsError);
|
|
5106
5415
|
}
|
|
5107
5416
|
}
|
|
5108
5417
|
});
|
|
5109
5418
|
parser.on("error", (error) => {
|
|
5419
|
+
const gtfsError = toGtfsError(error, {
|
|
5420
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5421
|
+
code: "GTFS_CSV_PARSE_FAILED" /* GTFS_CSV_PARSE_FAILED */,
|
|
5422
|
+
category: "parse" /* PARSE */,
|
|
5423
|
+
details: { file: filename }
|
|
5424
|
+
});
|
|
5110
5425
|
if (task.ignoreErrors) {
|
|
5111
|
-
|
|
5112
|
-
|
|
5426
|
+
task.logError(
|
|
5427
|
+
`Parser error for ${filename}: ${gtfsError.message}`
|
|
5428
|
+
);
|
|
5429
|
+
reportTaskError(task, gtfsError);
|
|
5113
5430
|
resolve();
|
|
5114
5431
|
} else {
|
|
5115
|
-
reject(
|
|
5432
|
+
reject(gtfsError);
|
|
5116
5433
|
}
|
|
5117
5434
|
});
|
|
5118
5435
|
createReadStream(filepath).pipe(stripBomStream()).pipe(parser);
|
|
@@ -5121,10 +5438,24 @@ var importGtfsFiles = async (db, task) => {
|
|
|
5121
5438
|
if (isValidJSON(data) === false) {
|
|
5122
5439
|
if (task.ignoreErrors) {
|
|
5123
5440
|
task.logError(`Invalid JSON in ${filename}`);
|
|
5441
|
+
reportTaskError(
|
|
5442
|
+
task,
|
|
5443
|
+
new GtfsError(`Invalid JSON in ${filename}`, {
|
|
5444
|
+
code: "GTFS_JSON_INVALID" /* GTFS_JSON_INVALID */,
|
|
5445
|
+
category: "parse" /* PARSE */,
|
|
5446
|
+
details: { file: filename }
|
|
5447
|
+
})
|
|
5448
|
+
);
|
|
5124
5449
|
resolve();
|
|
5125
5450
|
return;
|
|
5126
5451
|
} else {
|
|
5127
|
-
reject(
|
|
5452
|
+
reject(
|
|
5453
|
+
new GtfsError(`Invalid JSON in ${filename}`, {
|
|
5454
|
+
code: "GTFS_JSON_INVALID" /* GTFS_JSON_INVALID */,
|
|
5455
|
+
category: "parse" /* PARSE */,
|
|
5456
|
+
details: { file: filename }
|
|
5457
|
+
})
|
|
5458
|
+
);
|
|
5128
5459
|
return;
|
|
5129
5460
|
}
|
|
5130
5461
|
}
|
|
@@ -5142,23 +5473,37 @@ var importGtfsFiles = async (db, task) => {
|
|
|
5142
5473
|
);
|
|
5143
5474
|
resolve();
|
|
5144
5475
|
} catch (error) {
|
|
5476
|
+
const gtfsError = toGtfsError(error, {
|
|
5477
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5478
|
+
code: "GTFS_DB_OPERATION_FAILED" /* GTFS_DB_OPERATION_FAILED */,
|
|
5479
|
+
category: "database" /* DATABASE */,
|
|
5480
|
+
details: { file: filename, sqlitePath: task.sqlitePath }
|
|
5481
|
+
});
|
|
5145
5482
|
if (task.ignoreErrors) {
|
|
5146
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5147
5483
|
task.logError(
|
|
5148
|
-
`Error inserting data for ${filename}: ${
|
|
5484
|
+
`Error inserting data for ${filename}: ${gtfsError.message}`
|
|
5149
5485
|
);
|
|
5486
|
+
reportTaskError(task, gtfsError);
|
|
5150
5487
|
resolve();
|
|
5151
5488
|
} else {
|
|
5152
|
-
reject(
|
|
5489
|
+
reject(gtfsError);
|
|
5153
5490
|
}
|
|
5154
5491
|
}
|
|
5155
5492
|
}).catch((error) => {
|
|
5493
|
+
const gtfsError = toGtfsError(error, {
|
|
5494
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5495
|
+
code: "GTFS_CSV_PARSE_FAILED" /* GTFS_CSV_PARSE_FAILED */,
|
|
5496
|
+
category: "parse" /* PARSE */,
|
|
5497
|
+
details: { file: filename }
|
|
5498
|
+
});
|
|
5156
5499
|
if (task.ignoreErrors) {
|
|
5157
|
-
|
|
5158
|
-
|
|
5500
|
+
task.logError(
|
|
5501
|
+
`Error reading ${filename}: ${gtfsError.message}`
|
|
5502
|
+
);
|
|
5503
|
+
reportTaskError(task, gtfsError);
|
|
5159
5504
|
resolve();
|
|
5160
5505
|
} else {
|
|
5161
|
-
reject(
|
|
5506
|
+
reject(gtfsError);
|
|
5162
5507
|
}
|
|
5163
5508
|
});
|
|
5164
5509
|
} else {
|
|
@@ -5169,7 +5514,17 @@ var importGtfsFiles = async (db, task) => {
|
|
|
5169
5514
|
resolve();
|
|
5170
5515
|
} else {
|
|
5171
5516
|
reject(
|
|
5172
|
-
new
|
|
5517
|
+
new GtfsError(
|
|
5518
|
+
`Unsupported file type: ${model.filenameExtension}`,
|
|
5519
|
+
{
|
|
5520
|
+
code: "GTFS_UNSUPPORTED_FILE_TYPE" /* GTFS_UNSUPPORTED_FILE_TYPE */,
|
|
5521
|
+
category: "parse" /* PARSE */,
|
|
5522
|
+
details: {
|
|
5523
|
+
file: filename,
|
|
5524
|
+
extension: model.filenameExtension
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5527
|
+
)
|
|
5173
5528
|
);
|
|
5174
5529
|
}
|
|
5175
5530
|
}
|
|
@@ -5181,6 +5536,7 @@ async function importGtfs(initialConfig) {
|
|
|
5181
5536
|
const startTime = process.hrtime.bigint();
|
|
5182
5537
|
const config = setDefaultConfig(initialConfig);
|
|
5183
5538
|
validateConfigForImport(config);
|
|
5539
|
+
const report = config.includeImportReport ? createImportReport() : void 0;
|
|
5184
5540
|
try {
|
|
5185
5541
|
const db = openDb(config);
|
|
5186
5542
|
const agencyCount = config.agencies.length;
|
|
@@ -5208,7 +5564,8 @@ async function importGtfs(initialConfig) {
|
|
|
5208
5564
|
currentTimestamp: Math.floor(Date.now() / 1e3),
|
|
5209
5565
|
log: log(config),
|
|
5210
5566
|
logWarning: logWarning(config),
|
|
5211
|
-
logError: logError(config)
|
|
5567
|
+
logError: logError(config),
|
|
5568
|
+
report
|
|
5212
5569
|
};
|
|
5213
5570
|
if ("url" in agency2) {
|
|
5214
5571
|
Object.assign(task, { url: agency2.url });
|
|
@@ -5223,11 +5580,18 @@ async function importGtfs(initialConfig) {
|
|
|
5223
5580
|
await updateGtfsRealtimeData(task);
|
|
5224
5581
|
await rm2(tempPath, { recursive: true });
|
|
5225
5582
|
} catch (error) {
|
|
5583
|
+
const wrappedError = toGtfsError(error, {
|
|
5584
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5585
|
+
code: "GTFS_CSV_PARSE_FAILED" /* GTFS_CSV_PARSE_FAILED */,
|
|
5586
|
+
category: "parse" /* PARSE */
|
|
5587
|
+
});
|
|
5226
5588
|
if (config.ignoreErrors) {
|
|
5227
|
-
|
|
5228
|
-
|
|
5589
|
+
logError(config)(formatGtfsError(wrappedError));
|
|
5590
|
+
if (report) {
|
|
5591
|
+
addImportError(report, wrappedError);
|
|
5592
|
+
}
|
|
5229
5593
|
} else {
|
|
5230
|
-
throw
|
|
5594
|
+
throw wrappedError;
|
|
5231
5595
|
}
|
|
5232
5596
|
}
|
|
5233
5597
|
});
|
|
@@ -5241,11 +5605,29 @@ async function importGtfs(initialConfig) {
|
|
|
5241
5605
|
);
|
|
5242
5606
|
} catch (error) {
|
|
5243
5607
|
if (error.code === "SQLITE_CANTOPEN") {
|
|
5244
|
-
|
|
5245
|
-
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json
|
|
5608
|
+
const dbOpenError = new GtfsError(
|
|
5609
|
+
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`,
|
|
5610
|
+
{
|
|
5611
|
+
code: "DB_OPEN_FAILED" /* DB_OPEN_FAILED */,
|
|
5612
|
+
category: "database" /* DATABASE */,
|
|
5613
|
+
details: {
|
|
5614
|
+
sqlitePath: config.sqlitePath,
|
|
5615
|
+
dbCode: error.code
|
|
5616
|
+
},
|
|
5617
|
+
cause: error
|
|
5618
|
+
}
|
|
5246
5619
|
);
|
|
5620
|
+
logError(config)(dbOpenError.message);
|
|
5621
|
+
throw dbOpenError;
|
|
5247
5622
|
}
|
|
5248
|
-
throw error
|
|
5623
|
+
throw toGtfsError(error, {
|
|
5624
|
+
message: error instanceof Error ? error.message : String(error),
|
|
5625
|
+
code: "GTFS_CSV_PARSE_FAILED" /* GTFS_CSV_PARSE_FAILED */,
|
|
5626
|
+
category: "parse" /* PARSE */
|
|
5627
|
+
});
|
|
5628
|
+
}
|
|
5629
|
+
if (report) {
|
|
5630
|
+
return report;
|
|
5249
5631
|
}
|
|
5250
5632
|
}
|
|
5251
5633
|
|
|
@@ -5468,7 +5850,11 @@ function getCalendars(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5468
5850
|
function getServiceIdsByDate(date, options = {}) {
|
|
5469
5851
|
const db = options.db ?? openDb();
|
|
5470
5852
|
if (!date) {
|
|
5471
|
-
throw new
|
|
5853
|
+
throw new GtfsError("`date` is a required query parameter", {
|
|
5854
|
+
code: "GTFS_QUERY_INVALID" /* GTFS_QUERY_INVALID */,
|
|
5855
|
+
category: "query" /* QUERY */,
|
|
5856
|
+
details: { field: "date" }
|
|
5857
|
+
});
|
|
5472
5858
|
}
|
|
5473
5859
|
const dayOfWeek = getDayOfWeekFromDate(date);
|
|
5474
5860
|
const results = db.prepare(
|
|
@@ -5937,7 +6323,11 @@ function getStoptimes(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5937
6323
|
);
|
|
5938
6324
|
if (query.date) {
|
|
5939
6325
|
if (typeof query.date !== "number") {
|
|
5940
|
-
throw new
|
|
6326
|
+
throw new GtfsError("`date` must be a number in yyyymmdd format", {
|
|
6327
|
+
code: "GTFS_QUERY_INVALID" /* GTFS_QUERY_INVALID */,
|
|
6328
|
+
category: "query" /* QUERY */,
|
|
6329
|
+
details: { field: "date", value: query.date }
|
|
6330
|
+
});
|
|
5941
6331
|
}
|
|
5942
6332
|
const serviceIds = getServiceIdsByDate(query.date, options);
|
|
5943
6333
|
const tripSubquery = `SELECT DISTINCT trip_id FROM trips WHERE service_id IN (${serviceIds.map((id) => sqlString4.escape(id)).join(",")})`;
|
|
@@ -5945,7 +6335,11 @@ function getStoptimes(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5945
6335
|
}
|
|
5946
6336
|
if (query.start_time) {
|
|
5947
6337
|
if (typeof query.start_time !== "string") {
|
|
5948
|
-
throw new
|
|
6338
|
+
throw new GtfsError("`start_time` must be a string in HH:mm:ss format", {
|
|
6339
|
+
code: "GTFS_QUERY_INVALID" /* GTFS_QUERY_INVALID */,
|
|
6340
|
+
category: "query" /* QUERY */,
|
|
6341
|
+
details: { field: "start_time", value: query.start_time }
|
|
6342
|
+
});
|
|
5949
6343
|
}
|
|
5950
6344
|
whereClauses.push(
|
|
5951
6345
|
`arrival_timestamp >= ${calculateSecondsFromMidnight(query.start_time)}`
|
|
@@ -5953,7 +6347,11 @@ function getStoptimes(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5953
6347
|
}
|
|
5954
6348
|
if (query.end_time) {
|
|
5955
6349
|
if (typeof query.end_time !== "string") {
|
|
5956
|
-
throw new
|
|
6350
|
+
throw new GtfsError("`end_time` must be a string in HH:mm:ss format", {
|
|
6351
|
+
code: "GTFS_QUERY_INVALID" /* GTFS_QUERY_INVALID */,
|
|
6352
|
+
category: "query" /* QUERY */,
|
|
6353
|
+
details: { field: "end_time", value: query.end_time }
|
|
6354
|
+
});
|
|
5957
6355
|
}
|
|
5958
6356
|
whereClauses.push(
|
|
5959
6357
|
`departure_timestamp <= ${calculateSecondsFromMidnight(query.end_time)}`
|
|
@@ -6019,7 +6417,11 @@ function getTrips(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
6019
6417
|
);
|
|
6020
6418
|
if (query.date) {
|
|
6021
6419
|
if (typeof query.date !== "number") {
|
|
6022
|
-
throw new
|
|
6420
|
+
throw new GtfsError("`date` must be a number in yyyymmdd format", {
|
|
6421
|
+
code: "GTFS_QUERY_INVALID" /* GTFS_QUERY_INVALID */,
|
|
6422
|
+
category: "query" /* QUERY */,
|
|
6423
|
+
details: { field: "date", value: query.date }
|
|
6424
|
+
});
|
|
6023
6425
|
}
|
|
6024
6426
|
const serviceIds = getServiceIdsByDate(query.date, options);
|
|
6025
6427
|
whereClauses.push(
|
|
@@ -6299,10 +6701,15 @@ function getRunsPieces(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
6299
6701
|
).all();
|
|
6300
6702
|
}
|
|
6301
6703
|
export {
|
|
6704
|
+
GtfsError,
|
|
6705
|
+
GtfsErrorCategory,
|
|
6706
|
+
GtfsErrorCode,
|
|
6707
|
+
GtfsWarningCode,
|
|
6302
6708
|
advancedQuery,
|
|
6303
6709
|
closeDb,
|
|
6304
6710
|
deleteDb,
|
|
6305
6711
|
exportGtfs,
|
|
6712
|
+
formatGtfsError,
|
|
6306
6713
|
generateFolderName,
|
|
6307
6714
|
getAgencies2 as getAgencies,
|
|
6308
6715
|
getAreas,
|
|
@@ -6363,6 +6770,8 @@ export {
|
|
|
6363
6770
|
getTripsDatedVehicleJourneys,
|
|
6364
6771
|
getVehiclePositions,
|
|
6365
6772
|
importGtfs,
|
|
6773
|
+
isGtfsError,
|
|
6774
|
+
isGtfsValidationError,
|
|
6366
6775
|
openDb,
|
|
6367
6776
|
prepDirectory,
|
|
6368
6777
|
untildify,
|