gtfs 4.17.7 → 4.18.1

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
@@ -130,6 +130,12 @@ var agency = {
130
130
  name: "agency_email",
131
131
  type: "text",
132
132
  nocase: true
133
+ },
134
+ {
135
+ name: "cemv_support",
136
+ type: "integer",
137
+ min: 0,
138
+ max: 2
133
139
  }
134
140
  ]
135
141
  };
@@ -1060,6 +1066,12 @@ var routes = {
1060
1066
  name: "network_id",
1061
1067
  type: "text",
1062
1068
  prefix: true
1069
+ },
1070
+ {
1071
+ name: "cemv_support",
1072
+ type: "integer",
1073
+ min: 0,
1074
+ max: 2
1063
1075
  }
1064
1076
  ]
1065
1077
  };
@@ -1314,6 +1326,12 @@ var stops = {
1314
1326
  {
1315
1327
  name: "platform_code",
1316
1328
  type: "text"
1329
+ },
1330
+ {
1331
+ name: "stop_access",
1332
+ type: "integer",
1333
+ min: 0,
1334
+ max: 1
1317
1335
  }
1318
1336
  ]
1319
1337
  };
@@ -4147,7 +4165,7 @@ function isValidJSON(string) {
4147
4165
  try {
4148
4166
  JSON.parse(string);
4149
4167
  return true;
4150
- } catch (error) {
4168
+ } catch {
4151
4169
  return false;
4152
4170
  }
4153
4171
  }
@@ -4212,7 +4230,7 @@ function formatProperties(properties) {
4212
4230
  if (formattedRouteTextColor) {
4213
4231
  formattedProperties.route_text_color = formattedRouteTextColor;
4214
4232
  }
4215
- if (properties.routes) {
4233
+ if (properties.routes && Array.isArray(properties.routes)) {
4216
4234
  formattedProperties.routes = properties.routes.map(
4217
4235
  (route) => formatProperties(route)
4218
4236
  );
@@ -4277,7 +4295,8 @@ function setDefaultConfig(initialConfig) {
4277
4295
  ignoreDuplicates: false,
4278
4296
  ignoreErrors: false,
4279
4297
  gtfsRealtimeExpirationSeconds: 0,
4280
- verbose: true
4298
+ verbose: true,
4299
+ downloadTimeout: 3e4
4281
4300
  };
4282
4301
  return {
4283
4302
  ...defaults,
@@ -4425,56 +4444,9 @@ function pluralize(singularWord, pluralWord, count) {
4425
4444
  }
4426
4445
 
4427
4446
  // src/lib/import-gtfs-realtime.ts
4428
- async function fetchGtfsRealtimeData(urlConfig, task) {
4429
- task.log(`Downloading GTFS-Realtime from ${urlConfig.url}`);
4430
- const response = await fetch(urlConfig.url, {
4431
- method: "GET",
4432
- headers: {
4433
- ...urlConfig.headers ?? {},
4434
- "Accept-Encoding": "gzip"
4435
- },
4436
- signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
4437
- });
4438
- if (response.status !== 200) {
4439
- task.logWarning(
4440
- `Unable to download GTFS-Realtime from ${urlConfig.url}. Got status ${response.status}.`
4441
- );
4442
- return null;
4443
- }
4444
- const buffer = await response.arrayBuffer();
4445
- const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
4446
- new Uint8Array(buffer)
4447
- );
4448
- return GtfsRealtimeBindings.transit_realtime.FeedMessage.toObject(message, {
4449
- enums: String,
4450
- longs: String,
4451
- bytes: String,
4452
- defaults: false,
4453
- arrays: true,
4454
- objects: true,
4455
- oneofs: true
4456
- });
4457
- }
4458
- function removeExpiredRealtimeData(config) {
4459
- const db = openDb(config);
4460
- log(config)(`Removing expired GTFS-Realtime data`);
4461
- db.prepare(
4462
- `DELETE FROM vehicle_positions WHERE expiration_timestamp <= strftime('%s','now')`
4463
- ).run();
4464
- db.prepare(
4465
- `DELETE FROM trip_updates WHERE expiration_timestamp <= strftime('%s','now')`
4466
- ).run();
4467
- db.prepare(
4468
- `DELETE FROM stop_time_updates WHERE expiration_timestamp <= strftime('%s','now')`
4469
- ).run();
4470
- db.prepare(
4471
- `DELETE FROM service_alerts WHERE expiration_timestamp <= strftime('%s','now')`
4472
- ).run();
4473
- db.prepare(
4474
- `DELETE FROM service_alert_informed_entities WHERE expiration_timestamp <= strftime('%s','now')`
4475
- ).run();
4476
- log(config)(`Removed expired GTFS-Realtime data\r`, true);
4477
- }
4447
+ var BATCH_SIZE = 1e3;
4448
+ var MAX_RETRIES = 3;
4449
+ var RETRY_DELAY = 1e3;
4478
4450
  function prepareRealtimeFieldValue(entity, column, task) {
4479
4451
  if (column.name === "created_timestamp") {
4480
4452
  return task.currentTimestamp;
@@ -4491,163 +4463,265 @@ function prepareRealtimeFieldValue(entity, column, task) {
4491
4463
  );
4492
4464
  return column.type === "json" ? JSON.stringify(prefixedValue) : prefixedValue;
4493
4465
  }
4494
- async function processRealtimeAlerts(db, gtfsRealtimeData, task) {
4495
- const alertStmt = db.prepare(
4496
- `REPLACE INTO ${serviceAlerts.filenameBase} (${serviceAlerts.schema.map((column) => column.name).join(
4497
- ", "
4498
- )}) VALUES (${serviceAlerts.schema.map(() => "?").join(", ")})`
4499
- );
4500
- const informedEntityStmt = db.prepare(
4501
- `REPLACE INTO ${serviceAlertInformedEntities.filenameBase} (${serviceAlertInformedEntities.schema.map((column) => column.name).join(
4502
- ", "
4503
- )}) VALUES (${serviceAlertInformedEntities.schema.map(() => "?").join(", ")})`
4466
+ function createPreparedStatement(db, model) {
4467
+ const columns = model.schema.map((column) => column.name);
4468
+ const placeholders = model.schema.map(() => "?").join(", ");
4469
+ return db.prepare(
4470
+ `REPLACE INTO ${model.filenameBase} (${columns.join(", ")}) VALUES (${placeholders})`
4504
4471
  );
4505
- let totalLineCount = 0;
4506
- db.transaction(() => {
4507
- for (const entity of gtfsRealtimeData.entity) {
4508
- const fieldValues = serviceAlerts.schema.map(
4509
- (column) => prepareRealtimeFieldValue(entity, column, task)
4472
+ }
4473
+ async function processBatch(items, batchSize, processor) {
4474
+ let totalRecordCount = 0;
4475
+ let totalErrorCount = 0;
4476
+ for (let i = 0; i < items.length; i += batchSize) {
4477
+ const batch = items.slice(i, i + batchSize);
4478
+ try {
4479
+ const result = await processor(batch);
4480
+ totalRecordCount += result.recordCount;
4481
+ totalErrorCount += result.errorCount;
4482
+ } catch (error) {
4483
+ const errorMessage = error instanceof Error ? error.message : String(error);
4484
+ totalErrorCount += batch.length;
4485
+ console.error(`Batch processing error: ${errorMessage}`);
4486
+ }
4487
+ }
4488
+ return { recordCount: totalRecordCount, errorCount: totalErrorCount };
4489
+ }
4490
+ async function fetchGtfsRealtimeData(type, task) {
4491
+ const urlConfig = getUrlConfig(type, task);
4492
+ if (!urlConfig) {
4493
+ return null;
4494
+ }
4495
+ task.log(`Importing - GTFS-Realtime from ${urlConfig.url}`);
4496
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
4497
+ try {
4498
+ const response = await fetch(urlConfig.url, {
4499
+ method: "GET",
4500
+ headers: {
4501
+ ...urlConfig.headers ?? {},
4502
+ "Accept-Encoding": "gzip"
4503
+ },
4504
+ signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
4505
+ });
4506
+ if (response.status !== 200) {
4507
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
4508
+ }
4509
+ const buffer = await response.arrayBuffer();
4510
+ const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
4511
+ new Uint8Array(buffer)
4510
4512
  );
4511
- try {
4512
- alertStmt.run(fieldValues);
4513
- if (entity.alert.informedEntity?.length) {
4514
- const informedEntities = entity.alert.informedEntity.map(
4515
- (informedEntity) => {
4513
+ const feedMessage = GtfsRealtimeBindings.transit_realtime.FeedMessage.toObject(message, {
4514
+ enums: String,
4515
+ longs: String,
4516
+ bytes: String,
4517
+ defaults: false,
4518
+ arrays: true,
4519
+ objects: true,
4520
+ oneofs: true
4521
+ });
4522
+ return feedMessage;
4523
+ } catch (error) {
4524
+ const errorMessage = error instanceof Error ? error.message : String(error);
4525
+ if (attempt === MAX_RETRIES) {
4526
+ if (task.ignoreErrors) {
4527
+ task.logError(
4528
+ `Failed to fetch ${type} after ${MAX_RETRIES} attempts: ${errorMessage}`
4529
+ );
4530
+ return null;
4531
+ }
4532
+ throw error;
4533
+ }
4534
+ task.logWarning(`Attempt ${attempt} failed for ${type}: ${errorMessage}`);
4535
+ await new Promise(
4536
+ (resolve) => setTimeout(resolve, RETRY_DELAY * attempt)
4537
+ );
4538
+ }
4539
+ }
4540
+ return null;
4541
+ }
4542
+ function getUrlConfig(type, task) {
4543
+ switch (type) {
4544
+ case "alerts":
4545
+ return task.realtimeAlerts;
4546
+ case "tripupdates":
4547
+ return task.realtimeTripUpdates;
4548
+ case "vehiclepositions":
4549
+ return task.realtimeVehiclePositions;
4550
+ default:
4551
+ return void 0;
4552
+ }
4553
+ }
4554
+ function createServiceAlertsProcessor(db, task) {
4555
+ const alertStmt = createPreparedStatement(db, serviceAlerts);
4556
+ const informedEntityStmt = createPreparedStatement(
4557
+ db,
4558
+ serviceAlertInformedEntities
4559
+ );
4560
+ return async (batch) => {
4561
+ let recordCount = 0;
4562
+ let errorCount = 0;
4563
+ db.transaction(() => {
4564
+ for (const entity of batch) {
4565
+ try {
4566
+ const alertValues = serviceAlerts.schema.map((column) => prepareRealtimeFieldValue(entity, column, task));
4567
+ alertStmt.run(alertValues);
4568
+ recordCount++;
4569
+ if (entity.alert?.informedEntity?.length) {
4570
+ for (const informedEntity of entity.alert.informedEntity) {
4516
4571
  informedEntity.parent = entity;
4517
- return serviceAlertInformedEntities.schema.map(
4572
+ const entityValues = serviceAlertInformedEntities.schema.map(
4518
4573
  (column) => prepareRealtimeFieldValue(informedEntity, column, task)
4519
4574
  );
4575
+ informedEntityStmt.run(entityValues);
4576
+ recordCount++;
4520
4577
  }
4521
- );
4522
- for (const values of informedEntities) {
4523
- informedEntityStmt.run(values);
4524
4578
  }
4579
+ } catch (error) {
4580
+ const errorMessage = error instanceof Error ? error.message : String(error);
4581
+ errorCount++;
4582
+ task.logWarning(`Alert processing error: ${errorMessage}`);
4525
4583
  }
4526
- totalLineCount++;
4527
- } catch (error) {
4528
- task.logWarning(`Import error: ${error.message}`);
4529
4584
  }
4530
- }
4531
- task.log(
4532
- `Importing - GTFS-Realtime service alerts - ${totalLineCount} entries imported\r`,
4533
- true
4534
- );
4535
- })();
4585
+ })();
4586
+ return { recordCount, errorCount };
4587
+ };
4536
4588
  }
4537
- async function processRealtimeTripUpdates(db, gtfsRealtimeData, task) {
4538
- let totalLineCount = 0;
4539
- const tripUpdateStmt = db.prepare(
4540
- `REPLACE INTO ${tripUpdates.filenameBase} (${tripUpdates.schema.map((column) => column.name).join(
4541
- ", "
4542
- )}) VALUES (${tripUpdates.schema.map(() => "?").join(", ")})`
4589
+ function createTripUpdatesProcessor(db, task) {
4590
+ const tripUpdateStmt = createPreparedStatement(
4591
+ db,
4592
+ tripUpdates
4543
4593
  );
4544
- const stopTimeStmt = db.prepare(
4545
- `REPLACE INTO ${stopTimeUpdates.filenameBase} (${stopTimeUpdates.schema.map((column) => column.name).join(
4546
- ", "
4547
- )}) VALUES (${stopTimeUpdates.schema.map(() => "?").join(", ")})`
4594
+ const stopTimeStmt = createPreparedStatement(
4595
+ db,
4596
+ stopTimeUpdates
4548
4597
  );
4549
- db.transaction(() => {
4550
- for (const entity of gtfsRealtimeData.entity) {
4551
- try {
4552
- const fieldValues = tripUpdates.schema.map(
4553
- (column) => prepareRealtimeFieldValue(entity, column, task)
4554
- );
4555
- tripUpdateStmt.run(fieldValues);
4556
- for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) {
4557
- stopTimeUpdate.parent = entity;
4558
- const values = stopTimeUpdates.schema.map(
4559
- (column) => prepareRealtimeFieldValue(stopTimeUpdate, column, task)
4560
- );
4561
- stopTimeStmt.run(values);
4598
+ return async (batch) => {
4599
+ let recordCount = 0;
4600
+ let errorCount = 0;
4601
+ db.transaction(() => {
4602
+ for (const entity of batch) {
4603
+ try {
4604
+ const tripUpdateValues = tripUpdates.schema.map((column) => prepareRealtimeFieldValue(entity, column, task));
4605
+ tripUpdateStmt.run(tripUpdateValues);
4606
+ recordCount++;
4607
+ if (entity.tripUpdate?.stopTimeUpdate?.length) {
4608
+ for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) {
4609
+ stopTimeUpdate.parent = entity;
4610
+ const stopTimeValues = stopTimeUpdates.schema.map(
4611
+ (column) => prepareRealtimeFieldValue(stopTimeUpdate, column, task)
4612
+ );
4613
+ stopTimeStmt.run(stopTimeValues);
4614
+ recordCount++;
4615
+ }
4616
+ }
4617
+ } catch (error) {
4618
+ const errorMessage = error instanceof Error ? error.message : String(error);
4619
+ errorCount++;
4620
+ task.logWarning(`Trip update processing error: ${errorMessage}`);
4562
4621
  }
4563
- totalLineCount++;
4564
- } catch (error) {
4565
- task.logWarning(`Import error: ${error.message}`);
4566
4622
  }
4567
- }
4568
- task.log(
4569
- `Importing - GTFS-Realtime trip updates - ${totalLineCount} entries imported\r`,
4570
- true
4571
- );
4572
- })();
4623
+ })();
4624
+ return { recordCount, errorCount };
4625
+ };
4573
4626
  }
4574
- async function processRealtimeVehiclePositions(db, gtfsRealtimeData, task) {
4575
- let totalLineCount = 0;
4576
- const vehiclePositionStmt = db.prepare(
4577
- `REPLACE INTO ${vehiclePositions.filenameBase} (${vehiclePositions.schema.map((column) => column.name).join(
4578
- ", "
4579
- )}) VALUES (${vehiclePositions.schema.map(() => "?").join(", ")})`
4627
+ function createVehiclePositionsProcessor(db, task) {
4628
+ const vehiclePositionStmt = createPreparedStatement(
4629
+ db,
4630
+ vehiclePositions
4580
4631
  );
4581
- db.transaction(() => {
4582
- for (const entity of gtfsRealtimeData.entity) {
4583
- try {
4584
- const fieldValues = vehiclePositions.schema.map((column) => prepareRealtimeFieldValue(entity, column, task));
4585
- vehiclePositionStmt.run(fieldValues);
4586
- totalLineCount++;
4587
- } catch (error) {
4588
- task.logWarning(`Import error: ${error.message}`);
4632
+ return async (batch) => {
4633
+ let recordCount = 0;
4634
+ let errorCount = 0;
4635
+ db.transaction(() => {
4636
+ for (const entity of batch) {
4637
+ try {
4638
+ const fieldValues = vehiclePositions.schema.map((column) => prepareRealtimeFieldValue(entity, column, task));
4639
+ vehiclePositionStmt.run(fieldValues);
4640
+ recordCount++;
4641
+ } catch (error) {
4642
+ const errorMessage = error instanceof Error ? error.message : String(error);
4643
+ errorCount++;
4644
+ task.logWarning(`Vehicle position processing error: ${errorMessage}`);
4645
+ }
4589
4646
  }
4647
+ })();
4648
+ return { recordCount, errorCount };
4649
+ };
4650
+ }
4651
+ function removeExpiredRealtimeData(config) {
4652
+ const db = openDb(config);
4653
+ log(config)(`Removing expired GTFS-Realtime data`);
4654
+ db.transaction(() => {
4655
+ const tables = [
4656
+ "vehicle_positions",
4657
+ "trip_updates",
4658
+ "stop_time_updates",
4659
+ "service_alerts",
4660
+ "service_alert_informed_entities"
4661
+ ];
4662
+ for (const table of tables) {
4663
+ db.prepare(
4664
+ `DELETE FROM ${table} WHERE expiration_timestamp <= strftime('%s','now')`
4665
+ ).run();
4590
4666
  }
4591
- task.log(
4592
- `Importing - GTFS-Realtime vehicle positions - ${totalLineCount} entries imported\r`,
4593
- true
4594
- );
4595
4667
  })();
4668
+ log(config)(`Removed expired GTFS-Realtime data\r`, true);
4596
4669
  }
4597
4670
  async function updateGtfsRealtimeData(task) {
4598
- if (task.realtimeAlerts === void 0 && task.realtimeTripUpdates === void 0 && task.realtimeVehiclePositions === void 0) {
4671
+ if (!task.realtimeAlerts && !task.realtimeTripUpdates && !task.realtimeVehiclePositions) {
4599
4672
  return;
4600
4673
  }
4674
+ const [alertsData, tripUpdatesData, vehiclePositionsData] = await Promise.all(
4675
+ [
4676
+ task.realtimeAlerts?.url ? fetchGtfsRealtimeData("alerts", task) : null,
4677
+ task.realtimeTripUpdates?.url ? fetchGtfsRealtimeData("tripupdates", task) : null,
4678
+ task.realtimeVehiclePositions?.url ? fetchGtfsRealtimeData("vehiclepositions", task) : null
4679
+ ]
4680
+ );
4601
4681
  const db = openDb({ sqlitePath: task.sqlitePath });
4602
- if (task.realtimeAlerts?.url) {
4603
- try {
4604
- const alertsData = await fetchGtfsRealtimeData(task.realtimeAlerts, task);
4605
- if (alertsData?.entity) {
4606
- await processRealtimeAlerts(db, alertsData, task);
4607
- }
4608
- } catch (error) {
4609
- if (task.ignoreErrors) {
4610
- task.logError(error.message);
4611
- } else {
4612
- throw error;
4613
- }
4614
- }
4682
+ const recordCounts = {
4683
+ alerts: 0,
4684
+ tripupdates: 0,
4685
+ vehiclepositions: 0
4686
+ };
4687
+ const processingPromises = [];
4688
+ if (alertsData?.entity?.length) {
4689
+ processingPromises.push(
4690
+ processBatch(
4691
+ alertsData.entity,
4692
+ BATCH_SIZE,
4693
+ createServiceAlertsProcessor(db, task)
4694
+ ).then((result) => {
4695
+ recordCounts.alerts = result.recordCount;
4696
+ })
4697
+ );
4615
4698
  }
4616
- if (task.realtimeTripUpdates?.url) {
4617
- try {
4618
- const tripUpdatesData = await fetchGtfsRealtimeData(
4619
- task.realtimeTripUpdates,
4620
- task
4621
- );
4622
- if (tripUpdatesData?.entity) {
4623
- await processRealtimeTripUpdates(db, tripUpdatesData, task);
4624
- }
4625
- } catch (error) {
4626
- if (task.ignoreErrors) {
4627
- task.logError(error.message);
4628
- } else {
4629
- throw error;
4630
- }
4631
- }
4699
+ if (tripUpdatesData?.entity?.length) {
4700
+ processingPromises.push(
4701
+ processBatch(
4702
+ tripUpdatesData.entity,
4703
+ BATCH_SIZE,
4704
+ createTripUpdatesProcessor(db, task)
4705
+ ).then((result) => {
4706
+ recordCounts.tripupdates = result.recordCount;
4707
+ })
4708
+ );
4632
4709
  }
4633
- if (task.realtimeVehiclePositions?.url) {
4634
- try {
4635
- const vehiclePositionsData = await fetchGtfsRealtimeData(
4636
- task.realtimeVehiclePositions,
4637
- task
4638
- );
4639
- if (vehiclePositionsData?.entity) {
4640
- await processRealtimeVehiclePositions(db, vehiclePositionsData, task);
4641
- }
4642
- } catch (error) {
4643
- if (task.ignoreErrors) {
4644
- task.logError(error.message);
4645
- } else {
4646
- throw error;
4647
- }
4648
- }
4710
+ if (vehiclePositionsData?.entity?.length) {
4711
+ processingPromises.push(
4712
+ processBatch(
4713
+ vehiclePositionsData.entity,
4714
+ BATCH_SIZE,
4715
+ createVehiclePositionsProcessor(db, task)
4716
+ ).then((result) => {
4717
+ recordCounts.vehiclepositions = result.recordCount;
4718
+ })
4719
+ );
4649
4720
  }
4650
- task.log(`GTFS-Realtime data import complete`);
4721
+ await Promise.all(processingPromises);
4722
+ task.log(
4723
+ `GTFS-Realtime import complete: ${recordCounts.alerts} alerts, ${recordCounts.tripupdates} trip updates, ${recordCounts.vehiclepositions} vehicle positions`
4724
+ );
4651
4725
  }
4652
4726
  async function updateGtfsRealtime(initialConfig) {
4653
4727
  const config = setDefaultConfig(initialConfig);
@@ -4681,8 +4755,9 @@ async function updateGtfsRealtime(initialConfig) {
4681
4755
  };
4682
4756
  await updateGtfsRealtimeData(task);
4683
4757
  } catch (error) {
4758
+ const errorMessage = error instanceof Error ? error.message : String(error);
4684
4759
  if (config.ignoreErrors) {
4685
- logError(config)(error.message);
4760
+ logError(config)(errorMessage);
4686
4761
  } else {
4687
4762
  throw error;
4688
4763
  }
@@ -4697,7 +4772,7 @@ async function updateGtfsRealtime(initialConfig) {
4697
4772
  `
4698
4773
  );
4699
4774
  } catch (error) {
4700
- if (error?.code === "SQLITE_CANTOPEN") {
4775
+ if (error.code === "SQLITE_CANTOPEN") {
4701
4776
  logError(config)(
4702
4777
  `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
4703
4778
  );
@@ -4736,7 +4811,7 @@ var extractGtfsFiles = async (task) => {
4736
4811
  throw new Error("No `path` specified in config");
4737
4812
  }
4738
4813
  const gtfsPath = untildify(task.path);
4739
- task.log(`Importing GTFS from ${task.path}\r`);
4814
+ task.log(`Importing static GTFS from ${task.path}\r`);
4740
4815
  if (path2.extname(gtfsPath) === ".zip") {
4741
4816
  try {
4742
4817
  await unzip(gtfsPath, task.downloadDir);
@@ -4819,11 +4894,11 @@ var createGtfsTables = (db) => {
4819
4894
  if (column.type === "time") {
4820
4895
  sqlColumnCreateStatements.push(
4821
4896
  `${getTimestampColumnName(column.name)} INTEGER GENERATED ALWAYS AS (
4822
- CASE
4823
- WHEN ${column.name} IS NULL OR ${column.name} = '' THEN NULL
4897
+ CASE
4898
+ WHEN ${column.name} IS NULL OR ${column.name} = '' THEN NULL
4824
4899
  ELSE CAST(
4825
- substr(${column.name}, 1, instr(${column.name}, ':') - 1) * 3600 +
4826
- substr(${column.name}, instr(${column.name}, ':') + 1, 2) * 60 +
4900
+ substr(${column.name}, 1, instr(${column.name}, ':') - 1) * 3600 +
4901
+ substr(${column.name}, instr(${column.name}, ':') + 1, 2) * 60 +
4827
4902
  substr(${column.name}, -2) AS INTEGER
4828
4903
  )
4829
4904
  END
@@ -4880,7 +4955,7 @@ var formatGtfsLine = (line, model, totalLineCount) => {
4880
4955
  continue;
4881
4956
  }
4882
4957
  if (type === "date") {
4883
- value = value.replace(/-/g, "");
4958
+ value = value?.toString().replace(/-/g, "");
4884
4959
  if (value.length !== 8) {
4885
4960
  throw new Error(
4886
4961
  `Invalid date in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}.`
@@ -4896,203 +4971,212 @@ var formatGtfsLine = (line, model, totalLineCount) => {
4896
4971
  }
4897
4972
  return formattedLine;
4898
4973
  };
4899
- var BATCH_SIZE = 1e5;
4900
- var importGtfsFiles = (db, task) => mapSeries2(
4901
- Object.values(models_exports),
4902
- (model) => new Promise((resolve, reject) => {
4903
- let totalLineCount = 0;
4904
- const filename = `${model.filenameBase}.${model.filenameExtension}`;
4905
- if (task.exclude && task.exclude.includes(model.filenameBase)) {
4906
- task.log(`Skipping - ${filename}\r`);
4907
- resolve();
4908
- return;
4909
- }
4910
- if (model.extension === "gtfs-realtime") {
4911
- resolve();
4912
- return;
4913
- }
4914
- const filepath = path2.join(task.downloadDir, `${filename}`);
4915
- if (!existsSync2(filepath)) {
4916
- if (!model.nonstandard) {
4917
- task.log(`Importing - ${filename} - No file found\r`);
4974
+ var BATCH_SIZE2 = 1e5;
4975
+ var importGtfsFiles = async (db, task) => {
4976
+ await mapSeries2(
4977
+ Object.values(models_exports),
4978
+ (model) => new Promise((resolve, reject) => {
4979
+ let totalLineCount = 0;
4980
+ const filename = `${model.filenameBase}.${model.filenameExtension}`;
4981
+ if (task.exclude && task.exclude.includes(model.filenameBase)) {
4982
+ task.log(`Skipping - ${filename}\r`);
4983
+ resolve();
4984
+ return;
4918
4985
  }
4919
- resolve();
4920
- return;
4921
- }
4922
- task.log(`Importing - ${filename}\r`);
4923
- const columns = model.schema;
4924
- const prefixedColumns = new Set(
4925
- columns.filter((column) => column.prefix).map((column) => column.name)
4926
- );
4927
- const prepareStatement = `INSERT ${task.ignoreDuplicates ? "OR IGNORE" : ""} INTO ${model.filenameBase} (${columns.map(({ name }) => name).join(", ")}) VALUES (${columns.map(({ name }) => `@${name}`).join(", ")})`;
4928
- const insert = db.prepare(prepareStatement);
4929
- const insertLines = db.transaction((lines) => {
4930
- for (const [rowNumber, line] of Object.entries(lines)) {
4931
- try {
4932
- if (task.prefix === void 0) {
4933
- insert.run(line);
4934
- } else {
4935
- const prefixedLine = Object.fromEntries(
4936
- Object.entries(
4937
- line
4938
- ).map(([columnName, value]) => [
4939
- columnName,
4940
- applyPrefixToValue(
4941
- value,
4942
- prefixedColumns.has(columnName),
4943
- task.prefix
4944
- )
4945
- ])
4946
- );
4947
- insert.run(prefixedLine);
4948
- }
4949
- } catch (error) {
4950
- if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
4951
- const primaryColumns = columns.filter(
4952
- (column) => column.primary
4953
- );
4954
- task.logWarning(
4955
- `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`
4956
- );
4957
- }
4958
- task.logWarning(
4959
- `Check ${filename} for invalid data on line ${rowNumber + 1}.`
4960
- );
4961
- throw error;
4986
+ if (model.extension === "gtfs-realtime") {
4987
+ resolve();
4988
+ return;
4989
+ }
4990
+ const filepath = path2.join(task.downloadDir, `${filename}`);
4991
+ if (!existsSync2(filepath)) {
4992
+ if (!model.nonstandard) {
4993
+ task.log(`Importing - ${filename} - No file found\r`);
4962
4994
  }
4995
+ resolve();
4996
+ return;
4963
4997
  }
4964
- });
4965
- if (model.filenameExtension === "txt") {
4966
- const parser = parse({
4967
- columns: true,
4968
- relax_quotes: true,
4969
- trim: true,
4970
- skip_empty_lines: true,
4971
- ...task.csvOptions
4972
- });
4973
- let lines = [];
4974
- parser.on("readable", () => {
4975
- try {
4976
- let record;
4977
- while (record = parser.read()) {
4978
- totalLineCount += 1;
4979
- lines.push(formatGtfsLine(record, model, totalLineCount));
4980
- if (lines.length >= BATCH_SIZE) {
4981
- insertLines(lines);
4982
- lines = [];
4983
- task.log(
4984
- `Importing - ${filename} - ${totalLineCount} lines imported\r`,
4985
- true
4998
+ task.log(`Importing - ${filename}\r`);
4999
+ const columns = model.schema;
5000
+ const prefixedColumns = new Set(
5001
+ columns.filter((column) => column.prefix).map((column) => column.name)
5002
+ );
5003
+ const prepareStatement = `INSERT ${task.ignoreDuplicates ? "OR IGNORE" : ""} INTO ${model.filenameBase} (${columns.map(({ name }) => name).join(", ")}) VALUES (${columns.map(({ name }) => `@${name}`).join(", ")})`;
5004
+ const insert = db.prepare(prepareStatement);
5005
+ const insertLines = db.transaction((lines) => {
5006
+ for (const [rowNumber, line] of Object.entries(lines)) {
5007
+ try {
5008
+ if (task.prefix === void 0) {
5009
+ insert.run(line);
5010
+ } else {
5011
+ const prefixedLine = Object.fromEntries(
5012
+ Object.entries(
5013
+ line
5014
+ ).map(([columnName, value]) => [
5015
+ columnName,
5016
+ applyPrefixToValue(
5017
+ value,
5018
+ prefixedColumns.has(columnName),
5019
+ task.prefix
5020
+ )
5021
+ ])
4986
5022
  );
5023
+ insert.run(prefixedLine);
4987
5024
  }
4988
- }
4989
- } catch (error) {
4990
- if (task.ignoreErrors) {
4991
- task.logError(`Error processing ${filename}: ${error.message}`);
4992
- resolve();
4993
- } else {
4994
- reject(error);
5025
+ } catch (error) {
5026
+ if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
5027
+ const primaryColumns = columns.filter(
5028
+ (column) => column.primary
5029
+ );
5030
+ task.logWarning(
5031
+ `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
+ );
5033
+ }
5034
+ task.logWarning(
5035
+ `Check ${filename} for invalid data on line ${rowNumber + 1}.`
5036
+ );
5037
+ throw error;
4995
5038
  }
4996
5039
  }
4997
5040
  });
4998
- parser.on("end", () => {
4999
- try {
5000
- if (lines.length > 0) {
5001
- try {
5002
- insertLines(lines);
5003
- } catch (error) {
5004
- if (task.ignoreErrors) {
5005
- task.logError(
5006
- `Error inserting data for ${filename}: ${error.message}`
5041
+ if (model.filenameExtension === "txt") {
5042
+ const parser = parse({
5043
+ columns: true,
5044
+ relax_quotes: true,
5045
+ trim: true,
5046
+ skip_empty_lines: true,
5047
+ ...task.csvOptions
5048
+ });
5049
+ let lines = [];
5050
+ parser.on("readable", () => {
5051
+ try {
5052
+ let record;
5053
+ while (record = parser.read()) {
5054
+ totalLineCount += 1;
5055
+ lines.push(formatGtfsLine(record, model, totalLineCount));
5056
+ if (lines.length >= BATCH_SIZE2) {
5057
+ insertLines(lines);
5058
+ lines = [];
5059
+ task.log(
5060
+ `Importing - ${filename} - ${totalLineCount} lines imported\r`,
5061
+ true
5007
5062
  );
5008
- resolve();
5009
- return;
5010
- } else {
5011
- reject(error);
5012
- return;
5013
5063
  }
5014
5064
  }
5065
+ } catch (error) {
5066
+ if (task.ignoreErrors) {
5067
+ const errorMessage = error instanceof Error ? error.message : String(error);
5068
+ task.logError(`Error processing ${filename}: ${errorMessage}`);
5069
+ resolve();
5070
+ } else {
5071
+ reject(error);
5072
+ }
5015
5073
  }
5016
- task.log(
5017
- `Importing - ${filename} - ${totalLineCount} lines imported\r`,
5018
- true
5019
- );
5020
- resolve();
5021
- } catch (error) {
5022
- if (task.ignoreErrors) {
5023
- task.logError(`Error finalizing ${filename}: ${error.message}`);
5074
+ });
5075
+ parser.on("end", () => {
5076
+ try {
5077
+ if (lines.length > 0) {
5078
+ try {
5079
+ insertLines(lines);
5080
+ } catch (error) {
5081
+ if (task.ignoreErrors) {
5082
+ const errorMessage = error instanceof Error ? error.message : String(error);
5083
+ task.logError(
5084
+ `Error inserting data for ${filename}: ${errorMessage}`
5085
+ );
5086
+ resolve();
5087
+ return;
5088
+ } else {
5089
+ reject(error);
5090
+ return;
5091
+ }
5092
+ }
5093
+ }
5094
+ task.log(
5095
+ `Importing - ${filename} - ${totalLineCount} lines imported\r`,
5096
+ true
5097
+ );
5024
5098
  resolve();
5025
- } else {
5026
- reject(error);
5099
+ } catch (error) {
5100
+ if (task.ignoreErrors) {
5101
+ const errorMessage = error instanceof Error ? error.message : String(error);
5102
+ task.logError(`Error finalizing ${filename}: ${errorMessage}`);
5103
+ resolve();
5104
+ } else {
5105
+ reject(error);
5106
+ }
5027
5107
  }
5028
- }
5029
- });
5030
- parser.on("error", (error) => {
5031
- if (task.ignoreErrors) {
5032
- task.logError(`Parser error for ${filename}: ${error.message}`);
5033
- resolve();
5034
- } else {
5035
- reject(error);
5036
- }
5037
- });
5038
- createReadStream(filepath).pipe(stripBomStream()).pipe(parser);
5039
- } else if (model.filenameExtension === "geojson") {
5040
- readFile2(filepath, "utf8").then((data) => {
5041
- if (isValidJSON(data) === false) {
5108
+ });
5109
+ parser.on("error", (error) => {
5042
5110
  if (task.ignoreErrors) {
5043
- task.logError(`Invalid JSON in ${filename}`);
5111
+ const errorMessage = error instanceof Error ? error.message : String(error);
5112
+ task.logError(`Parser error for ${filename}: ${errorMessage}`);
5044
5113
  resolve();
5045
- return;
5046
5114
  } else {
5047
- reject(new Error(`Invalid JSON in ${filename}`));
5048
- return;
5115
+ reject(error);
5049
5116
  }
5050
- }
5051
- totalLineCount += 1;
5052
- const line = formatGtfsLine(
5053
- { geojson: data },
5054
- model,
5055
- totalLineCount
5056
- );
5057
- try {
5058
- insertLines([line]);
5059
- task.log(
5060
- `Importing - ${filename} - ${totalLineCount} lines imported\r`,
5061
- true
5117
+ });
5118
+ createReadStream(filepath).pipe(stripBomStream()).pipe(parser);
5119
+ } else if (model.filenameExtension === "geojson") {
5120
+ readFile2(filepath, "utf8").then((data) => {
5121
+ if (isValidJSON(data) === false) {
5122
+ if (task.ignoreErrors) {
5123
+ task.logError(`Invalid JSON in ${filename}`);
5124
+ resolve();
5125
+ return;
5126
+ } else {
5127
+ reject(new Error(`Invalid JSON in ${filename}`));
5128
+ return;
5129
+ }
5130
+ }
5131
+ totalLineCount += 1;
5132
+ const line = formatGtfsLine(
5133
+ { geojson: data },
5134
+ model,
5135
+ totalLineCount
5062
5136
  );
5063
- resolve();
5064
- } catch (error) {
5065
- if (task.ignoreErrors) {
5066
- task.logError(
5067
- `Error inserting data for ${filename}: ${error.message}`
5137
+ try {
5138
+ insertLines([line]);
5139
+ task.log(
5140
+ `Importing - ${filename} - ${totalLineCount} lines imported\r`,
5141
+ true
5068
5142
  );
5069
5143
  resolve();
5144
+ } catch (error) {
5145
+ if (task.ignoreErrors) {
5146
+ const errorMessage = error instanceof Error ? error.message : String(error);
5147
+ task.logError(
5148
+ `Error inserting data for ${filename}: ${errorMessage}`
5149
+ );
5150
+ resolve();
5151
+ } else {
5152
+ reject(error);
5153
+ }
5154
+ }
5155
+ }).catch((error) => {
5156
+ if (task.ignoreErrors) {
5157
+ const errorMessage = error instanceof Error ? error.message : String(error);
5158
+ task.logError(`Error reading ${filename}: ${errorMessage}`);
5159
+ resolve();
5070
5160
  } else {
5071
5161
  reject(error);
5072
5162
  }
5073
- }
5074
- }).catch((error) => {
5163
+ });
5164
+ } else {
5075
5165
  if (task.ignoreErrors) {
5076
- task.logError(`Error reading ${filename}: ${error.message}`);
5166
+ task.logError(
5167
+ `Unsupported file type: ${model.filenameExtension} for ${filename}`
5168
+ );
5077
5169
  resolve();
5078
5170
  } else {
5079
- reject(error);
5171
+ reject(
5172
+ new Error(`Unsupported file type: ${model.filenameExtension}`)
5173
+ );
5080
5174
  }
5081
- });
5082
- } else {
5083
- if (task.ignoreErrors) {
5084
- task.logError(
5085
- `Unsupported file type: ${model.filenameExtension} for ${filename}`
5086
- );
5087
- resolve();
5088
- } else {
5089
- reject(
5090
- new Error(`Unsupported file type: ${model.filenameExtension}`)
5091
- );
5092
5175
  }
5093
- }
5094
- })
5095
- );
5176
+ })
5177
+ );
5178
+ task.log(`Static GTFS import complete`);
5179
+ };
5096
5180
  async function importGtfs(initialConfig) {
5097
5181
  const startTime = process.hrtime.bigint();
5098
5182
  const config = setDefaultConfig(initialConfig);
@@ -5109,7 +5193,6 @@ async function importGtfs(initialConfig) {
5109
5193
  const tempPath = temporaryDirectory();
5110
5194
  const task = {
5111
5195
  exclude: agency2.exclude,
5112
- url: agency2.url,
5113
5196
  headers: agency2.headers,
5114
5197
  realtimeAlerts: agency2.realtimeAlerts,
5115
5198
  realtimeTripUpdates: agency2.realtimeTripUpdates,
@@ -5117,7 +5200,6 @@ async function importGtfs(initialConfig) {
5117
5200
  downloadDir: tempPath,
5118
5201
  downloadTimeout: config.downloadTimeout,
5119
5202
  gtfsRealtimeExpirationSeconds: config.gtfsRealtimeExpirationSeconds,
5120
- path: agency2.path,
5121
5203
  csvOptions: config.csvOptions || {},
5122
5204
  ignoreDuplicates: config.ignoreDuplicates,
5123
5205
  ignoreErrors: config.ignoreErrors,
@@ -5128,8 +5210,13 @@ async function importGtfs(initialConfig) {
5128
5210
  logWarning: logWarning(config),
5129
5211
  logError: logError(config)
5130
5212
  };
5131
- if (task.url) {
5213
+ if ("url" in agency2) {
5214
+ Object.assign(task, { url: agency2.url });
5132
5215
  await downloadGtfsFiles(task);
5216
+ } else {
5217
+ Object.assign(task, {
5218
+ path: agency2.path
5219
+ });
5133
5220
  }
5134
5221
  await extractGtfsFiles(task);
5135
5222
  await importGtfsFiles(db, task);
@@ -5137,7 +5224,8 @@ async function importGtfs(initialConfig) {
5137
5224
  await rm2(tempPath, { recursive: true });
5138
5225
  } catch (error) {
5139
5226
  if (config.ignoreErrors) {
5140
- logError(config)(error.message);
5227
+ const errorMessage = error instanceof Error ? error.message : String(error);
5228
+ logError(config)(errorMessage);
5141
5229
  } else {
5142
5230
  throw error;
5143
5231
  }
@@ -5152,7 +5240,7 @@ async function importGtfs(initialConfig) {
5152
5240
  `
5153
5241
  );
5154
5242
  } catch (error) {
5155
- if (error?.code === "SQLITE_CANTOPEN") {
5243
+ if (error.code === "SQLITE_CANTOPEN") {
5156
5244
  logError(config)(
5157
5245
  `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
5158
5246
  );
@@ -5171,7 +5259,7 @@ import mapSeries3 from "promise-map-series";
5171
5259
  var getAgencies = (db, config) => {
5172
5260
  try {
5173
5261
  return db.prepare("SELECT agency_name FROM agency;").all();
5174
- } catch (error) {
5262
+ } catch {
5175
5263
  if (config.sqlitePath === ":memory:") {
5176
5264
  throw new Error(
5177
5265
  'No agencies found in SQLite. You are using an in-memory database - if running this from command line be sure to specify a value for `sqlitePath` in config.json other than ":memory:".'
@@ -5238,11 +5326,17 @@ var exportGtfs = async (initialConfig) => {
5238
5326
  }
5239
5327
  } else if (model.filenameBase === "fare_attributes") {
5240
5328
  for (const line of lines) {
5241
- line.price = formatCurrency(line.price, line.currency_type);
5329
+ line.price = formatCurrency(
5330
+ line.price,
5331
+ line.currency_type
5332
+ );
5242
5333
  }
5243
5334
  } else if (model.filenameBase === "fare_products") {
5244
5335
  for (const line of lines) {
5245
- line.amount = formatCurrency(line.amount, line.currency);
5336
+ line.amount = formatCurrency(
5337
+ line.amount,
5338
+ line.currency
5339
+ );
5246
5340
  }
5247
5341
  }
5248
5342
  const columns = without(
@@ -5617,10 +5711,7 @@ function getRoutes(query = {}, fields = [], orderBy2 = [], options = {}) {
5617
5711
  let whereClause = "";
5618
5712
  const orderByClause = formatOrderByClause(orderBy2);
5619
5713
  const routeQuery = omit3(query, ["stop_id", "service_id"]);
5620
- const tripQuery = pick(query, [
5621
- "stop_id",
5622
- "service_id"
5623
- ]);
5714
+ const tripQuery = pick(query, ["stop_id", "service_id"]);
5624
5715
  const whereClauses = Object.entries(routeQuery).map(
5625
5716
  ([key, value]) => formatWhereClause(key, value)
5626
5717
  );
@@ -5668,7 +5759,12 @@ function getShapes(query = {}, fields = [], orderBy2 = [], options = {}) {
5668
5759
  "service_id",
5669
5760
  "direction_id"
5670
5761
  ]);
5671
- const tripQuery = pick2(query, ["route_id", "trip_id", "service_id", "direction_id"]);
5762
+ const tripQuery = pick2(query, [
5763
+ "route_id",
5764
+ "trip_id",
5765
+ "service_id",
5766
+ "direction_id"
5767
+ ]);
5672
5768
  const whereClauses = Object.entries(shapeQuery).map(
5673
5769
  ([key, value]) => formatWhereClause(key, value)
5674
5770
  );
@@ -5692,12 +5788,7 @@ function getShapesAsGeoJSON(query = {}, options = {}) {
5692
5788
  route_id: route.route_id,
5693
5789
  ...omit4(query, "route_id")
5694
5790
  };
5695
- const shapes2 = getShapes(
5696
- shapeQuery,
5697
- ["shape_id", "shape_pt_sequence", "shape_pt_lon", "shape_pt_lat"],
5698
- [],
5699
- options
5700
- );
5791
+ const shapes2 = getShapes(shapeQuery, [], [], options);
5701
5792
  if (shapes2.length === 0) {
5702
5793
  return;
5703
5794
  }
@@ -5775,7 +5866,7 @@ function getStops(query = {}, fields = [], orderBy2 = [], options = {}) {
5775
5866
  if (options.bounding_box_side_m !== void 0) {
5776
5867
  stopQueryOmitKeys.push("stop_lat", "stop_lon");
5777
5868
  }
5778
- let stopQuery = omit5(query, stopQueryOmitKeys);
5869
+ const stopQuery = omit5(query, stopQueryOmitKeys);
5779
5870
  const tripQuery = pick3(query, [
5780
5871
  "route_id",
5781
5872
  "trip_id",
@@ -5840,7 +5931,7 @@ function getStoptimes(query = {}, fields = [], orderBy2 = [], options = {}) {
5840
5931
  let whereClause = "";
5841
5932
  const orderByClause = formatOrderByClause(orderBy2);
5842
5933
  const stoptimeQueryOmitKeys = ["date", "start_time", "end_time"];
5843
- let stoptimeQuery = omit6(query, stoptimeQueryOmitKeys);
5934
+ const stoptimeQuery = omit6(query, stoptimeQueryOmitKeys);
5844
5935
  const whereClauses = Object.entries(stoptimeQuery).map(
5845
5936
  ([key, value]) => formatWhereClause(key, value)
5846
5937
  );
@@ -5848,7 +5939,7 @@ function getStoptimes(query = {}, fields = [], orderBy2 = [], options = {}) {
5848
5939
  if (typeof query.date !== "number") {
5849
5940
  throw new Error("`date` must be a number in yyyymmdd format");
5850
5941
  }
5851
- const serviceIds = getServiceIdsByDate(query.date);
5942
+ const serviceIds = getServiceIdsByDate(query.date, options);
5852
5943
  const tripSubquery = `SELECT DISTINCT trip_id FROM trips WHERE service_id IN (${serviceIds.map((id) => sqlString4.escape(id)).join(",")})`;
5853
5944
  whereClauses.push(`trip_id IN (${tripSubquery})`);
5854
5945
  }
@@ -5922,7 +6013,7 @@ function getTrips(query = {}, fields = [], orderBy2 = [], options = {}) {
5922
6013
  let whereClause = "";
5923
6014
  const orderByClause = formatOrderByClause(orderBy2);
5924
6015
  const tripQueryOmitKeys = ["date"];
5925
- let tripQuery = omit7(query, tripQueryOmitKeys);
6016
+ const tripQuery = omit7(query, tripQueryOmitKeys);
5926
6017
  const whereClauses = Object.entries(tripQuery).map(
5927
6018
  ([key, value]) => formatWhereClause(key, value)
5928
6019
  );
@@ -5930,7 +6021,7 @@ function getTrips(query = {}, fields = [], orderBy2 = [], options = {}) {
5930
6021
  if (typeof query.date !== "number") {
5931
6022
  throw new Error("`date` must be a number in yyyymmdd format");
5932
6023
  }
5933
- const serviceIds = getServiceIdsByDate(query.date);
6024
+ const serviceIds = getServiceIdsByDate(query.date, options);
5934
6025
  whereClauses.push(
5935
6026
  `service_id IN (${serviceIds.map((id) => sqlString5.escape(id)).join(",")})`
5936
6027
  );
@@ -6212,6 +6303,7 @@ export {
6212
6303
  closeDb,
6213
6304
  deleteDb,
6214
6305
  exportGtfs,
6306
+ generateFolderName,
6215
6307
  getAgencies2 as getAgencies,
6216
6308
  getAreas,
6217
6309
  getAttributions,
@@ -6272,6 +6364,9 @@ export {
6272
6364
  getVehiclePositions,
6273
6365
  importGtfs,
6274
6366
  openDb,
6367
+ prepDirectory,
6368
+ untildify,
6369
+ unzip,
6275
6370
  updateGtfsRealtime
6276
6371
  };
6277
6372
  //# sourceMappingURL=index.js.map