minotor 7.0.2 → 8.0.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.
Files changed (58) hide show
  1. package/.cspell.json +11 -1
  2. package/CHANGELOG.md +8 -3
  3. package/README.md +26 -24
  4. package/dist/cli.mjs +1243 -267
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/gtfs/transfers.d.ts +13 -4
  7. package/dist/gtfs/trips.d.ts +12 -7
  8. package/dist/parser.cjs.js +494 -71
  9. package/dist/parser.cjs.js.map +1 -1
  10. package/dist/parser.esm.js +494 -71
  11. package/dist/parser.esm.js.map +1 -1
  12. package/dist/router.cjs.js +1 -1
  13. package/dist/router.cjs.js.map +1 -1
  14. package/dist/router.d.ts +2 -2
  15. package/dist/router.esm.js +1 -1
  16. package/dist/router.esm.js.map +1 -1
  17. package/dist/router.umd.js +1 -1
  18. package/dist/router.umd.js.map +1 -1
  19. package/dist/routing/__tests__/plotter.test.d.ts +1 -0
  20. package/dist/routing/plotter.d.ts +42 -3
  21. package/dist/routing/result.d.ts +23 -7
  22. package/dist/routing/route.d.ts +2 -0
  23. package/dist/routing/router.d.ts +78 -19
  24. package/dist/timetable/__tests__/tripId.test.d.ts +1 -0
  25. package/dist/timetable/io.d.ts +4 -2
  26. package/dist/timetable/proto/timetable.d.ts +13 -1
  27. package/dist/timetable/route.d.ts +41 -8
  28. package/dist/timetable/timetable.d.ts +18 -3
  29. package/dist/timetable/tripId.d.ts +15 -0
  30. package/package.json +1 -1
  31. package/src/__e2e__/router.test.ts +114 -105
  32. package/src/__e2e__/timetable/stops.bin +2 -2
  33. package/src/__e2e__/timetable/timetable.bin +2 -2
  34. package/src/cli/repl.ts +259 -1
  35. package/src/gtfs/__tests__/transfers.test.ts +468 -12
  36. package/src/gtfs/__tests__/trips.test.ts +350 -28
  37. package/src/gtfs/parser.ts +16 -4
  38. package/src/gtfs/transfers.ts +61 -18
  39. package/src/gtfs/trips.ts +97 -22
  40. package/src/router.ts +2 -2
  41. package/src/routing/__tests__/plotter.test.ts +230 -0
  42. package/src/routing/__tests__/result.test.ts +486 -125
  43. package/src/routing/__tests__/route.test.ts +7 -3
  44. package/src/routing/__tests__/router.test.ts +378 -172
  45. package/src/routing/plotter.ts +279 -48
  46. package/src/routing/result.ts +114 -34
  47. package/src/routing/route.ts +0 -3
  48. package/src/routing/router.ts +332 -211
  49. package/src/timetable/__tests__/io.test.ts +33 -1
  50. package/src/timetable/__tests__/route.test.ts +10 -3
  51. package/src/timetable/__tests__/timetable.test.ts +225 -57
  52. package/src/timetable/__tests__/tripId.test.ts +27 -0
  53. package/src/timetable/io.ts +71 -10
  54. package/src/timetable/proto/timetable.proto +14 -2
  55. package/src/timetable/proto/timetable.ts +218 -20
  56. package/src/timetable/route.ts +152 -19
  57. package/src/timetable/timetable.ts +45 -6
  58. package/src/timetable/tripId.ts +29 -0
@@ -59,7 +59,7 @@ describe('GTFS transfers parser', () => {
59
59
  ],
60
60
  ]);
61
61
 
62
- const transfers = await parseTransfers(mockedStream, stopsMap);
62
+ const result = await parseTransfers(mockedStream, stopsMap);
63
63
  const expectedTransfers = new Map([
64
64
  [
65
65
  0, // Internal ID for stop '1100084'
@@ -83,7 +83,8 @@ describe('GTFS transfers parser', () => {
83
83
  ],
84
84
  ]);
85
85
 
86
- assert.deepEqual(transfers, expectedTransfers);
86
+ assert.deepEqual(result.transfers, expectedTransfers);
87
+ assert.deepEqual(result.tripContinuations, new Map());
87
88
  });
88
89
 
89
90
  it('should ignore impossible transfer types', async () => {
@@ -138,8 +139,9 @@ describe('GTFS transfers parser', () => {
138
139
  ],
139
140
  ]);
140
141
 
141
- const transfers = await parseTransfers(mockedStream, stopsMap);
142
- assert.deepEqual(transfers, new Map());
142
+ const result = await parseTransfers(mockedStream, stopsMap);
143
+ assert.deepEqual(result.transfers, new Map());
144
+ assert.deepEqual(result.tripContinuations, new Map());
143
145
  });
144
146
 
145
147
  it('should ignore unsupported transfer types between routes', async () => {
@@ -173,8 +175,9 @@ describe('GTFS transfers parser', () => {
173
175
  ],
174
176
  ]);
175
177
 
176
- const transfers = await parseTransfers(mockedStream, stopsMap);
177
- assert.deepEqual(transfers, new Map());
178
+ const result = await parseTransfers(mockedStream, stopsMap);
179
+ assert.deepEqual(result.transfers, new Map());
180
+ assert.deepEqual(result.tripContinuations, new Map());
178
181
  });
179
182
 
180
183
  it('should ignore unsupported transfer types between trips', async () => {
@@ -208,8 +211,9 @@ describe('GTFS transfers parser', () => {
208
211
  ],
209
212
  ]);
210
213
 
211
- const transfers = await parseTransfers(mockedStream, stopsMap);
212
- assert.deepEqual(transfers, new Map());
214
+ const result = await parseTransfers(mockedStream, stopsMap);
215
+ assert.deepEqual(result.transfers, new Map());
216
+ assert.deepEqual(result.tripContinuations, new Map());
213
217
  });
214
218
 
215
219
  it('should allow missing minimum transfer time', async () => {
@@ -242,9 +246,9 @@ describe('GTFS transfers parser', () => {
242
246
  },
243
247
  ],
244
248
  ]);
245
- const transfers = await parseTransfers(mockedStream, stopsMap);
249
+ const result = await parseTransfers(mockedStream, stopsMap);
246
250
  assert.deepEqual(
247
- transfers,
251
+ result.transfers,
248
252
  new Map([
249
253
  [
250
254
  0, // Internal ID for stop '1100084'
@@ -257,6 +261,7 @@ describe('GTFS transfers parser', () => {
257
261
  ],
258
262
  ]),
259
263
  );
264
+ assert.deepEqual(result.tripContinuations, new Map());
260
265
  });
261
266
 
262
267
  it('should handle empty transfers', async () => {
@@ -268,7 +273,458 @@ describe('GTFS transfers parser', () => {
268
273
 
269
274
  const stopsMap: GtfsStopsMap = new Map();
270
275
 
271
- const transfers = await parseTransfers(mockedStream, stopsMap);
272
- assert.deepEqual(transfers, new Map());
276
+ const result = await parseTransfers(mockedStream, stopsMap);
277
+ assert.deepEqual(result.transfers, new Map());
278
+ assert.deepEqual(result.tripContinuations, new Map());
279
+ });
280
+
281
+ it('should correctly parse valid trip continuations (in-seat transfers)', async () => {
282
+ const mockedStream = new Readable();
283
+ mockedStream.push(
284
+ 'from_stop_id,to_stop_id,from_trip_id,to_trip_id,transfer_type\n',
285
+ );
286
+ mockedStream.push('"stop1","stop2","trip1","trip2","4"\n');
287
+ mockedStream.push('"stop3","stop4","trip3","trip4","4"\n');
288
+ mockedStream.push(null);
289
+
290
+ const stopsMap: GtfsStopsMap = new Map([
291
+ [
292
+ 'stop1',
293
+ {
294
+ id: 0,
295
+ sourceStopId: 'stop1',
296
+ name: 'Stop 1',
297
+ children: [],
298
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
299
+ },
300
+ ],
301
+ [
302
+ 'stop2',
303
+ {
304
+ id: 1,
305
+ sourceStopId: 'stop2',
306
+ name: 'Stop 2',
307
+ children: [],
308
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
309
+ },
310
+ ],
311
+ [
312
+ 'stop3',
313
+ {
314
+ id: 2,
315
+ sourceStopId: 'stop3',
316
+ name: 'Stop 3',
317
+ children: [],
318
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
319
+ },
320
+ ],
321
+ [
322
+ 'stop4',
323
+ {
324
+ id: 3,
325
+ sourceStopId: 'stop4',
326
+ name: 'Stop 4',
327
+ children: [],
328
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
329
+ },
330
+ ],
331
+ ]);
332
+
333
+ const result = await parseTransfers(mockedStream, stopsMap);
334
+
335
+ const expectedTripContinuations = new Map([
336
+ [
337
+ 0, // from_stop_id 'stop1' -> internal ID 0
338
+ [
339
+ {
340
+ fromTrip: 'trip1',
341
+ toTrip: 'trip2',
342
+ hopOnStop: 1, // to_stop_id 'stop2' -> internal ID 1
343
+ },
344
+ ],
345
+ ],
346
+ [
347
+ 2, // from_stop_id 'stop3' -> internal ID 2
348
+ [
349
+ {
350
+ fromTrip: 'trip3',
351
+ toTrip: 'trip4',
352
+ hopOnStop: 3, // to_stop_id 'stop4' -> internal ID 3
353
+ },
354
+ ],
355
+ ],
356
+ ]);
357
+
358
+ assert.deepEqual(result.transfers, new Map());
359
+ assert.deepEqual(result.tripContinuations, expectedTripContinuations);
360
+ });
361
+
362
+ it('should handle multiple trip continuations from the same stop', async () => {
363
+ const mockedStream = new Readable();
364
+ mockedStream.push(
365
+ 'from_stop_id,to_stop_id,from_trip_id,to_trip_id,transfer_type\n',
366
+ );
367
+ mockedStream.push('"stop1","stop2","trip1","trip2","4"\n');
368
+ mockedStream.push('"stop1","stop3","trip1","trip3","4"\n');
369
+ mockedStream.push(null);
370
+
371
+ const stopsMap: GtfsStopsMap = new Map([
372
+ [
373
+ 'stop1',
374
+ {
375
+ id: 0,
376
+ sourceStopId: 'stop1',
377
+ name: 'Stop 1',
378
+ children: [],
379
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
380
+ },
381
+ ],
382
+ [
383
+ 'stop2',
384
+ {
385
+ id: 1,
386
+ sourceStopId: 'stop2',
387
+ name: 'Stop 2',
388
+ children: [],
389
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
390
+ },
391
+ ],
392
+ [
393
+ 'stop3',
394
+ {
395
+ id: 2,
396
+ sourceStopId: 'stop3',
397
+ name: 'Stop 3',
398
+ children: [],
399
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
400
+ },
401
+ ],
402
+ ]);
403
+
404
+ const result = await parseTransfers(mockedStream, stopsMap);
405
+
406
+ const expectedTripContinuations = new Map([
407
+ [
408
+ 0, // from_stop_id 'stop1' -> internal ID 0
409
+ [
410
+ {
411
+ fromTrip: 'trip1',
412
+ toTrip: 'trip2',
413
+ hopOnStop: 1, // to_stop_id 'stop2' -> internal ID 1
414
+ },
415
+ {
416
+ fromTrip: 'trip1',
417
+ toTrip: 'trip3',
418
+ hopOnStop: 2, // to_stop_id 'stop3' -> internal ID 2
419
+ },
420
+ ],
421
+ ],
422
+ ]);
423
+
424
+ assert.deepEqual(result.transfers, new Map());
425
+ assert.deepEqual(result.tripContinuations, expectedTripContinuations);
426
+ });
427
+
428
+ it('should mix regular transfers and trip continuations correctly', async () => {
429
+ const mockedStream = new Readable();
430
+ mockedStream.push(
431
+ 'from_stop_id,to_stop_id,from_trip_id,to_trip_id,transfer_type,min_transfer_time\n',
432
+ );
433
+ mockedStream.push('"stop1","stop2","","","2","120"\n'); // Regular transfer
434
+ mockedStream.push('"stop1","stop3","trip1","trip2","4",""\n'); // Trip continuation
435
+ mockedStream.push('"stop2","stop3","","","0",""\n'); // Regular transfer
436
+ mockedStream.push(null);
437
+
438
+ const stopsMap: GtfsStopsMap = new Map([
439
+ [
440
+ 'stop1',
441
+ {
442
+ id: 0,
443
+ sourceStopId: 'stop1',
444
+ name: 'Stop 1',
445
+ children: [],
446
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
447
+ },
448
+ ],
449
+ [
450
+ 'stop2',
451
+ {
452
+ id: 1,
453
+ sourceStopId: 'stop2',
454
+ name: 'Stop 2',
455
+ children: [],
456
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
457
+ },
458
+ ],
459
+ [
460
+ 'stop3',
461
+ {
462
+ id: 2,
463
+ sourceStopId: 'stop3',
464
+ name: 'Stop 3',
465
+ children: [],
466
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
467
+ },
468
+ ],
469
+ ]);
470
+
471
+ const result = await parseTransfers(mockedStream, stopsMap);
472
+
473
+ const expectedTransfers = new Map([
474
+ [
475
+ 0, // from_stop_id 'stop1' -> internal ID 0
476
+ [
477
+ {
478
+ destination: 1, // to_stop_id 'stop2' -> internal ID 1
479
+ type: 'REQUIRES_MINIMAL_TIME',
480
+ minTransferTime: Duration.fromSeconds(120),
481
+ },
482
+ ],
483
+ ],
484
+ [
485
+ 1, // from_stop_id 'stop2' -> internal ID 1
486
+ [
487
+ {
488
+ destination: 2, // to_stop_id 'stop3' -> internal ID 2
489
+ type: 'RECOMMENDED',
490
+ },
491
+ ],
492
+ ],
493
+ ]);
494
+
495
+ const expectedTripContinuations = new Map([
496
+ [
497
+ 0, // from_stop_id 'stop1' -> internal ID 0
498
+ [
499
+ {
500
+ fromTrip: 'trip1',
501
+ toTrip: 'trip2',
502
+ hopOnStop: 2, // to_stop_id 'stop3' -> internal ID 2
503
+ },
504
+ ],
505
+ ],
506
+ ]);
507
+
508
+ assert.deepEqual(result.transfers, expectedTransfers);
509
+ assert.deepEqual(result.tripContinuations, expectedTripContinuations);
510
+ });
511
+
512
+ it('should ignore trip continuations with undefined trip IDs', async () => {
513
+ const mockedStream = new Readable();
514
+ mockedStream.push('from_stop_id,to_stop_id,transfer_type\n');
515
+ mockedStream.push('"stop1","stop2","4"\n');
516
+ mockedStream.push(null);
517
+
518
+ const stopsMap: GtfsStopsMap = new Map([
519
+ [
520
+ 'stop1',
521
+ {
522
+ id: 0,
523
+ sourceStopId: 'stop1',
524
+ name: 'Stop 1',
525
+ children: [],
526
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
527
+ },
528
+ ],
529
+ [
530
+ 'stop2',
531
+ {
532
+ id: 1,
533
+ sourceStopId: 'stop2',
534
+ name: 'Stop 2',
535
+ children: [],
536
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
537
+ },
538
+ ],
539
+ ]);
540
+
541
+ const result = await parseTransfers(mockedStream, stopsMap);
542
+
543
+ assert.deepEqual(result.transfers, new Map());
544
+ assert.deepEqual(result.tripContinuations, new Map());
545
+ });
546
+
547
+ it('should ignore trip continuations with empty string trip IDs', async () => {
548
+ const mockedStream = new Readable();
549
+ mockedStream.push(
550
+ 'from_stop_id,to_stop_id,from_trip_id,to_trip_id,transfer_type\n',
551
+ );
552
+ mockedStream.push('"stop1","stop2","trip1","","4"\n');
553
+ mockedStream.push('"stop3","stop4","","trip4","4"\n');
554
+ mockedStream.push('"stop5","stop6","","","4"\n');
555
+ mockedStream.push(null);
556
+
557
+ const stopsMap: GtfsStopsMap = new Map([
558
+ [
559
+ 'stop1',
560
+ {
561
+ id: 0,
562
+ sourceStopId: 'stop1',
563
+ name: 'Stop 1',
564
+ children: [],
565
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
566
+ },
567
+ ],
568
+ [
569
+ 'stop2',
570
+ {
571
+ id: 1,
572
+ sourceStopId: 'stop2',
573
+ name: 'Stop 2',
574
+ children: [],
575
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
576
+ },
577
+ ],
578
+ [
579
+ 'stop3',
580
+ {
581
+ id: 2,
582
+ sourceStopId: 'stop3',
583
+ name: 'Stop 3',
584
+ children: [],
585
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
586
+ },
587
+ ],
588
+ [
589
+ 'stop4',
590
+ {
591
+ id: 3,
592
+ sourceStopId: 'stop4',
593
+ name: 'Stop 4',
594
+ children: [],
595
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
596
+ },
597
+ ],
598
+ [
599
+ 'stop5',
600
+ {
601
+ id: 4,
602
+ sourceStopId: 'stop5',
603
+ name: 'Stop 5',
604
+ children: [],
605
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
606
+ },
607
+ ],
608
+ [
609
+ 'stop6',
610
+ {
611
+ id: 5,
612
+ sourceStopId: 'stop6',
613
+ name: 'Stop 6',
614
+ children: [],
615
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
616
+ },
617
+ ],
618
+ ]);
619
+
620
+ const result = await parseTransfers(mockedStream, stopsMap);
621
+
622
+ assert.deepEqual(result.transfers, new Map());
623
+ assert.deepEqual(result.tripContinuations, new Map());
624
+ });
625
+
626
+ it('should handle complex scenario with multiple transfer types from same stop', async () => {
627
+ const mockedStream = new Readable();
628
+ mockedStream.push(
629
+ 'from_stop_id,to_stop_id,from_trip_id,to_trip_id,transfer_type,min_transfer_time\n',
630
+ );
631
+ mockedStream.push('"stop1","stop2","","","2","120"\n'); // Regular transfer to stop2
632
+ mockedStream.push('"stop1","stop3","trip1","trip2","4",""\n'); // Trip continuation to stop3
633
+ mockedStream.push('"stop1","stop4","","","0",""\n'); // Another regular transfer to stop4
634
+ mockedStream.push('"stop1","stop5","trip3","trip4","4",""\n'); // Another trip continuation to stop5
635
+ mockedStream.push(null);
636
+
637
+ const stopsMap: GtfsStopsMap = new Map([
638
+ [
639
+ 'stop1',
640
+ {
641
+ id: 0,
642
+ sourceStopId: 'stop1',
643
+ name: 'Stop 1',
644
+ children: [],
645
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
646
+ },
647
+ ],
648
+ [
649
+ 'stop2',
650
+ {
651
+ id: 1,
652
+ sourceStopId: 'stop2',
653
+ name: 'Stop 2',
654
+ children: [],
655
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
656
+ },
657
+ ],
658
+ [
659
+ 'stop3',
660
+ {
661
+ id: 2,
662
+ sourceStopId: 'stop3',
663
+ name: 'Stop 3',
664
+ children: [],
665
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
666
+ },
667
+ ],
668
+ [
669
+ 'stop4',
670
+ {
671
+ id: 3,
672
+ sourceStopId: 'stop4',
673
+ name: 'Stop 4',
674
+ children: [],
675
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
676
+ },
677
+ ],
678
+ [
679
+ 'stop5',
680
+ {
681
+ id: 4,
682
+ sourceStopId: 'stop5',
683
+ name: 'Stop 5',
684
+ children: [],
685
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
686
+ },
687
+ ],
688
+ ]);
689
+
690
+ const result = await parseTransfers(mockedStream, stopsMap);
691
+
692
+ const expectedTransfers = new Map([
693
+ [
694
+ 0, // from_stop_id 'stop1' -> internal ID 0
695
+ [
696
+ {
697
+ destination: 1, // to_stop_id 'stop2' -> internal ID 1
698
+ type: 'REQUIRES_MINIMAL_TIME',
699
+ minTransferTime: Duration.fromSeconds(120),
700
+ },
701
+ {
702
+ destination: 3, // to_stop_id 'stop4' -> internal ID 3
703
+ type: 'RECOMMENDED',
704
+ },
705
+ ],
706
+ ],
707
+ ]);
708
+
709
+ const expectedTripContinuations = new Map([
710
+ [
711
+ 0, // from_stop_id 'stop1' -> internal ID 0
712
+ [
713
+ {
714
+ fromTrip: 'trip1',
715
+ toTrip: 'trip2',
716
+ hopOnStop: 2, // to_stop_id 'stop3' -> internal ID 2
717
+ },
718
+ {
719
+ fromTrip: 'trip3',
720
+ toTrip: 'trip4',
721
+ hopOnStop: 4, // to_stop_id 'stop5' -> internal ID 4
722
+ },
723
+ ],
724
+ ],
725
+ ]);
726
+
727
+ assert.deepEqual(result.transfers, expectedTransfers);
728
+ assert.deepEqual(result.tripContinuations, expectedTripContinuations);
273
729
  });
274
730
  });