gtfs 4.19.1 → 4.19.2

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/README.md CHANGED
@@ -181,6 +181,8 @@ For GTFS files that contain more than one agency, you only need to list each GTF
181
181
  | `headers` | object | An object of HTTP headers in key:value format to use when fetching GTFS from the `url` specified. Optional. |
182
182
  | `prefix` | string | A prefix to be added to every ID field maintain uniqueness when importing multiple GTFS from multiple agencies. Optional. |
183
183
  | `exclude` | array | An array of GTFS file names (without `.txt`) to exclude when importing. Optional. |
184
+ | `fillEmptyAgencyId` | boolean | When `true`, fills empty `agency_id` on routes, fares, and other files for single-agency feeds. Useful for shared databases. Defaults to `false`. Optional. |
185
+ | `agencyId` | string | Explicit `agency_id` to use when `fillEmptyAgencyId` is `true` and `agency.txt` does not define one. Also backfills the `agency_id` on the agency row itself. If `agency.txt` already defines an `agency_id`, that value takes precedence. Optional. |
184
186
  | `realtimeAlerts` | object | An object containing a `url` field for GTFS-Realtime alerts and a `headers` field in key:value format to use when fetching GTFS-Realtime data. Optional. |
185
187
  | `realtimeTripUpdates` | object | An object containing a `url` field for GTFS-Realtime trip updates and a `headers` field in key:value format to use when fetching GTFS-Realtime data. Optional. |
186
188
  | `realtimeVehiclePositions` | object | An object containing a `url` field for GTFS-Realtime vehicle positions and a `headers` field in key:value format to use when fetching GTFS-Realtime data. Optional. |
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Tt as formatError, st as exportGtfs, xt as getConfig } from "../src-DuXcyA_N.js";
2
+ import { Tt as formatError, st as exportGtfs, xt as getConfig } from "../src-CdVKeHzG.js";
3
3
  import yargs from "yargs";
4
4
  import { hideBin } from "yargs/helpers";
5
5
  import PrettyError from "pretty-error";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Tt as formatError, ct as importGtfs, ft as openDb, ut as closeDb, xt as getConfig } from "../src-DuXcyA_N.js";
2
+ import { Tt as formatError, ct as importGtfs, ft as openDb, ut as closeDb, xt as getConfig } from "../src-CdVKeHzG.js";
3
3
  import yargs from "yargs";
4
4
  import { hideBin } from "yargs/helpers";
5
5
  import PrettyError from "pretty-error";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Tt as formatError, lt as updateGtfsRealtime, xt as getConfig } from "../src-DuXcyA_N.js";
2
+ import { Tt as formatError, lt as updateGtfsRealtime, xt as getConfig } from "../src-CdVKeHzG.js";
3
3
  import yargs from "yargs";
4
4
  import { hideBin } from "yargs/helpers";
5
5
  import PrettyError from "pretty-error";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Options } from "csv-parse";
2
- import Database, { Database as Database$1 } from "better-sqlite3";
2
+ import Database$1, { Database } from "better-sqlite3";
3
3
 
4
4
  //#region src/lib/errors.d.ts
5
5
  declare enum GtfsErrorCategory {
@@ -121,6 +121,20 @@ interface BaseConfigAgency {
121
121
  * A prefix to be added to every ID field maintain uniqueness when importing multiple GTFS from multiple agencies
122
122
  */
123
123
  prefix?: string;
124
+ /**
125
+ * When set to true and the feed contains exactly one agency, populates any empty `agency_id` fields
126
+ * on routes, fare_attributes, and other relevant files. Useful when merging single-agency feeds into
127
+ * a shared database.
128
+ *
129
+ * @defaultValue false
130
+ */
131
+ fillEmptyAgencyId?: boolean;
132
+ /**
133
+ * Explicit `agency_id` to use when `fillEmptyAgencyId` is true and `agency.txt` does not define
134
+ * one. Also backfills the `agency_id` on the agency row itself. If `agency.txt` already defines
135
+ * an `agency_id` and it differs from this value, the value from `agency.txt` takes precedence.
136
+ */
137
+ agencyId?: string;
124
138
  }
125
139
  type ConfigAgency = BaseConfigAgency & ({
126
140
  /**
@@ -137,7 +151,7 @@ interface Config {
137
151
  /**
138
152
  * An existing database instance to use instead of relying on node-gtfs to connect.
139
153
  */
140
- db?: Database$1;
154
+ db?: Database;
141
155
  /**
142
156
  * A path to an SQLite database. Defaults to using an in-memory database.
143
157
  */
@@ -233,7 +247,7 @@ type SqlWhere = Record<string, null | SqlValue | SqlValue[]>;
233
247
  type QueryResult<Base extends object, Select extends keyof Base> = [Select] extends [never] ? Base : Pick<Base, Select>;
234
248
  type SqlOrderBy = Array<[string, 'ASC' | 'DESC']>;
235
249
  interface QueryOptions {
236
- db?: Database$1;
250
+ db?: Database;
237
251
  bounding_box_side_m?: number;
238
252
  }
239
253
  interface Agency {
@@ -830,15 +844,15 @@ declare const exportGtfs: (initialConfig: Config) => Promise<void>;
830
844
  //#endregion
831
845
  //#region src/lib/db.d.ts
832
846
  declare function openDb(config?: {
833
- db?: Database.Database;
847
+ db?: Database$1.Database;
834
848
  sqlitePath?: string;
835
- } | null): Database.Database;
836
- declare function closeDb(db?: Database.Database | null): void;
837
- declare function deleteDb(db?: Database.Database | null): void;
849
+ } | null): Database$1.Database;
850
+ declare function closeDb(db?: Database$1.Database | null): void;
851
+ declare function deleteDb(db?: Database$1.Database | null): void;
838
852
  //#endregion
839
853
  //#region src/lib/advancedQuery.d.ts
840
854
  declare function advancedQuery(table: string, advancedQueryOptions: {
841
- db?: Database.Database;
855
+ db?: Database$1.Database;
842
856
  query?: SqlWhere;
843
857
  fields?: string[];
844
858
  orderBy?: SqlOrderBy;
@@ -1210,20 +1224,20 @@ declare function getVehiclePositions<Fields extends keyof VehiclePosition>(query
1210
1224
  //#region src/lib/gtfs-realtime/service-alerts.d.ts
1211
1225
  declare function getServiceAlerts<Fields extends keyof ServiceAlert>(query?: SqlWhere, fields?: Fields[], orderBy?: SqlOrderBy, options?: QueryOptions): {
1212
1226
  informed_entities: ServiceAlertInformedEntity[];
1213
- start_time: string | null;
1214
- end_time: string | null;
1215
1227
  id: string;
1216
- created_timestamp: UnixTimestamp;
1217
- expiration_timestamp: UnixTimestamp;
1218
1228
  active_period: string | null;
1219
1229
  cause: string | null;
1220
1230
  effect: string | null;
1221
1231
  url: string | null;
1232
+ start_time: string | null;
1233
+ end_time: string | null;
1222
1234
  header_text: string;
1223
1235
  description_text: string;
1224
1236
  tts_header_text: string | null;
1225
1237
  tts_description_text: string | null;
1226
1238
  severity_level: string | null;
1239
+ created_timestamp: UnixTimestamp;
1240
+ expiration_timestamp: UnixTimestamp;
1227
1241
  }[];
1228
1242
  //#endregion
1229
1243
  //#region src/lib/gtfs-realtime/service-alert-informed-entities.d.ts
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { $ as getCalendars, A as getStopsAsGeoJSON, B as getNetworks, C as getCalendarAttributes, Ct as untildify, D as getTimeframes, E as getTransfers, F as getRouteAttributes, G as getFrequencies, H as getLocationGroupStops, I as getRoutes, J as getFareRules, K as getFeedInfo, L as getRouteNetworks, M as getStopAreas, N as getShapes, O as getStoptimes, P as getShapesAsGeoJSON, Q as getFareAttributes, R as getRiderCategories, S as getDirections, St as prepDirectory, T as getTranslations, U as getLocationGroups, V as getLocations, W as getLevels, X as getFareMedia, Y as getFareProducts, Z as getFareLegRules, _ as getTimetableNotesReferences, _t as formatGtfsError, a as getDeadheads, at as getAgencies, b as getTimetableStopOrders, bt as generateFolderName, c as getVehiclePositions, ct as importGtfs, d as getTripCapacities, dt as deleteDb, et as getServiceIdsByDate, f as getRidership, ft as openDb, g as getTripsDatedVehicleJourneys, gt as GtfsWarningCode, h as getBoardAlights, ht as GtfsErrorCode, i as getDeadheadTimes, it as getAreas, j as getStopAttributes, k as getStops, l as getTripUpdates, lt as updateGtfsRealtime, m as getRideFeedInfo, mt as GtfsErrorCategory, n as getRunEvents, nt as getBookingRules, o as getServiceAlertInformedEntities, ot as advancedQuery, p as getRiderTrips, pt as GtfsError, q as getFareTransferRules, r as getOpsLocations, rt as getAttributions, s as getServiceAlerts, st as exportGtfs, t as getRunsPieces, tt as getCalendarDates, u as getStopTimeUpdates, ut as closeDb, v as getTimetableNotes, vt as isGtfsError, w as getTrips, wt as unzip, x as getTimetables, y as getTimetablePages, yt as isGtfsValidationError, z as getPathways } from "./src-DuXcyA_N.js";
1
+ import { $ as getCalendars, A as getStopsAsGeoJSON, B as getNetworks, C as getCalendarAttributes, Ct as untildify, D as getTimeframes, E as getTransfers, F as getRouteAttributes, G as getFrequencies, H as getLocationGroupStops, I as getRoutes, J as getFareRules, K as getFeedInfo, L as getRouteNetworks, M as getStopAreas, N as getShapes, O as getStoptimes, P as getShapesAsGeoJSON, Q as getFareAttributes, R as getRiderCategories, S as getDirections, St as prepDirectory, T as getTranslations, U as getLocationGroups, V as getLocations, W as getLevels, X as getFareMedia, Y as getFareProducts, Z as getFareLegRules, _ as getTimetableNotesReferences, _t as formatGtfsError, a as getDeadheads, at as getAgencies, b as getTimetableStopOrders, bt as generateFolderName, c as getVehiclePositions, ct as importGtfs, d as getTripCapacities, dt as deleteDb, et as getServiceIdsByDate, f as getRidership, ft as openDb, g as getTripsDatedVehicleJourneys, gt as GtfsWarningCode, h as getBoardAlights, ht as GtfsErrorCode, i as getDeadheadTimes, it as getAreas, j as getStopAttributes, k as getStops, l as getTripUpdates, lt as updateGtfsRealtime, m as getRideFeedInfo, mt as GtfsErrorCategory, n as getRunEvents, nt as getBookingRules, o as getServiceAlertInformedEntities, ot as advancedQuery, p as getRiderTrips, pt as GtfsError, q as getFareTransferRules, r as getOpsLocations, rt as getAttributions, s as getServiceAlerts, st as exportGtfs, t as getRunsPieces, tt as getCalendarDates, u as getStopTimeUpdates, ut as closeDb, v as getTimetableNotes, vt as isGtfsError, w as getTrips, wt as unzip, x as getTimetables, y as getTimetablePages, yt as isGtfsValidationError, z as getPathways } from "./src-CdVKeHzG.js";
2
2
 
3
3
  export { GtfsError, GtfsErrorCategory, GtfsErrorCode, GtfsWarningCode, advancedQuery, closeDb, deleteDb, exportGtfs, formatGtfsError, generateFolderName, getAgencies, getAreas, getAttributions, getBoardAlights, getBookingRules, getCalendarAttributes, getCalendarDates, getCalendars, getDeadheadTimes, getDeadheads, getDirections, getFareAttributes, getFareLegRules, getFareMedia, getFareProducts, getFareRules, getFareTransferRules, getFeedInfo, getFrequencies, getLevels, getLocationGroupStops, getLocationGroups, getLocations, getNetworks, getOpsLocations, getPathways, getRideFeedInfo, getRiderCategories, getRiderTrips, getRidership, getRouteAttributes, getRouteNetworks, getRoutes, getRunEvents, getRunsPieces, getServiceAlertInformedEntities, getServiceAlerts, getServiceIdsByDate, getShapes, getShapesAsGeoJSON, getStopAreas, getStopAttributes, getStopTimeUpdates, getStops, getStopsAsGeoJSON, getStoptimes, getTimeframes, getTimetableNotes, getTimetableNotesReferences, getTimetablePages, getTimetableStopOrders, getTimetables, getTransfers, getTranslations, getTripCapacities, getTripUpdates, getTrips, getTripsDatedVehicleJourneys, getVehiclePositions, importGtfs, isGtfsError, isGtfsValidationError, openDb, prepDirectory, untildify, unzip, updateGtfsRealtime };
@@ -7,7 +7,7 @@ import stripBomStream from "strip-bom-stream";
7
7
  import { temporaryDirectory } from "tempy";
8
8
  import mapSeries from "promise-map-series";
9
9
  import fs from "fs";
10
- import Database from "better-sqlite3";
10
+ import Database$1 from "better-sqlite3";
11
11
  import { homedir } from "node:os";
12
12
  import { cloneDeep, compact, filter, get, groupBy, last, noop, omit, omitBy, orderBy, pick, snakeCase, sortBy, without } from "lodash-es";
13
13
  import sanitize from "sanitize-filename";
@@ -281,7 +281,7 @@ function formatGtfsError(error, options = { verbosity: "developer" }) {
281
281
  //#region src/lib/db.ts
282
282
  const dbs = {};
283
283
  function setupDb(sqlitePath) {
284
- const db = new Database(untildify(sqlitePath));
284
+ const db = new Database$1(untildify(sqlitePath));
285
285
  db.pragma("journal_mode = OFF");
286
286
  db.pragma("synchronous = OFF");
287
287
  db.pragma("temp_store = MEMORY");
@@ -1134,6 +1134,42 @@ const extractGtfsFiles = async (task) => {
1134
1134
  });
1135
1135
  }
1136
1136
  };
1137
+ /**
1138
+ * Reads agency.txt from disk after extraction to find the single agency_id for a feed.
1139
+ * Returns the raw agency_id string if there is exactly one agency with a non-empty
1140
+ * agency_id, or undefined otherwise.
1141
+ */
1142
+ const getSingleAgencyId = (downloadDir, csvOptions, logWarning) => new Promise((resolve) => {
1143
+ const filepath = path.join(downloadDir, "agency.txt");
1144
+ if (!existsSync(filepath)) {
1145
+ resolve(void 0);
1146
+ return;
1147
+ }
1148
+ const rows = [];
1149
+ const parser = parse({
1150
+ columns: true,
1151
+ relax_quotes: true,
1152
+ trim: true,
1153
+ skip_empty_lines: true,
1154
+ ...csvOptions
1155
+ });
1156
+ parser.on("readable", () => {
1157
+ let record;
1158
+ while (record = parser.read()) rows.push(record);
1159
+ });
1160
+ parser.on("end", () => {
1161
+ if (rows.length !== 1) {
1162
+ resolve(void 0);
1163
+ return;
1164
+ }
1165
+ resolve(rows[0].agency_id?.trim() || void 0);
1166
+ });
1167
+ parser.on("error", (err) => {
1168
+ logWarning(`Unable to parse agency.txt for \`fillEmptyAgencyId\`: ${err.message}`);
1169
+ resolve(void 0);
1170
+ });
1171
+ createReadStream(filepath).pipe(stripBomStream()).pipe(parser);
1172
+ });
1137
1173
  const createGtfsTables = (db) => {
1138
1174
  for (const model of Object.values(models_exports)) {
1139
1175
  if (!model.schema) continue;
@@ -1179,7 +1215,20 @@ const createGtfsIndexes = (db) => {
1179
1215
  }
1180
1216
  }
1181
1217
  };
1182
- const formatGtfsLine = (line, model, totalLineCount) => {
1218
+ const AGENCY_ID_BACKFILL_MODELS = /* @__PURE__ */ new Set([
1219
+ "agency",
1220
+ "routes",
1221
+ "fare_attributes",
1222
+ "trip_capacity",
1223
+ "rider_trip",
1224
+ "ridership"
1225
+ ]);
1226
+ function shouldBackfillAgencyId(model, formattedLine) {
1227
+ if (AGENCY_ID_BACKFILL_MODELS.has(model.filenameBase)) return true;
1228
+ if (model.filenameBase === "attributions") return formattedLine.route_id == null && formattedLine.trip_id == null;
1229
+ return false;
1230
+ }
1231
+ const formatGtfsLine = (line, model, totalLineCount, fillEmptyAgencyId, agencyId) => {
1183
1232
  const lineNumber = totalLineCount + 1;
1184
1233
  const formattedLine = {};
1185
1234
  const filenameBase = model.filenameBase;
@@ -1215,6 +1264,7 @@ const formatGtfsLine = (line, model, totalLineCount) => {
1215
1264
  if (type === "json") value = JSON.stringify(value);
1216
1265
  formattedLine[name] = value;
1217
1266
  }
1267
+ if (fillEmptyAgencyId && agencyId !== void 0 && formattedLine.agency_id == null && shouldBackfillAgencyId(model, formattedLine)) formattedLine.agency_id = agencyId;
1218
1268
  return formattedLine;
1219
1269
  };
1220
1270
  const BATCH_SIZE = 1e5;
@@ -1291,7 +1341,7 @@ const importGtfsFiles = async (db, task) => {
1291
1341
  let record;
1292
1342
  while (record = parser.read()) {
1293
1343
  totalLineCount += 1;
1294
- lines.push(formatGtfsLine(record, model, totalLineCount));
1344
+ lines.push(formatGtfsLine(record, model, totalLineCount, task.fillEmptyAgencyId, task.agencyId));
1295
1345
  if (lines.length >= BATCH_SIZE) {
1296
1346
  insertLines(lines);
1297
1347
  lines = [];
@@ -1388,7 +1438,7 @@ const importGtfsFiles = async (db, task) => {
1388
1438
  return;
1389
1439
  }
1390
1440
  totalLineCount += 1;
1391
- const line = formatGtfsLine({ geojson: data }, model, totalLineCount);
1441
+ const line = formatGtfsLine({ geojson: data }, model, totalLineCount, task.fillEmptyAgencyId, task.agencyId);
1392
1442
  try {
1393
1443
  insertLines([line]);
1394
1444
  task.log(`Importing - ${filename} - ${totalLineCount} lines imported\r`, true);
@@ -1463,6 +1513,8 @@ async function importGtfs(initialConfig) {
1463
1513
  ignoreErrors: config.ignoreErrors,
1464
1514
  sqlitePath: config.sqlitePath,
1465
1515
  prefix: agency.prefix,
1516
+ fillEmptyAgencyId: agency.fillEmptyAgencyId ?? false,
1517
+ agencyId: agency.agencyId,
1466
1518
  currentTimestamp: Math.floor(Date.now() / 1e3),
1467
1519
  log: log(config),
1468
1520
  logWarning: logWarning(config),
@@ -1474,6 +1526,13 @@ async function importGtfs(initialConfig) {
1474
1526
  await downloadGtfsFiles(task);
1475
1527
  } else Object.assign(task, { path: agency.path });
1476
1528
  await extractGtfsFiles(task);
1529
+ if (task.fillEmptyAgencyId) {
1530
+ const agencyIdFromGtfs = await getSingleAgencyId(task.downloadDir, task.csvOptions, task.logWarning);
1531
+ if (agencyIdFromGtfs !== void 0) {
1532
+ if (task.agencyId !== void 0 && task.agencyId !== agencyIdFromGtfs) task.logWarning(`\`agencyId\` "${task.agencyId}" does not match the \`agency_id\` "${agencyIdFromGtfs}" in agency.txt. Using the value from agency.txt.`);
1533
+ task.agencyId = agencyIdFromGtfs;
1534
+ } else if (task.agencyId === void 0) task.logWarning("`fillEmptyAgencyId` is set but a single `agency_id` could not be determined for this feed and no `agencyId` was provided in config. `agency_id` will not be backfilled.");
1535
+ }
1477
1536
  await importGtfsFiles(db, task);
1478
1537
  await updateGtfsRealtimeData(task);
1479
1538
  await rm(tempPath, { recursive: true });
@@ -2308,7 +2367,7 @@ function getVehiclePositions(query = {}, fields = [], orderBy = [], options = {}
2308
2367
 
2309
2368
  //#endregion
2310
2369
  //#region src/lib/gtfs-realtime/service-alerts.ts
2311
- const ENTITY_COLUMNS = new Set([
2370
+ const ENTITY_COLUMNS = /* @__PURE__ */ new Set([
2312
2371
  "alert_id",
2313
2372
  "stop_id",
2314
2373
  "route_id",
@@ -2414,4 +2473,4 @@ function getRunsPieces(query = {}, fields = [], orderBy = [], options = {}) {
2414
2473
 
2415
2474
  //#endregion
2416
2475
  export { getCalendars as $, getStopsAsGeoJSON as A, getNetworks as B, getCalendarAttributes as C, untildify as Ct, getTimeframes as D, getTransfers as E, getRouteAttributes as F, getFrequencies as G, getLocationGroupStops as H, getRoutes as I, getFareRules as J, getFeedInfo as K, getRouteNetworks as L, getStopAreas as M, getShapes as N, getStoptimes as O, getShapesAsGeoJSON as P, getFareAttributes as Q, getRiderCategories as R, getDirections as S, prepDirectory as St, getTranslations as T, formatError as Tt, getLocationGroups as U, getLocations as V, getLevels as W, getFareMedia as X, getFareProducts as Y, getFareLegRules as Z, getTimetableNotesReferences as _, formatGtfsError as _t, getDeadheads as a, getAgencies as at, getTimetableStopOrders as b, generateFolderName as bt, getVehiclePositions as c, importGtfs as ct, getTripCapacities as d, deleteDb as dt, getServiceIdsByDate as et, getRidership as f, openDb as ft, getTripsDatedVehicleJourneys as g, GtfsWarningCode as gt, getBoardAlights as h, GtfsErrorCode as ht, getDeadheadTimes as i, getAreas as it, getStopAttributes as j, getStops as k, getTripUpdates as l, updateGtfsRealtime as lt, getRideFeedInfo as m, GtfsErrorCategory as mt, getRunEvents as n, getBookingRules as nt, getServiceAlertInformedEntities as o, advancedQuery as ot, getRiderTrips as p, GtfsError as pt, getFareTransferRules as q, getOpsLocations as r, getAttributions as rt, getServiceAlerts as s, exportGtfs as st, getRunsPieces as t, getCalendarDates as tt, getStopTimeUpdates as u, closeDb as ut, getTimetableNotes as v, isGtfsError as vt, getTrips as w, unzip as wt, getTimetables as x, getConfig as xt, getTimetablePages as y, isGtfsValidationError as yt, getPathways as z };
2417
- //# sourceMappingURL=src-DuXcyA_N.js.map
2476
+ //# sourceMappingURL=src-CdVKeHzG.js.map