@golemio/pid 5.7.2 → 5.7.3-dev.2378110705

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 (112) hide show
  1. package/dist/const.d.ts +0 -1
  2. package/dist/const.js +1 -2
  3. package/dist/const.js.map +1 -1
  4. package/dist/integration-engine/ropid-gtfs/data-access/cache/NotPublicVehiclesRedisRepository.d.ts +14 -0
  5. package/dist/integration-engine/ropid-gtfs/data-access/cache/NotPublicVehiclesRedisRepository.js +47 -0
  6. package/dist/integration-engine/ropid-gtfs/data-access/cache/NotPublicVehiclesRedisRepository.js.map +1 -0
  7. package/dist/integration-engine/ropid-gtfs/datasources/StaticDataSourceFactory.d.ts +3 -2
  8. package/dist/integration-engine/ropid-gtfs/datasources/StaticDataSourceFactory.js +4 -2
  9. package/dist/integration-engine/ropid-gtfs/datasources/StaticDataSourceFactory.js.map +1 -1
  10. package/dist/integration-engine/ropid-gtfs/datasources/static-data/NotPublicVehiclesDataSourceProvider.d.ts +9 -0
  11. package/dist/integration-engine/ropid-gtfs/datasources/static-data/NotPublicVehiclesDataSourceProvider.js +47 -0
  12. package/dist/integration-engine/ropid-gtfs/datasources/static-data/NotPublicVehiclesDataSourceProvider.js.map +1 -0
  13. package/dist/integration-engine/ropid-gtfs/ioc/Di.js +6 -0
  14. package/dist/integration-engine/ropid-gtfs/ioc/Di.js.map +1 -1
  15. package/dist/integration-engine/ropid-gtfs/ioc/RopidGtfsContainerToken.d.ts +3 -0
  16. package/dist/integration-engine/ropid-gtfs/ioc/RopidGtfsContainerToken.js +3 -0
  17. package/dist/integration-engine/ropid-gtfs/ioc/RopidGtfsContainerToken.js.map +1 -1
  18. package/dist/integration-engine/ropid-gtfs/workers/timetables/TimetableWorker.js +1 -0
  19. package/dist/integration-engine/ropid-gtfs/workers/timetables/TimetableWorker.js.map +1 -1
  20. package/dist/integration-engine/ropid-gtfs/workers/timetables/tasks/EnsureCacheTask.d.ts +9 -0
  21. package/dist/integration-engine/ropid-gtfs/workers/timetables/tasks/EnsureCacheTask.js +49 -0
  22. package/dist/integration-engine/ropid-gtfs/workers/timetables/tasks/EnsureCacheTask.js.map +1 -0
  23. package/dist/integration-engine/ropid-gtfs/workers/timetables/tasks/SaveStaticDataTask.d.ts +6 -1
  24. package/dist/integration-engine/ropid-gtfs/workers/timetables/tasks/SaveStaticDataTask.js +39 -10
  25. package/dist/integration-engine/ropid-gtfs/workers/timetables/tasks/SaveStaticDataTask.js.map +1 -1
  26. package/dist/integration-engine/vehicle-positions/ioc/Di.js +2 -0
  27. package/dist/integration-engine/vehicle-positions/ioc/Di.js.map +1 -1
  28. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.d.ts +1 -0
  29. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.js +1 -0
  30. package/dist/integration-engine/vehicle-positions/ioc/VPContainerToken.js.map +1 -1
  31. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/BusMessageFilter.d.ts +10 -5
  32. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/BusMessageFilter.js +37 -11
  33. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/BusMessageFilter.js.map +1 -1
  34. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/CommonMessageProcessor.d.ts +4 -1
  35. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/CommonMessageProcessor.js +22 -9
  36. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/CommonMessageProcessor.js.map +1 -1
  37. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/TramMessageFilter.d.ts +7 -3
  38. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/TramMessageFilter.js +28 -10
  39. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/TramMessageFilter.js.map +1 -1
  40. package/dist/integration-engine/vehicle-positions/workers/runs/helpers/interfaces/ICommonMessageFilter.d.ts +1 -1
  41. package/dist/integration-engine/vehicle-positions/workers/runs/tasks/SaveBusRunsToDBTask.js +1 -1
  42. package/dist/integration-engine/vehicle-positions/workers/runs/tasks/SaveBusRunsToDBTask.js.map +1 -1
  43. package/dist/integration-engine/vehicle-positions/workers/runs/tasks/SaveTramRunsToDBTask.js +1 -1
  44. package/dist/integration-engine/vehicle-positions/workers/runs/tasks/SaveTramRunsToDBTask.js.map +1 -1
  45. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/interfaces/IUpdateRunsGtfsTripInput.d.ts +6 -0
  46. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/UpdateRunsGtfsTripIdTask.js +3 -2
  47. package/dist/integration-engine/vehicle-positions/workers/vehicle-positions/tasks/UpdateRunsGtfsTripIdTask.js.map +1 -1
  48. package/dist/output-gateway/public/data-access/redis/PublicGtfsDepartureRepository.js +0 -1
  49. package/dist/output-gateway/public/data-access/redis/PublicGtfsDepartureRepository.js.map +1 -1
  50. package/dist/output-gateway/public/service/facade/DetailedTripFacade.js +1 -1
  51. package/dist/output-gateway/public/service/facade/DetailedTripFacade.js.map +1 -1
  52. package/dist/output-gateway/public/service/helpers/trip-scope/strategy/ShapesTripScopeHandler.js +1 -1
  53. package/dist/output-gateway/public/service/helpers/trip-scope/strategy/ShapesTripScopeHandler.js.map +1 -1
  54. package/dist/output-gateway/public/service/helpers/trip-scope/strategy/StopTimesTripScopeHandler.js +1 -1
  55. package/dist/output-gateway/public/service/helpers/trip-scope/strategy/StopTimesTripScopeHandler.js.map +1 -1
  56. package/dist/output-gateway/vehicle-positions/data-access/PositionRepository.d.ts +10 -3
  57. package/dist/output-gateway/vehicle-positions/data-access/PositionRepository.js +30 -7
  58. package/dist/output-gateway/vehicle-positions/data-access/PositionRepository.js.map +1 -1
  59. package/dist/output-gateway/vehicle-positions/data-access/TripRepository.d.ts +7 -3
  60. package/dist/output-gateway/vehicle-positions/data-access/TripRepository.js +30 -7
  61. package/dist/output-gateway/vehicle-positions/data-access/TripRepository.js.map +1 -1
  62. package/dist/output-gateway/vehicle-positions/data-access/VehicleDescriptorRepository.d.ts +10 -3
  63. package/dist/output-gateway/vehicle-positions/data-access/VehicleDescriptorRepository.js +30 -7
  64. package/dist/output-gateway/vehicle-positions/data-access/VehicleDescriptorRepository.js.map +1 -1
  65. package/dist/output-gateway/vehicle-positions/data-access/VehicleTypeRepository.d.ts +10 -3
  66. package/dist/output-gateway/vehicle-positions/data-access/VehicleTypeRepository.js +30 -7
  67. package/dist/output-gateway/vehicle-positions/data-access/VehicleTypeRepository.js.map +1 -1
  68. package/dist/output-gateway/vehicle-positions/data-access/cache/NotPublicVehiclesRedisRepository.d.ts +10 -0
  69. package/dist/output-gateway/vehicle-positions/data-access/cache/NotPublicVehiclesRedisRepository.js +45 -0
  70. package/dist/output-gateway/vehicle-positions/data-access/cache/NotPublicVehiclesRedisRepository.js.map +1 -0
  71. package/dist/output-gateway/vehicle-positions/data-access/index.js +9 -18
  72. package/dist/output-gateway/vehicle-positions/data-access/index.js.map +1 -1
  73. package/dist/output-gateway/vehicle-positions/data-access/interfaces/ITripWithPositionRepository.d.ts +3 -2
  74. package/dist/output-gateway/vehicle-positions/data-access/interfaces/IVPRepositoryInstances.d.ts +6 -6
  75. package/dist/output-gateway/vehicle-positions/data-access/views/ProcessedPositionRepository.d.ts +7 -3
  76. package/dist/output-gateway/vehicle-positions/data-access/views/ProcessedPositionRepository.js +30 -7
  77. package/dist/output-gateway/vehicle-positions/data-access/views/ProcessedPositionRepository.js.map +1 -1
  78. package/dist/output-gateway/vehicle-positions/data-access/views/TripWithLastPositionRepository.d.ts +17 -7
  79. package/dist/output-gateway/vehicle-positions/data-access/views/TripWithLastPositionRepository.js +112 -42
  80. package/dist/output-gateway/vehicle-positions/data-access/views/TripWithLastPositionRepository.js.map +1 -1
  81. package/dist/output-gateway/vehicle-positions/ioc/Di.d.ts +3 -0
  82. package/dist/output-gateway/vehicle-positions/ioc/Di.js +23 -0
  83. package/dist/output-gateway/vehicle-positions/ioc/Di.js.map +1 -0
  84. package/dist/output-gateway/vehicle-positions/ioc/OgVehiclePositionsToken.d.ts +9 -0
  85. package/dist/output-gateway/vehicle-positions/ioc/OgVehiclePositionsToken.js +15 -0
  86. package/dist/output-gateway/vehicle-positions/ioc/OgVehiclePositionsToken.js.map +1 -0
  87. package/dist/schema-definitions/datasources/static-data/NotPublicVehiclesJsonSchema.d.ts +3 -0
  88. package/dist/schema-definitions/datasources/static-data/NotPublicVehiclesJsonSchema.js +40 -0
  89. package/dist/schema-definitions/datasources/static-data/NotPublicVehiclesJsonSchema.js.map +1 -0
  90. package/dist/schema-definitions/datasources/static-data/index.d.ts +1 -0
  91. package/dist/schema-definitions/datasources/static-data/index.js +1 -0
  92. package/dist/schema-definitions/datasources/static-data/index.js.map +1 -1
  93. package/dist/schema-definitions/datasources/static-data/interfaces/NotPublicVehiclesDataInterface.d.ts +35 -0
  94. package/dist/schema-definitions/datasources/static-data/interfaces/NotPublicVehiclesDataInterface.js +18 -0
  95. package/dist/schema-definitions/datasources/static-data/interfaces/NotPublicVehiclesDataInterface.js.map +1 -0
  96. package/dist/schema-definitions/datasources/static-data/interfaces/index.d.ts +1 -0
  97. package/dist/schema-definitions/datasources/static-data/interfaces/index.js +1 -0
  98. package/dist/schema-definitions/datasources/static-data/interfaces/index.js.map +1 -1
  99. package/dist/schema-definitions/ropid-gtfs/redis/const.d.ts +2 -0
  100. package/dist/schema-definitions/ropid-gtfs/redis/const.js +3 -1
  101. package/dist/schema-definitions/ropid-gtfs/redis/const.js.map +1 -1
  102. package/dist/schema-definitions/vehicle-positions/models/interfaces/ICommonRunWithMessageDto.d.ts +6 -0
  103. package/docs/asyncapi.yaml +17 -2
  104. package/docs/cache/types/redis.md +58 -2
  105. package/docs/examples/not_public_vehicles.json +9 -0
  106. package/docs/implementation_documentation.md +116 -3
  107. package/docs/jis/index.md +17 -0
  108. package/docs/processing/input_realtime_data/http_mpvnet.md +21 -0
  109. package/docs/processing/state_position_tracking/http_mpvnet.md +1 -0
  110. package/docs/processing/state_position_tracking/tcp_dpp_common.md +1 -0
  111. package/docs/processing/transferboards_filtering.md +312 -0
  112. package/package.json +8 -5
@@ -0,0 +1,312 @@
1
+ ## v4/pid/transferboards filtering pipeline (newcomer guide)
2
+
3
+ This section documents every filtering and transformation step executed by the `GET /v4/pid/transferboards` endpoint, in the order they actually run. The goal is to help a developer who is new to the codebase understand **what** each stage does, **why** it exists, and **where** the code lives.
4
+
5
+ ---
6
+
7
+ ### Overview
8
+
9
+ The endpoint answers the question: _"Given that I am on vehicle X arriving at stop Y, which onward connections should I show on the transfer board?"_
10
+
11
+ The answer is not simply "all departures from stop Y in the next hour" — many departures must be filtered out because they go back the way the passenger just came, or depart too soon to catch, or are irrelevant in-network transfers. The pipeline also specially handles **guaranteed transfers** (connections the operator has contractually promised to wait for).
12
+
13
+ **Primary source files:**
14
+ - `src/output-gateway/pid/controllers/v4/V4TransferBoardsController.ts` — orchestrates the full pipeline
15
+ - `src/output-gateway/pid/service/facade/TransferFacade.ts` — fetches the cache, runs line-direction filters
16
+ - `src/output-gateway/pid/helpers/TransferBoardFilter.ts` — static filter methods (time, name, deduplication)
17
+ - `src/output-gateway/pid/helpers/TransferBoardSorter.ts` — sort pipeline
18
+ - `src/output-gateway/pid/service/transformations/TransferDepartureCacheTransformation.ts` — output grouping/capping
19
+
20
+ ---
21
+
22
+ ### Stage 1 — Request validation and parameter parsing
23
+
24
+ **Where:** `V4PIDRouter.ts` (validation middleware) + `V4TransferBoardsController.ts` (top of `getTransferBoards`)
25
+
26
+ The endpoint accepts two mutually exclusive identification modes:
27
+
28
+ - **ASW mode** — `aswId` + `vehicleRegistrationNumber` + `routeType`
29
+ - **CIS mode** — `cisId` + `tripNumber` (returns HTTP 501; not yet implemented)
30
+
31
+ If neither set of params is provided, or both are, validation rejects the request early. CIS mode always returns `501 Not Implemented`.
32
+
33
+ ---
34
+
35
+ ### Stage 2 — Stop resolution (ASW → GTFS stop IDs)
36
+
37
+ **Where:** `TransferFacade.getTransferCache()` → `gtfsStopsCache:*`
38
+
39
+ A single ASW node can map to multiple GTFS `stop_id` values (e.g. platforms A and B at the same tram stop). The facade:
40
+
41
+ 1. Looks up all GTFS `stop_id` values for the given `aswId` from `gtfsStopsCache:{aswId}`.
42
+ 2. Fetches the stop names for those `stop_id` values.
43
+ 3. Groups them into **same-name clusters** — stops that share a `stop_name` (e.g. both platforms of "Náměstí Míru") are treated as one logical transfer point.
44
+
45
+ This grouping matters later: only departures from stops in the same cluster pass the same-stop-name filters.
46
+
47
+ ---
48
+
49
+ ### Stage 3 — Current vehicle lookup
50
+
51
+ **Where:** `V4TransferBoardsController.ts` + `vpPublicStopTimeCache:*`
52
+
53
+ The controller looks up the arriving vehicle's trip data using `service-{routeType}-{registrationNumber}` as the key into `vpPublicStopTimeCache`. For buses, a fallback to the trolleybus key is tried if the bus key is missing (registration numbers are shared between the two types).
54
+
55
+ This gives the controller the vehicle's **current `trip_id`** (called `currentTripId` throughout the codebase), which is central to guaranteed transfer detection and direction filtering.
56
+
57
+ If the vehicle is not found in the stop-time cache, the response is `404 Not Found`.
58
+
59
+ ---
60
+
61
+ ### Stage 4 — Arrival time detection
62
+
63
+ **Where:** `V4TransferBoardsController.ts` → `gtfsDelayComputation:{tripId}`
64
+
65
+ The controller reads the vehicle's current trip from `gtfsDelayComputation`, which contains a per-stop array with both planned and predicted (delayed) times. It finds the entry for the target stop and extracts:
66
+
67
+ - `plannedTimeFrom` — the GTFS scheduled arrival time at this stop
68
+ - `delayedTimeFrom` — the real-time predicted arrival time (may differ due to delays)
69
+
70
+ Both values are used to set the departure cache query window in the next stage.
71
+
72
+ ---
73
+
74
+ ### Stage 5 — Departure cache fetch
75
+
76
+ **Where:** `TransferFacade.getTransferCache()` → `gtfsPublicDepartureCache:*`
77
+
78
+ The facade fetches candidates from `gtfsPublicDepartureCache:{stopId}` (a Redis sorted set, scored by planned departure timestamp) for each GTFS `stop_id` in scope.
79
+
80
+ **Time window:**
81
+ ```
82
+ from = plannedTimeFrom − 61 minutes
83
+ to = plannedTimeFrom + 60 minutes + delayMinutes
84
+ ```
85
+
86
+ The 61-minute lookback exists to catch transfer lines whose scheduled departure is in the past but which may still arrive at the stop after the current vehicle due to their own delay of up to 60 minutes.
87
+
88
+ **Fetch limit:** `max(80, min(120, limit × 10))` items per stop. The wide over-fetch ensures that enough candidates survive later filtering to fill the requested `limit`.
89
+
90
+ ---
91
+
92
+ ### Stage 6 — Self-removal
93
+
94
+ **Where:** `V4TransferBoardsController.ts`
95
+
96
+ The current vehicle's own trip (`currentTripId`) is removed from the candidate list. It should never appear as a "transfer option" for itself.
97
+
98
+ ---
99
+
100
+ ### Stage 7 — Pre-filter: bus/tram/trolleybus at wrong-name stops
101
+
102
+ **Where:** `TransferFacade.getTransferCache()` (inline, before returning)
103
+
104
+ Before the expensive line-direction filter runs, a cheap pre-filter drops departures that are:
105
+ - Route type **bus, tram, or trolleybus** (metro/train/ferry/funicular pass through unconditionally)
106
+ - From a stop whose `stop_name` differs from the current stop's name
107
+
108
+ **Exception:** if the departure has a non-empty `connections[]` array (meaning it is a possible guaranteed transfer), it is kept regardless of stop name. This is because a guaranteed transfer bus might legitimately be at a different-named stop.
109
+
110
+ This pre-filter is a performance optimization — it eliminates obviously irrelevant candidates before the costlier stages.
111
+
112
+ ---
113
+
114
+ ### Stage 8 — Sort + real-time vehicle position join
115
+
116
+ **Where:** `V4TransferBoardsController.ts`
117
+
118
+ Remaining candidates are sorted by `departure_datetime` ascending, then each is joined with live vehicle positions from `vpPublicCache` (if the trip has an active vehicle). This enriches each candidate with:
119
+
120
+ - `predictedDepartureDate` / `predictedDepartureTimestamp` — the RT-adjusted departure time
121
+ - `is_wheelchair_accessible` — resolved from the RT vehicle descriptor if available (see Stage 9)
122
+
123
+ ---
124
+
125
+ ### Stage 9 — Wheelchair accessibility resolution
126
+
127
+ **Where:** `DepartureBoardMapper.determineWheelchairAccessibility()`
128
+
129
+ For each candidate departure, accessibility is determined differently by mode:
130
+
131
+ | Mode | Source |
132
+ |---|---|
133
+ | **Metro** | `wheelchair_boarding` from the GTFS stop (stop-level, static) |
134
+ | **All other** | `real_wheelchair_accessible` from the RT vehicle descriptor (boolean), or if absent: GTFS static `wheelchair_accessible` (trip-level, `GtfsTripWheelchairAccessEnum`) |
135
+
136
+ The fallback to GTFS static data is the fix introduced in v5.7.1 (item 3 above).
137
+
138
+ ---
139
+
140
+ ### Stage 10 — Guaranteed transfer detection
141
+
142
+ **Where:** `V4TransferBoardsController.ts` (`havingConnectionFromTripId`)
143
+
144
+ A departure is a **guaranteed transfer** (Czech: _zaručený přestup_) when:
145
+
146
+ 1. Its `connections[]` array contains an entry where `from_trip_id === currentTripId`, AND
147
+ 2. The current vehicle's predicted arrival time is within the wait window:
148
+ ```
149
+ vehicleArrivalMs < departurePlannedMs + max_wait_sec × 1000
150
+ ```
151
+
152
+ Guaranteed transfers receive special treatment in all subsequent stages — they can bypass nearly every filter and are always shown if time constraints allow.
153
+
154
+ The result is two sets: `guaranteedTripIds` and `guaranteedConnections`.
155
+
156
+ ---
157
+
158
+ ### Stage 11 — Minimal transfer time filter
159
+
160
+ **Where:** `TransferBoardFilter.minimalTransferTime()`
161
+
162
+ Departures whose scheduled time is too close to the vehicle's arrival are dropped — there is not enough time for passengers to board. Thresholds differ by mode:
163
+
164
+ | Mode | Minimum gap |
165
+ |---|---|
166
+ | Bus, tram, trolleybus | 60 seconds |
167
+ | Metro, train, ferry, funicular | 120 seconds |
168
+
169
+ **Guaranteed transfers are exempt** from this filter — the operator has guaranteed the connection, so time is irrelevant.
170
+
171
+ ---
172
+
173
+ ### Stage 12 — Same-stop-name filter (Stage 2)
174
+
175
+ **Where:** `TransferBoardFilter.sameStopNameTransfer()`
176
+
177
+ A second pass of the stop-name check, applied after guaranteed transfers are identified. Rules:
178
+
179
+ - Bus/tram/trolleybus at a stop with a different name than the arrival stop → **dropped**
180
+ - Metro/train/ferry/funicular → **kept** (they serve different physical locations by design)
181
+ - Any trip in `guaranteedTripIds` → **kept** (guaranteed transfer exemption)
182
+ - Any trip in `keepAlwaysTripIds` (see Stage 14) → **kept**
183
+
184
+ This stage is separate from the Stage 7 pre-filter because at Stage 7 we did not yet know which trips were guaranteed.
185
+
186
+ ---
187
+
188
+ ### Stage 13 — Categorise trips for line-direction filter
189
+
190
+ **Where:** `V4TransferBoardsController.ts`
191
+
192
+ Before running the expensive direction filter, the controller categorises remaining candidates:
193
+
194
+ - **Always keep:** guaranteed transfers + "keep always" types (metro, train, ferry, funicular)
195
+ - **Needs filtering:** everything else (bus, tram, trolleybus that are not guaranteed)
196
+
197
+ Only the "needs filtering" set is passed to `findRelevantTripIdsFromLines`. This avoids running direction analysis on trips that will be kept unconditionally.
198
+
199
+ ---
200
+
201
+ ### Stage 14 — Guaranteed transfer time adjustment
202
+
203
+ **Where:** `V4TransferBoardsController.ts`
204
+
205
+ For guaranteed transfers, the displayed departure time is **replaced** with the arriving vehicle's own predicted arrival time. This is the time the bus/tram will actually wait until, so showing the vehicle's arrival time as the "departure" is more useful to passengers than the originally scheduled departure time.
206
+
207
+ ---
208
+
209
+ ### Stage 15 — Sort (pre-output)
210
+
211
+ **Where:** `TransferBoardSorter.sort()`
212
+
213
+ A four-criterion stable sort applied before the direction filter:
214
+
215
+ 1. **Metro first** — metro departures before all others
216
+ 2. **Metro name ascending** — within metro, sort by `route_short_name` (A before B before C)
217
+ 3. **Departure time ascending** — earlier departures first
218
+ 4. **`direction_id` ascending** — tiebreaker for trips with identical departure times
219
+
220
+ ---
221
+
222
+ ### Stage 16 — Line-direction filter (`findRelevantTripIdsFromLines`)
223
+
224
+ **Where:** `TransferFacade.findRelevantTripIdsFromLines()`
225
+
226
+ This is the most complex stage. It determines whether each candidate trip is going somewhere _new_ for the passenger, or whether it is effectively doubling back along the route the passenger just arrived on.
227
+
228
+ For each candidate trip, the facade loads its stop sequence from `gtfsDelayComputation` or `vpPublicStopTimeCache`. It then applies two sequential sub-filters:
229
+
230
+ #### Sub-filter A: Forward-subset check (`filterOutForwardSubgroupOfLine`)
231
+
232
+ **Question:** Does this candidate trip's future stops form a strict subset of the current vehicle's future stops?
233
+
234
+ If every stop the candidate will visit is also a future stop on the current vehicle's route, then there is no point transferring — the passenger will get there anyway without changing. Such trips are **dropped**.
235
+
236
+ **Example:** Current vehicle is bus 207 going A→B→C→D. Candidate bus 150 goes B→C. Since B and C are both future stops of 207, transferring to 150 is redundant → dropped.
237
+
238
+ #### Sub-filter B: Backtracking conflict check (`filterOutLinesWithBacktrackingConflict`)
239
+
240
+ **Question:** Do this candidate trip's next N stops overlap with the current vehicle's previous N stops in a way that suggests it is heading back the same way?
241
+
242
+ Configured by `countPreviousStopsToAllow` (default `0`). The value defines how many of the current vehicle's previous stops the candidate trip is allowed to share in its upcoming stops. If the candidate shares more than that number, it is **dropped** (it's going backwards). `0` means no overlap is permitted at all.
243
+
244
+ The strictness of the stop matching is controlled by `isOppositeDirectionFilterStopNameStrict` (default `true`): when true, both `stop_id` AND `stop_name` must match; when false, only `stop_id`.
245
+
246
+ ---
247
+
248
+ ### Stage 17 — Combine approved trip IDs
249
+
250
+ **Where:** `TransferBoardFilter.keepAlwaysLinesAndTripIds()`
251
+
252
+ The final set of approved trip IDs is the union of:
253
+ - `guaranteedTripIds` (from Stage 10)
254
+ - `keepAlwaysTripIds` (metro/train/ferry/funicular, from Stage 13)
255
+ - Trips that passed the direction filter (from Stage 16)
256
+
257
+ All other trips are discarded from the candidate list here.
258
+
259
+ ---
260
+
261
+ ### Stage 18 — Output grouping and per-route cap
262
+
263
+ **Where:** `TransferDepartureCacheTransformation.transform()`
264
+
265
+ Before the final limit is applied, departures are grouped by route and capped to prevent one line from dominating the board:
266
+
267
+ | Mode | Grouping key | Cap |
268
+ |---|---|---|
269
+ | **Metro** | `route_short_name + stop_id` | 2 departure times merged into one `departure_timestamp.minutes[]` entry |
270
+ | **Non-metro** | `route_short_name + direction_id` | 2 separate departure entries per group |
271
+
272
+ For non-metro, `direction_id` is taken from the live vehicle positions cache (keyed by `route_short_name + trip_headsign`), defaulting to `0` when not available.
273
+
274
+ ---
275
+
276
+ ### Stage 19 — Final filter: limit, time window, deduplication
277
+
278
+ **Where:** `TransferBoardFilter.filterDepartures()`
279
+
280
+ The last stage applies three rules:
281
+
282
+ 1. **Time window cut:** departures after `arrivalTime + 1 hour` are dropped.
283
+ 2. **Trip deduplication:** if two entries share the same `trip_id`, only the first (earliest) is kept.
284
+ 3. **Limit:** the result is truncated to the requested `limit` (query parameter, default typically 10).
285
+
286
+ **Guaranteed transfers are exempt from the time window and limit** — they always appear regardless of how many other results there are, as long as they are still within the `max_wait_sec` window.
287
+
288
+ ---
289
+
290
+ ### Quick reference: which filter each vehicle type is subject to
291
+
292
+ | Filter | Bus/Tram/Trolleybus | Metro/Train/Ferry/Funicular | Guaranteed transfer |
293
+ |---|---|---|---|
294
+ | Stage 7 pre-filter (wrong stop name) | Yes | No | No (exempt) |
295
+ | Stage 11 min transfer time | Yes (60 s) | Yes (120 s) | **No** |
296
+ | Stage 12 same-stop-name | Yes | No | **No** |
297
+ | Stage 16 direction filter | Yes | **No** | **No** |
298
+ | Stage 17 combine | All types pass via their category | — | — |
299
+ | Stage 18 per-route cap (2 entries) | Yes | Yes (metro grouped differently) | Yes |
300
+ | Stage 19 limit | Yes | Yes | **No** |
301
+
302
+ ---
303
+
304
+ ### Key Redis keys used
305
+
306
+ | Key pattern | Purpose |
307
+ |---|---|
308
+ | `gtfsStopsCache:{aswId}` | ASW node → GTFS stop_id mapping |
309
+ | `gtfsPublicDepartureCache:{stopId}` | Sorted set of precomputed departures (score = departure Unix timestamp) |
310
+ | `gtfsDelayComputation:{tripId}` | Per-trip RT stop times, delay, shapes |
311
+ | `vpPublicStopTimeCache:{vehicleKey}` | Per-vehicle stop time lookup (used to identify current trip) |
312
+ | `vpPublicCache:*` | Geo-indexed real-time vehicle positions |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@golemio/pid",
3
- "version": "5.7.2",
3
+ "version": "5.7.3-dev.2378110705",
4
4
  "description": "Golemio PID Module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,13 +9,16 @@
9
9
  "build-minimal": "run-s 'build -- --sourceMap false --declaration false'",
10
10
  "build-watch": "run-s 'build -- --watch --preserveWatchOutput'",
11
11
  "refresh-precomputed-tables": "cross-env NODE_ENV='test' TZ='UTC' ts-node -r tsconfig-paths/register -r dotenv/config test/scripts/refresh-precomputed-tables.ts",
12
- "pretest": "golemio import-db-data && run-s \"refresh-precomputed-tables\"",
13
- "test": "cross-env NODE_ENV='test' TZ='UTC' mocha --exit --check-leaks --timeout 120000 --reporter-option maxDiffSize=0 -r ts-node/register -r tsconfig-paths/register --file 'test/setup.ts' -r dotenv/config 'test/**/*.test.ts'",
14
- "test-debug": "run-s 'test -- --inspect-brk=9230'",
12
+ "prepare-db": "golemio import-db-data && run-s \"refresh-precomputed-tables\"",
13
+ "test:debug": "run-s 'test -- --inspect-brk=9230'",
14
+ "test:only": "cross-env NODE_ENV='test' TZ='UTC' mocha --exit --check-leaks --timeout 120000 --reporter-option maxDiffSize=0 -r ts-node/register -r tsconfig-paths/register --file 'test/setup.ts' -r dotenv/config 'test/**/*.test.ts'",
15
+ "test": "run-s prepare-db test:only",
15
16
  "apidocs-test": "npm run apidocs-test-output",
16
17
  "apidocs-test-input": "cross-env NODE_ENV=test golemio swagger api-test --oas docs/openapi-input.yaml --script test/api-docs/input-gateway/server.js --filter test/api-docs/input-gateway/portman-filter.json",
17
18
  "apidocs-test-output": "cross-env NODE_ENV=test golemio swagger api-test --oas docs/openapi-output.yaml --script test/api-docs/output-gateway/server.js --config test/api-docs/output-gateway/portman-config.json",
18
- "code-coverage": "nyc run-s 'test -- -r source-map-support/register'",
19
+ "coverage:only": "nyc --reporter=text --reporter=lcov run-s 'test:only -- -r source-map-support/register'",
20
+ "code-coverage": "run-s prepare-db coverage:only",
21
+ "format:check-staged": "pretty-quick --staged --pattern '**/*.ts'",
19
22
  "generate-docs": "typedoc --out docs/typedoc src",
20
23
  "lint": "eslint --cache \"{src,test}/**/*.ts\"",
21
24
  "validate-dependencies": "dependency-cruiser --config .dependency-cruiser.js src"