gtfs 4.15.6 → 4.15.7
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 +95 -76
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +128 -119
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +41 -38
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.d.ts +15 -15
- package/dist/index.js +130 -121
- package/dist/index.js.map +1 -1
- package/dist/models/models.d.ts +49 -36
- package/dist/models/models.js +50 -34
- package/dist/models/models.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/gtfs-import.js
CHANGED
|
@@ -18,60 +18,13 @@ import { omit, snakeCase } from "lodash-es";
|
|
|
18
18
|
import sanitize from "sanitize-filename";
|
|
19
19
|
import untildify from "untildify";
|
|
20
20
|
import StreamZip from "node-stream-zip";
|
|
21
|
-
async function getConfig(argv2) {
|
|
22
|
-
let config;
|
|
23
|
-
let data;
|
|
24
|
-
try {
|
|
25
|
-
if (argv2.configPath) {
|
|
26
|
-
const configPath = path.resolve(untildify(argv2.configPath));
|
|
27
|
-
data = await readFile(configPath, "utf8");
|
|
28
|
-
config = Object.assign(JSON.parse(data), argv2);
|
|
29
|
-
} else if (argv2.gtfsPath || argv2.gtfsUrl || argv2.sqlitePath) {
|
|
30
|
-
const agencies = [
|
|
31
|
-
...argv2.gtfsPath ? [{ path: argv2.gtfsPath }] : [],
|
|
32
|
-
...argv2.gtfsUrl ? [{ url: argv2.gtfsUrl }] : []
|
|
33
|
-
];
|
|
34
|
-
config = {
|
|
35
|
-
agencies,
|
|
36
|
-
...omit(argv2, ["path", "url"])
|
|
37
|
-
};
|
|
38
|
-
} else if (existsSync(path.resolve("./config.json"))) {
|
|
39
|
-
data = await readFile(path.resolve("./config.json"), "utf8");
|
|
40
|
-
config = Object.assign(JSON.parse(data), argv2);
|
|
41
|
-
console.log("Using configuration from ./config.json");
|
|
42
|
-
} else {
|
|
43
|
-
throw new Error(
|
|
44
|
-
"Cannot find configuration file. Use config-sample.json as a starting point, pass --configPath option."
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
return config;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
if (error instanceof SyntaxError) {
|
|
50
|
-
throw new Error(
|
|
51
|
-
`Cannot parse configuration file. Check to ensure that it is valid JSON. Error: ${error.message}`
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
throw error;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
async function unzip(zipfilePath, exportPath) {
|
|
58
|
-
try {
|
|
59
|
-
const zip = new StreamZip.async({ file: zipfilePath });
|
|
60
|
-
await zip.extract(null, exportPath);
|
|
61
|
-
await zip.close();
|
|
62
|
-
} catch (error) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`Failed to extract zip file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
21
|
|
|
69
22
|
// src/lib/log-utils.ts
|
|
70
23
|
import { clearLine, cursorTo } from "node:readline";
|
|
71
24
|
import { noop } from "lodash-es";
|
|
72
25
|
import * as colors from "yoctocolors";
|
|
73
26
|
function log(config) {
|
|
74
|
-
if (
|
|
27
|
+
if (config.verbose === false) {
|
|
75
28
|
return noop;
|
|
76
29
|
}
|
|
77
30
|
if (config.logFunction) {
|
|
@@ -116,6 +69,55 @@ function formatError(error) {
|
|
|
116
69
|
return colors.red(`${colors.underline("Error")}: ${cleanMessage}`);
|
|
117
70
|
}
|
|
118
71
|
|
|
72
|
+
// src/lib/file-utils.ts
|
|
73
|
+
async function getConfig(argv2) {
|
|
74
|
+
let config;
|
|
75
|
+
let data;
|
|
76
|
+
try {
|
|
77
|
+
if (argv2.configPath) {
|
|
78
|
+
const configPath = path.resolve(untildify(argv2.configPath));
|
|
79
|
+
data = await readFile(configPath, "utf8");
|
|
80
|
+
config = Object.assign(JSON.parse(data), argv2);
|
|
81
|
+
} else if (argv2.gtfsPath || argv2.gtfsUrl || argv2.sqlitePath) {
|
|
82
|
+
const agencies = [
|
|
83
|
+
...argv2.gtfsPath ? [{ path: argv2.gtfsPath }] : [],
|
|
84
|
+
...argv2.gtfsUrl ? [{ url: argv2.gtfsUrl }] : []
|
|
85
|
+
];
|
|
86
|
+
config = {
|
|
87
|
+
agencies,
|
|
88
|
+
...omit(argv2, ["path", "url"])
|
|
89
|
+
};
|
|
90
|
+
} else if (existsSync(path.resolve("./config.json"))) {
|
|
91
|
+
data = await readFile(path.resolve("./config.json"), "utf8");
|
|
92
|
+
config = Object.assign(JSON.parse(data), argv2);
|
|
93
|
+
log(config)("Using configuration from ./config.json");
|
|
94
|
+
} else {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"Cannot find configuration file. Use config-sample.json as a starting point, pass --configPath option."
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return config;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error instanceof SyntaxError) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Cannot parse configuration file. Check to ensure that it is valid JSON. Error: ${error.message}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function unzip(zipfilePath, exportPath) {
|
|
110
|
+
try {
|
|
111
|
+
const zip = new StreamZip.async({ file: zipfilePath });
|
|
112
|
+
await zip.extract(null, exportPath);
|
|
113
|
+
await zip.close();
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Failed to extract zip file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
119
121
|
// src/lib/import-gtfs.ts
|
|
120
122
|
import path2 from "node:path";
|
|
121
123
|
import { createReadStream, existsSync as existsSync2, lstatSync } from "node:fs";
|
|
@@ -264,7 +266,8 @@ var attributions = {
|
|
|
264
266
|
{
|
|
265
267
|
name: "attribution_id",
|
|
266
268
|
type: "text",
|
|
267
|
-
prefix: true
|
|
269
|
+
prefix: true,
|
|
270
|
+
primary: true
|
|
268
271
|
},
|
|
269
272
|
{
|
|
270
273
|
name: "agency_id",
|
|
@@ -697,26 +700,31 @@ var fareRules = {
|
|
|
697
700
|
name: "fare_id",
|
|
698
701
|
type: "text",
|
|
699
702
|
required: true,
|
|
703
|
+
primary: true,
|
|
700
704
|
prefix: true
|
|
701
705
|
},
|
|
702
706
|
{
|
|
703
707
|
name: "route_id",
|
|
704
708
|
type: "text",
|
|
709
|
+
primary: true,
|
|
705
710
|
prefix: true
|
|
706
711
|
},
|
|
707
712
|
{
|
|
708
713
|
name: "origin_id",
|
|
709
714
|
type: "text",
|
|
715
|
+
primary: true,
|
|
710
716
|
prefix: true
|
|
711
717
|
},
|
|
712
718
|
{
|
|
713
719
|
name: "destination_id",
|
|
714
720
|
type: "text",
|
|
721
|
+
primary: true,
|
|
715
722
|
prefix: true
|
|
716
723
|
},
|
|
717
724
|
{
|
|
718
725
|
name: "contains_id",
|
|
719
726
|
type: "text",
|
|
727
|
+
primary: true,
|
|
720
728
|
prefix: true
|
|
721
729
|
}
|
|
722
730
|
]
|
|
@@ -923,14 +931,16 @@ var locationGroupStops = {
|
|
|
923
931
|
type: "text",
|
|
924
932
|
prefix: true,
|
|
925
933
|
index: true,
|
|
926
|
-
required: true
|
|
934
|
+
required: true,
|
|
935
|
+
primary: true
|
|
927
936
|
},
|
|
928
937
|
{
|
|
929
938
|
name: "stop_id",
|
|
930
939
|
type: "text",
|
|
931
940
|
required: true,
|
|
932
941
|
prefix: true,
|
|
933
|
-
index: true
|
|
942
|
+
index: true,
|
|
943
|
+
primary: true
|
|
934
944
|
}
|
|
935
945
|
]
|
|
936
946
|
};
|
|
@@ -1191,12 +1201,14 @@ var stopAreas = {
|
|
|
1191
1201
|
name: "area_id",
|
|
1192
1202
|
type: "text",
|
|
1193
1203
|
required: true,
|
|
1204
|
+
primary: true,
|
|
1194
1205
|
prefix: true
|
|
1195
1206
|
},
|
|
1196
1207
|
{
|
|
1197
1208
|
name: "stop_id",
|
|
1198
1209
|
type: "text",
|
|
1199
1210
|
required: true,
|
|
1211
|
+
primary: true,
|
|
1200
1212
|
prefix: true
|
|
1201
1213
|
}
|
|
1202
1214
|
]
|
|
@@ -1420,16 +1432,19 @@ var timeframes = {
|
|
|
1420
1432
|
},
|
|
1421
1433
|
{
|
|
1422
1434
|
name: "start_time",
|
|
1423
|
-
type: "text"
|
|
1435
|
+
type: "text",
|
|
1436
|
+
primary: true
|
|
1424
1437
|
},
|
|
1425
1438
|
{
|
|
1426
1439
|
name: "end_time",
|
|
1427
|
-
type: "text"
|
|
1440
|
+
type: "text",
|
|
1441
|
+
primary: true
|
|
1428
1442
|
},
|
|
1429
1443
|
{
|
|
1430
1444
|
name: "service_id",
|
|
1431
1445
|
type: "text",
|
|
1432
1446
|
required: true,
|
|
1447
|
+
primary: true,
|
|
1433
1448
|
index: true,
|
|
1434
1449
|
prefix: true
|
|
1435
1450
|
}
|
|
@@ -1616,21 +1631,19 @@ var timetables = {
|
|
|
1616
1631
|
filenameExtension: "txt",
|
|
1617
1632
|
nonstandard: true,
|
|
1618
1633
|
schema: [
|
|
1619
|
-
{
|
|
1620
|
-
name: "id",
|
|
1621
|
-
type: "integer",
|
|
1622
|
-
primary: true,
|
|
1623
|
-
prefix: true
|
|
1624
|
-
},
|
|
1625
1634
|
{
|
|
1626
1635
|
name: "timetable_id",
|
|
1627
1636
|
type: "text",
|
|
1628
|
-
prefix: true
|
|
1637
|
+
prefix: true,
|
|
1638
|
+
required: true,
|
|
1639
|
+
primary: true
|
|
1629
1640
|
},
|
|
1630
1641
|
{
|
|
1631
1642
|
name: "route_id",
|
|
1632
1643
|
type: "text",
|
|
1633
|
-
prefix: true
|
|
1644
|
+
prefix: true,
|
|
1645
|
+
required: true,
|
|
1646
|
+
primary: true
|
|
1634
1647
|
},
|
|
1635
1648
|
{
|
|
1636
1649
|
name: "direction_id",
|
|
@@ -1764,6 +1777,7 @@ var timetablePages = {
|
|
|
1764
1777
|
name: "timetable_page_id",
|
|
1765
1778
|
type: "text",
|
|
1766
1779
|
primary: true,
|
|
1780
|
+
required: true,
|
|
1767
1781
|
prefix: true
|
|
1768
1782
|
},
|
|
1769
1783
|
{
|
|
@@ -1783,28 +1797,28 @@ var timetableStopOrder = {
|
|
|
1783
1797
|
filenameExtension: "txt",
|
|
1784
1798
|
nonstandard: true,
|
|
1785
1799
|
schema: [
|
|
1786
|
-
{
|
|
1787
|
-
name: "id",
|
|
1788
|
-
type: "integer",
|
|
1789
|
-
primary: true,
|
|
1790
|
-
prefix: true
|
|
1791
|
-
},
|
|
1792
1800
|
{
|
|
1793
1801
|
name: "timetable_id",
|
|
1794
1802
|
type: "text",
|
|
1795
1803
|
index: true,
|
|
1796
|
-
prefix: true
|
|
1804
|
+
prefix: true,
|
|
1805
|
+
required: true,
|
|
1806
|
+
primary: true
|
|
1797
1807
|
},
|
|
1798
1808
|
{
|
|
1799
1809
|
name: "stop_id",
|
|
1800
1810
|
type: "text",
|
|
1801
|
-
prefix: true
|
|
1811
|
+
prefix: true,
|
|
1812
|
+
required: true,
|
|
1813
|
+
primary: true
|
|
1802
1814
|
},
|
|
1803
1815
|
{
|
|
1804
1816
|
name: "stop_sequence",
|
|
1805
1817
|
type: "integer",
|
|
1806
1818
|
min: 0,
|
|
1807
|
-
index: true
|
|
1819
|
+
index: true,
|
|
1820
|
+
required: true,
|
|
1821
|
+
primary: true
|
|
1808
1822
|
}
|
|
1809
1823
|
]
|
|
1810
1824
|
};
|
|
@@ -1819,7 +1833,8 @@ var timetableNotes = {
|
|
|
1819
1833
|
name: "note_id",
|
|
1820
1834
|
type: "text",
|
|
1821
1835
|
primary: true,
|
|
1822
|
-
prefix: true
|
|
1836
|
+
prefix: true,
|
|
1837
|
+
required: true
|
|
1823
1838
|
},
|
|
1824
1839
|
{
|
|
1825
1840
|
name: "symbol",
|
|
@@ -1828,7 +1843,8 @@ var timetableNotes = {
|
|
|
1828
1843
|
{
|
|
1829
1844
|
name: "note",
|
|
1830
1845
|
type: "text",
|
|
1831
|
-
nocase: true
|
|
1846
|
+
nocase: true,
|
|
1847
|
+
required: true
|
|
1832
1848
|
}
|
|
1833
1849
|
]
|
|
1834
1850
|
};
|
|
@@ -1842,37 +1858,39 @@ var timetableNotesReferences = {
|
|
|
1842
1858
|
{
|
|
1843
1859
|
name: "note_id",
|
|
1844
1860
|
type: "text",
|
|
1845
|
-
prefix: true
|
|
1861
|
+
prefix: true,
|
|
1862
|
+
required: true,
|
|
1863
|
+
primary: true
|
|
1846
1864
|
},
|
|
1847
1865
|
{
|
|
1848
1866
|
name: "timetable_id",
|
|
1849
1867
|
type: "text",
|
|
1850
|
-
|
|
1851
|
-
|
|
1868
|
+
prefix: true,
|
|
1869
|
+
primary: true
|
|
1852
1870
|
},
|
|
1853
1871
|
{
|
|
1854
1872
|
name: "route_id",
|
|
1855
1873
|
type: "text",
|
|
1856
|
-
|
|
1857
|
-
|
|
1874
|
+
prefix: true,
|
|
1875
|
+
primary: true
|
|
1858
1876
|
},
|
|
1859
1877
|
{
|
|
1860
1878
|
name: "trip_id",
|
|
1861
1879
|
type: "text",
|
|
1862
|
-
|
|
1863
|
-
|
|
1880
|
+
prefix: true,
|
|
1881
|
+
primary: true
|
|
1864
1882
|
},
|
|
1865
1883
|
{
|
|
1866
1884
|
name: "stop_id",
|
|
1867
1885
|
type: "text",
|
|
1868
|
-
|
|
1869
|
-
|
|
1886
|
+
prefix: true,
|
|
1887
|
+
primary: true
|
|
1870
1888
|
},
|
|
1871
1889
|
{
|
|
1872
1890
|
name: "stop_sequence",
|
|
1873
1891
|
type: "integer",
|
|
1874
1892
|
min: 0,
|
|
1875
|
-
|
|
1893
|
+
primary: true
|
|
1876
1894
|
},
|
|
1877
1895
|
{
|
|
1878
1896
|
name: "show_on_stoptime",
|
|
@@ -3305,7 +3323,8 @@ function setDefaultConfig(initialConfig) {
|
|
|
3305
3323
|
sqlitePath: ":memory:",
|
|
3306
3324
|
ignoreDuplicates: false,
|
|
3307
3325
|
ignoreErrors: false,
|
|
3308
|
-
gtfsRealtimeExpirationSeconds: 0
|
|
3326
|
+
gtfsRealtimeExpirationSeconds: 0,
|
|
3327
|
+
verbose: true
|
|
3309
3328
|
};
|
|
3310
3329
|
return {
|
|
3311
3330
|
...defaults,
|
|
@@ -3654,18 +3673,31 @@ var createGtfsTables = (db) => {
|
|
|
3654
3673
|
return;
|
|
3655
3674
|
}
|
|
3656
3675
|
const columns = model.schema.map((column) => {
|
|
3657
|
-
|
|
3676
|
+
const checks = [];
|
|
3658
3677
|
if (column.min !== void 0 && column.max) {
|
|
3659
|
-
|
|
3678
|
+
checks.push(
|
|
3679
|
+
`${column.name} >= ${column.min} AND ${column.name} <= ${column.max}`
|
|
3680
|
+
);
|
|
3660
3681
|
} else if (column.min) {
|
|
3661
|
-
|
|
3682
|
+
checks.push(`${column.name} >= ${column.min}`);
|
|
3662
3683
|
} else if (column.max) {
|
|
3663
|
-
|
|
3684
|
+
checks.push(`${column.name} <= ${column.max}`);
|
|
3685
|
+
}
|
|
3686
|
+
if (column.type === "integer") {
|
|
3687
|
+
checks.push(
|
|
3688
|
+
`(TYPEOF(${column.name}) = 'integer' OR ${column.name} IS NULL)`
|
|
3689
|
+
);
|
|
3690
|
+
}
|
|
3691
|
+
if (column.type === "real") {
|
|
3692
|
+
checks.push(
|
|
3693
|
+
`(TYPEOF(${column.name}) = 'real' OR ${column.name} IS NULL)`
|
|
3694
|
+
);
|
|
3664
3695
|
}
|
|
3665
3696
|
const required = column.required ? "NOT NULL" : "";
|
|
3666
3697
|
const columnDefault = column.default ? "DEFAULT " + column.default : "";
|
|
3667
3698
|
const columnCollation = column.nocase ? "COLLATE NOCASE" : "";
|
|
3668
|
-
|
|
3699
|
+
const checkClause = checks.length > 0 ? `CHECK(${checks.join(" AND ")})` : "";
|
|
3700
|
+
return `${column.name} ${column.type} ${checkClause} ${required} ${columnDefault} ${columnCollation}`;
|
|
3669
3701
|
});
|
|
3670
3702
|
const primaryColumns = model.schema.filter((column) => column.primary);
|
|
3671
3703
|
if (primaryColumns.length > 0) {
|
|
@@ -3708,38 +3740,15 @@ var formatGtfsLine = (line, model, totalLineCount) => {
|
|
|
3708
3740
|
}
|
|
3709
3741
|
continue;
|
|
3710
3742
|
}
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
}
|
|
3719
|
-
value = parseInt(value, 10);
|
|
3720
|
-
break;
|
|
3721
|
-
case "integer":
|
|
3722
|
-
value = parseInt(value, 10);
|
|
3723
|
-
break;
|
|
3724
|
-
case "real":
|
|
3725
|
-
value = parseFloat(value);
|
|
3726
|
-
break;
|
|
3727
|
-
}
|
|
3728
|
-
if (Number.isNaN(value)) {
|
|
3729
|
-
formattedLine[name] = null;
|
|
3730
|
-
continue;
|
|
3743
|
+
if (type === "date") {
|
|
3744
|
+
value = value.replace(/-/g, "");
|
|
3745
|
+
if (value.length !== 8) {
|
|
3746
|
+
throw new Error(
|
|
3747
|
+
`Invalid date in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}.`
|
|
3748
|
+
);
|
|
3749
|
+
}
|
|
3731
3750
|
}
|
|
3732
3751
|
formattedLine[name] = value;
|
|
3733
|
-
if (min !== void 0 && value < min) {
|
|
3734
|
-
throw new Error(
|
|
3735
|
-
`Invalid value in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}: below minimum value of ${min}.`
|
|
3736
|
-
);
|
|
3737
|
-
}
|
|
3738
|
-
if (max !== void 0 && value > max) {
|
|
3739
|
-
throw new Error(
|
|
3740
|
-
`Invalid value in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}: above maximum value of ${max}.`
|
|
3741
|
-
);
|
|
3742
|
-
}
|
|
3743
3752
|
}
|
|
3744
3753
|
for (const [timeColumnName, timestampColumnName] of TIME_COLUMN_PAIRS) {
|
|
3745
3754
|
const value = formattedLine[timeColumnName];
|
|
@@ -3808,7 +3817,7 @@ var importGtfsFiles = (db, task) => mapSeries2(
|
|
|
3808
3817
|
);
|
|
3809
3818
|
}
|
|
3810
3819
|
task.logWarning(
|
|
3811
|
-
`Check ${filename} for invalid data on
|
|
3820
|
+
`Check ${filename} for invalid data on line ${rowNumber + 1}.`
|
|
3812
3821
|
);
|
|
3813
3822
|
throw error;
|
|
3814
3823
|
}
|