gtfs 4.15.2 → 4.15.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/index.js CHANGED
@@ -3484,6 +3484,21 @@ function formatOrderByClause(orderBy2) {
3484
3484
  }
3485
3485
  return orderByClause;
3486
3486
  }
3487
+ function getDayOfWeekFromDate(date) {
3488
+ const daysOfWeek = [
3489
+ "sunday",
3490
+ "monday",
3491
+ "tuesday",
3492
+ "wednesday",
3493
+ "thursday",
3494
+ "friday",
3495
+ "saturday"
3496
+ ];
3497
+ const dateString = date.toString().replace(/(\d{4})(\d{2})(\d{2})/, "$1-$2-$3");
3498
+ const [year, month, day] = dateString.split("-").map(Number);
3499
+ const dayOfWeek = new Date(year, month - 1, day).getDay();
3500
+ return daysOfWeek[dayOfWeek];
3501
+ }
3487
3502
 
3488
3503
  // src/lib/import-gtfs-realtime.ts
3489
3504
  function getNestedProperty(obj, defaultValue, path3) {
@@ -3548,9 +3563,8 @@ async function fetchGtfsRealtimeData(urlConfig, task) {
3548
3563
  });
3549
3564
  }
3550
3565
  function removeExpiredRealtimeData(config) {
3551
- const log2 = log(config);
3552
3566
  const db = openDb(config);
3553
- log2(`Removing expired GTFS-Realtime data`);
3567
+ log(config)(`Removing expired GTFS-Realtime data`);
3554
3568
  db.prepare(
3555
3569
  `DELETE FROM vehicle_positions WHERE expiration_timestamp <= strftime('%s','now')`
3556
3570
  ).run();
@@ -3566,7 +3580,7 @@ function removeExpiredRealtimeData(config) {
3566
3580
  db.prepare(
3567
3581
  `DELETE FROM service_alert_targets WHERE expiration_timestamp <= strftime('%s','now')`
3568
3582
  ).run();
3569
- log2(`Removed expired GTFS-Realtime data\r`, true);
3583
+ log(config)(`Removed expired GTFS-Realtime data\r`, true);
3570
3584
  }
3571
3585
  function prepareRealtimeFieldValue(entity, column, task) {
3572
3586
  if (column.name === "created_timestamp") {
@@ -3720,13 +3734,10 @@ async function updateGtfsRealtimeData(task) {
3720
3734
  async function updateGtfsRealtime(initialConfig) {
3721
3735
  const config = setDefaultConfig(initialConfig);
3722
3736
  validateConfigForImport(config);
3723
- const log2 = log(config);
3724
- const logError2 = logError(config);
3725
- const logWarning2 = logWarning(config);
3726
3737
  try {
3727
3738
  openDb(config);
3728
3739
  const agencyCount = config.agencies.length;
3729
- log2(
3740
+ log(config)(
3730
3741
  `Starting GTFS-Realtime refresh for ${pluralize(
3731
3742
  "agencies",
3732
3743
  agencyCount,
@@ -3745,20 +3756,20 @@ async function updateGtfsRealtime(initialConfig) {
3745
3756
  ignoreErrors: config.ignoreErrors,
3746
3757
  sqlitePath: config.sqlitePath,
3747
3758
  currentTimestamp: Math.floor(Date.now() / 1e3),
3748
- log: log2,
3749
- logWarning: logWarning2,
3750
- logError: logError2
3759
+ log: log(config),
3760
+ logWarning: logWarning(config),
3761
+ logError: logError(config)
3751
3762
  };
3752
3763
  await updateGtfsRealtimeData(task);
3753
3764
  } catch (error) {
3754
3765
  if (config.ignoreErrors) {
3755
- logError2(error.message);
3766
+ logError(config)(error.message);
3756
3767
  } else {
3757
3768
  throw error;
3758
3769
  }
3759
3770
  }
3760
3771
  });
3761
- log2(
3772
+ log(config)(
3762
3773
  `Completed GTFS-Realtime refresh for ${pluralize(
3763
3774
  "agencies",
3764
3775
  agencyCount,
@@ -3767,16 +3778,13 @@ async function updateGtfsRealtime(initialConfig) {
3767
3778
  `
3768
3779
  );
3769
3780
  } catch (error) {
3770
- handleDatabaseError(error, config, logError2);
3771
- }
3772
- }
3773
- function handleDatabaseError(error, config, logError2) {
3774
- if (error?.code === "SQLITE_CANTOPEN") {
3775
- logError2(
3776
- `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
3777
- );
3781
+ if (error?.code === "SQLITE_CANTOPEN") {
3782
+ logError(config)(
3783
+ `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
3784
+ );
3785
+ }
3786
+ throw error;
3778
3787
  }
3779
- throw error;
3780
3788
  }
3781
3789
 
3782
3790
  // src/lib/import-gtfs.ts
@@ -4122,13 +4130,10 @@ async function importGtfs(initialConfig) {
4122
4130
  timer.start();
4123
4131
  const config = setDefaultConfig(initialConfig);
4124
4132
  validateConfigForImport(config);
4125
- const log2 = log(config);
4126
- const logError2 = logError(config);
4127
- const logWarning2 = logWarning(config);
4128
4133
  try {
4129
4134
  const db = openDb(config);
4130
4135
  const agencyCount = config.agencies.length;
4131
- log2(
4136
+ log(config)(
4132
4137
  `Starting GTFS import for ${pluralize2("file", agencyCount, true)} using SQLite database at ${config.sqlitePath}`
4133
4138
  );
4134
4139
  createGtfsTables(db);
@@ -4152,9 +4157,9 @@ async function importGtfs(initialConfig) {
4152
4157
  sqlitePath: config.sqlitePath,
4153
4158
  prefix: agency2.prefix,
4154
4159
  currentTimestamp: Math.floor(Date.now() / 1e3),
4155
- log: log2,
4156
- logWarning: logWarning2,
4157
- logError: logError2
4160
+ log: log(config),
4161
+ logWarning: logWarning(config),
4162
+ logError: logError(config)
4158
4163
  };
4159
4164
  if (task.url) {
4160
4165
  await downloadGtfsFiles(task);
@@ -4164,36 +4169,30 @@ async function importGtfs(initialConfig) {
4164
4169
  await updateGtfsRealtimeData(task);
4165
4170
  await rm2(tempPath, { recursive: true });
4166
4171
  } catch (error) {
4167
- handleImportError(error, config, logError2);
4172
+ if (config.ignoreErrors) {
4173
+ logError(config)(error.message);
4174
+ } else {
4175
+ throw error;
4176
+ }
4168
4177
  }
4169
4178
  });
4170
- log2(`Creating DB indexes`);
4179
+ log(config)(`Creating DB indexes`);
4171
4180
  createGtfsIndexes(db);
4172
4181
  const seconds = Math.round(timer.time() / 1e3);
4173
4182
  timer.stop();
4174
- log2(
4183
+ log(config)(
4175
4184
  `Completed GTFS import for ${pluralize2("agency", agencyCount, true)} in ${seconds} seconds
4176
4185
  `
4177
4186
  );
4178
4187
  } catch (error) {
4179
- handleDatabaseError2(error, config, logError2);
4180
- }
4181
- }
4182
- function handleImportError(error, config, logError2) {
4183
- if (config.ignoreErrors) {
4184
- logError2(error.message);
4185
- } else {
4188
+ if (error?.code === "SQLITE_CANTOPEN") {
4189
+ logError(config)(
4190
+ `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
4191
+ );
4192
+ }
4186
4193
  throw error;
4187
4194
  }
4188
4195
  }
4189
- function handleDatabaseError2(error, config, logError2) {
4190
- if (error?.code === "SQLITE_CANTOPEN") {
4191
- logError2(
4192
- `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
4193
- );
4194
- }
4195
- throw error;
4196
- }
4197
4196
 
4198
4197
  // src/lib/export.ts
4199
4198
  import path2 from "node:path";
@@ -4220,8 +4219,6 @@ var getAgencies = (db, config) => {
4220
4219
  };
4221
4220
  var exportGtfs = async (initialConfig) => {
4222
4221
  const config = setDefaultConfig(initialConfig);
4223
- const log2 = log(config);
4224
- const logWarning2 = logWarning(config);
4225
4222
  const db = openDb(config);
4226
4223
  const agencies = getAgencies(db, config);
4227
4224
  const agencyCount = agencies.length;
@@ -4230,11 +4227,11 @@ var exportGtfs = async (initialConfig) => {
4230
4227
  "No agencies found in SQLite. Be sure to first import data into SQLite using `gtfs-import` or `importGtfs(config);`"
4231
4228
  );
4232
4229
  } else if (agencyCount > 1) {
4233
- logWarning2(
4230
+ logWarning(config)(
4234
4231
  "More than one agency is defined in config.json. Export will merge all into one GTFS file."
4235
4232
  );
4236
4233
  }
4237
- log2(
4234
+ log(config)(
4238
4235
  `Starting GTFS export for ${pluralize3(
4239
4236
  "agency",
4240
4237
  agencyCount,
@@ -4259,7 +4256,7 @@ var exportGtfs = async (initialConfig) => {
4259
4256
  const lines = db.prepare(`SELECT * FROM ${tableName};`).all();
4260
4257
  if (!lines || lines.length === 0) {
4261
4258
  if (!model.nonstandard) {
4262
- log2(
4259
+ log(config)(
4263
4260
  `Skipping (no data) - ${model.filenameBase}.${model.filenameExtension}\r`
4264
4261
  );
4265
4262
  }
@@ -4301,17 +4298,23 @@ var exportGtfs = async (initialConfig) => {
4301
4298
  `Unexpected filename extension: ${model.filenameExtension}`
4302
4299
  );
4303
4300
  }
4304
- log2(`Exporting - ${model.filenameBase}.${model.filenameExtension}\r`);
4301
+ log(config)(
4302
+ `Exporting - ${model.filenameBase}.${model.filenameExtension}\r`
4303
+ );
4305
4304
  return `${model.filenameBase}.${model.filenameExtension}`;
4306
4305
  }
4307
4306
  );
4308
4307
  if (compact2(exportedFiles).length === 0) {
4309
- log2("No GTFS data exported. Be sure to first import data into SQLite.");
4308
+ log(config)(
4309
+ "No GTFS data exported. Be sure to first import data into SQLite."
4310
+ );
4310
4311
  return;
4311
4312
  }
4312
- log2(`Completed GTFS export to ${exportPath}`);
4313
- log2(`Completed GTFS export for ${pluralize3("agency", agencyCount, true)}
4314
- `);
4313
+ log(config)(`Completed GTFS export to ${exportPath}`);
4314
+ log(config)(
4315
+ `Completed GTFS export for ${pluralize3("agency", agencyCount, true)}
4316
+ `
4317
+ );
4315
4318
  };
4316
4319
 
4317
4320
  // src/lib/advancedQuery.ts
@@ -4407,6 +4410,31 @@ function getCalendars(query = {}, fields = [], orderBy2 = [], options = {}) {
4407
4410
  `${selectClause} FROM ${tableName} ${whereClause} ${orderByClause};`
4408
4411
  ).all();
4409
4412
  }
4413
+ function getServiceIdsByDate(date, options = {}) {
4414
+ const db = options.db ?? openDb();
4415
+ if (!date) {
4416
+ throw new Error("`date` is a required query parameter");
4417
+ }
4418
+ const dayOfWeek = getDayOfWeekFromDate(date);
4419
+ const results = db.prepare(
4420
+ `
4421
+ SELECT service_id FROM (
4422
+ SELECT service_id
4423
+ FROM calendar
4424
+ WHERE start_date <= ? AND end_date >= ? AND ${dayOfWeek} = 1
4425
+ UNION
4426
+ SELECT service_id
4427
+ FROM calendar_dates
4428
+ WHERE date = ? AND exception_type = 1
4429
+ )
4430
+ EXCEPT
4431
+ SELECT service_id
4432
+ FROM calendar_dates
4433
+ WHERE date = ? AND exception_type = 2
4434
+ `
4435
+ ).all(date, date, date, date);
4436
+ return results.map((record) => record.service_id);
4437
+ }
4410
4438
 
4411
4439
  // src/lib/gtfs/fare-attributes.ts
4412
4440
  function getFareAttributes(query = {}, fields = [], orderBy2 = [], options = {}) {
@@ -4827,12 +4855,46 @@ function getStopsAsGeoJSON(query = {}, options = {}) {
4827
4855
  }
4828
4856
 
4829
4857
  // src/lib/gtfs/stop-times.ts
4858
+ import { omit as omit6 } from "lodash-es";
4859
+ import sqlString5 from "sqlstring-sqlite";
4830
4860
  function getStoptimes(query = {}, fields = [], orderBy2 = [], options = {}) {
4831
4861
  const db = options.db ?? openDb();
4832
4862
  const tableName = "stop_times";
4833
4863
  const selectClause = formatSelectClause(fields);
4834
- const whereClause = formatWhereClauses(query);
4864
+ let whereClause = "";
4835
4865
  const orderByClause = formatOrderByClause(orderBy2);
4866
+ const stoptimeQueryOmitKeys = ["date", "start_time", "end_time"];
4867
+ let stoptimeQuery = omit6(query, stoptimeQueryOmitKeys);
4868
+ const whereClauses = Object.entries(stoptimeQuery).map(
4869
+ ([key, value]) => formatWhereClause(key, value)
4870
+ );
4871
+ if (query.date) {
4872
+ if (typeof query.date !== "number") {
4873
+ throw new Error("`date` must be a number in yyyymmdd format");
4874
+ }
4875
+ const serviceIds = getServiceIdsByDate(query.date);
4876
+ const tripSubquery = `SELECT DISTINCT trip_id FROM trips WHERE service_id IN (${serviceIds.map((id) => sqlString5.escape(id)).join(",")})`;
4877
+ whereClauses.push(`trip_id IN (${tripSubquery})`);
4878
+ }
4879
+ if (query.start_time) {
4880
+ if (typeof query.start_time !== "string") {
4881
+ throw new Error("`start_time` must be a string in HH:mm:ss format");
4882
+ }
4883
+ whereClauses.push(
4884
+ `arrival_timestamp >= ${calculateSecondsFromMidnight(query.start_time)}`
4885
+ );
4886
+ }
4887
+ if (query.end_time) {
4888
+ if (typeof query.end_time !== "string") {
4889
+ throw new Error("`end_time` must be a string in HH:mm:ss format");
4890
+ }
4891
+ whereClauses.push(
4892
+ `departure_timestamp <= ${calculateSecondsFromMidnight(query.end_time)}`
4893
+ );
4894
+ }
4895
+ if (whereClauses.length > 0) {
4896
+ whereClause = `WHERE ${whereClauses.join(" AND ")}`;
4897
+ }
4836
4898
  return db.prepare(
4837
4899
  `${selectClause} FROM ${tableName} ${whereClause} ${orderByClause};`
4838
4900
  ).all();
@@ -5190,6 +5252,7 @@ export {
5190
5252
  getRunEvents,
5191
5253
  getRunsPieces,
5192
5254
  getServiceAlerts,
5255
+ getServiceIdsByDate,
5193
5256
  getShapes,
5194
5257
  getShapesAsGeoJSON,
5195
5258
  getStopAreas,