minotor 7.0.2 → 9.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 (60) 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 +1786 -791
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/gtfs/transfers.d.ts +29 -5
  7. package/dist/gtfs/trips.d.ts +10 -5
  8. package/dist/parser.cjs.js +972 -525
  9. package/dist/parser.cjs.js.map +1 -1
  10. package/dist/parser.esm.js +972 -525
  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__/tripBoardingId.test.d.ts +1 -0
  25. package/dist/timetable/io.d.ts +4 -2
  26. package/dist/timetable/proto/timetable.d.ts +15 -1
  27. package/dist/timetable/route.d.ts +48 -23
  28. package/dist/timetable/timetable.d.ts +24 -7
  29. package/dist/timetable/tripBoardingId.d.ts +34 -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 +245 -1
  35. package/src/gtfs/__tests__/parser.test.ts +19 -4
  36. package/src/gtfs/__tests__/transfers.test.ts +773 -37
  37. package/src/gtfs/__tests__/trips.test.ts +308 -27
  38. package/src/gtfs/parser.ts +36 -6
  39. package/src/gtfs/transfers.ts +193 -19
  40. package/src/gtfs/trips.ts +58 -21
  41. package/src/router.ts +2 -2
  42. package/src/routing/__tests__/plotter.test.ts +230 -0
  43. package/src/routing/__tests__/result.test.ts +486 -125
  44. package/src/routing/__tests__/route.test.ts +7 -3
  45. package/src/routing/__tests__/router.test.ts +380 -172
  46. package/src/routing/plotter.ts +279 -48
  47. package/src/routing/result.ts +114 -34
  48. package/src/routing/route.ts +0 -3
  49. package/src/routing/router.ts +344 -211
  50. package/src/timetable/__tests__/io.test.ts +34 -1
  51. package/src/timetable/__tests__/route.test.ts +74 -81
  52. package/src/timetable/__tests__/timetable.test.ts +232 -61
  53. package/src/timetable/__tests__/tripBoardingId.test.ts +57 -0
  54. package/src/timetable/io.ts +72 -10
  55. package/src/timetable/proto/timetable.proto +16 -2
  56. package/src/timetable/proto/timetable.ts +256 -22
  57. package/src/timetable/route.ts +174 -58
  58. package/src/timetable/timetable.ts +66 -16
  59. package/src/timetable/tripBoardingId.ts +94 -0
  60. package/tsconfig.json +2 -2
@@ -0,0 +1,230 @@
1
+ import assert from 'node:assert';
2
+ import { describe, it } from 'node:test';
3
+
4
+ import { Timetable } from '../../router.js';
5
+ import { Stop, StopId } from '../../stops/stops.js';
6
+ import { StopsIndex } from '../../stops/stopsIndex.js';
7
+ import { Route } from '../../timetable/route.js';
8
+ import { Time } from '../../timetable/time.js';
9
+ import { ServiceRoute, StopAdjacency } from '../../timetable/timetable.js';
10
+ import { Plotter } from '../plotter.js';
11
+ import { Query } from '../query.js';
12
+ import { Result } from '../result.js';
13
+ import { RoutingEdge } from '../router.js';
14
+
15
+ describe('Plotter', () => {
16
+ const stop1: Stop = {
17
+ id: 0,
18
+ sourceStopId: 'stop1',
19
+ name: 'Lausanne',
20
+ children: [],
21
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
22
+ };
23
+
24
+ const stop2: Stop = {
25
+ id: 1,
26
+ sourceStopId: 'stop2',
27
+ name: 'Fribourg',
28
+ children: [],
29
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
30
+ };
31
+
32
+ const stopsAdjacency: StopAdjacency[] = [{ routes: [0] }, { routes: [0] }];
33
+
34
+ const routesAdjacency = [
35
+ Route.of({
36
+ id: 0,
37
+ serviceRouteId: 0,
38
+ trips: [
39
+ {
40
+ stops: [
41
+ {
42
+ id: 0,
43
+ arrivalTime: Time.fromString('08:00:00'),
44
+ departureTime: Time.fromString('08:05:00'),
45
+ },
46
+ {
47
+ id: 1,
48
+ arrivalTime: Time.fromString('08:30:00'),
49
+ departureTime: Time.fromString('08:35:00'),
50
+ },
51
+ ],
52
+ },
53
+ ],
54
+ }),
55
+ ];
56
+
57
+ const routes: ServiceRoute[] = [
58
+ {
59
+ type: 'RAIL',
60
+ name: 'IC 1',
61
+ routes: [0],
62
+ },
63
+ ];
64
+
65
+ const mockStopsIndex = new StopsIndex([stop1, stop2]);
66
+ const mockTimetable = new Timetable(stopsAdjacency, routesAdjacency, routes);
67
+ const mockQuery = new Query.Builder()
68
+ .from('stop1')
69
+ .to(new Set(['stop2']))
70
+ .departureTime(Time.fromHMS(8, 0, 0))
71
+ .build();
72
+
73
+ describe('plotDotGraph', () => {
74
+ it('should generate valid DOT graph structure', () => {
75
+ const result = new Result(
76
+ mockQuery,
77
+ {
78
+ earliestArrivals: new Map(),
79
+ graph: [],
80
+ destinations: [],
81
+ },
82
+ mockStopsIndex,
83
+ mockTimetable,
84
+ );
85
+
86
+ const plotter = new Plotter(result);
87
+ const dotGraph = plotter.plotDotGraph();
88
+
89
+ assert(dotGraph.includes('digraph RoutingGraph {'));
90
+ assert(dotGraph.includes('// Stations'));
91
+ assert(dotGraph.includes('// Edges'));
92
+ assert(dotGraph.endsWith('}'));
93
+ });
94
+
95
+ it('should include station nodes', () => {
96
+ const graph: Map<StopId, RoutingEdge>[] = [
97
+ new Map([[0, { arrival: Time.fromHMS(8, 0, 0) }]]),
98
+ ];
99
+
100
+ const result = new Result(
101
+ mockQuery,
102
+ {
103
+ earliestArrivals: new Map(),
104
+ graph,
105
+ destinations: [0],
106
+ },
107
+ mockStopsIndex,
108
+ mockTimetable,
109
+ );
110
+
111
+ const plotter = new Plotter(result);
112
+ const dotGraph = plotter.plotDotGraph();
113
+
114
+ assert(dotGraph.includes('"s_0"'));
115
+ assert(dotGraph.includes('Lausanne'));
116
+ assert(dotGraph.includes('shape=box'));
117
+ });
118
+
119
+ it('should handle empty graph gracefully', () => {
120
+ const result = new Result(
121
+ mockQuery,
122
+ {
123
+ earliestArrivals: new Map(),
124
+ graph: [],
125
+ destinations: [],
126
+ },
127
+ mockStopsIndex,
128
+ mockTimetable,
129
+ );
130
+
131
+ const plotter = new Plotter(result);
132
+ const dotGraph = plotter.plotDotGraph();
133
+
134
+ assert(dotGraph.includes('digraph RoutingGraph {'));
135
+ assert(dotGraph.endsWith('}'));
136
+ });
137
+
138
+ it('should escape special characters', () => {
139
+ const specialStop: Stop = {
140
+ id: 2,
141
+ sourceStopId: 'test"stop\\with\nlines\rand\ttabs',
142
+ name: 'Station "Test"\nWith\\Special\rChars\tAndTabs',
143
+ lat: 0,
144
+ lon: 0,
145
+ children: [],
146
+ parent: undefined,
147
+ locationType: 'SIMPLE_STOP_OR_PLATFORM',
148
+ };
149
+
150
+ const specialStopsIndex = new StopsIndex([stop1, stop2, specialStop]);
151
+ const graph: Map<StopId, RoutingEdge>[] = [
152
+ new Map([[2, { arrival: Time.fromHMS(8, 0, 0) }]]),
153
+ ];
154
+
155
+ const result = new Result(
156
+ mockQuery,
157
+ {
158
+ earliestArrivals: new Map(),
159
+ graph,
160
+ destinations: [2],
161
+ },
162
+ specialStopsIndex,
163
+ mockTimetable,
164
+ );
165
+
166
+ const plotter = new Plotter(result);
167
+ const dotGraph = plotter.plotDotGraph();
168
+
169
+ // Check that special characters are properly escaped in the station label
170
+ assert(
171
+ dotGraph.includes(
172
+ 'Station \\"Test\\"\\nWith\\\\Special\\rChars\\tAndTabs\\n2',
173
+ ),
174
+ 'Station name should have properly escaped special characters',
175
+ );
176
+ });
177
+
178
+ it('should use correct colors', () => {
179
+ const graph: Map<StopId, RoutingEdge>[] = [
180
+ new Map([[0, { arrival: Time.fromHMS(8, 0, 0) }]]),
181
+ new Map([
182
+ [
183
+ 1,
184
+ {
185
+ from: 0,
186
+ to: 1,
187
+ arrival: Time.fromHMS(8, 30, 0),
188
+ routeId: 0,
189
+ tripIndex: 0,
190
+ },
191
+ ],
192
+ ]),
193
+ new Map([
194
+ [
195
+ 1,
196
+ {
197
+ from: 0,
198
+ to: 1,
199
+ arrival: Time.fromHMS(8, 45, 0),
200
+ type: 'WALKING',
201
+ minTransferTime: Time.fromHMS(0, 5, 0),
202
+ },
203
+ ],
204
+ ]),
205
+ ];
206
+
207
+ const result = new Result(
208
+ mockQuery,
209
+ {
210
+ earliestArrivals: new Map(),
211
+ graph,
212
+ destinations: [1],
213
+ },
214
+ mockStopsIndex,
215
+ mockTimetable,
216
+ );
217
+
218
+ const plotter = new Plotter(result);
219
+ const dotGraph = plotter.plotDotGraph();
220
+ assert(
221
+ dotGraph.includes('color="#60a5fa"'),
222
+ 'Round 1 should use blue color (#60a5fa)',
223
+ );
224
+ assert(
225
+ dotGraph.includes('color="#ff9800"'),
226
+ 'Round 2 should use orange color (#ff9800)',
227
+ );
228
+ });
229
+ });
230
+ });