gtfs 4.17.6 → 4.18.0
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 +32 -11
- package/dist/bin/gtfs-export.js +25 -18
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +443 -316
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +253 -196
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.d.ts +145 -7
- package/dist/index.js +568 -427
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ import { readFile } from 'fs/promises';
|
|
|
69
69
|
import path from 'node:path';
|
|
70
70
|
|
|
71
71
|
const config = JSON.parse(
|
|
72
|
-
await readFile(path.join(import.meta.dirname, 'config.json'))
|
|
72
|
+
await readFile(path.join(import.meta.dirname, 'config.json'), 'utf8')
|
|
73
73
|
);
|
|
74
74
|
|
|
75
75
|
try {
|
|
@@ -157,7 +157,7 @@ Copy `config-sample.json` to `config.json` and then add your projects configurat
|
|
|
157
157
|
| [`exportPath`](#exportpath) | string | A path to a directory to put exported GTFS files. Optional, defaults to `gtfs-export/<agency_name>`. |
|
|
158
158
|
| [`gtfsRealtimeExpirationSeconds`](#gtfsrealtimeexpirationseconds) | integer | Amount of time in seconds to allow GTFS-Realtime data to be stored in database before allowing to be deleted. Optional, defaults to 0. |
|
|
159
159
|
| [`ignoreDuplicates`](#ignoreduplicates) | boolean | Whether or not to ignore unique constraints on ids when importing GTFS, such as `trip_id`, `calendar_id`. Optional, defaults to false. |
|
|
160
|
-
| [`ignoreErrors`](#ignoreerrors) | boolean | Whether or not to ignore errors during the import process. If true,
|
|
160
|
+
| [`ignoreErrors`](#ignoreerrors) | boolean | Whether or not to ignore errors during the import process. If true, failed files will be skipped while the rest are processed. Optional, defaults to false. |
|
|
161
161
|
| [`sqlitePath`](#sqlitepath) | string | A path to an SQLite database. Optional, defaults to using an in-memory database. |
|
|
162
162
|
| [`verbose`](#verbose) | boolean | Whether or not to print output to the console. Optional, defaults to true. |
|
|
163
163
|
|
|
@@ -362,7 +362,7 @@ importGtfs({
|
|
|
362
362
|
|
|
363
363
|
### downloadTimeout
|
|
364
364
|
|
|
365
|
-
{Integer} A number of milliseconds to wait when downloading GTFS before throwing an error. Optional.
|
|
365
|
+
{Integer} A number of milliseconds to wait when downloading GTFS before throwing an error. Optional, defaults to `30000` (30 seconds).
|
|
366
366
|
|
|
367
367
|
```json
|
|
368
368
|
{
|
|
@@ -371,7 +371,7 @@ importGtfs({
|
|
|
371
371
|
"path": "/path/to/the/unzipped/gtfs/"
|
|
372
372
|
}
|
|
373
373
|
],
|
|
374
|
-
"downloadTimeout":
|
|
374
|
+
"downloadTimeout": 30000
|
|
375
375
|
}
|
|
376
376
|
```
|
|
377
377
|
|
|
@@ -431,7 +431,28 @@ importGtfs({
|
|
|
431
431
|
|
|
432
432
|
### ignoreErrors
|
|
433
433
|
|
|
434
|
-
{Boolean}
|
|
434
|
+
{Boolean} Controls error handling behavior during GTFS import. When `true`, the import process will continue even when encountering errors, logging them instead of stopping execution. Defaults to `false`.
|
|
435
|
+
|
|
436
|
+
**When enabled, `ignoreErrors` will:**
|
|
437
|
+
- Continue processing other GTFS files when one file fails
|
|
438
|
+
- Log error messages instead of throwing exceptions
|
|
439
|
+
- Skip problematic records within files while importing valid ones
|
|
440
|
+
- Handle various error types including:
|
|
441
|
+
- Invalid CSV data or malformed records
|
|
442
|
+
- JSON parsing errors (for GeoJSON files)
|
|
443
|
+
- Database constraint violations
|
|
444
|
+
- File read/write errors
|
|
445
|
+
- GTFS-Realtime API failures
|
|
446
|
+
|
|
447
|
+
**Use cases:**
|
|
448
|
+
- Importing from multiple GTFS sources where some may have data quality issues
|
|
449
|
+
- Processing large datasets where minor errors shouldn't halt the entire import
|
|
450
|
+
- Development/testing scenarios where you want to see all errors at once
|
|
451
|
+
|
|
452
|
+
**⚠️ Important considerations:**
|
|
453
|
+
- Errors are logged but not thrown, so you may miss critical data issues
|
|
454
|
+
- Partial imports may result in incomplete or inconsistent data
|
|
455
|
+
- Consider using the `exclude` config option to skip problematic files entirely instead of ignoring errors
|
|
435
456
|
|
|
436
457
|
```json
|
|
437
458
|
{
|
|
@@ -510,7 +531,7 @@ import { readFile } from 'fs/promises';
|
|
|
510
531
|
import path from 'node:path';
|
|
511
532
|
|
|
512
533
|
const config = JSON.parse(
|
|
513
|
-
await readFile(path.join(import.meta.dirname, 'config.json'))
|
|
534
|
+
await readFile(path.join(import.meta.dirname, 'config.json'), 'utf8')
|
|
514
535
|
);
|
|
515
536
|
|
|
516
537
|
await importGtfs(config);
|
|
@@ -556,7 +577,7 @@ import { readFile } from 'fs/promises';
|
|
|
556
577
|
import path from 'node:path';
|
|
557
578
|
|
|
558
579
|
const config = JSON.parse(
|
|
559
|
-
await readFile(path.join(import.meta.dirname, 'config.json'))
|
|
580
|
+
await readFile(path.join(import.meta.dirname, 'config.json'), 'utf8')
|
|
560
581
|
);
|
|
561
582
|
|
|
562
583
|
await updateGtfsRealtime(config);
|
|
@@ -639,7 +660,7 @@ import { readFile } from 'fs/promises';
|
|
|
639
660
|
import path from 'node:path';
|
|
640
661
|
|
|
641
662
|
const config = JSON.parse(
|
|
642
|
-
await readFile(path.join(import.meta.dirname, 'config.json'))
|
|
663
|
+
await readFile(path.join(import.meta.dirname, 'config.json'), 'utf8')
|
|
643
664
|
);
|
|
644
665
|
|
|
645
666
|
const db = openDb(config);
|
|
@@ -681,7 +702,7 @@ import { readFile } from 'fs/promises';
|
|
|
681
702
|
import path from 'node:path';
|
|
682
703
|
|
|
683
704
|
const config = JSON.parse(
|
|
684
|
-
await readFile(path.join(import.meta.dirname, 'config.json'))
|
|
705
|
+
await readFile(path.join(import.meta.dirname, 'config.json'), 'utf8')
|
|
685
706
|
);
|
|
686
707
|
|
|
687
708
|
const db = openDb(config);
|
|
@@ -703,7 +724,7 @@ import { readFile } from 'fs/promises';
|
|
|
703
724
|
import path from 'node:path';
|
|
704
725
|
|
|
705
726
|
const config = JSON.parse(
|
|
706
|
-
await readFile(path.join(import.meta.dirname, 'config.json'))
|
|
727
|
+
await readFile(path.join(import.meta.dirname, 'config.json'), 'utf8')
|
|
707
728
|
);
|
|
708
729
|
|
|
709
730
|
const db = openDb(config);
|
|
@@ -725,7 +746,7 @@ import { readFile } from 'fs/promises';
|
|
|
725
746
|
import path from 'node:path';
|
|
726
747
|
|
|
727
748
|
const config = JSON.parse(
|
|
728
|
-
await readFile(path.join(import.meta.dirname, 'config.json'))
|
|
749
|
+
await readFile(path.join(import.meta.dirname, 'config.json'), 'utf8')
|
|
729
750
|
);
|
|
730
751
|
const db = openDb(config);
|
|
731
752
|
const stops = getStops(
|
package/dist/bin/gtfs-export.js
CHANGED
|
@@ -13,10 +13,10 @@ import PrettyError from "pretty-error";
|
|
|
13
13
|
// src/lib/file-utils.ts
|
|
14
14
|
import path from "path";
|
|
15
15
|
import { existsSync } from "fs";
|
|
16
|
+
import { homedir } from "os";
|
|
16
17
|
import { mkdir, readFile, rm } from "fs/promises";
|
|
17
18
|
import { omit, snakeCase } from "lodash-es";
|
|
18
19
|
import sanitize from "sanitize-filename";
|
|
19
|
-
import untildify from "untildify";
|
|
20
20
|
import StreamZip from "node-stream-zip";
|
|
21
21
|
|
|
22
22
|
// src/lib/log-utils.ts
|
|
@@ -60,6 +60,7 @@ function formatError(error) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// src/lib/file-utils.ts
|
|
63
|
+
var homeDirectory = homedir();
|
|
63
64
|
async function getConfig(argv2) {
|
|
64
65
|
let config;
|
|
65
66
|
let data;
|
|
@@ -106,17 +107,17 @@ function generateFolderName(folderName) {
|
|
|
106
107
|
}
|
|
107
108
|
return snakeCase(sanitize(folderName));
|
|
108
109
|
}
|
|
110
|
+
function untildify(pathWithTilde) {
|
|
111
|
+
return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
|
|
112
|
+
}
|
|
109
113
|
|
|
110
114
|
// src/lib/import-gtfs.ts
|
|
111
115
|
import path2 from "path";
|
|
112
116
|
import { createReadStream, existsSync as existsSync2, lstatSync } from "fs";
|
|
113
117
|
import { cp, readdir, rename, readFile as readFile2, rm as rm2, writeFile } from "fs/promises";
|
|
114
118
|
import { parse } from "csv-parse";
|
|
115
|
-
import pluralize2 from "pluralize";
|
|
116
119
|
import stripBomStream from "strip-bom-stream";
|
|
117
120
|
import { temporaryDirectory } from "tempy";
|
|
118
|
-
import Timer from "timer-machine";
|
|
119
|
-
import untildify3 from "untildify";
|
|
120
121
|
import mapSeries2 from "promise-map-series";
|
|
121
122
|
|
|
122
123
|
// src/models/models.ts
|
|
@@ -4078,10 +4079,9 @@ var vehicles = {
|
|
|
4078
4079
|
|
|
4079
4080
|
// src/lib/db.ts
|
|
4080
4081
|
import Database from "better-sqlite3";
|
|
4081
|
-
import untildify2 from "untildify";
|
|
4082
4082
|
var dbs = {};
|
|
4083
4083
|
function setupDb(sqlitePath) {
|
|
4084
|
-
const db = new Database(
|
|
4084
|
+
const db = new Database(untildify(sqlitePath));
|
|
4085
4085
|
db.pragma("journal_mode = OFF");
|
|
4086
4086
|
db.pragma("synchronous = OFF");
|
|
4087
4087
|
db.pragma("temp_store = MEMORY");
|
|
@@ -4128,7 +4128,6 @@ import {
|
|
|
4128
4128
|
import { feature, featureCollection } from "@turf/helpers";
|
|
4129
4129
|
|
|
4130
4130
|
// src/lib/import-gtfs-realtime.ts
|
|
4131
|
-
import pluralize from "pluralize";
|
|
4132
4131
|
import GtfsRealtimeBindings from "gtfs-realtime-bindings";
|
|
4133
4132
|
import mapSeries from "promise-map-series";
|
|
4134
4133
|
import { get } from "lodash-es";
|
|
@@ -4142,7 +4141,8 @@ function setDefaultConfig(initialConfig) {
|
|
|
4142
4141
|
ignoreDuplicates: false,
|
|
4143
4142
|
ignoreErrors: false,
|
|
4144
4143
|
gtfsRealtimeExpirationSeconds: 0,
|
|
4145
|
-
verbose: true
|
|
4144
|
+
verbose: true,
|
|
4145
|
+
downloadTimeout: 3e4
|
|
4146
4146
|
};
|
|
4147
4147
|
return {
|
|
4148
4148
|
...defaults,
|
|
@@ -4158,20 +4158,21 @@ function formatCurrency(value, currency) {
|
|
|
4158
4158
|
const fractionPart = parts.find((part) => part.type === "fraction")?.value ?? "";
|
|
4159
4159
|
return `${integerPart}${fractionPart !== "" ? `.${fractionPart}` : ""}`;
|
|
4160
4160
|
}
|
|
4161
|
+
function pluralize(singularWord, pluralWord, count) {
|
|
4162
|
+
return count === 1 ? singularWord : pluralWord;
|
|
4163
|
+
}
|
|
4161
4164
|
|
|
4162
4165
|
// src/lib/export.ts
|
|
4163
4166
|
import path3 from "path";
|
|
4164
4167
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
4165
4168
|
import { without, compact as compact2 } from "lodash-es";
|
|
4166
|
-
import pluralize3 from "pluralize";
|
|
4167
4169
|
import { stringify } from "csv-stringify";
|
|
4168
4170
|
import sqlString2 from "sqlstring-sqlite";
|
|
4169
4171
|
import mapSeries3 from "promise-map-series";
|
|
4170
|
-
import untildify4 from "untildify";
|
|
4171
4172
|
var getAgencies = (db, config) => {
|
|
4172
4173
|
try {
|
|
4173
4174
|
return db.prepare("SELECT agency_name FROM agency;").all();
|
|
4174
|
-
} catch
|
|
4175
|
+
} catch {
|
|
4175
4176
|
if (config.sqlitePath === ":memory:") {
|
|
4176
4177
|
throw new Error(
|
|
4177
4178
|
'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:".'
|
|
@@ -4197,15 +4198,15 @@ var exportGtfs = async (initialConfig) => {
|
|
|
4197
4198
|
);
|
|
4198
4199
|
}
|
|
4199
4200
|
log(config)(
|
|
4200
|
-
`Starting GTFS export for ${
|
|
4201
|
+
`Starting GTFS export for ${pluralize(
|
|
4201
4202
|
"agency",
|
|
4202
|
-
|
|
4203
|
-
|
|
4203
|
+
"agencies",
|
|
4204
|
+
agencyCount
|
|
4204
4205
|
)} using SQLite database at ${config.sqlitePath}`
|
|
4205
4206
|
);
|
|
4206
4207
|
const folderName = generateFolderName(agencies[0].agency_name);
|
|
4207
4208
|
const defaultExportPath = path3.join(process.cwd(), "gtfs-export", folderName);
|
|
4208
|
-
const exportPath =
|
|
4209
|
+
const exportPath = untildify(config.exportPath || defaultExportPath);
|
|
4209
4210
|
await prepDirectory(exportPath);
|
|
4210
4211
|
const modelsToExport = Object.values(models_exports).filter(
|
|
4211
4212
|
(model) => model.extension !== "gtfs-realtime"
|
|
@@ -4238,11 +4239,17 @@ var exportGtfs = async (initialConfig) => {
|
|
|
4238
4239
|
}
|
|
4239
4240
|
} else if (model.filenameBase === "fare_attributes") {
|
|
4240
4241
|
for (const line of lines) {
|
|
4241
|
-
line.price = formatCurrency(
|
|
4242
|
+
line.price = formatCurrency(
|
|
4243
|
+
line.price,
|
|
4244
|
+
line.currency_type
|
|
4245
|
+
);
|
|
4242
4246
|
}
|
|
4243
4247
|
} else if (model.filenameBase === "fare_products") {
|
|
4244
4248
|
for (const line of lines) {
|
|
4245
|
-
line.amount = formatCurrency(
|
|
4249
|
+
line.amount = formatCurrency(
|
|
4250
|
+
line.amount,
|
|
4251
|
+
line.currency
|
|
4252
|
+
);
|
|
4246
4253
|
}
|
|
4247
4254
|
}
|
|
4248
4255
|
const columns = without(
|
|
@@ -4273,7 +4280,7 @@ var exportGtfs = async (initialConfig) => {
|
|
|
4273
4280
|
}
|
|
4274
4281
|
log(config)(`Completed GTFS export to ${exportPath}`);
|
|
4275
4282
|
log(config)(
|
|
4276
|
-
`Completed GTFS export for ${
|
|
4283
|
+
`Completed GTFS export for ${pluralize("agency", "agencies", agencyCount)}
|
|
4277
4284
|
`
|
|
4278
4285
|
);
|
|
4279
4286
|
};
|