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