lt-public-transport-sdk 1.0.0 → 1.2.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/LICENSE +21 -21
- package/README.md +495 -283
- package/dist/config.d.ts +41 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -0
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +12 -13
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/gtfs/index.d.ts +2 -2
- package/dist/gtfs/index.d.ts.map +1 -1
- package/dist/gtfs/index.js +2 -2
- package/dist/gtfs/index.js.map +1 -1
- package/dist/gtfs/parser.d.ts +49 -2
- package/dist/gtfs/parser.d.ts.map +1 -1
- package/dist/gtfs/parser.js +328 -2
- package/dist/gtfs/parser.js.map +1 -1
- package/dist/gtfs/sync.d.ts +26 -2
- package/dist/gtfs/sync.d.ts.map +1 -1
- package/dist/gtfs/sync.js +205 -12
- package/dist/gtfs/sync.js.map +1 -1
- package/dist/index.d.ts +151 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +254 -36
- package/dist/index.js.map +1 -1
- package/dist/parsers/gps-lite.d.ts +34 -36
- package/dist/parsers/gps-lite.d.ts.map +1 -1
- package/dist/parsers/gps-lite.js +77 -69
- package/dist/parsers/gps-lite.js.map +1 -1
- package/dist/parsers/index.d.ts +2 -1
- package/dist/parsers/index.d.ts.map +1 -1
- package/dist/parsers/index.js +4 -1
- package/dist/parsers/index.js.map +1 -1
- package/dist/schemas.d.ts +242 -13
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +183 -33
- package/dist/schemas.js.map +1 -1
- package/dist/types.d.ts +106 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/scripts/test-city-specific.d.ts +0 -2
- package/dist/scripts/test-city-specific.d.ts.map +0 -1
- package/dist/scripts/test-city-specific.js +0 -264
- package/dist/scripts/test-city-specific.js.map +0 -1
- package/dist/scripts/test-config-options.d.ts +0 -2
- package/dist/scripts/test-config-options.d.ts.map +0 -1
- package/dist/scripts/test-config-options.js +0 -166
- package/dist/scripts/test-config-options.js.map +0 -1
- package/dist/scripts/test-data-quality.d.ts +0 -2
- package/dist/scripts/test-data-quality.d.ts.map +0 -1
- package/dist/scripts/test-data-quality.js +0 -204
- package/dist/scripts/test-data-quality.js.map +0 -1
- package/dist/scripts/test-error-handling.d.ts +0 -2
- package/dist/scripts/test-error-handling.d.ts.map +0 -1
- package/dist/scripts/test-error-handling.js +0 -146
- package/dist/scripts/test-error-handling.js.map +0 -1
- package/dist/scripts/test-live.d.ts +0 -2
- package/dist/scripts/test-live.d.ts.map +0 -1
- package/dist/scripts/test-live.js +0 -121
- package/dist/scripts/test-live.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,283 +1,495 @@
|
|
|
1
|
-
# lt-public-transport-sdk
|
|
2
|
-
|
|
3
|
-
A robust, type-safe TypeScript SDK for accessing **real-time public transport data** in Lithuanian cities.
|
|
4
|
-
|
|
5
|
-
It handles the complexity of parsing raw GPS streams (HTML/CSV), normalizing coordinate formats, handling text encodings (Windows-1257), and merging live data with static GTFS schedules (routes, stops).
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- **
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
console.log(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
| **
|
|
71
|
-
| **
|
|
72
|
-
| **
|
|
73
|
-
| **
|
|
74
|
-
| **
|
|
75
|
-
| **
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
1
|
+
# lt-public-transport-sdk
|
|
2
|
+
|
|
3
|
+
A robust, type-safe TypeScript SDK for accessing **real-time public transport data** in Lithuanian cities.
|
|
4
|
+
|
|
5
|
+
It handles the complexity of parsing raw GPS streams (HTML/CSV), normalizing coordinate formats, handling text encodings (Windows-1257), and merging live data with static GTFS schedules (routes, stops).
|
|
6
|
+
|
|
7
|
+
## 🚀 Why use this SDK?
|
|
8
|
+
|
|
9
|
+
Raw public transport data in Lithuania (from stops.lt) is fragmented and messy:
|
|
10
|
+
|
|
11
|
+
- **Inconsistent Formats**: Vilnius uses 18 CSV columns, Kaunas uses 14, Panevėžys uses 9 (with no header).
|
|
12
|
+
- **Weird Encodings**: Data is often in `windows-1257` or mixed encodings.
|
|
13
|
+
- **Weird Coordinates**: Some cities provide WGS84, others provide integers divided by 1,000,000.
|
|
14
|
+
- **Sparse Data**: GPS streams often lack route names or destinations, requiring lookup against GTFS data.
|
|
15
|
+
|
|
16
|
+
**This SDK solves all of that.** It provides a **single, unified `Vehicle` interface** regardless of the city or underlying data format.
|
|
17
|
+
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
- **Unified API**: One method (`getVehicles`) validates and normalizes data for all supported cities.
|
|
21
|
+
- **Auto-Enrichment**: optional automatic merging of live GPS data with GTFS routes (adds `destination`, `tripId`).
|
|
22
|
+
- **Extensible**: Add custom cities or override existing configurations without waiting for SDK updates.
|
|
23
|
+
- **Resilience**: Robust error handling for network timeouts, stale data, and malformed responses.
|
|
24
|
+
- **Type-Safe**: Built with TypeScript and Zod for runtime validation.
|
|
25
|
+
|
|
26
|
+
## 📦 Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install lt-public-transport-sdk
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## ⚡ Quick Start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { LtTransport } from "lt-public-transport-sdk";
|
|
36
|
+
|
|
37
|
+
// 1. Initialize the client
|
|
38
|
+
const client = new LtTransport({
|
|
39
|
+
autoEnrich: true, // Automatically download GTFS and match routes
|
|
40
|
+
cacheDir: "./.cache", // Where to store GTFS/ZIP files
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
async function main() {
|
|
44
|
+
try {
|
|
45
|
+
// 2. Get real-time vehicles for Vilnius
|
|
46
|
+
console.log("Fetching Vilnius buses...");
|
|
47
|
+
const vehicles = await client.getVehicles("vilnius");
|
|
48
|
+
|
|
49
|
+
console.log(`Found ${vehicles.length} active vehicles:`);
|
|
50
|
+
|
|
51
|
+
vehicles.slice(0, 3).forEach((v) => {
|
|
52
|
+
console.log(`🚌 [${v.route}] ${v.vehicleNumber} -> ${v.destination}`);
|
|
53
|
+
console.log(` 📍 ${v.latitude}, ${v.longitude} (${v.speed} km/h)`);
|
|
54
|
+
});
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error("Error fetching data:", err);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
main();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 🏙️ Supported Cities
|
|
64
|
+
|
|
65
|
+
| City | Tier | GPS Data | GTFS Data | Formats Handled |
|
|
66
|
+
| ---------------- | --------- | -------- | --------- | -------------------------------------- |
|
|
67
|
+
| **Vilnius** | 🥇 Gold | ✅ Full | ✅ Yes | 18-col CSV, GTFS lookup |
|
|
68
|
+
| **Kaunas** | 🥇 Gold | ✅ Full | ✅ Yes | 14-col CSV, Trip/Stop matching |
|
|
69
|
+
| **Klaipėda** | 🥇 Gold | ✅ Full | ✅ Yes | 12-col CSV |
|
|
70
|
+
| **Alytus** | 🥇 Gold | ✅ Full | ✅ Yes | 13-col CSV |
|
|
71
|
+
| **Druskininkai** | 🥇 Gold | ✅ Full | ✅ Yes | 13-col CSV |
|
|
72
|
+
| **Panevėžys** | 🥈 Silver | ✅ Lite | ✅ Yes | 9-col (no header), Integer coords |
|
|
73
|
+
| **Tauragė** | 🥈 Silver | ✅ Lite | ✅ Yes | 8-col (no header), Alphanumeric routes |
|
|
74
|
+
| **Šiauliai** | 🥉 Bronze | ❌ No | ✅ Yes | GTFS only |
|
|
75
|
+
| **Utena** | 🥉 Bronze | ❌ No | ✅ Yes | GTFS only |
|
|
76
|
+
|
|
77
|
+
## 🧩 How It Works
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
> **Note**: Bronze tier cities (Šiauliai, Utena) do not publish real-time GPS data. Calling `getVehicles()` will throw a `GpsNotAvailableError`, but `getRoutes()` and `getStops()` **work normally** with GTFS data.
|
|
82
|
+
|
|
83
|
+
### `LtTransport` Class
|
|
84
|
+
|
|
85
|
+
#### Configuration
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const client = new LtTransport({
|
|
89
|
+
// Directory to store downloaded GTFS zip files (default: os.tmpdir())
|
|
90
|
+
cacheDir: "./cache",
|
|
91
|
+
|
|
92
|
+
// Max age of GPS data to consider "live" (default: 5 minutes)
|
|
93
|
+
staleThresholdMs: 5 * 60 * 1000,
|
|
94
|
+
|
|
95
|
+
// Filter out vehicles that haven't updated recently (default: false)
|
|
96
|
+
filterStale: true,
|
|
97
|
+
|
|
98
|
+
// Fetch and parse GTFS to add destination/route info (default: true)
|
|
99
|
+
autoEnrich: true,
|
|
100
|
+
|
|
101
|
+
// Filter out coordinates outside Lithuania (default: true)
|
|
102
|
+
filterInvalidCoords: true,
|
|
103
|
+
|
|
104
|
+
// Request timeout in ms (default: 10000)
|
|
105
|
+
requestTimeout: 10000,
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Extensibility: Custom Cities & Overrides
|
|
110
|
+
|
|
111
|
+
The SDK supports adding new cities or overriding existing configurations **without waiting for SDK updates**:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { LtTransport, type CityConfig } from "lt-public-transport-sdk";
|
|
115
|
+
|
|
116
|
+
const client = new LtTransport({
|
|
117
|
+
// Add a completely new city
|
|
118
|
+
customCities: {
|
|
119
|
+
marijampole: {
|
|
120
|
+
id: "marijampole",
|
|
121
|
+
tier: "silver",
|
|
122
|
+
gps: {
|
|
123
|
+
enabled: true,
|
|
124
|
+
format: "lite",
|
|
125
|
+
url: "https://www.stops.lt/marijampole/gps.txt",
|
|
126
|
+
},
|
|
127
|
+
gtfs: {
|
|
128
|
+
enabled: true,
|
|
129
|
+
url: "https://www.stops.lt/marijampole/marijampole/gtfs.zip",
|
|
130
|
+
},
|
|
131
|
+
// Required for 'lite' format: define column indices
|
|
132
|
+
liteFormat: {
|
|
133
|
+
minColumns: 9,
|
|
134
|
+
vehicleIdIndex: 7,
|
|
135
|
+
routeIndex: 1,
|
|
136
|
+
coordIndices: [3, 2], // [latIndex, lonIndex]
|
|
137
|
+
speedIndex: 4,
|
|
138
|
+
bearingIndex: 5,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// Override existing city config (e.g., if format changed)
|
|
144
|
+
cityOverrides: {
|
|
145
|
+
panevezys: {
|
|
146
|
+
liteFormat: {
|
|
147
|
+
minColumns: 10, // They added a column
|
|
148
|
+
vehicleIdIndex: 8,
|
|
149
|
+
routeIndex: 1,
|
|
150
|
+
coordIndices: [3, 2],
|
|
151
|
+
speedIndex: 4,
|
|
152
|
+
bearingIndex: 5,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Custom cities work with all methods
|
|
159
|
+
const vehicles = await client.getVehicles("marijampole");
|
|
160
|
+
const cities = client.getCities(); // includes 'marijampole'
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
> **Note**: Invalid configurations throw helpful `ZodError` messages explaining what's wrong.
|
|
164
|
+
|
|
165
|
+
#### Methods
|
|
166
|
+
|
|
167
|
+
#### Methods
|
|
168
|
+
|
|
169
|
+
| Method | Returns | Description |
|
|
170
|
+
| ---------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------- |
|
|
171
|
+
| **`getVehicles(city)`** | `Promise<Vehicle[]>` | Fetches real-time vehicle positions. If `autoEnrich` is true, ensures GTFS data is synced and merges it. |
|
|
172
|
+
| **`sync(city, force?)`** | `Promise<SyncResult>` | Manually downloads/updates GTFS static data (routes/stops). Throttled to 60s unless `force=true`. |
|
|
173
|
+
| **`getRoutes(city)`** | `Promise<Route[]>` | Returns the list of static routes from GTFS. Requires prior `sync()`. |
|
|
174
|
+
| **`getStops(city)`** | `Promise<Stop[]>` | Returns the list of static stops from GTFS. Requires prior `sync()`. |
|
|
175
|
+
| **`getTrips(city)`** | `Promise<Trip[]>` | Returns all trips with route/service/shape linkage. Requires prior `sync()`. |
|
|
176
|
+
| **`getShapes(city)`** | `Promise<Map<string, ShapePoint[]>>` | Returns route polylines grouped by shape ID. Requires prior `sync()`. |
|
|
177
|
+
| **`getCalendar(city)`** | `Promise<Calendar[]>` | Returns service calendars (which days services operate). Requires prior `sync()`. |
|
|
178
|
+
| **`getCalendarDates(city)`** | `Promise<CalendarDate[]>` | Returns calendar exceptions (holidays, special dates). Requires prior `sync()`. |
|
|
179
|
+
| **`getAgencies(city)`** | `Promise<Agency[]>` | Returns transit agency information. Requires prior `sync()`. |
|
|
180
|
+
| **`getSchedule(city)`** | `Promise<Map<string, StopTime[]>>` | Returns stop times grouped by trip ID (⚠️ large dataset). Requires prior `sync()`. |
|
|
181
|
+
| **`getCities()`** | `CityId[]` | Returns a list of all supported city identifiers. |
|
|
182
|
+
| **`getCityConfig(city)`** | `CityConfig` | Returns configuration details (tier, URLs) for a specific city. |
|
|
183
|
+
|
|
184
|
+
### Key Types
|
|
185
|
+
|
|
186
|
+
#### `Vehicle`
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
interface Vehicle {
|
|
190
|
+
/** Unique identifier (e.g. "vilnius-1234") */
|
|
191
|
+
readonly id: string;
|
|
192
|
+
|
|
193
|
+
/** Vehicle number visible on the bus/trolley */
|
|
194
|
+
readonly vehicleNumber: string;
|
|
195
|
+
|
|
196
|
+
/** Route number (e.g. "3G", "10") */
|
|
197
|
+
readonly route: string;
|
|
198
|
+
|
|
199
|
+
/** Type of vehicle */
|
|
200
|
+
readonly type: "bus" | "trolleybus" | "ferry" | "unknown";
|
|
201
|
+
|
|
202
|
+
/** Latitude in WGS84 decimal degrees */
|
|
203
|
+
readonly latitude: number;
|
|
204
|
+
|
|
205
|
+
/** Longitude in WGS84 decimal degrees */
|
|
206
|
+
readonly longitude: number;
|
|
207
|
+
|
|
208
|
+
/** Bearing in degrees (0-360) */
|
|
209
|
+
readonly bearing: number;
|
|
210
|
+
|
|
211
|
+
/** Speed in km/h */
|
|
212
|
+
readonly speed: number;
|
|
213
|
+
|
|
214
|
+
/** Destination name (e.g. "Santariškės") */
|
|
215
|
+
readonly destination: string | null;
|
|
216
|
+
|
|
217
|
+
/** Delay in seconds (positive = late, negative = early) */
|
|
218
|
+
readonly delaySeconds: number | null;
|
|
219
|
+
|
|
220
|
+
/** Trip identifier from source system */
|
|
221
|
+
readonly tripId: string | null;
|
|
222
|
+
|
|
223
|
+
/** GTFS trip reference (Vilnius only) */
|
|
224
|
+
readonly gtfsTripId: string | null;
|
|
225
|
+
|
|
226
|
+
/** Whether data is older than staleThreshold */
|
|
227
|
+
readonly isStale: boolean;
|
|
228
|
+
|
|
229
|
+
/** When the position was measured */
|
|
230
|
+
readonly measuredAt: Date;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
#### `Route` (GTFS)
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
interface Route {
|
|
238
|
+
/** Unique route ID (e.g. "routes_123") */
|
|
239
|
+
readonly id: string;
|
|
240
|
+
|
|
241
|
+
/** Short name (e.g. "3G") */
|
|
242
|
+
readonly shortName: string;
|
|
243
|
+
|
|
244
|
+
/** Long name (e.g. "Oro uostas - Centras - Fabijoniškės") */
|
|
245
|
+
readonly longName: string;
|
|
246
|
+
|
|
247
|
+
/** Transport type */
|
|
248
|
+
readonly type: VehicleType;
|
|
249
|
+
|
|
250
|
+
/** Route color (hex without #) */
|
|
251
|
+
readonly color: string;
|
|
252
|
+
|
|
253
|
+
/** Text color (hex without #) */
|
|
254
|
+
readonly textColor: string;
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
#### `Stop` (GTFS)
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
interface Stop {
|
|
262
|
+
/** Unique stop ID */
|
|
263
|
+
readonly id: string;
|
|
264
|
+
|
|
265
|
+
/** Check digit code (if available) */
|
|
266
|
+
readonly code: string | null;
|
|
267
|
+
|
|
268
|
+
/** Stop name */
|
|
269
|
+
readonly name: string;
|
|
270
|
+
|
|
271
|
+
/** Stop description/details */
|
|
272
|
+
readonly description: string | null;
|
|
273
|
+
|
|
274
|
+
/** Latitude */
|
|
275
|
+
readonly latitude: number;
|
|
276
|
+
|
|
277
|
+
/** Longitude */
|
|
278
|
+
readonly longitude: number;
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### `Trip` (GTFS)
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
interface Trip {
|
|
286
|
+
/** Unique trip ID */
|
|
287
|
+
readonly id: string;
|
|
288
|
+
|
|
289
|
+
/** Route ID reference */
|
|
290
|
+
readonly routeId: string;
|
|
291
|
+
|
|
292
|
+
/** Service/calendar ID */
|
|
293
|
+
readonly serviceId: string;
|
|
294
|
+
|
|
295
|
+
/** Trip destination headsign */
|
|
296
|
+
readonly headsign: string;
|
|
297
|
+
|
|
298
|
+
/** Trip short name (optional) */
|
|
299
|
+
readonly shortName: string | null;
|
|
300
|
+
|
|
301
|
+
/** Direction (0 or 1) */
|
|
302
|
+
readonly directionId: number | null;
|
|
303
|
+
|
|
304
|
+
/** Shape ID reference (for drawing route path) */
|
|
305
|
+
readonly shapeId: string | null;
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### `ShapePoint` (GTFS)
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
interface ShapePoint {
|
|
313
|
+
/** Shape ID */
|
|
314
|
+
readonly shapeId: string;
|
|
315
|
+
|
|
316
|
+
/** Latitude */
|
|
317
|
+
readonly latitude: number;
|
|
318
|
+
|
|
319
|
+
/** Longitude */
|
|
320
|
+
readonly longitude: number;
|
|
321
|
+
|
|
322
|
+
/** Point sequence in shape */
|
|
323
|
+
readonly sequence: number;
|
|
324
|
+
|
|
325
|
+
/** Distance traveled along shape (optional) */
|
|
326
|
+
readonly distanceTraveled: number | null;
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### `Calendar` (GTFS)
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
interface Calendar {
|
|
334
|
+
/** Service ID */
|
|
335
|
+
readonly serviceId: string;
|
|
336
|
+
|
|
337
|
+
/** Operates on Mondays */
|
|
338
|
+
readonly monday: boolean;
|
|
339
|
+
|
|
340
|
+
/** Operates on Tuesdays */
|
|
341
|
+
readonly tuesday: boolean;
|
|
342
|
+
|
|
343
|
+
/** Operates on Wednesdays */
|
|
344
|
+
readonly wednesday: boolean;
|
|
345
|
+
|
|
346
|
+
/** Operates on Thursdays */
|
|
347
|
+
readonly thursday: boolean;
|
|
348
|
+
|
|
349
|
+
/** Operates on Fridays */
|
|
350
|
+
readonly friday: boolean;
|
|
351
|
+
|
|
352
|
+
/** Operates on Saturdays */
|
|
353
|
+
readonly saturday: boolean;
|
|
354
|
+
|
|
355
|
+
/** Operates on Sundays */
|
|
356
|
+
readonly sunday: boolean;
|
|
357
|
+
|
|
358
|
+
/** Service start date (ISO format YYYY-MM-DD) */
|
|
359
|
+
readonly startDate: string;
|
|
360
|
+
|
|
361
|
+
/** Service end date (ISO format YYYY-MM-DD) */
|
|
362
|
+
readonly endDate: string;
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### `CalendarDate` (GTFS)
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
interface CalendarDate {
|
|
370
|
+
/** Service ID */
|
|
371
|
+
readonly serviceId: string;
|
|
372
|
+
|
|
373
|
+
/** Exception date (ISO format YYYY-MM-DD) */
|
|
374
|
+
readonly date: string;
|
|
375
|
+
|
|
376
|
+
/** Exception type */
|
|
377
|
+
readonly exceptionType: "added" | "removed";
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### `Agency` (GTFS)
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
interface Agency {
|
|
385
|
+
/** Agency ID (optional if only one agency) */
|
|
386
|
+
readonly id: string | null;
|
|
387
|
+
|
|
388
|
+
/** Agency name */
|
|
389
|
+
readonly name: string;
|
|
390
|
+
|
|
391
|
+
/** Agency website URL */
|
|
392
|
+
readonly url: string;
|
|
393
|
+
|
|
394
|
+
/** Agency timezone */
|
|
395
|
+
readonly timezone: string;
|
|
396
|
+
|
|
397
|
+
/** Agency language code */
|
|
398
|
+
readonly language: string | null;
|
|
399
|
+
|
|
400
|
+
/** Agency phone number */
|
|
401
|
+
readonly phone: string | null;
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
#### `StopTime` (GTFS)
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
interface StopTime {
|
|
409
|
+
/** Trip ID */
|
|
410
|
+
readonly tripId: string;
|
|
411
|
+
|
|
412
|
+
/** Stop ID */
|
|
413
|
+
readonly stopId: string;
|
|
414
|
+
|
|
415
|
+
/** Arrival time (HH:MM:SS format, may exceed 24:00:00) */
|
|
416
|
+
readonly arrivalTime: string;
|
|
417
|
+
|
|
418
|
+
/** Departure time (HH:MM:SS format, may exceed 24:00:00) */
|
|
419
|
+
readonly departureTime: string;
|
|
420
|
+
|
|
421
|
+
/** Stop sequence (0-based) */
|
|
422
|
+
readonly sequence: number;
|
|
423
|
+
|
|
424
|
+
/** Stop headsign override */
|
|
425
|
+
readonly stopHeadsign: string | null;
|
|
426
|
+
|
|
427
|
+
/** Pickup type (0=regular, 1=none, etc.) */
|
|
428
|
+
readonly pickupType: number | null;
|
|
429
|
+
|
|
430
|
+
/** Drop-off type (0=regular, 1=none, etc.) */
|
|
431
|
+
readonly dropOffType: number | null;
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## 💾 Caching & Best Practices
|
|
436
|
+
|
|
437
|
+
By default, GTFS data (routes & stops) is cached in your system's temporary directory (`os.tmpdir()/lt-transport-sdk-cache`).
|
|
438
|
+
|
|
439
|
+
### Microservices / Docker
|
|
440
|
+
|
|
441
|
+
If you are running in a containerized environment (Docker, Kubernetes), it is recommended to:
|
|
442
|
+
|
|
443
|
+
1. **Persist the cache**: Mount a volume to avoid re-downloading GTFS data (5-10MB) on every container restart.
|
|
444
|
+
2. **Configure path**: Set `cacheDir` to your mounted volume.
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
const client = new LtTransport({
|
|
448
|
+
cacheDir: "/data/cache", // Mount a volume here
|
|
449
|
+
});
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Serverless (AWS Lambda, etc.)
|
|
453
|
+
|
|
454
|
+
The SDK works in serverless environments by writing to `/tmp`. Note that "cold starts" will incur a latency penalty while GTFS data is downloaded and parsed.
|
|
455
|
+
|
|
456
|
+
## ⚠️ Error Handling
|
|
457
|
+
|
|
458
|
+
The SDK throws specific errors you can catch:
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import {
|
|
462
|
+
GpsNotAvailableError, // City doesn't support GPS
|
|
463
|
+
InvalidCityError, // Typo in city name
|
|
464
|
+
SyncRequiredError, // Need to run syncGtfs() (if autoEnrich: false)
|
|
465
|
+
TransportNetworkError, // Network timeout/failure
|
|
466
|
+
} from "lt-public-transport-sdk/errors";
|
|
467
|
+
|
|
468
|
+
try {
|
|
469
|
+
await client.getVehicles("unknown-city");
|
|
470
|
+
} catch (err) {
|
|
471
|
+
if (err instanceof InvalidCityError) {
|
|
472
|
+
console.log("Invalid city ID!");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## 🛠️ Development & Testing
|
|
478
|
+
|
|
479
|
+
This project uses **Vitest** for testing and **TypeScript** for building.
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
# Install dependencies
|
|
483
|
+
npm install
|
|
484
|
+
|
|
485
|
+
# Run unit tests (fast, using fixtures)
|
|
486
|
+
npm test
|
|
487
|
+
|
|
488
|
+
# Run integration tests (hits live APIs - verify functionality)
|
|
489
|
+
npm run build
|
|
490
|
+
npm run test:integration
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## 📄 License
|
|
494
|
+
|
|
495
|
+
MIT
|