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.
@@ -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 (!config.verbose) {
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
- index: true,
1851
- prefix: true
1868
+ prefix: true,
1869
+ primary: true
1852
1870
  },
1853
1871
  {
1854
1872
  name: "route_id",
1855
1873
  type: "text",
1856
- index: true,
1857
- prefix: true
1874
+ prefix: true,
1875
+ primary: true
1858
1876
  },
1859
1877
  {
1860
1878
  name: "trip_id",
1861
1879
  type: "text",
1862
- index: true,
1863
- prefix: true
1880
+ prefix: true,
1881
+ primary: true
1864
1882
  },
1865
1883
  {
1866
1884
  name: "stop_id",
1867
1885
  type: "text",
1868
- index: true,
1869
- prefix: true
1886
+ prefix: true,
1887
+ primary: true
1870
1888
  },
1871
1889
  {
1872
1890
  name: "stop_sequence",
1873
1891
  type: "integer",
1874
1892
  min: 0,
1875
- index: true
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
- let check = "";
3676
+ const checks = [];
3658
3677
  if (column.min !== void 0 && column.max) {
3659
- check = `CHECK( ${column.name} >= ${column.min} AND ${column.name} <= ${column.max} )`;
3678
+ checks.push(
3679
+ `${column.name} >= ${column.min} AND ${column.name} <= ${column.max}`
3680
+ );
3660
3681
  } else if (column.min) {
3661
- check = `CHECK( ${column.name} >= ${column.min} )`;
3682
+ checks.push(`${column.name} >= ${column.min}`);
3662
3683
  } else if (column.max) {
3663
- check = `CHECK( ${column.name} <= ${column.max} )`;
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
- return `${column.name} ${column.type} ${check} ${required} ${columnDefault} ${columnCollation}`;
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
- switch (type) {
3712
- case "date":
3713
- value = value.replace(/-/g, "");
3714
- if (value.length !== 8) {
3715
- throw new Error(
3716
- `Invalid date in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}.`
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 row ${rowNumber}.`
3820
+ `Check ${filename} for invalid data on line ${rowNumber + 1}.`
3812
3821
  );
3813
3822
  throw error;
3814
3823
  }