gtfs 4.17.2 → 4.17.4

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.
@@ -11,16 +11,16 @@ import { hideBin } from "yargs/helpers";
11
11
  import PrettyError from "pretty-error";
12
12
 
13
13
  // src/lib/file-utils.ts
14
- import path from "node:path";
15
- import { existsSync } from "node:fs";
16
- import { mkdir, readFile, rm } from "node:fs/promises";
14
+ import path from "path";
15
+ import { existsSync } from "fs";
16
+ import { mkdir, readFile, rm } from "fs/promises";
17
17
  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
21
 
22
22
  // src/lib/log-utils.ts
23
- import { clearLine, cursorTo } from "node:readline";
23
+ import { clearLine, cursorTo } from "readline";
24
24
  import { noop } from "lodash-es";
25
25
  import * as colors from "yoctocolors";
26
26
  function log(config) {
@@ -119,9 +119,9 @@ async function unzip(zipfilePath, exportPath) {
119
119
  }
120
120
 
121
121
  // src/lib/import-gtfs.ts
122
- import path2 from "node:path";
123
- import { createReadStream, existsSync as existsSync2, lstatSync } from "node:fs";
124
- import { cp, readdir, rename, readFile as readFile2, rm as rm2, writeFile } from "node:fs/promises";
122
+ import path2 from "path";
123
+ import { createReadStream, existsSync as existsSync2, lstatSync } from "fs";
124
+ import { cp, readdir, rename, readFile as readFile2, rm as rm2, writeFile } from "fs/promises";
125
125
  import { parse } from "csv-parse";
126
126
  import pluralize2 from "pluralize";
127
127
  import stripBomStream from "strip-bom-stream";
@@ -143,12 +143,14 @@ __export(models_exports, {
143
143
  calendarDates: () => calendarDates,
144
144
  deadheadTimes: () => deadheadTimes,
145
145
  deadheads: () => deadheads,
146
+ devices: () => devices,
146
147
  directions: () => directions,
147
148
  fareAttributes: () => fareAttributes,
148
149
  fareLegRules: () => fareLegRules,
149
150
  fareMedia: () => fareMedia,
150
151
  fareProducts: () => fareProducts,
151
152
  fareRules: () => fareRules,
153
+ fareTransactions: () => fareTransactions,
152
154
  fareTransferRules: () => fareTransferRules,
153
155
  feedInfo: () => feedInfo,
154
156
  frequencies: () => frequencies,
@@ -157,7 +159,9 @@ __export(models_exports, {
157
159
  locationGroups: () => locationGroups,
158
160
  locations: () => locations,
159
161
  networks: () => networks,
162
+ operators: () => operators,
160
163
  opsLocations: () => opsLocations,
164
+ passengerEvents: () => passengerEvents,
161
165
  pathways: () => pathways,
162
166
  rideFeedInfo: () => rideFeedInfo,
163
167
  riderCategories: () => riderCategories,
@@ -171,10 +175,12 @@ __export(models_exports, {
171
175
  serviceAlertInformedEntities: () => serviceAlertInformedEntities,
172
176
  serviceAlerts: () => serviceAlerts,
173
177
  shapes: () => shapes,
178
+ stationActivities: () => stationActivities,
174
179
  stopAreas: () => stopAreas,
175
180
  stopAttributes: () => stopAttributes,
176
181
  stopTimeUpdates: () => stopTimeUpdates,
177
182
  stopTimes: () => stopTimes,
183
+ stopVisits: () => stopVisits,
178
184
  stops: () => stops,
179
185
  timeframes: () => timeframes,
180
186
  timetableNotes: () => timetableNotes,
@@ -182,13 +188,18 @@ __export(models_exports, {
182
188
  timetablePages: () => timetablePages,
183
189
  timetableStopOrder: () => timetableStopOrder,
184
190
  timetables: () => timetables,
191
+ trainCars: () => trainCars,
185
192
  transfers: () => transfers,
186
193
  translations: () => translations,
187
194
  tripCapacity: () => tripCapacity,
188
195
  tripUpdates: () => tripUpdates,
189
196
  trips: () => trips,
190
197
  tripsDatedVehicleJourney: () => tripsDatedVehicleJourney,
191
- vehiclePositions: () => vehiclePositions
198
+ tripsPerformed: () => tripsPerformed,
199
+ vehicleLocations: () => vehicleLocations,
200
+ vehiclePositions: () => vehiclePositions,
201
+ vehicleTrainCars: () => vehicleTrainCars,
202
+ vehicles: () => vehicles
192
203
  });
193
204
 
194
205
  // src/models/gtfs/agency.ts
@@ -662,6 +673,7 @@ var fareProducts = {
662
673
  {
663
674
  name: "rider_category_id",
664
675
  type: "text",
676
+ primary: true,
665
677
  prefix: true
666
678
  },
667
679
  {
@@ -3236,138 +3248,961 @@ var runsPieces = {
3236
3248
  ]
3237
3249
  };
3238
3250
 
3239
- // src/lib/db.ts
3240
- import Database from "better-sqlite3";
3241
- import untildify2 from "untildify";
3242
- var dbs = {};
3243
- function setupDb(sqlitePath) {
3244
- const db = new Database(untildify2(sqlitePath));
3245
- db.pragma("journal_mode = OFF");
3246
- db.pragma("synchronous = OFF");
3247
- db.pragma("temp_store = MEMORY");
3248
- dbs[sqlitePath] = db;
3249
- return db;
3250
- }
3251
- function openDb(config = null) {
3252
- if (config) {
3253
- const { sqlitePath = ":memory:", db } = config;
3254
- if (db) {
3255
- return db;
3256
- }
3257
- if (dbs[sqlitePath]) {
3258
- return dbs[sqlitePath];
3259
- }
3260
- return setupDb(sqlitePath);
3261
- }
3262
- if (Object.keys(dbs).length === 0) {
3263
- return setupDb(":memory:");
3264
- }
3265
- if (Object.keys(dbs).length === 1) {
3266
- const filename = Object.keys(dbs)[0];
3267
- return dbs[filename];
3268
- }
3269
- if (Object.keys(dbs).length > 1) {
3270
- throw new Error(
3271
- "Multiple databases open, please specify which one to use."
3272
- );
3273
- }
3274
- throw new Error("Unable to find database connection.");
3275
- }
3276
- function closeDb(db = null) {
3277
- if (Object.keys(dbs).length === 0) {
3278
- throw new Error(
3279
- "No database connection. Call `openDb(config)` before using any methods."
3280
- );
3281
- }
3282
- if (!db) {
3283
- if (Object.keys(dbs).length > 1) {
3284
- throw new Error(
3285
- "Multiple database connections. Pass the db you want to close as a parameter to `closeDb`."
3286
- );
3251
+ // src/models/tides/devices.ts
3252
+ var devices = {
3253
+ filenameBase: "devices",
3254
+ filenameExtension: "txt",
3255
+ nonstandard: true,
3256
+ extension: "tides",
3257
+ schema: [
3258
+ {
3259
+ name: "device_id",
3260
+ type: "text",
3261
+ required: true,
3262
+ primary: true
3263
+ },
3264
+ {
3265
+ name: "stop_id",
3266
+ type: "text"
3267
+ },
3268
+ {
3269
+ name: "vehicle_id",
3270
+ type: "text"
3271
+ },
3272
+ {
3273
+ name: "train_car_id",
3274
+ type: "text"
3275
+ },
3276
+ {
3277
+ name: "device_type",
3278
+ type: "text"
3279
+ },
3280
+ {
3281
+ name: "device_vendor",
3282
+ type: "text"
3283
+ },
3284
+ {
3285
+ name: "device_model",
3286
+ type: "text"
3287
+ },
3288
+ {
3289
+ name: "device_location",
3290
+ type: "text"
3287
3291
  }
3288
- db = dbs[Object.keys(dbs)[0]];
3289
- }
3290
- db.close();
3291
- delete dbs[db.name];
3292
- }
3293
-
3294
- // src/lib/geojson-utils.ts
3295
- import {
3296
- cloneDeep,
3297
- compact,
3298
- filter,
3299
- groupBy,
3300
- last,
3301
- omit as omit2,
3302
- sortBy,
3303
- omitBy
3304
- } from "lodash-es";
3305
- import { feature, featureCollection } from "@turf/helpers";
3306
- function isValidJSON(string) {
3307
- try {
3308
- JSON.parse(string);
3309
- return true;
3310
- } catch (error) {
3311
- return false;
3312
- }
3313
- }
3292
+ ]
3293
+ };
3314
3294
 
3315
- // src/lib/import-gtfs-realtime.ts
3316
- import pluralize from "pluralize";
3317
- import GtfsRealtimeBindings from "gtfs-realtime-bindings";
3318
- import mapSeries from "promise-map-series";
3319
- import { get } from "lodash-es";
3295
+ // src/models/tides/fare-transactions.ts
3296
+ var fareTransactions = {
3297
+ filenameBase: "fare_transactions",
3298
+ filenameExtension: "txt",
3299
+ nonstandard: true,
3300
+ extension: "tides",
3301
+ schema: [
3302
+ {
3303
+ name: "transaction_id",
3304
+ type: "text",
3305
+ required: true,
3306
+ index: true
3307
+ },
3308
+ {
3309
+ name: "service_date",
3310
+ type: "date",
3311
+ required: true
3312
+ },
3313
+ {
3314
+ name: "event_timestamp",
3315
+ type: "text",
3316
+ required: true
3317
+ },
3318
+ {
3319
+ name: "location_ping_id",
3320
+ type: "text"
3321
+ },
3322
+ {
3323
+ name: "amount",
3324
+ type: "real",
3325
+ required: true
3326
+ },
3327
+ {
3328
+ name: "currency_type",
3329
+ type: "text"
3330
+ },
3331
+ {
3332
+ name: "fare_action",
3333
+ type: "text",
3334
+ required: true
3335
+ },
3336
+ {
3337
+ name: "trip_id_performed",
3338
+ type: "text"
3339
+ },
3340
+ {
3341
+ name: "trip_id_scheduled",
3342
+ type: "text"
3343
+ },
3344
+ {
3345
+ name: "pattern_id",
3346
+ type: "text"
3347
+ },
3348
+ {
3349
+ name: "trip_stop_sequence",
3350
+ type: "integer",
3351
+ min: 1
3352
+ },
3353
+ {
3354
+ name: "scheduled_stop_sequence",
3355
+ type: "integer",
3356
+ min: 0
3357
+ },
3358
+ {
3359
+ name: "vehicle_id",
3360
+ type: "text"
3361
+ },
3362
+ {
3363
+ name: "device_id",
3364
+ type: "text"
3365
+ },
3366
+ {
3367
+ name: "fare_id",
3368
+ type: "text"
3369
+ },
3370
+ {
3371
+ name: "stop_id",
3372
+ type: "text"
3373
+ },
3374
+ {
3375
+ name: "num_riders",
3376
+ type: "integer",
3377
+ min: 0
3378
+ },
3379
+ {
3380
+ name: "fare_media_id",
3381
+ type: "text"
3382
+ },
3383
+ {
3384
+ name: "rider_category",
3385
+ type: "text"
3386
+ },
3387
+ {
3388
+ name: "fare_product",
3389
+ type: "text"
3390
+ },
3391
+ {
3392
+ name: "fare_period",
3393
+ type: "text"
3394
+ },
3395
+ {
3396
+ name: "fare_capped",
3397
+ type: "text",
3398
+ required: true
3399
+ },
3400
+ {
3401
+ name: "token_id",
3402
+ type: "text"
3403
+ },
3404
+ {
3405
+ name: "balance",
3406
+ type: "real"
3407
+ }
3408
+ ]
3409
+ };
3320
3410
 
3321
- // src/lib/utils.ts
3322
- import sqlString from "sqlstring-sqlite";
3323
- import Long from "long";
3324
- function validateConfigForImport(config) {
3325
- if (!config.agencies || config.agencies.length === 0) {
3326
- throw new Error("No `agencies` specified in config");
3327
- }
3328
- for (const [index, agency2] of config.agencies.entries()) {
3329
- if (!agency2.path && !agency2.url) {
3330
- throw new Error(
3331
- `No Agency \`url\` or \`path\` specified in config for agency index ${index}.`
3332
- );
3411
+ // src/models/tides/operators.ts
3412
+ var operators = {
3413
+ filenameBase: "operators",
3414
+ filenameExtension: "txt",
3415
+ nonstandard: true,
3416
+ extension: "tides",
3417
+ schema: [
3418
+ {
3419
+ name: "operator_id",
3420
+ type: "text",
3421
+ required: true,
3422
+ primary: true
3333
3423
  }
3334
- }
3335
- return config;
3336
- }
3337
- function setDefaultConfig(initialConfig) {
3338
- const defaults = {
3339
- sqlitePath: ":memory:",
3340
- ignoreDuplicates: false,
3341
- ignoreErrors: false,
3342
- gtfsRealtimeExpirationSeconds: 0,
3343
- verbose: true
3344
- };
3345
- return {
3346
- ...defaults,
3347
- ...initialConfig
3348
- };
3349
- }
3350
- function convertLongTimeToDate(longDate) {
3351
- const { high, low, unsigned } = longDate;
3352
- return new Date(
3353
- Long.fromBits(low, high, unsigned).toNumber() * 1e3
3354
- ).toISOString();
3355
- }
3356
- function calculateSecondsFromMidnight(time) {
3357
- if (!time || typeof time !== "string") {
3358
- return null;
3359
- }
3360
- const [hours, minutes, seconds] = time.split(":").map(Number);
3361
- if ([hours, minutes, seconds].some(isNaN) || minutes >= 60 || seconds >= 60) {
3362
- return null;
3363
- }
3364
- return hours * 3600 + minutes * 60 + seconds;
3365
- }
3366
- function padLeadingZeros(time) {
3367
- const split = time.split(":").map((d) => String(Number(d)).padStart(2, "0"));
3368
- if (split.length !== 3) {
3369
- return null;
3370
- }
3424
+ ]
3425
+ };
3426
+
3427
+ // src/models/tides/passenger-events.ts
3428
+ var passengerEvents = {
3429
+ filenameBase: "passenger_events",
3430
+ filenameExtension: "txt",
3431
+ nonstandard: true,
3432
+ extension: "tides",
3433
+ schema: [
3434
+ {
3435
+ name: "passenger_event_id",
3436
+ type: "text",
3437
+ required: true,
3438
+ index: true
3439
+ },
3440
+ {
3441
+ name: "service_date",
3442
+ type: "date",
3443
+ required: true
3444
+ },
3445
+ {
3446
+ name: "event_timestamp",
3447
+ type: "text",
3448
+ required: true
3449
+ },
3450
+ {
3451
+ name: "location_ping_id",
3452
+ type: "text"
3453
+ },
3454
+ {
3455
+ name: "trip_id_performed",
3456
+ type: "text"
3457
+ },
3458
+ {
3459
+ name: "trip_id_scheduled",
3460
+ type: "text"
3461
+ },
3462
+ {
3463
+ name: "trip_stop_sequence",
3464
+ type: "integer",
3465
+ min: 1
3466
+ },
3467
+ {
3468
+ name: "scheduled_stop_sequence",
3469
+ type: "integer",
3470
+ min: 0
3471
+ },
3472
+ {
3473
+ name: "event_type",
3474
+ type: "text",
3475
+ required: true
3476
+ },
3477
+ {
3478
+ name: "vehicle_id",
3479
+ type: "text",
3480
+ required: true
3481
+ },
3482
+ {
3483
+ name: "device_id",
3484
+ type: "text"
3485
+ },
3486
+ {
3487
+ name: "train_car_id",
3488
+ type: "text"
3489
+ },
3490
+ {
3491
+ name: "stop_id",
3492
+ type: "text"
3493
+ },
3494
+ {
3495
+ name: "pattern_id",
3496
+ type: "text"
3497
+ },
3498
+ {
3499
+ name: "event_count",
3500
+ type: "integer",
3501
+ min: 0
3502
+ }
3503
+ ]
3504
+ };
3505
+
3506
+ // src/models/tides/station-activities.ts
3507
+ var stationActivities = {
3508
+ filenameBase: "station_activities",
3509
+ filenameExtension: "txt",
3510
+ nonstandard: true,
3511
+ extension: "tides",
3512
+ schema: [
3513
+ {
3514
+ name: "service_date",
3515
+ type: "date",
3516
+ required: true,
3517
+ primary: true
3518
+ },
3519
+ {
3520
+ name: "stop_id",
3521
+ type: "text",
3522
+ required: true,
3523
+ primary: true
3524
+ },
3525
+ {
3526
+ name: "time_period_start",
3527
+ type: "text",
3528
+ required: true,
3529
+ primary: true
3530
+ },
3531
+ {
3532
+ name: "time_period_end",
3533
+ type: "text",
3534
+ required: true,
3535
+ primary: true
3536
+ },
3537
+ {
3538
+ name: "time_period_category",
3539
+ type: "text"
3540
+ },
3541
+ {
3542
+ name: "total_entries",
3543
+ type: "integer",
3544
+ min: 0
3545
+ },
3546
+ {
3547
+ name: "total_exits",
3548
+ type: "integer",
3549
+ min: 0
3550
+ },
3551
+ {
3552
+ name: "number_of_transactions",
3553
+ type: "integer",
3554
+ min: 0
3555
+ },
3556
+ {
3557
+ name: "bike_entries",
3558
+ type: "integer",
3559
+ min: 0
3560
+ },
3561
+ {
3562
+ name: "bike_exits",
3563
+ type: "integer",
3564
+ min: 0
3565
+ },
3566
+ {
3567
+ name: "ramp_entries",
3568
+ type: "integer",
3569
+ min: 0
3570
+ },
3571
+ {
3572
+ name: "ramp_exits",
3573
+ type: "integer",
3574
+ min: 0
3575
+ }
3576
+ ]
3577
+ };
3578
+
3579
+ // src/models/tides/stop-visits.ts
3580
+ var stopVisits = {
3581
+ filenameBase: "stop_visits",
3582
+ filenameExtension: "txt",
3583
+ nonstandard: true,
3584
+ extension: "tides",
3585
+ schema: [
3586
+ {
3587
+ name: "service_date",
3588
+ type: "date",
3589
+ required: true,
3590
+ primary: true
3591
+ },
3592
+ {
3593
+ name: "trip_id_performed",
3594
+ type: "text",
3595
+ required: true,
3596
+ primary: true
3597
+ },
3598
+ {
3599
+ name: "trip_stop_sequence",
3600
+ type: "integer",
3601
+ min: 1,
3602
+ required: true,
3603
+ primary: true
3604
+ },
3605
+ {
3606
+ name: "scheduled_stop_sequence",
3607
+ type: "integer",
3608
+ min: 0
3609
+ },
3610
+ {
3611
+ name: "pattern_id",
3612
+ type: "text"
3613
+ },
3614
+ {
3615
+ name: "vehicle_id",
3616
+ type: "text"
3617
+ },
3618
+ {
3619
+ name: "dwell",
3620
+ type: "integer",
3621
+ min: 0
3622
+ },
3623
+ {
3624
+ name: "stop_id",
3625
+ type: "text"
3626
+ },
3627
+ {
3628
+ name: "timepoint",
3629
+ type: "text"
3630
+ },
3631
+ {
3632
+ name: "schedule_arrival_time",
3633
+ type: "text"
3634
+ },
3635
+ {
3636
+ name: "schedule_departure_time",
3637
+ type: "text"
3638
+ },
3639
+ {
3640
+ name: "actual_arrival_time",
3641
+ type: "text"
3642
+ },
3643
+ {
3644
+ name: "actual_departure_time",
3645
+ type: "text"
3646
+ },
3647
+ {
3648
+ name: "distance",
3649
+ type: "integer",
3650
+ min: 0
3651
+ },
3652
+ {
3653
+ name: "boarding_1",
3654
+ type: "integer",
3655
+ min: 0
3656
+ },
3657
+ {
3658
+ name: "alighting_1",
3659
+ type: "integer",
3660
+ min: 0
3661
+ },
3662
+ {
3663
+ name: "boarding_2",
3664
+ type: "integer",
3665
+ min: 0
3666
+ },
3667
+ {
3668
+ name: "alighting_2",
3669
+ type: "integer",
3670
+ min: 0
3671
+ },
3672
+ {
3673
+ name: "departure_load",
3674
+ type: "integer",
3675
+ min: 0
3676
+ },
3677
+ {
3678
+ name: "door_open",
3679
+ type: "text"
3680
+ },
3681
+ {
3682
+ name: "door_close",
3683
+ type: "text"
3684
+ },
3685
+ {
3686
+ name: "door_status",
3687
+ type: "text"
3688
+ },
3689
+ {
3690
+ name: "ramp_deployed_time",
3691
+ type: "text"
3692
+ },
3693
+ {
3694
+ name: "ramp_failure",
3695
+ type: "text"
3696
+ },
3697
+ {
3698
+ name: "kneel_deployed_time",
3699
+ type: "integer",
3700
+ min: 0
3701
+ },
3702
+ {
3703
+ name: "lift_deployed_time",
3704
+ type: "integer",
3705
+ min: 0
3706
+ },
3707
+ {
3708
+ name: "bike_rack_deployed",
3709
+ type: "text"
3710
+ },
3711
+ {
3712
+ name: "bike_load",
3713
+ type: "integer",
3714
+ min: 0
3715
+ },
3716
+ {
3717
+ name: "revenue",
3718
+ type: "real"
3719
+ },
3720
+ {
3721
+ name: "number_of_transactions",
3722
+ type: "integer",
3723
+ min: 0
3724
+ },
3725
+ {
3726
+ name: "schedule_relationship",
3727
+ type: "text"
3728
+ }
3729
+ ]
3730
+ };
3731
+
3732
+ // src/models/tides/train-cars.ts
3733
+ var trainCars = {
3734
+ filenameBase: "train_cars",
3735
+ filenameExtension: "txt",
3736
+ nonstandard: true,
3737
+ extension: "tides",
3738
+ schema: [
3739
+ {
3740
+ name: "train_car_id",
3741
+ type: "text",
3742
+ required: true,
3743
+ primary: true
3744
+ },
3745
+ {
3746
+ name: "model_name",
3747
+ type: "text"
3748
+ },
3749
+ {
3750
+ name: "facility_name",
3751
+ type: "text"
3752
+ },
3753
+ {
3754
+ name: "capacity_seated",
3755
+ type: "integer",
3756
+ min: 0
3757
+ },
3758
+ {
3759
+ name: "capacity_wheelchair",
3760
+ type: "integer",
3761
+ min: 0
3762
+ },
3763
+ {
3764
+ name: "capacity_bike",
3765
+ type: "integer",
3766
+ min: 0
3767
+ },
3768
+ {
3769
+ name: "bike_rack",
3770
+ type: "text"
3771
+ },
3772
+ {
3773
+ name: "capacity_standing",
3774
+ type: "integer",
3775
+ min: 0
3776
+ },
3777
+ {
3778
+ name: "train_car_type",
3779
+ type: "text"
3780
+ }
3781
+ ]
3782
+ };
3783
+
3784
+ // src/models/tides/trips-performed.ts
3785
+ var tripsPerformed = {
3786
+ filenameBase: "trips_performed",
3787
+ filenameExtension: "txt",
3788
+ nonstandard: true,
3789
+ extension: "tides",
3790
+ schema: [
3791
+ {
3792
+ name: "service_date",
3793
+ type: "date",
3794
+ required: true,
3795
+ primary: true
3796
+ },
3797
+ {
3798
+ name: "trip_id_performed",
3799
+ type: "text",
3800
+ required: true,
3801
+ primary: true
3802
+ },
3803
+ {
3804
+ name: "vehicle_id",
3805
+ type: "text",
3806
+ required: true
3807
+ },
3808
+ {
3809
+ name: "trip_id_scheduled",
3810
+ type: "text"
3811
+ },
3812
+ {
3813
+ name: "route_id",
3814
+ type: "text"
3815
+ },
3816
+ {
3817
+ name: "route_type",
3818
+ type: "text"
3819
+ },
3820
+ {
3821
+ name: "ntd_mode",
3822
+ type: "text"
3823
+ },
3824
+ {
3825
+ name: "route_type_agency",
3826
+ type: "text"
3827
+ },
3828
+ {
3829
+ name: "shape_id",
3830
+ type: "text"
3831
+ },
3832
+ {
3833
+ name: "pattern_id",
3834
+ type: "text"
3835
+ },
3836
+ {
3837
+ name: "direction_id",
3838
+ type: "integer",
3839
+ min: 0,
3840
+ max: 1
3841
+ },
3842
+ {
3843
+ name: "operator_id",
3844
+ type: "text"
3845
+ },
3846
+ {
3847
+ name: "block_id",
3848
+ type: "text"
3849
+ },
3850
+ {
3851
+ name: "trip_start_stop_id",
3852
+ type: "text"
3853
+ },
3854
+ {
3855
+ name: "trip_end_stop_id",
3856
+ type: "text"
3857
+ },
3858
+ {
3859
+ name: "schedule_trip_start",
3860
+ type: "text"
3861
+ },
3862
+ {
3863
+ name: "schedule_trip_end",
3864
+ type: "text"
3865
+ },
3866
+ {
3867
+ name: "actual_trip_start",
3868
+ type: "text"
3869
+ },
3870
+ {
3871
+ name: "actual_trip_end",
3872
+ type: "text"
3873
+ },
3874
+ {
3875
+ name: "trip_type",
3876
+ type: "text"
3877
+ },
3878
+ {
3879
+ name: "schedule_relationship",
3880
+ type: "text"
3881
+ }
3882
+ ]
3883
+ };
3884
+
3885
+ // src/models/tides/vehicle-train-cars.ts
3886
+ var vehicleTrainCars = {
3887
+ filenameBase: "vehicle_train_cars",
3888
+ filenameExtension: "txt",
3889
+ nonstandard: true,
3890
+ extension: "tides",
3891
+ schema: [
3892
+ {
3893
+ name: "vehicle_id",
3894
+ type: "text",
3895
+ required: true,
3896
+ primary: true
3897
+ },
3898
+ {
3899
+ name: "train_car_id",
3900
+ type: "text",
3901
+ required: true,
3902
+ primary: true
3903
+ },
3904
+ {
3905
+ name: "train_car_order",
3906
+ type: "integer",
3907
+ min: 0
3908
+ },
3909
+ {
3910
+ name: "operator_id",
3911
+ type: "text"
3912
+ }
3913
+ ]
3914
+ };
3915
+
3916
+ // src/models/tides/vehicle-locations.ts
3917
+ var vehicleLocations = {
3918
+ filenameBase: "vehicle_locations",
3919
+ filenameExtension: "txt",
3920
+ nonstandard: true,
3921
+ extension: "tides",
3922
+ schema: [
3923
+ {
3924
+ name: "location_ping_id",
3925
+ type: "text",
3926
+ required: true,
3927
+ primary: true
3928
+ },
3929
+ {
3930
+ name: "service_date",
3931
+ type: "date"
3932
+ },
3933
+ {
3934
+ name: "event_timestamp",
3935
+ type: "text",
3936
+ required: true
3937
+ },
3938
+ {
3939
+ name: "trip_id_performed",
3940
+ type: "text"
3941
+ },
3942
+ {
3943
+ name: "trip_id_scheduled",
3944
+ type: "text"
3945
+ },
3946
+ {
3947
+ name: "trip_stop_sequence",
3948
+ type: "integer",
3949
+ min: 1
3950
+ },
3951
+ {
3952
+ name: "scheduled_stop_sequence",
3953
+ type: "integer",
3954
+ min: 0
3955
+ },
3956
+ {
3957
+ name: "vehicle_id",
3958
+ type: "text",
3959
+ required: true
3960
+ },
3961
+ {
3962
+ name: "device_id",
3963
+ type: "text"
3964
+ },
3965
+ {
3966
+ name: "pattern_id",
3967
+ type: "text"
3968
+ },
3969
+ {
3970
+ name: "stop_id",
3971
+ type: "text"
3972
+ },
3973
+ {
3974
+ name: "current_status",
3975
+ type: "text"
3976
+ },
3977
+ {
3978
+ name: "latitude",
3979
+ type: "real",
3980
+ min: -90,
3981
+ max: 90
3982
+ },
3983
+ {
3984
+ name: "longitude",
3985
+ type: "real",
3986
+ min: -180,
3987
+ max: 180
3988
+ },
3989
+ {
3990
+ name: "gps_quality",
3991
+ type: "text"
3992
+ },
3993
+ {
3994
+ name: "heading",
3995
+ type: "real",
3996
+ min: 0,
3997
+ max: 360
3998
+ },
3999
+ {
4000
+ name: "speed",
4001
+ type: "real",
4002
+ min: 0
4003
+ },
4004
+ {
4005
+ name: "odometer",
4006
+ type: "real",
4007
+ min: 0
4008
+ },
4009
+ {
4010
+ name: "schedule_deviation",
4011
+ type: "integer"
4012
+ },
4013
+ {
4014
+ name: "headway_deviation",
4015
+ type: "integer"
4016
+ },
4017
+ {
4018
+ name: "trip_type",
4019
+ type: "text"
4020
+ },
4021
+ {
4022
+ name: "schedule_relationship",
4023
+ type: "text"
4024
+ }
4025
+ ]
4026
+ };
4027
+
4028
+ // src/models/tides/vehicles.ts
4029
+ var vehicles = {
4030
+ filenameBase: "vehicles",
4031
+ filenameExtension: "txt",
4032
+ nonstandard: true,
4033
+ extension: "tides",
4034
+ schema: [
4035
+ {
4036
+ name: "vehicle_id",
4037
+ type: "text",
4038
+ required: true,
4039
+ primary: true
4040
+ },
4041
+ {
4042
+ name: "vehicle_start",
4043
+ type: "text"
4044
+ },
4045
+ {
4046
+ name: "vehicle_end",
4047
+ type: "text"
4048
+ },
4049
+ {
4050
+ name: "model_name",
4051
+ type: "text"
4052
+ },
4053
+ {
4054
+ name: "facility_name",
4055
+ type: "text"
4056
+ },
4057
+ {
4058
+ name: "capacity_seated",
4059
+ type: "integer",
4060
+ min: 0
4061
+ },
4062
+ {
4063
+ name: "capacity_wheelchair",
4064
+ type: "integer",
4065
+ min: 0
4066
+ },
4067
+ {
4068
+ name: "capacity_bike",
4069
+ type: "integer",
4070
+ min: 0
4071
+ },
4072
+ {
4073
+ name: "bike_rack",
4074
+ type: "text"
4075
+ },
4076
+ {
4077
+ name: "capacity_standing",
4078
+ type: "integer",
4079
+ min: 0
4080
+ }
4081
+ ]
4082
+ };
4083
+
4084
+ // src/lib/db.ts
4085
+ import Database from "better-sqlite3";
4086
+ import untildify2 from "untildify";
4087
+ var dbs = {};
4088
+ function setupDb(sqlitePath) {
4089
+ const db = new Database(untildify2(sqlitePath));
4090
+ db.pragma("journal_mode = OFF");
4091
+ db.pragma("synchronous = OFF");
4092
+ db.pragma("temp_store = MEMORY");
4093
+ dbs[sqlitePath] = db;
4094
+ return db;
4095
+ }
4096
+ function openDb(config = null) {
4097
+ if (config) {
4098
+ const { sqlitePath = ":memory:", db } = config;
4099
+ if (db) {
4100
+ return db;
4101
+ }
4102
+ if (dbs[sqlitePath]) {
4103
+ return dbs[sqlitePath];
4104
+ }
4105
+ return setupDb(sqlitePath);
4106
+ }
4107
+ if (Object.keys(dbs).length === 0) {
4108
+ return setupDb(":memory:");
4109
+ }
4110
+ if (Object.keys(dbs).length === 1) {
4111
+ const filename = Object.keys(dbs)[0];
4112
+ return dbs[filename];
4113
+ }
4114
+ if (Object.keys(dbs).length > 1) {
4115
+ throw new Error(
4116
+ "Multiple databases open, please specify which one to use."
4117
+ );
4118
+ }
4119
+ throw new Error("Unable to find database connection.");
4120
+ }
4121
+ function closeDb(db = null) {
4122
+ if (Object.keys(dbs).length === 0) {
4123
+ throw new Error(
4124
+ "No database connection. Call `openDb(config)` before using any methods."
4125
+ );
4126
+ }
4127
+ if (!db) {
4128
+ if (Object.keys(dbs).length > 1) {
4129
+ throw new Error(
4130
+ "Multiple database connections. Pass the db you want to close as a parameter to `closeDb`."
4131
+ );
4132
+ }
4133
+ db = dbs[Object.keys(dbs)[0]];
4134
+ }
4135
+ db.close();
4136
+ delete dbs[db.name];
4137
+ }
4138
+
4139
+ // src/lib/geojson-utils.ts
4140
+ import {
4141
+ cloneDeep,
4142
+ compact,
4143
+ filter,
4144
+ groupBy,
4145
+ last,
4146
+ omit as omit2,
4147
+ sortBy,
4148
+ omitBy
4149
+ } from "lodash-es";
4150
+ import { feature, featureCollection } from "@turf/helpers";
4151
+ function isValidJSON(string) {
4152
+ try {
4153
+ JSON.parse(string);
4154
+ return true;
4155
+ } catch (error) {
4156
+ return false;
4157
+ }
4158
+ }
4159
+
4160
+ // src/lib/import-gtfs-realtime.ts
4161
+ import pluralize from "pluralize";
4162
+ import GtfsRealtimeBindings from "gtfs-realtime-bindings";
4163
+ import mapSeries from "promise-map-series";
4164
+ import { get } from "lodash-es";
4165
+
4166
+ // src/lib/utils.ts
4167
+ import sqlString from "sqlstring-sqlite";
4168
+ import Long from "long";
4169
+ function validateConfigForImport(config) {
4170
+ if (!config.agencies || config.agencies.length === 0) {
4171
+ throw new Error("No `agencies` specified in config");
4172
+ }
4173
+ for (const [index, agency2] of config.agencies.entries()) {
4174
+ if (!agency2.path && !agency2.url) {
4175
+ throw new Error(
4176
+ `No Agency \`url\` or \`path\` specified in config for agency index ${index}.`
4177
+ );
4178
+ }
4179
+ }
4180
+ return config;
4181
+ }
4182
+ function setDefaultConfig(initialConfig) {
4183
+ const defaults = {
4184
+ sqlitePath: ":memory:",
4185
+ ignoreDuplicates: false,
4186
+ ignoreErrors: false,
4187
+ gtfsRealtimeExpirationSeconds: 0,
4188
+ verbose: true
4189
+ };
4190
+ return {
4191
+ ...defaults,
4192
+ ...initialConfig
4193
+ };
4194
+ }
4195
+ function convertLongTimeToDate(longDate) {
4196
+ const { high, low, unsigned } = longDate;
4197
+ return new Date(
4198
+ Long.fromBits(low, high, unsigned).toNumber() * 1e3
4199
+ ).toISOString();
4200
+ }
4201
+ function padLeadingZeros(time) {
4202
+ const split = time.split(":").map((d) => String(Number(d)).padStart(2, "0"));
4203
+ if (split.length !== 3) {
4204
+ return null;
4205
+ }
3371
4206
  return split.join(":");
3372
4207
  }
3373
4208
  function getTimestampColumnName(columnName) {
@@ -3587,18 +4422,6 @@ async function updateGtfsRealtimeData(task) {
3587
4422
  }
3588
4423
 
3589
4424
  // src/lib/import-gtfs.ts
3590
- var timeCache = {};
3591
- var formatAndCacheTime = (value) => {
3592
- const cached = timeCache[value];
3593
- if (cached !== void 0) {
3594
- return cached;
3595
- }
3596
- const timeAsSecondsFromMidnight = calculateSecondsFromMidnight(value);
3597
- const timeAsString = padLeadingZeros(value);
3598
- const computed = [timeAsSecondsFromMidnight, timeAsString];
3599
- timeCache[value] = computed;
3600
- return computed;
3601
- };
3602
4425
  var getTextFiles = async (folderPath) => {
3603
4426
  const files = await readdir(folderPath);
3604
4427
  return files.filter((filename) => filename.slice(-3) === "txt");
@@ -3710,7 +4533,16 @@ var createGtfsTables = (db) => {
3710
4533
  );
3711
4534
  if (column.type === "time") {
3712
4535
  sqlColumnCreateStatements.push(
3713
- `${getTimestampColumnName(column.name)} INTEGER`
4536
+ `${getTimestampColumnName(column.name)} INTEGER GENERATED ALWAYS AS (
4537
+ CASE
4538
+ WHEN ${column.name} IS NULL OR ${column.name} = '' THEN NULL
4539
+ ELSE CAST(
4540
+ substr(${column.name}, 1, instr(${column.name}, ':') - 1) * 3600 +
4541
+ substr(${column.name}, instr(${column.name}, ':') + 1, 2) * 60 +
4542
+ substr(${column.name}, -2) AS INTEGER
4543
+ )
4544
+ END
4545
+ ) STORED`
3714
4546
  );
3715
4547
  }
3716
4548
  }
@@ -3755,9 +4587,6 @@ var formatGtfsLine = (line, model, totalLineCount) => {
3755
4587
  let value = line[name];
3756
4588
  if (value === "" || value === void 0 || value === null) {
3757
4589
  formattedLine[name] = null;
3758
- if (type === "time") {
3759
- formattedLine[getTimestampColumnName(name)] = null;
3760
- }
3761
4590
  if (required) {
3762
4591
  throw new Error(
3763
4592
  `Missing required value in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}.`
@@ -3773,9 +4602,7 @@ var formatGtfsLine = (line, model, totalLineCount) => {
3773
4602
  );
3774
4603
  }
3775
4604
  } else if (type === "time") {
3776
- const [timeAsSecondsFromMidnight, timeAsString] = formatAndCacheTime(value);
3777
- value = timeAsString;
3778
- formattedLine[getTimestampColumnName(name)] = timeAsSecondsFromMidnight ?? null;
4605
+ value = padLeadingZeros(value);
3779
4606
  }
3780
4607
  if (type === "json") {
3781
4608
  value = JSON.stringify(value);
@@ -3808,19 +4635,7 @@ var importGtfsFiles = (db, task) => mapSeries2(
3808
4635
  return;
3809
4636
  }
3810
4637
  task.log(`Importing - ${filename}\r`);
3811
- const columns = model.schema.flatMap((column) => {
3812
- if (column.type === "time") {
3813
- return [
3814
- column,
3815
- {
3816
- name: getTimestampColumnName(column.name),
3817
- type: "integer",
3818
- index: true
3819
- }
3820
- ];
3821
- }
3822
- return column;
3823
- });
4638
+ const columns = model.schema;
3824
4639
  const prefixedColumns = new Set(
3825
4640
  columns.filter((column) => column.prefix).map((column) => column.name)
3826
4641
  );
@@ -4005,6 +4820,8 @@ async function importGtfs(initialConfig) {
4005
4820
  }
4006
4821
 
4007
4822
  // src/lib/export.ts
4823
+ import path3 from "path";
4824
+ import { writeFile as writeFile2 } from "fs/promises";
4008
4825
  import { without, compact as compact2 } from "lodash-es";
4009
4826
  import pluralize3 from "pluralize";
4010
4827
  import { stringify } from "csv-stringify";