zet-api 1.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.
- package/.github/workflows/publish-to-npm.yml +80 -0
- package/LICENSE +674 -0
- package/README.md +257 -0
- package/dist/auth/manager.d.ts +19 -0
- package/dist/auth/manager.js +228 -0
- package/dist/auth/types.d.ts +277 -0
- package/dist/auth/types.js +64 -0
- package/dist/core/auth-manager.d.ts +54 -0
- package/dist/core/auth-manager.js +159 -0
- package/dist/core/auth-types.d.ts +238 -0
- package/dist/core/auth-types.js +54 -0
- package/dist/core/config.d.ts +16 -0
- package/dist/core/config.js +18 -0
- package/dist/core/constants.d.ts +25 -0
- package/dist/core/constants.js +51 -0
- package/dist/core/gtfs-types.d.ts +198 -0
- package/dist/core/gtfs-types.js +70 -0
- package/dist/core/manager.d.ts +39 -0
- package/dist/core/manager.js +359 -0
- package/dist/core/parsers.d.ts +893 -0
- package/dist/core/parsers.js +110 -0
- package/dist/core/utils.d.ts +4 -0
- package/dist/core/utils.js +59 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/live-polling.d.ts +1 -0
- package/dist/live-polling.js +105 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +35 -0
- package/dist/text.d.ts +1 -0
- package/dist/text.js +35 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Route, Stop, News, GetRoutesInput, GetRouteTripsInput, GetStopsInput, GetTripStopTimesInput, SearchRoutesInput, SearchStopsInput, GetLiveVehiclesInput, TripWithDates, TripStopTimeWithDates, NewsWithDates, Vehicle } from './parsers';
|
|
2
|
+
import { GetStopIncomingTripsInput, StopIncomingTripWithDates, Account } from '../auth/types';
|
|
3
|
+
import { ZetAuthManager } from '../auth/manager';
|
|
4
|
+
import { RouteTypeEnum } from './constants';
|
|
5
|
+
export declare class ZetManager {
|
|
6
|
+
private cachedRoutes;
|
|
7
|
+
private cachedStops;
|
|
8
|
+
private cachedNews;
|
|
9
|
+
private routesCachedAt;
|
|
10
|
+
private stopsCachedAt;
|
|
11
|
+
private newsCachedAt;
|
|
12
|
+
authManager: ZetAuthManager;
|
|
13
|
+
private timeoutMs;
|
|
14
|
+
private cacheTTL;
|
|
15
|
+
constructor(cacheTTL?: number, timeoutMs?: number);
|
|
16
|
+
getRoutes(): Promise<Route[]>;
|
|
17
|
+
getRoutesInternal(options: GetRoutesInput): Promise<Route[]>;
|
|
18
|
+
getRouteById(routeId: number): Promise<Route | null>;
|
|
19
|
+
searchRoutes(options: SearchRoutesInput): Promise<Route[]>;
|
|
20
|
+
getRouteTrips(options: GetRouteTripsInput): Promise<TripWithDates[]>;
|
|
21
|
+
getTripStopTimes(options: GetTripStopTimesInput): Promise<TripStopTimeWithDates[]>;
|
|
22
|
+
getLiveTripsForRoute(routeId: number, daysFromToday?: number): Promise<Map<string, TripStopTimeWithDates[]>>;
|
|
23
|
+
getActiveTripIds(routeId: number, daysFromToday?: number, tripsOverride?: TripWithDates[]): Promise<string[]>;
|
|
24
|
+
getAccountInfo(): Promise<Account>;
|
|
25
|
+
getStops(routeType?: RouteTypeEnum): Promise<Stop[]>;
|
|
26
|
+
getStopIncomingTrips(options: GetStopIncomingTripsInput): Promise<StopIncomingTripWithDates[]>;
|
|
27
|
+
getStopsInternal(options: GetStopsInput): Promise<Stop[]>;
|
|
28
|
+
getStopById(stopId: string, routeType?: RouteTypeEnum): Promise<Stop | null>;
|
|
29
|
+
searchStops(options: SearchStopsInput): Promise<Stop[]>;
|
|
30
|
+
private getNewsfeedInternal;
|
|
31
|
+
getNewsfeed(): Promise<NewsWithDates[]>;
|
|
32
|
+
getNewsByRoute(routeId: number): Promise<News[]>;
|
|
33
|
+
getLiveVehicles(options?: GetLiveVehiclesInput): Promise<Vehicle[]>;
|
|
34
|
+
getVehicleById(vehicleId: string): Promise<Vehicle | null>;
|
|
35
|
+
refreshCache(): Promise<void>;
|
|
36
|
+
clearCache(): void;
|
|
37
|
+
private fetchAndCacheRoutes;
|
|
38
|
+
private fetchAndCacheStops;
|
|
39
|
+
}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.ZetManager = void 0;
|
|
40
|
+
const parsers_1 = require("./parsers");
|
|
41
|
+
const types_1 = require("../auth/types");
|
|
42
|
+
const utils_1 = require("./utils");
|
|
43
|
+
const manager_1 = require("../auth/manager");
|
|
44
|
+
const config_1 = __importStar(require("./config"));
|
|
45
|
+
const axios_1 = __importDefault(require("axios"));
|
|
46
|
+
class ZetManager {
|
|
47
|
+
cachedRoutes = null;
|
|
48
|
+
cachedStops = null;
|
|
49
|
+
cachedNews = null;
|
|
50
|
+
routesCachedAt = null;
|
|
51
|
+
stopsCachedAt = null;
|
|
52
|
+
newsCachedAt = null;
|
|
53
|
+
authManager = new manager_1.ZetAuthManager();
|
|
54
|
+
timeoutMs = 10000;
|
|
55
|
+
cacheTTL = -1;
|
|
56
|
+
constructor(cacheTTL = -1, timeoutMs = 10000) {
|
|
57
|
+
if (cacheTTL < -1)
|
|
58
|
+
throw new Error('cacheTTL must be -1 (no expiry) or a non-negative number in milliseconds.');
|
|
59
|
+
if (timeoutMs <= 0)
|
|
60
|
+
throw new Error('timeoutMs must be a positive number in milliseconds.');
|
|
61
|
+
this.timeoutMs = timeoutMs;
|
|
62
|
+
this.cacheTTL = cacheTTL;
|
|
63
|
+
}
|
|
64
|
+
// Routes.
|
|
65
|
+
async getRoutes() {
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
const needsRefresh = !this.cachedRoutes || (this.cacheTTL >= 0 && this.routesCachedAt && now - this.routesCachedAt > this.cacheTTL);
|
|
68
|
+
if (needsRefresh)
|
|
69
|
+
await this.fetchAndCacheRoutes();
|
|
70
|
+
if (!this.cachedRoutes)
|
|
71
|
+
throw new Error('Failed to fetch routes.');
|
|
72
|
+
return this.cachedRoutes;
|
|
73
|
+
}
|
|
74
|
+
async getRoutesInternal(options) {
|
|
75
|
+
let routes = await this.getRoutes();
|
|
76
|
+
if (options.routeId !== undefined)
|
|
77
|
+
routes = routes.filter((route) => route.id === options.routeId);
|
|
78
|
+
if (options.routeType !== undefined)
|
|
79
|
+
routes = routes.filter((route) => route.routeType === options.routeType);
|
|
80
|
+
return routes;
|
|
81
|
+
}
|
|
82
|
+
async getRouteById(routeId) {
|
|
83
|
+
const routes = await this.getRoutesInternal({ routeId });
|
|
84
|
+
return routes[0] || null;
|
|
85
|
+
}
|
|
86
|
+
async searchRoutes(options) {
|
|
87
|
+
const routes = await this.getRoutesInternal({ routeType: options.routeType });
|
|
88
|
+
const normalizedQuery = (0, utils_1.normalizeString)(options.query);
|
|
89
|
+
const filtered = routes.filter((route) => {
|
|
90
|
+
const normalizedName = (0, utils_1.normalizeString)(route.normalizedSearchName);
|
|
91
|
+
return normalizedName.includes(normalizedQuery) || route.shortName.toLowerCase().includes(options.query.toLowerCase());
|
|
92
|
+
});
|
|
93
|
+
return filtered.slice(0, options.limit || 10);
|
|
94
|
+
}
|
|
95
|
+
// Trips.
|
|
96
|
+
async getRouteTrips(options) {
|
|
97
|
+
const url = `${config_1.default.timetableServiceUrl}/routeTrips`;
|
|
98
|
+
const params = {
|
|
99
|
+
routeId: options.routeId.toString(),
|
|
100
|
+
daysFromToday: (options.daysFromToday || 0).toString(),
|
|
101
|
+
};
|
|
102
|
+
const response = await axios_1.default.get(url, { params, headers: config_1.headers, timeout: this.timeoutMs }).catch((err) => err.response);
|
|
103
|
+
if (!response || response.status !== 200)
|
|
104
|
+
throw new Error(`Failed to fetch route trips: ${response?.statusText || 'Unknown error'}`);
|
|
105
|
+
const parsed = parsers_1.RouteTripsResponseSchema.safeParse(response.data);
|
|
106
|
+
if (!parsed.success)
|
|
107
|
+
throw new Error(`Failed to parse route trips data: ${(0, utils_1.parseZodError)(parsed.error).join(', ')}.`);
|
|
108
|
+
return parsed.data.map((trip) => ({
|
|
109
|
+
...trip,
|
|
110
|
+
departureDateTime: new Date(trip.departureDateTime),
|
|
111
|
+
arrivalDateTime: new Date(trip.arrivalDateTime),
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
async getTripStopTimes(options) {
|
|
115
|
+
const url = `${config_1.default.timetableServiceUrl}/tripStopTimes`;
|
|
116
|
+
const params = {
|
|
117
|
+
tripId: options.tripId,
|
|
118
|
+
daysFromToday: (options.daysFromToday || 0).toString(),
|
|
119
|
+
};
|
|
120
|
+
const response = await axios_1.default.get(url, { params, headers: config_1.headers, timeout: this.timeoutMs }).catch((err) => err.response);
|
|
121
|
+
if (!response || response.status !== 200)
|
|
122
|
+
throw new Error(`Failed to fetch trip stop times: ${response?.statusText || 'Unknown error'}`);
|
|
123
|
+
const parsed = parsers_1.TripStopTimesResponseSchema.safeParse(response.data);
|
|
124
|
+
if (!parsed.success)
|
|
125
|
+
throw new Error(`Failed to parse trip stop times data: ${(0, utils_1.parseZodError)(parsed.error).join(', ')}.`);
|
|
126
|
+
return parsed.data.map((stopTime) => ({
|
|
127
|
+
...stopTime,
|
|
128
|
+
expectedArrivalDateTime: new Date(stopTime.expectedArrivalDateTime),
|
|
129
|
+
trip: {
|
|
130
|
+
...stopTime.trip,
|
|
131
|
+
departureDateTime: new Date(stopTime.trip.departureDateTime),
|
|
132
|
+
arrivalDateTime: new Date(stopTime.trip.arrivalDateTime),
|
|
133
|
+
},
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
async getLiveTripsForRoute(routeId, daysFromToday = 0) {
|
|
137
|
+
const trips = await this.getRouteTrips({ routeId, daysFromToday });
|
|
138
|
+
const liveData = new Map();
|
|
139
|
+
const now = new Date();
|
|
140
|
+
const activeTrips = trips.filter((trip) => {
|
|
141
|
+
const isActive = trip.departureDateTime <= now && trip.arrivalDateTime >= now;
|
|
142
|
+
return isActive && trip.vehicles.some((v) => v.position);
|
|
143
|
+
});
|
|
144
|
+
const stopTimesPromises = activeTrips.map(async (trip) => {
|
|
145
|
+
try {
|
|
146
|
+
const stopTimes = await this.getTripStopTimes({ tripId: trip.id, daysFromToday });
|
|
147
|
+
return { tripId: trip.id, stopTimes };
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return { tripId: trip.id, stopTimes: [] };
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
const results = await Promise.all(stopTimesPromises);
|
|
154
|
+
for (const result of results) {
|
|
155
|
+
if (result.stopTimes.length > 0) {
|
|
156
|
+
liveData.set(result.tripId, result.stopTimes);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return liveData;
|
|
160
|
+
}
|
|
161
|
+
async getActiveTripIds(routeId, daysFromToday = 0, tripsOverride) {
|
|
162
|
+
const trips = tripsOverride || await this.getRouteTrips({ routeId, daysFromToday });
|
|
163
|
+
const now = new Date();
|
|
164
|
+
return trips
|
|
165
|
+
.filter((trip) => trip.departureDateTime <= now && trip.arrivalDateTime >= now && trip.vehicles.some((v) => v.position))
|
|
166
|
+
.map((trip) => trip.id);
|
|
167
|
+
}
|
|
168
|
+
// Account,
|
|
169
|
+
async getAccountInfo() {
|
|
170
|
+
if (!this.authManager.isAuthenticated())
|
|
171
|
+
throw new Error('Authentication required. Please login using getAuthManager().login() first.');
|
|
172
|
+
const token = await this.authManager.getAccessToken();
|
|
173
|
+
if (!token)
|
|
174
|
+
throw new Error('Failed to get access token. Please login again.');
|
|
175
|
+
const response = await axios_1.default.get(config_1.default.accountServiceUrl, {
|
|
176
|
+
headers: {
|
|
177
|
+
...config_1.headers,
|
|
178
|
+
Authorization: `Bearer ${token}`,
|
|
179
|
+
},
|
|
180
|
+
}).catch((err) => err.response);
|
|
181
|
+
if (!response || response.status !== 200)
|
|
182
|
+
throw new Error(`Failed to fetch account info: ${response?.statusText || 'Unknown error'}`);
|
|
183
|
+
const parsed = types_1.AccountSchema.safeParse(response.data);
|
|
184
|
+
if (!parsed.success)
|
|
185
|
+
throw new Error(`Failed to parse account info data: ${(0, utils_1.parseZodError)(parsed.error).join(', ')}.`);
|
|
186
|
+
return parsed.data;
|
|
187
|
+
}
|
|
188
|
+
// Stops.
|
|
189
|
+
async getStops(routeType) {
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
const needsRefresh = !this.cachedStops || (this.cacheTTL >= 0 && this.stopsCachedAt && now - this.stopsCachedAt > this.cacheTTL);
|
|
192
|
+
if (needsRefresh)
|
|
193
|
+
await this.fetchAndCacheStops();
|
|
194
|
+
if (!this.cachedStops)
|
|
195
|
+
throw new Error('Failed to fetch stops.');
|
|
196
|
+
if (routeType !== undefined)
|
|
197
|
+
return this.cachedStops.filter((stop) => stop.routeType === routeType);
|
|
198
|
+
return this.cachedStops;
|
|
199
|
+
}
|
|
200
|
+
async getStopIncomingTrips(options) {
|
|
201
|
+
if (!this.authManager.isAuthenticated())
|
|
202
|
+
throw new Error('Authentication required. Please login using getAuthManager().login() first.');
|
|
203
|
+
const token = await this.authManager.getAccessToken();
|
|
204
|
+
if (!token)
|
|
205
|
+
throw new Error('Failed to get access token. Please login again.');
|
|
206
|
+
const url = `${config_1.default.timetableServiceUrl}/stopIncomingTrips`;
|
|
207
|
+
const params = {
|
|
208
|
+
stopId: options.stopId,
|
|
209
|
+
isMapView: (options.isMapView || false).toString(),
|
|
210
|
+
};
|
|
211
|
+
const response = await axios_1.default.get(url, {
|
|
212
|
+
params, timeout: this.timeoutMs,
|
|
213
|
+
headers: {
|
|
214
|
+
...config_1.headers,
|
|
215
|
+
Authorization: `Bearer ${token}`,
|
|
216
|
+
},
|
|
217
|
+
}).catch((err) => err.response);
|
|
218
|
+
if (!response || response.status !== 200)
|
|
219
|
+
throw new Error(`Failed to fetch stop incoming trips: ${response?.statusText || 'Unknown error'}`);
|
|
220
|
+
const parsed = types_1.StopIncomingTripsResponseSchema.safeParse(response.data);
|
|
221
|
+
if (!parsed.success)
|
|
222
|
+
throw new Error(`Failed to parse stop incoming trips data: ${(0, utils_1.parseZodError)(parsed.error).join(', ')}.`);
|
|
223
|
+
return parsed.data.map((trip) => ({
|
|
224
|
+
...trip,
|
|
225
|
+
expectedArrivalDateTime: new Date(trip.expectedArrivalDateTime),
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
async getStopsInternal(options) {
|
|
229
|
+
let stops = await this.getStops();
|
|
230
|
+
if (options.stopId)
|
|
231
|
+
stops = stops.filter((stop) => stop.id === options.stopId);
|
|
232
|
+
if (options.stopName) {
|
|
233
|
+
const normalizedName = (0, utils_1.normalizeString)(options.stopName);
|
|
234
|
+
stops = stops.filter((stop) => {
|
|
235
|
+
const normalizedStopName = (0, utils_1.normalizeString)(stop.normalizedSearchName);
|
|
236
|
+
return normalizedStopName.includes(normalizedName);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (options.routeType !== undefined)
|
|
240
|
+
stops = stops.filter((stop) => stop.routeType === options.routeType);
|
|
241
|
+
return stops;
|
|
242
|
+
}
|
|
243
|
+
async getStopById(stopId, routeType) {
|
|
244
|
+
const stops = await this.getStopsInternal({ stopId, routeType });
|
|
245
|
+
return stops[0] || null;
|
|
246
|
+
}
|
|
247
|
+
async searchStops(options) {
|
|
248
|
+
const stops = await this.getStopsInternal({ routeType: options.routeType });
|
|
249
|
+
const normalizedQuery = (0, utils_1.normalizeString)(options.query);
|
|
250
|
+
const filtered = stops.filter((stop) => {
|
|
251
|
+
const normalizedName = (0, utils_1.normalizeString)(stop.normalizedSearchName);
|
|
252
|
+
return normalizedName.includes(normalizedQuery);
|
|
253
|
+
});
|
|
254
|
+
return filtered.slice(0, options.limit || 10);
|
|
255
|
+
}
|
|
256
|
+
// News.
|
|
257
|
+
async getNewsfeedInternal() {
|
|
258
|
+
const now = Date.now();
|
|
259
|
+
const needsRefresh = !this.cachedNews || (this.cacheTTL >= 0 && this.newsCachedAt && now - this.newsCachedAt > this.cacheTTL);
|
|
260
|
+
if (!needsRefresh && this.cachedNews)
|
|
261
|
+
return this.cachedNews;
|
|
262
|
+
const url = config_1.default.newsProxyServiceUrl;
|
|
263
|
+
const response = await axios_1.default.get(url, { headers: config_1.headers, timeout: this.timeoutMs }).catch((err) => err.response);
|
|
264
|
+
if (!response || response.status !== 200)
|
|
265
|
+
throw new Error(`Failed to fetch newsfeed: ${response?.statusText || 'Unknown error'}`);
|
|
266
|
+
const parsed = parsers_1.NewsfeedResponseSchema.safeParse(response.data);
|
|
267
|
+
if (!parsed.success)
|
|
268
|
+
throw new Error(`Failed to parse newsfeed data: ${(0, utils_1.parseZodError)(parsed.error).join(', ')}.`);
|
|
269
|
+
this.cachedNews = parsed.data;
|
|
270
|
+
this.newsCachedAt = now;
|
|
271
|
+
return parsed.data;
|
|
272
|
+
}
|
|
273
|
+
async getNewsfeed() {
|
|
274
|
+
const news = await this.getNewsfeedInternal();
|
|
275
|
+
return news.map((item) => ({
|
|
276
|
+
...item,
|
|
277
|
+
datePublished: new Date(item.datePublished),
|
|
278
|
+
validFrom: new Date(item.validFrom),
|
|
279
|
+
validTo: new Date(item.validTo),
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
async getNewsByRoute(routeId) {
|
|
283
|
+
const news = await this.getNewsfeedInternal();
|
|
284
|
+
return news.filter((item) => item.lines.includes(routeId));
|
|
285
|
+
}
|
|
286
|
+
// Vehicles.
|
|
287
|
+
async getLiveVehicles(options) {
|
|
288
|
+
const routes = options?.routeId ? [await this.getRouteById(options.routeId)].filter((r) => r !== null) : await this.getRoutes();
|
|
289
|
+
const vehicles = [];
|
|
290
|
+
for (const route of routes) {
|
|
291
|
+
try {
|
|
292
|
+
const trips = await this.getRouteTrips({ routeId: route.id, daysFromToday: 0 });
|
|
293
|
+
for (const trip of trips) {
|
|
294
|
+
for (const vehicle of trip.vehicles) {
|
|
295
|
+
if (options?.vehicleId && vehicle.id !== options.vehicleId)
|
|
296
|
+
continue;
|
|
297
|
+
if (vehicle.position) {
|
|
298
|
+
vehicles.push(vehicle);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return vehicles;
|
|
308
|
+
}
|
|
309
|
+
async getVehicleById(vehicleId) {
|
|
310
|
+
const vehicles = await this.getLiveVehicles({ vehicleId });
|
|
311
|
+
return vehicles[0] || null;
|
|
312
|
+
}
|
|
313
|
+
// Cache.
|
|
314
|
+
async refreshCache() {
|
|
315
|
+
this.cachedRoutes = null;
|
|
316
|
+
this.cachedStops = null;
|
|
317
|
+
this.cachedNews = null;
|
|
318
|
+
this.routesCachedAt = null;
|
|
319
|
+
this.stopsCachedAt = null;
|
|
320
|
+
this.newsCachedAt = null;
|
|
321
|
+
await Promise.all([
|
|
322
|
+
this.fetchAndCacheRoutes(),
|
|
323
|
+
this.fetchAndCacheStops(),
|
|
324
|
+
this.getNewsfeed(),
|
|
325
|
+
]);
|
|
326
|
+
}
|
|
327
|
+
clearCache() {
|
|
328
|
+
this.cachedRoutes = null;
|
|
329
|
+
this.cachedStops = null;
|
|
330
|
+
this.cachedNews = null;
|
|
331
|
+
this.routesCachedAt = null;
|
|
332
|
+
this.stopsCachedAt = null;
|
|
333
|
+
this.newsCachedAt = null;
|
|
334
|
+
}
|
|
335
|
+
// Private.
|
|
336
|
+
async fetchAndCacheRoutes() {
|
|
337
|
+
const url = `${config_1.default.timetableServiceUrl}/routes`;
|
|
338
|
+
const response = await axios_1.default.get(url, { headers: config_1.headers, timeout: this.timeoutMs }).catch((err) => err.response);
|
|
339
|
+
if (!response || response.status !== 200)
|
|
340
|
+
throw new Error(`Failed to fetch routes: ${response?.statusText || 'Unknown error'}`);
|
|
341
|
+
const parsed = parsers_1.RoutesResponseSchema.safeParse(response.data);
|
|
342
|
+
if (!parsed.success)
|
|
343
|
+
throw new Error(`Failed to parse routes data: ${(0, utils_1.parseZodError)(parsed.error).join(', ')}.`);
|
|
344
|
+
this.cachedRoutes = parsed.data;
|
|
345
|
+
this.routesCachedAt = Date.now();
|
|
346
|
+
}
|
|
347
|
+
async fetchAndCacheStops() {
|
|
348
|
+
const url = `${config_1.default.timetableServiceUrl}/stops`;
|
|
349
|
+
const response = await axios_1.default.get(url, { headers: config_1.headers, timeout: this.timeoutMs }).catch((err) => err.response);
|
|
350
|
+
if (!response || response.status !== 200)
|
|
351
|
+
throw new Error(`Failed to fetch stops: ${response?.statusText || 'Unknown error'}`);
|
|
352
|
+
const parsed = parsers_1.StopsResponseSchema.safeParse(response.data);
|
|
353
|
+
if (!parsed.success)
|
|
354
|
+
throw new Error(`Failed to parse stops data: ${(0, utils_1.parseZodError)(parsed.error).join(', ')}.`);
|
|
355
|
+
this.cachedStops = parsed.data;
|
|
356
|
+
this.stopsCachedAt = Date.now();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
exports.ZetManager = ZetManager;
|