lt-public-transport-sdk 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/LICENSE +21 -0
- package/README.md +283 -0
- package/assets/architecture.png +0 -0
- package/dist/config.d.ts +221 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +200 -0
- package/dist/config.js.map +1 -0
- package/dist/enrichment/index.d.ts +6 -0
- package/dist/enrichment/index.d.ts.map +1 -0
- package/dist/enrichment/index.js +6 -0
- package/dist/enrichment/index.js.map +1 -0
- package/dist/enrichment/route-matcher.d.ts +64 -0
- package/dist/enrichment/route-matcher.d.ts.map +1 -0
- package/dist/enrichment/route-matcher.js +121 -0
- package/dist/enrichment/route-matcher.js.map +1 -0
- package/dist/errors.d.ts +70 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +104 -0
- package/dist/errors.js.map +1 -0
- package/dist/gtfs/index.d.ts +7 -0
- package/dist/gtfs/index.d.ts.map +1 -0
- package/dist/gtfs/index.js +7 -0
- package/dist/gtfs/index.js.map +1 -0
- package/dist/gtfs/parser.d.ts +39 -0
- package/dist/gtfs/parser.d.ts.map +1 -0
- package/dist/gtfs/parser.js +189 -0
- package/dist/gtfs/parser.js.map +1 -0
- package/dist/gtfs/sync.d.ts +72 -0
- package/dist/gtfs/sync.d.ts.map +1 -0
- package/dist/gtfs/sync.js +271 -0
- package/dist/gtfs/sync.js.map +1 -0
- package/dist/index.d.ts +203 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +342 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/gps-full.d.ts +39 -0
- package/dist/parsers/gps-full.d.ts.map +1 -0
- package/dist/parsers/gps-full.js +212 -0
- package/dist/parsers/gps-full.js.map +1 -0
- package/dist/parsers/gps-lite.d.ts +60 -0
- package/dist/parsers/gps-lite.d.ts.map +1 -0
- package/dist/parsers/gps-lite.js +141 -0
- package/dist/parsers/gps-lite.js.map +1 -0
- package/dist/parsers/index.d.ts +7 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +7 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/schemas.d.ts +129 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +200 -0
- package/dist/schemas.js.map +1 -0
- package/dist/scripts/test-city-specific.d.ts +2 -0
- package/dist/scripts/test-city-specific.d.ts.map +1 -0
- package/dist/scripts/test-city-specific.js +264 -0
- package/dist/scripts/test-city-specific.js.map +1 -0
- package/dist/scripts/test-config-options.d.ts +2 -0
- package/dist/scripts/test-config-options.d.ts.map +1 -0
- package/dist/scripts/test-config-options.js +166 -0
- package/dist/scripts/test-config-options.js.map +1 -0
- package/dist/scripts/test-data-quality.d.ts +2 -0
- package/dist/scripts/test-data-quality.d.ts.map +1 -0
- package/dist/scripts/test-data-quality.js +204 -0
- package/dist/scripts/test-data-quality.js.map +1 -0
- package/dist/scripts/test-error-handling.d.ts +2 -0
- package/dist/scripts/test-error-handling.d.ts.map +1 -0
- package/dist/scripts/test-error-handling.js +146 -0
- package/dist/scripts/test-error-handling.js.map +1 -0
- package/dist/scripts/test-live.d.ts +2 -0
- package/dist/scripts/test-live.d.ts.map +1 -0
- package/dist/scripts/test-live.js +121 -0
- package/dist/scripts/test-live.js.map +1 -0
- package/dist/types.d.ts +120 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/coordinates.d.ts +68 -0
- package/dist/utils/coordinates.d.ts.map +1 -0
- package/dist/utils/coordinates.js +98 -0
- package/dist/utils/coordinates.js.map +1 -0
- package/dist/utils/encoding.d.ts +47 -0
- package/dist/utils/encoding.d.ts.map +1 -0
- package/dist/utils/encoding.js +153 -0
- package/dist/utils/encoding.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/time.d.ts +50 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +94 -0
- package/dist/utils/time.js.map +1 -0
- package/package.json +84 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GPS Full Format Parser with dynamic header-based column mapping
|
|
3
|
+
* @module parsers/gps-full
|
|
4
|
+
*
|
|
5
|
+
* Handles the "full" GPS format used by gold-tier cities (Vilnius, Kaunas, Klaipėda, Alytus, Druskininkai).
|
|
6
|
+
* Each city has different column layouts, so we parse headers dynamically.
|
|
7
|
+
*
|
|
8
|
+
* Column counts by city (empirically verified):
|
|
9
|
+
* - Vilnius: 18 columns
|
|
10
|
+
* - Kaunas: 14 columns
|
|
11
|
+
* - Klaipėda: 12 columns
|
|
12
|
+
* - Alytus: 13 columns
|
|
13
|
+
* - Druskininkai: 13 columns
|
|
14
|
+
*/
|
|
15
|
+
import { LT_TRANSPORT_TYPE_MAP } from '../types.js';
|
|
16
|
+
import { normalizeCoordinate, isValidLithuaniaCoord, normalizeBearing, normalizeSpeed, cleanTextField, secondsFromMidnightToDate, isDataStale, } from '../utils/index.js';
|
|
17
|
+
import { gpsFullRowSchema } from '../schemas.js';
|
|
18
|
+
/**
|
|
19
|
+
* Build a column map from header row.
|
|
20
|
+
*/
|
|
21
|
+
function buildColumnMap(headers) {
|
|
22
|
+
const indexMap = new Map();
|
|
23
|
+
headers.forEach((header, index) => {
|
|
24
|
+
const trimmed = header.trim();
|
|
25
|
+
if (trimmed) {
|
|
26
|
+
indexMap.set(trimmed, index);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
get(column) {
|
|
31
|
+
return indexMap.get(column);
|
|
32
|
+
},
|
|
33
|
+
getRequired(column) {
|
|
34
|
+
const index = indexMap.get(column);
|
|
35
|
+
if (index === undefined) {
|
|
36
|
+
throw new Error(`Required column '${column}' not found in headers`);
|
|
37
|
+
}
|
|
38
|
+
return index;
|
|
39
|
+
},
|
|
40
|
+
has(column) {
|
|
41
|
+
return indexMap.has(column);
|
|
42
|
+
},
|
|
43
|
+
columns: headers.map(h => h.trim()),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parse vehicle type from Lithuanian transport name.
|
|
48
|
+
*/
|
|
49
|
+
function parseVehicleType(transportName) {
|
|
50
|
+
const type = LT_TRANSPORT_TYPE_MAP[transportName];
|
|
51
|
+
return type ?? 'unknown';
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse GPS full format stream from a gold-tier city.
|
|
55
|
+
*
|
|
56
|
+
* Uses header-based dynamic column mapping to handle different
|
|
57
|
+
* column layouts across cities.
|
|
58
|
+
*
|
|
59
|
+
* @param text - Raw text content from gps_full.txt
|
|
60
|
+
* @param city - City identifier for context
|
|
61
|
+
* @param options - Parse options
|
|
62
|
+
* @returns Array of normalized Vehicle objects
|
|
63
|
+
*/
|
|
64
|
+
export function parseGpsFullStream(text, city, options = {}) {
|
|
65
|
+
const { staleThresholdMs = 5 * 60 * 1000, filterStale = false, filterInvalidCoords = true, } = options;
|
|
66
|
+
const lines = text.split('\n').filter(line => line.trim());
|
|
67
|
+
if (lines.length < 2) {
|
|
68
|
+
// No data (only header or empty)
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
// Parse header row
|
|
72
|
+
const firstLine = lines[0];
|
|
73
|
+
if (firstLine === undefined) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
// Strip UTF-8 BOM if present (stops.lt sometimes includes BOM)
|
|
77
|
+
const cleanHeader = firstLine.charCodeAt(0) === 0xFEFF
|
|
78
|
+
? firstLine.slice(1)
|
|
79
|
+
: firstLine;
|
|
80
|
+
const headers = cleanHeader.split(',');
|
|
81
|
+
const columnMap = buildColumnMap(headers);
|
|
82
|
+
// Validate required columns
|
|
83
|
+
const requiredColumns = [
|
|
84
|
+
'Transportas',
|
|
85
|
+
'Marsrutas',
|
|
86
|
+
'MasinosNumeris',
|
|
87
|
+
'Ilguma',
|
|
88
|
+
'Platuma',
|
|
89
|
+
];
|
|
90
|
+
for (const col of requiredColumns) {
|
|
91
|
+
if (!columnMap.has(col)) {
|
|
92
|
+
throw new Error(`Required column '${col}' not found in ${city} GPS data`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Parse data rows
|
|
96
|
+
const vehicles = [];
|
|
97
|
+
for (let i = 1; i < lines.length; i++) {
|
|
98
|
+
const line = lines[i];
|
|
99
|
+
if (line === undefined || line.trim() === '')
|
|
100
|
+
continue;
|
|
101
|
+
const cols = line.split(',');
|
|
102
|
+
try {
|
|
103
|
+
const vehicle = parseVehicleLine(cols, columnMap, city, staleThresholdMs);
|
|
104
|
+
if (vehicle) {
|
|
105
|
+
// Apply filters
|
|
106
|
+
if (filterInvalidCoords && !isValidLithuaniaCoord(vehicle.latitude, vehicle.longitude)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (filterStale && vehicle.isStale) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
vehicles.push(vehicle);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Skip malformed lines
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return vehicles;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Build a row object from CSV columns using the column map.
|
|
124
|
+
*/
|
|
125
|
+
function buildRowObject(cols, columnMap) {
|
|
126
|
+
const obj = {};
|
|
127
|
+
for (const colName of columnMap.columns) {
|
|
128
|
+
const idx = columnMap.get(colName);
|
|
129
|
+
if (idx !== undefined) {
|
|
130
|
+
obj[colName] = cols[idx]?.trim() ?? '';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return obj;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Parse a single vehicle line from GPS full format.
|
|
137
|
+
*/
|
|
138
|
+
function parseVehicleLine(cols, columnMap, city, staleThresholdMs) {
|
|
139
|
+
// Build row object from columns for Zod validation
|
|
140
|
+
const rowObject = buildRowObject(cols, columnMap);
|
|
141
|
+
// Validate with Zod schema - this catches format changes early
|
|
142
|
+
const parseResult = gpsFullRowSchema.safeParse(rowObject);
|
|
143
|
+
if (!parseResult.success) {
|
|
144
|
+
// Row doesn't match expected schema - skip it
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const row = parseResult.data;
|
|
148
|
+
// Skip if missing essential data
|
|
149
|
+
if (row.MasinosNumeris === '') {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
// Normalize coordinates
|
|
153
|
+
const longitude = normalizeCoordinate(row.Ilguma);
|
|
154
|
+
const latitude = normalizeCoordinate(row.Platuma);
|
|
155
|
+
// Extract optional fields
|
|
156
|
+
const speed = normalizeSpeed(row.Greitis);
|
|
157
|
+
const bearing = normalizeBearing(row.Azimutas);
|
|
158
|
+
const delaySeconds = row.NuokrypisSekundemis ?? null;
|
|
159
|
+
// Trip ID - different column name for Kaunas
|
|
160
|
+
const tripId = row.ReisoID ?? row.Grafikas ?? null;
|
|
161
|
+
// Destination name
|
|
162
|
+
const destination = row.KryptiesPavadinimas !== undefined && row.KryptiesPavadinimas !== ''
|
|
163
|
+
? cleanTextField(row.KryptiesPavadinimas)
|
|
164
|
+
: null;
|
|
165
|
+
// GTFS trip reference (Vilnius only)
|
|
166
|
+
const gtfsTripId = row.ReisoIdGTFS ?? null;
|
|
167
|
+
// Next stop ID (Kaunas only)
|
|
168
|
+
const nextStopId = row.SekanciosStotelesNum !== undefined
|
|
169
|
+
? String(row.SekanciosStotelesNum)
|
|
170
|
+
: null;
|
|
171
|
+
// Arrival time (Kaunas only) - this is FUTURE prediction, not measurement time
|
|
172
|
+
const arrivalTimeSeconds = row.AtvykimoLaikasSekundemis ?? null;
|
|
173
|
+
// Calculate measurement time
|
|
174
|
+
const measuredAt = calculateMeasuredAtFromRow(row);
|
|
175
|
+
const isStale = isDataStale(measuredAt, staleThresholdMs);
|
|
176
|
+
// Generate unique ID
|
|
177
|
+
const vehicleNumber = row.MasinosNumeris;
|
|
178
|
+
const route = row.Marsrutas;
|
|
179
|
+
const id = `${city}-${vehicleNumber}-${route}`;
|
|
180
|
+
return {
|
|
181
|
+
id,
|
|
182
|
+
vehicleNumber,
|
|
183
|
+
route,
|
|
184
|
+
type: parseVehicleType(row.Transportas),
|
|
185
|
+
latitude,
|
|
186
|
+
longitude,
|
|
187
|
+
bearing,
|
|
188
|
+
speed,
|
|
189
|
+
destination,
|
|
190
|
+
delaySeconds,
|
|
191
|
+
tripId,
|
|
192
|
+
gtfsTripId,
|
|
193
|
+
nextStopId,
|
|
194
|
+
arrivalTimeSeconds,
|
|
195
|
+
isStale,
|
|
196
|
+
measuredAt,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Calculate measurement time from validated row data.
|
|
201
|
+
*/
|
|
202
|
+
function calculateMeasuredAtFromRow(row) {
|
|
203
|
+
// Try MatavimoLaikas first (Vilnius, Alytus, Druskininkai)
|
|
204
|
+
if (row.MatavimoLaikas !== undefined && row.MatavimoLaikas > 0) {
|
|
205
|
+
return secondsFromMidnightToDate(row.MatavimoLaikas);
|
|
206
|
+
}
|
|
207
|
+
// Kaunas: AtvykimoLaikasSekundemis is FUTURE arrival, not measurement time.
|
|
208
|
+
// Klaipėda: No time field available.
|
|
209
|
+
// Fallback to server receive time (now) for both cases.
|
|
210
|
+
return new Date();
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=gps-full.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gps-full.js","sourceRoot":"","sources":["../../src/parsers/gps-full.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GPS Lite Format Parser for silver-tier cities
|
|
3
|
+
* @module parsers/gps-lite
|
|
4
|
+
*
|
|
5
|
+
* Handles the "lite" GPS format used by silver-tier cities (Panevėžys, Tauragė).
|
|
6
|
+
* These streams have no header row and fewer columns.
|
|
7
|
+
*
|
|
8
|
+
* Column counts by city (empirically verified):
|
|
9
|
+
* - Panevėžys: 9 columns (no header, route often empty)
|
|
10
|
+
* - Tauragė: 8 columns (no header, alphanumeric routes like S11, S19)
|
|
11
|
+
*/
|
|
12
|
+
import type { CityId, Vehicle } from '../types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Panevėžys format (9 columns, no header):
|
|
15
|
+
* [0] type - Always "2" (bus?)
|
|
16
|
+
* [1] route - Route name (often empty)
|
|
17
|
+
* [2] longitude - Integer format (÷1,000,000)
|
|
18
|
+
* [3] latitude - Integer format (÷1,000,000)
|
|
19
|
+
* [4] speed - Speed or delay?
|
|
20
|
+
* [5] azimuth - Bearing in degrees
|
|
21
|
+
* [6] (empty) - Unknown
|
|
22
|
+
* [7] vehicleId - Vehicle identifier
|
|
23
|
+
* [8] (empty) - Unknown
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Tauragė format (8 columns, no header):
|
|
27
|
+
* [0] type - Always "2" (bus?)
|
|
28
|
+
* [1] route - Route name (S11, S19) - ALPHANUMERIC!
|
|
29
|
+
* [2] longitude - Integer format (÷1,000,000)
|
|
30
|
+
* [3] latitude - Integer format (÷1,000,000)
|
|
31
|
+
* [4] speed - Speed in km/h
|
|
32
|
+
* [5] azimuth - Bearing in degrees
|
|
33
|
+
* [6] vehicleId - Vehicle identifier
|
|
34
|
+
* [7] (empty) - Unknown
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Options for GPS lite format parsing.
|
|
38
|
+
*/
|
|
39
|
+
export interface GpsLiteParseOptions {
|
|
40
|
+
/** Whether to filter out records with invalid coordinates (default: true) */
|
|
41
|
+
filterInvalidCoords?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Cities that use lite GPS format.
|
|
45
|
+
*/
|
|
46
|
+
export type LiteCityId = 'panevezys' | 'taurage';
|
|
47
|
+
/**
|
|
48
|
+
* Check if a city uses lite GPS format.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isLiteCity(city: CityId): city is LiteCityId;
|
|
51
|
+
/**
|
|
52
|
+
* Parse GPS lite format stream from a silver-tier city.
|
|
53
|
+
*
|
|
54
|
+
* @param text - Raw text content from gps.txt
|
|
55
|
+
* @param city - City identifier (must be 'panevezys' or 'taurage')
|
|
56
|
+
* @param options - Parse options
|
|
57
|
+
* @returns Array of normalized Vehicle objects
|
|
58
|
+
*/
|
|
59
|
+
export declare function parseGpsLiteStream(text: string, city: LiteCityId, options?: GpsLiteParseOptions): Vehicle[];
|
|
60
|
+
//# sourceMappingURL=gps-lite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gps-lite.d.ts","sourceRoot":"","sources":["../../src/parsers/gps-lite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAe,MAAM,aAAa,CAAC;AAahE;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;GAUG;AAMH;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAEjD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,UAAU,CAE3D;AAMD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,mBAAwB,GAChC,OAAO,EAAE,CAgCX"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GPS Lite Format Parser for silver-tier cities
|
|
3
|
+
* @module parsers/gps-lite
|
|
4
|
+
*
|
|
5
|
+
* Handles the "lite" GPS format used by silver-tier cities (Panevėžys, Tauragė).
|
|
6
|
+
* These streams have no header row and fewer columns.
|
|
7
|
+
*
|
|
8
|
+
* Column counts by city (empirically verified):
|
|
9
|
+
* - Panevėžys: 9 columns (no header, route often empty)
|
|
10
|
+
* - Tauragė: 8 columns (no header, alphanumeric routes like S11, S19)
|
|
11
|
+
*/
|
|
12
|
+
import { normalizeCoordinate, isValidLithuaniaCoord, normalizeBearing, normalizeSpeed, } from '../utils/index.js';
|
|
13
|
+
import { gpsLitePanevezysSchema, gpsLiteTaurageSchema } from '../schemas.js';
|
|
14
|
+
/**
|
|
15
|
+
* Check if a city uses lite GPS format.
|
|
16
|
+
*/
|
|
17
|
+
export function isLiteCity(city) {
|
|
18
|
+
return city === 'panevezys' || city === 'taurage';
|
|
19
|
+
}
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Main Parser
|
|
22
|
+
// =============================================================================
|
|
23
|
+
/**
|
|
24
|
+
* Parse GPS lite format stream from a silver-tier city.
|
|
25
|
+
*
|
|
26
|
+
* @param text - Raw text content from gps.txt
|
|
27
|
+
* @param city - City identifier (must be 'panevezys' or 'taurage')
|
|
28
|
+
* @param options - Parse options
|
|
29
|
+
* @returns Array of normalized Vehicle objects
|
|
30
|
+
*/
|
|
31
|
+
export function parseGpsLiteStream(text, city, options = {}) {
|
|
32
|
+
const { filterInvalidCoords = true } = options;
|
|
33
|
+
const lines = text.split('\n').filter(line => line.trim());
|
|
34
|
+
if (lines.length === 0) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const vehicles = [];
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
const cols = line.split(',');
|
|
40
|
+
try {
|
|
41
|
+
const vehicle = city === 'panevezys'
|
|
42
|
+
? parsePanevezysLine(cols, city)
|
|
43
|
+
: parseTaurageLine(cols, city);
|
|
44
|
+
if (vehicle) {
|
|
45
|
+
if (filterInvalidCoords && !isValidLithuaniaCoord(vehicle.latitude, vehicle.longitude)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
vehicles.push(vehicle);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Skip malformed lines
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return vehicles;
|
|
57
|
+
}
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// City-Specific Parsers
|
|
60
|
+
// =============================================================================
|
|
61
|
+
/**
|
|
62
|
+
* Parse a line from Panevėžys GPS lite format (9 columns).
|
|
63
|
+
*/
|
|
64
|
+
function parsePanevezysLine(cols, city) {
|
|
65
|
+
// Validate with Zod schema
|
|
66
|
+
const parseResult = gpsLitePanevezysSchema.safeParse(cols);
|
|
67
|
+
if (!parseResult.success) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const row = parseResult.data;
|
|
71
|
+
const vehicleNumber = row[7];
|
|
72
|
+
// Validate essential fields
|
|
73
|
+
if (vehicleNumber === '' || !Number.isFinite(row[2]) || !Number.isFinite(row[3])) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const longitude = normalizeCoordinate(row[2]);
|
|
77
|
+
const latitude = normalizeCoordinate(row[3]);
|
|
78
|
+
const speed = normalizeSpeed(row[4]);
|
|
79
|
+
const bearing = normalizeBearing(row[5]);
|
|
80
|
+
const route = row[1];
|
|
81
|
+
const id = `${city}-${vehicleNumber}`;
|
|
82
|
+
return {
|
|
83
|
+
id,
|
|
84
|
+
vehicleNumber,
|
|
85
|
+
route,
|
|
86
|
+
type: 'bus', // Lite format doesn't specify type
|
|
87
|
+
latitude,
|
|
88
|
+
longitude,
|
|
89
|
+
bearing,
|
|
90
|
+
speed,
|
|
91
|
+
destination: null, // Needs GTFS enrichment
|
|
92
|
+
delaySeconds: null,
|
|
93
|
+
tripId: null,
|
|
94
|
+
gtfsTripId: null,
|
|
95
|
+
nextStopId: null,
|
|
96
|
+
arrivalTimeSeconds: null,
|
|
97
|
+
isStale: false, // No timestamp in lite format
|
|
98
|
+
measuredAt: new Date(), // Use server receive time
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Parse a line from Tauragė GPS lite format (8 columns).
|
|
103
|
+
*/
|
|
104
|
+
function parseTaurageLine(cols, city) {
|
|
105
|
+
// Validate with Zod schema
|
|
106
|
+
const parseResult = gpsLiteTaurageSchema.safeParse(cols);
|
|
107
|
+
if (!parseResult.success) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const row = parseResult.data;
|
|
111
|
+
const vehicleNumber = row[6];
|
|
112
|
+
// Validate essential fields
|
|
113
|
+
if (vehicleNumber === '' || !Number.isFinite(row[2]) || !Number.isFinite(row[3])) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
const longitude = normalizeCoordinate(row[2]);
|
|
117
|
+
const latitude = normalizeCoordinate(row[3]);
|
|
118
|
+
const speed = normalizeSpeed(row[4]);
|
|
119
|
+
const bearing = normalizeBearing(row[5]);
|
|
120
|
+
const route = row[1];
|
|
121
|
+
const id = `${city}-${vehicleNumber}`;
|
|
122
|
+
return {
|
|
123
|
+
id,
|
|
124
|
+
vehicleNumber,
|
|
125
|
+
route,
|
|
126
|
+
type: 'bus', // Lite format doesn't specify type
|
|
127
|
+
latitude,
|
|
128
|
+
longitude,
|
|
129
|
+
bearing,
|
|
130
|
+
speed,
|
|
131
|
+
destination: null, // Needs GTFS enrichment
|
|
132
|
+
delaySeconds: null,
|
|
133
|
+
tripId: null,
|
|
134
|
+
gtfsTripId: null,
|
|
135
|
+
nextStopId: null,
|
|
136
|
+
arrivalTimeSeconds: null,
|
|
137
|
+
isStale: false, // No timestamp in lite format
|
|
138
|
+
measuredAt: new Date(), // Use server receive time
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=gps-lite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gps-lite.js","sourceRoot":"","sources":["../../src/parsers/gps-lite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAoD7E;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,SAAS,CAAC;AACpD,CAAC;AAED,gFAAgF;AAChF,cAAc;AACd,gFAAgF;AAEhF;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,IAAgB,EAChB,UAA+B,EAAE;IAEjC,MAAM,EAAE,mBAAmB,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAE/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAE3D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE7B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,KAAK,WAAW;gBAClC,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC;gBAChC,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAEjC,IAAI,OAAO,EAAE,CAAC;gBACZ,IAAI,mBAAmB,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;oBACvF,SAAS;gBACX,CAAC;gBACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;YACvB,SAAS;QACX,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,gFAAgF;AAChF,wBAAwB;AACxB,gFAAgF;AAEhF;;GAEG;AACH,SAAS,kBAAkB,CAAC,IAAc,EAAE,IAAY;IACtD,2BAA2B;IAC3B,MAAM,WAAW,GAAG,sBAAsB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3D,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC;IAC7B,MAAM,aAAa,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAE7B,4BAA4B;IAC5B,IAAI,aAAa,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,MAAM,EAAE,GAAG,GAAG,IAAI,IAAI,aAAa,EAAE,CAAC;IAEtC,OAAO;QACL,EAAE;QACF,aAAa;QACb,KAAK;QACL,IAAI,EAAE,KAAoB,EAAE,mCAAmC;QAC/D,QAAQ;QACR,SAAS;QACT,OAAO;QACP,KAAK;QACL,WAAW,EAAE,IAAI,EAAE,wBAAwB;QAC3C,YAAY,EAAE,IAAI;QAClB,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,IAAI;QAChB,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,KAAK,EAAE,8BAA8B;QAC9C,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,0BAA0B;KACnD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,IAAc,EAAE,IAAY;IACpD,2BAA2B;IAC3B,MAAM,WAAW,GAAG,oBAAoB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC;IAC7B,MAAM,aAAa,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAE7B,4BAA4B;IAC5B,IAAI,aAAa,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAErB,MAAM,EAAE,GAAG,GAAG,IAAI,IAAI,aAAa,EAAE,CAAC;IAEtC,OAAO;QACL,EAAE;QACF,aAAa;QACb,KAAK;QACL,IAAI,EAAE,KAAoB,EAAE,mCAAmC;QAC/D,QAAQ;QACR,SAAS;QACT,OAAO;QACP,KAAK;QACL,WAAW,EAAE,IAAI,EAAE,wBAAwB;QAC3C,YAAY,EAAE,IAAI;QAClB,MAAM,EAAE,IAAI;QACZ,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,IAAI;QAChB,kBAAkB,EAAE,IAAI;QACxB,OAAO,EAAE,KAAK,EAAE,8BAA8B;QAC9C,UAAU,EAAE,IAAI,IAAI,EAAE,EAAE,0BAA0B;KACnD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser module exports
|
|
3
|
+
* @module parsers
|
|
4
|
+
*/
|
|
5
|
+
export { parseGpsFullStream, type GpsFullParseOptions, } from './gps-full.js';
|
|
6
|
+
export { parseGpsLiteStream, isLiteCity, type GpsLiteParseOptions, type LiteCityId, } from './gps-lite.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,kBAAkB,EAClB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,KAAK,mBAAmB,EACxB,KAAK,UAAU,GAChB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,kBAAkB,GAEnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,UAAU,GAGX,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for runtime validation of external data
|
|
3
|
+
* @module schemas
|
|
4
|
+
*
|
|
5
|
+
* These schemas validate data from stops.lt GPS streams and GTFS files.
|
|
6
|
+
* They provide:
|
|
7
|
+
* - Type coercion (string → number)
|
|
8
|
+
* - Clear error messages when format changes
|
|
9
|
+
* - Documentation of expected data structure
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
/**
|
|
13
|
+
* Schema for a GPS full format row.
|
|
14
|
+
* Columns vary by city but these are the core fields present in all cities.
|
|
15
|
+
*/
|
|
16
|
+
export declare const gpsFullRowSchema: z.ZodObject<{
|
|
17
|
+
Transportas: z.ZodString;
|
|
18
|
+
Marsrutas: z.ZodString;
|
|
19
|
+
MasinosNumeris: z.ZodString;
|
|
20
|
+
Ilguma: z.ZodCoercedNumber<unknown>;
|
|
21
|
+
Platuma: z.ZodCoercedNumber<unknown>;
|
|
22
|
+
Greitis: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
23
|
+
Azimutas: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
24
|
+
ReisoID: z.ZodOptional<z.ZodString>;
|
|
25
|
+
Grafikas: z.ZodOptional<z.ZodString>;
|
|
26
|
+
ReisoPradziaMinutemis: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
27
|
+
NuokrypisSekundemis: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
28
|
+
MatavimoLaikas: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
29
|
+
SekanciosStotelesNum: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
30
|
+
AtvykimoLaikasSekundemis: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
31
|
+
MasinosTipas: z.ZodOptional<z.ZodString>;
|
|
32
|
+
KryptiesTipas: z.ZodOptional<z.ZodString>;
|
|
33
|
+
KryptiesPavadinimas: z.ZodOptional<z.ZodString>;
|
|
34
|
+
ReisoIdGTFS: z.ZodOptional<z.ZodString>;
|
|
35
|
+
IntervalasPries: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
36
|
+
IntervalasPaskui: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
37
|
+
}, z.core.$loose>;
|
|
38
|
+
export type GpsFullRow = z.infer<typeof gpsFullRowSchema>;
|
|
39
|
+
/**
|
|
40
|
+
* Schema for Panevėžys GPS lite format (9 columns, no header).
|
|
41
|
+
* Columns: type, route, lon, lat, speed, azimuth, ?, vehicleId, ?
|
|
42
|
+
*/
|
|
43
|
+
export declare const gpsLitePanevezysSchema: z.ZodTuple<[z.ZodString, z.ZodString, z.ZodCoercedNumber<unknown>, z.ZodCoercedNumber<unknown>, z.ZodCoercedNumber<unknown>, z.ZodCoercedNumber<unknown>, z.ZodString, z.ZodString, z.ZodString], null>;
|
|
44
|
+
/**
|
|
45
|
+
* Schema for Tauragė GPS lite format (8 columns, no header).
|
|
46
|
+
* Columns: type, route, lon, lat, speed, azimuth, vehicleId, ?
|
|
47
|
+
*/
|
|
48
|
+
export declare const gpsLiteTaurageSchema: z.ZodTuple<[z.ZodString, z.ZodString, z.ZodCoercedNumber<unknown>, z.ZodCoercedNumber<unknown>, z.ZodCoercedNumber<unknown>, z.ZodCoercedNumber<unknown>, z.ZodString, z.ZodString], null>;
|
|
49
|
+
export type GpsLitePanevezysRow = z.infer<typeof gpsLitePanevezysSchema>;
|
|
50
|
+
export type GpsLiteTaurageRow = z.infer<typeof gpsLiteTaurageSchema>;
|
|
51
|
+
/**
|
|
52
|
+
* Schema for a GTFS routes.txt row.
|
|
53
|
+
*/
|
|
54
|
+
export declare const gtfsRouteSchema: z.ZodObject<{
|
|
55
|
+
route_id: z.ZodString;
|
|
56
|
+
route_short_name: z.ZodString;
|
|
57
|
+
route_long_name: z.ZodDefault<z.ZodString>;
|
|
58
|
+
route_type: z.ZodCoercedNumber<unknown>;
|
|
59
|
+
route_color: z.ZodDefault<z.ZodString>;
|
|
60
|
+
route_text_color: z.ZodDefault<z.ZodString>;
|
|
61
|
+
agency_id: z.ZodOptional<z.ZodString>;
|
|
62
|
+
route_desc: z.ZodOptional<z.ZodString>;
|
|
63
|
+
route_url: z.ZodOptional<z.ZodString>;
|
|
64
|
+
}, z.core.$loose>;
|
|
65
|
+
export type GtfsRoute = z.infer<typeof gtfsRouteSchema>;
|
|
66
|
+
/**
|
|
67
|
+
* Schema for a GTFS stops.txt row.
|
|
68
|
+
*/
|
|
69
|
+
export declare const gtfsStopSchema: z.ZodObject<{
|
|
70
|
+
stop_id: z.ZodString;
|
|
71
|
+
stop_name: z.ZodString;
|
|
72
|
+
stop_lat: z.ZodCoercedNumber<unknown>;
|
|
73
|
+
stop_lon: z.ZodCoercedNumber<unknown>;
|
|
74
|
+
stop_code: z.ZodOptional<z.ZodString>;
|
|
75
|
+
stop_desc: z.ZodOptional<z.ZodString>;
|
|
76
|
+
location_type: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
77
|
+
parent_station: z.ZodOptional<z.ZodString>;
|
|
78
|
+
}, z.core.$loose>;
|
|
79
|
+
export type GtfsStop = z.infer<typeof gtfsStopSchema>;
|
|
80
|
+
/**
|
|
81
|
+
* Schema for LtTransport client configuration.
|
|
82
|
+
*/
|
|
83
|
+
export declare const clientConfigSchema: z.ZodObject<{
|
|
84
|
+
cacheDir: z.ZodOptional<z.ZodString>;
|
|
85
|
+
requestTimeout: z.ZodDefault<z.ZodNumber>;
|
|
86
|
+
userAgent: z.ZodDefault<z.ZodString>;
|
|
87
|
+
staleThresholdMs: z.ZodDefault<z.ZodNumber>;
|
|
88
|
+
autoEnrich: z.ZodDefault<z.ZodBoolean>;
|
|
89
|
+
filterInvalidCoords: z.ZodDefault<z.ZodBoolean>;
|
|
90
|
+
filterStale: z.ZodDefault<z.ZodBoolean>;
|
|
91
|
+
}, z.core.$strict>;
|
|
92
|
+
export type ValidatedClientConfig = z.infer<typeof clientConfigSchema>;
|
|
93
|
+
/**
|
|
94
|
+
* Schema for a fully parsed and normalized Vehicle object.
|
|
95
|
+
* Used to validate output before returning to user.
|
|
96
|
+
*/
|
|
97
|
+
export declare const vehicleSchema: z.ZodObject<{
|
|
98
|
+
id: z.ZodString;
|
|
99
|
+
vehicleNumber: z.ZodString;
|
|
100
|
+
route: z.ZodString;
|
|
101
|
+
type: z.ZodEnum<{
|
|
102
|
+
bus: "bus";
|
|
103
|
+
trolleybus: "trolleybus";
|
|
104
|
+
ferry: "ferry";
|
|
105
|
+
unknown: "unknown";
|
|
106
|
+
}>;
|
|
107
|
+
latitude: z.ZodCoercedNumber<unknown>;
|
|
108
|
+
longitude: z.ZodCoercedNumber<unknown>;
|
|
109
|
+
bearing: z.ZodNumber;
|
|
110
|
+
speed: z.ZodNumber;
|
|
111
|
+
destination: z.ZodNullable<z.ZodString>;
|
|
112
|
+
delaySeconds: z.ZodNullable<z.ZodNumber>;
|
|
113
|
+
tripId: z.ZodNullable<z.ZodString>;
|
|
114
|
+
gtfsTripId: z.ZodNullable<z.ZodString>;
|
|
115
|
+
nextStopId: z.ZodNullable<z.ZodString>;
|
|
116
|
+
arrivalTimeSeconds: z.ZodNullable<z.ZodNumber>;
|
|
117
|
+
isStale: z.ZodBoolean;
|
|
118
|
+
measuredAt: z.ZodDate;
|
|
119
|
+
}, z.core.$strip>;
|
|
120
|
+
export type ValidatedVehicle = z.infer<typeof vehicleSchema>;
|
|
121
|
+
/**
|
|
122
|
+
* Safely parse a value with a Zod schema, returning null on failure.
|
|
123
|
+
*/
|
|
124
|
+
export declare function safeParse<T>(schema: z.ZodType<T>, data: unknown): T | null;
|
|
125
|
+
/**
|
|
126
|
+
* Parse a value with a Zod schema, throwing a detailed error on failure.
|
|
127
|
+
*/
|
|
128
|
+
export declare function parseOrThrow<T>(schema: z.ZodType<T>, data: unknown, context?: string): T;
|
|
129
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuCxB;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;iBAwBnB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAM1D;;;GAGG;AACH,eAAO,MAAM,sBAAsB,yMAUjC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,oBAAoB,4LAS/B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMrE;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;iBAWlB,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAMxD;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;iBAUjB,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAMtD;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;kBAqBpB,CAAC;AAEZ,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAMvE;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;iBAiBxB,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAM7D;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,GAAG,IAAI,CAG1E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAOxF"}
|