From 1093bede90e1fbe23aae45b0b8e5c71b8d384505 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Nov 2025 11:24:48 +0000 Subject: [PATCH] Add GTFS-RT time/delay support and refactor API to use unified filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements two major improvements to the library: 1. **GTFS-RT Time/Delay Support:** - Added arrival_time and departure_time fields to StopTimeRealtime type - Updated mergeRealtimeData in stop-times.ts to query and export both time and delay fields - Now exports complete GTFS-RT information as per specification 2. **Unified Filter-Based API:** - Removed all getXXXByYYYY methods in favor of unified getXXXX methods with optional filters - All filter parameters now support both single values and arrays - Simplified API surface and improved consistency: - stops.ts: Removed getStopById, getStopByCode, getStopsByTrip, getAllStops - routes.ts: Removed getRouteById, getRoutesByAgency, getAllRoutes - trips.ts: Removed getTripById, getTripsByRoute, getTripsByRouteAndService, getTripsByRouteServiceAndDirection, getTripsByService - agencies.ts: Removed getAgencyById, getAllAgencies - stop-times.ts: Removed getStopTimesByTrip, getStopTimesByStop, getStopTimesByStopAndTrips, getStopTimesForStopRouteDirection - rt-stop-time-updates.ts: Removed getStopTimeUpdatesByTripId - gtfs-sqljs.ts: Removed wrapper methods for getXXXByYYYY, updated to use filters - Kept specialized methods like searchStopsByName that can't be merged into filters 3. **Updated Tests:** - Migrated all tests to use new filter-based API - Added tests for array filter support - All 81 tests passing The new API is more flexible and consistent. For example: - Old: `getStopById('STOP1')` → New: `getStops({ stopId: 'STOP1' })[0]` - New: `getStops({ stopId: ['STOP1', 'STOP2'] })` for multiple stops Note: Documentation (README and website) still needs to be updated with the new API examples. --- dist/index.d.mts | 116 ++++----- dist/index.d.ts | 116 ++++----- dist/index.js | 367 +++++++++++++--------------- dist/index.js.map | 2 +- dist/index.mjs | 367 +++++++++++++--------------- dist/index.mjs.map | 2 +- src/gtfs-sqljs.ts | 128 +++------- src/queries/agencies.ts | 48 +--- src/queries/routes.ts | 77 ++---- src/queries/rt-stop-time-updates.ts | 42 ++-- src/queries/stop-times.ts | 180 ++++---------- src/queries/stops.ts | 137 +++-------- src/queries/trips.ts | 167 +++---------- src/types/gtfs-rt.ts | 2 + tests/gtfs-sqljs.test.ts | 74 ++++-- tests/sample-feed.test.ts | 42 ++-- 16 files changed, 711 insertions(+), 1156 deletions(-) diff --git a/dist/index.d.mts b/dist/index.d.mts index 5b1d3ca..4c91b69 100644 --- a/dist/index.d.mts +++ b/dist/index.d.mts @@ -144,7 +144,9 @@ interface TripUpdate { } interface StopTimeRealtime { arrival_delay?: number; + arrival_time?: number; departure_delay?: number; + departure_time?: number; schedule_relationship?: ScheduleRelationship; } interface TripRealtime { @@ -395,7 +397,7 @@ interface Attribution { */ interface AgencyFilters { - agencyId?: string; + agencyId?: string | string[]; limit?: number; } @@ -404,10 +406,10 @@ interface AgencyFilters { */ interface StopFilters { - stopId?: string; - stopCode?: string; + stopId?: string | string[]; + stopCode?: string | string[]; name?: string; - tripId?: string; + tripId?: string | string[]; limit?: number; } @@ -416,8 +418,8 @@ interface StopFilters { */ interface RouteFilters { - routeId?: string; - agencyId?: string; + routeId?: string | string[]; + agencyId?: string | string[]; limit?: number; } @@ -426,11 +428,11 @@ interface RouteFilters { */ interface TripFilters { - tripId?: string; - routeId?: string; - serviceIds?: string[]; - directionId?: number; - agencyId?: string; + tripId?: string | string[]; + routeId?: string | string[]; + serviceIds?: string | string[]; + directionId?: number | number[]; + agencyId?: string | string[]; includeRealtime?: boolean; limit?: number; } @@ -443,12 +445,12 @@ interface TripWithRealtime extends Trip { */ interface StopTimeFilters { - tripId?: string; - stopId?: string; - routeId?: string; - serviceIds?: string[]; - directionId?: number; - agencyId?: string; + tripId?: string | string[]; + stopId?: string | string[]; + routeId?: string | string[]; + serviceIds?: string | string[]; + directionId?: number | number[]; + agencyId?: string | string[]; includeRealtime?: boolean; limit?: number; } @@ -464,9 +466,9 @@ interface TripUpdateFilters { } interface StopTimeUpdateFilters { - tripId?: string; - stopId?: string; - stopSequence?: number; + tripId?: string | string[]; + stopId?: string | string[]; + stopSequence?: number | number[]; limit?: number; } /** @@ -546,28 +548,19 @@ declare class GtfsSqlJs { * Get direct access to the database (for advanced queries) */ getDatabase(): Database; - /** - * Get an agency by its agency_id - */ - getAgencyById(agencyId: string): Agency | null; /** * Get agencies with optional filters + * Pass agencyId filter to get a specific agency */ getAgencies(filters?: AgencyFilters): Agency[]; - /** - * Get a stop by its stop_id - */ - getStopById(stopId: string): Stop | null; /** * Get stops with optional filters + * Pass stopId filter to get a specific stop */ getStops(filters?: StopFilters): Stop[]; - /** - * Get a route by its route_id - */ - getRouteById(routeId: string): Route | null; /** * Get routes with optional filters + * Pass routeId filter to get a specific route */ getRoutes(filters?: RouteFilters): Route[]; /** @@ -586,19 +579,16 @@ declare class GtfsSqlJs { * Get calendar date exceptions for a specific date */ getCalendarDatesForDate(date: string): CalendarDate[]; - /** - * Get a trip by its trip_id - */ - getTripById(tripId: string): Trip | null; /** * Get trips with optional filters + * Pass tripId filter to get a specific trip * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) * @param filters.limit - Limit number of results * * @example @@ -608,24 +598,25 @@ declare class GtfsSqlJs { * @example * // Get all trips for a route going in one direction * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 }); + * + * @example + * // Get a specific trip + * const trips = gtfs.getTrips({ tripId: 'TRIP_123' }); */ getTrips(filters?: TripFilters & { date?: string; }): Trip[]; - /** - * Get stop times for a trip (ordered by stop_sequence) - */ - getStopTimesByTrip(tripId: string): StopTime[]; /** * Get stop times with optional filters * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.stopId - Filter by stop ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.stopId - Filter by stop ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) + * @param filters.includeRealtime - Include realtime data (delay and time fields) * @param filters.limit - Limit number of results * * @example @@ -639,6 +630,13 @@ declare class GtfsSqlJs { * routeId: 'ROUTE_1', * date: '20240115' * }); + * + * @example + * // Get stop times with realtime data + * const stopTimes = gtfs.getStopTimes({ + * tripId: 'TRIP_123', + * includeRealtime: true + * }); */ getStopTimes(filters?: StopTimeFilters & { date?: string; @@ -670,36 +668,24 @@ declare class GtfsSqlJs { clearRealtimeData(): void; /** * Get alerts with optional filters + * Pass alertId filter to get a specific alert */ getAlerts(filters?: AlertFilters): Alert[]; - /** - * Get alert by ID - */ - getAlertById(alertId: string): Alert | null; /** * Get vehicle positions with optional filters + * Pass tripId filter to get vehicle position for a specific trip */ getVehiclePositions(filters?: VehiclePositionFilters): VehiclePosition[]; - /** - * Get vehicle position by trip ID - */ - getVehiclePositionByTripId(tripId: string): VehiclePosition | null; /** * Get trip updates with optional filters + * Pass tripId filter to get trip update for a specific trip */ getTripUpdates(filters?: TripUpdateFilters): TripUpdate[]; - /** - * Get trip update by trip ID - */ - getTripUpdateByTripId(tripId: string): TripUpdate | null; /** * Get stop time updates with optional filters + * Pass tripId filter to get stop time updates for a specific trip */ getStopTimeUpdates(filters?: StopTimeUpdateFilters): StopTimeUpdate[]; - /** - * Get stop time updates for a specific trip - */ - getStopTimeUpdatesByTripId(tripId: string): StopTimeUpdate[]; /** * Export all alerts without staleness filtering (for debugging) */ diff --git a/dist/index.d.ts b/dist/index.d.ts index 5b1d3ca..4c91b69 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -144,7 +144,9 @@ interface TripUpdate { } interface StopTimeRealtime { arrival_delay?: number; + arrival_time?: number; departure_delay?: number; + departure_time?: number; schedule_relationship?: ScheduleRelationship; } interface TripRealtime { @@ -395,7 +397,7 @@ interface Attribution { */ interface AgencyFilters { - agencyId?: string; + agencyId?: string | string[]; limit?: number; } @@ -404,10 +406,10 @@ interface AgencyFilters { */ interface StopFilters { - stopId?: string; - stopCode?: string; + stopId?: string | string[]; + stopCode?: string | string[]; name?: string; - tripId?: string; + tripId?: string | string[]; limit?: number; } @@ -416,8 +418,8 @@ interface StopFilters { */ interface RouteFilters { - routeId?: string; - agencyId?: string; + routeId?: string | string[]; + agencyId?: string | string[]; limit?: number; } @@ -426,11 +428,11 @@ interface RouteFilters { */ interface TripFilters { - tripId?: string; - routeId?: string; - serviceIds?: string[]; - directionId?: number; - agencyId?: string; + tripId?: string | string[]; + routeId?: string | string[]; + serviceIds?: string | string[]; + directionId?: number | number[]; + agencyId?: string | string[]; includeRealtime?: boolean; limit?: number; } @@ -443,12 +445,12 @@ interface TripWithRealtime extends Trip { */ interface StopTimeFilters { - tripId?: string; - stopId?: string; - routeId?: string; - serviceIds?: string[]; - directionId?: number; - agencyId?: string; + tripId?: string | string[]; + stopId?: string | string[]; + routeId?: string | string[]; + serviceIds?: string | string[]; + directionId?: number | number[]; + agencyId?: string | string[]; includeRealtime?: boolean; limit?: number; } @@ -464,9 +466,9 @@ interface TripUpdateFilters { } interface StopTimeUpdateFilters { - tripId?: string; - stopId?: string; - stopSequence?: number; + tripId?: string | string[]; + stopId?: string | string[]; + stopSequence?: number | number[]; limit?: number; } /** @@ -546,28 +548,19 @@ declare class GtfsSqlJs { * Get direct access to the database (for advanced queries) */ getDatabase(): Database; - /** - * Get an agency by its agency_id - */ - getAgencyById(agencyId: string): Agency | null; /** * Get agencies with optional filters + * Pass agencyId filter to get a specific agency */ getAgencies(filters?: AgencyFilters): Agency[]; - /** - * Get a stop by its stop_id - */ - getStopById(stopId: string): Stop | null; /** * Get stops with optional filters + * Pass stopId filter to get a specific stop */ getStops(filters?: StopFilters): Stop[]; - /** - * Get a route by its route_id - */ - getRouteById(routeId: string): Route | null; /** * Get routes with optional filters + * Pass routeId filter to get a specific route */ getRoutes(filters?: RouteFilters): Route[]; /** @@ -586,19 +579,16 @@ declare class GtfsSqlJs { * Get calendar date exceptions for a specific date */ getCalendarDatesForDate(date: string): CalendarDate[]; - /** - * Get a trip by its trip_id - */ - getTripById(tripId: string): Trip | null; /** * Get trips with optional filters + * Pass tripId filter to get a specific trip * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) * @param filters.limit - Limit number of results * * @example @@ -608,24 +598,25 @@ declare class GtfsSqlJs { * @example * // Get all trips for a route going in one direction * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 }); + * + * @example + * // Get a specific trip + * const trips = gtfs.getTrips({ tripId: 'TRIP_123' }); */ getTrips(filters?: TripFilters & { date?: string; }): Trip[]; - /** - * Get stop times for a trip (ordered by stop_sequence) - */ - getStopTimesByTrip(tripId: string): StopTime[]; /** * Get stop times with optional filters * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.stopId - Filter by stop ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.stopId - Filter by stop ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) + * @param filters.includeRealtime - Include realtime data (delay and time fields) * @param filters.limit - Limit number of results * * @example @@ -639,6 +630,13 @@ declare class GtfsSqlJs { * routeId: 'ROUTE_1', * date: '20240115' * }); + * + * @example + * // Get stop times with realtime data + * const stopTimes = gtfs.getStopTimes({ + * tripId: 'TRIP_123', + * includeRealtime: true + * }); */ getStopTimes(filters?: StopTimeFilters & { date?: string; @@ -670,36 +668,24 @@ declare class GtfsSqlJs { clearRealtimeData(): void; /** * Get alerts with optional filters + * Pass alertId filter to get a specific alert */ getAlerts(filters?: AlertFilters): Alert[]; - /** - * Get alert by ID - */ - getAlertById(alertId: string): Alert | null; /** * Get vehicle positions with optional filters + * Pass tripId filter to get vehicle position for a specific trip */ getVehiclePositions(filters?: VehiclePositionFilters): VehiclePosition[]; - /** - * Get vehicle position by trip ID - */ - getVehiclePositionByTripId(tripId: string): VehiclePosition | null; /** * Get trip updates with optional filters + * Pass tripId filter to get trip update for a specific trip */ getTripUpdates(filters?: TripUpdateFilters): TripUpdate[]; - /** - * Get trip update by trip ID - */ - getTripUpdateByTripId(tripId: string): TripUpdate | null; /** * Get stop time updates with optional filters + * Pass tripId filter to get stop time updates for a specific trip */ getStopTimeUpdates(filters?: StopTimeUpdateFilters): StopTimeUpdate[]; - /** - * Get stop time updates for a specific trip - */ - getStopTimeUpdatesByTripId(tripId: string): StopTimeUpdate[]; /** * Export all alerts without staleness filtering (for debugging) */ diff --git a/dist/index.js b/dist/index.js index c362e34..4cda141 100644 --- a/dist/index.js +++ b/dist/index.js @@ -993,8 +993,12 @@ function getAgencies(db, filters = {}) { const conditions = []; const params = []; if (agencyId) { - conditions.push("agency_id = ?"); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => "?").join(", "); + conditions.push(`agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } let sql = "SELECT * FROM agency"; if (conditions.length > 0) { @@ -1017,17 +1021,6 @@ function getAgencies(db, filters = {}) { stmt.free(); return agencies; } -function getAgencyById(db, agencyId) { - const stmt = db.prepare("SELECT * FROM agency WHERE agency_id = ?"); - stmt.bind([agencyId]); - if (stmt.step()) { - const row = stmt.getAsObject(); - stmt.free(); - return rowToAgency(row); - } - stmt.free(); - return null; -} function rowToAgency(row) { return { agency_id: String(row.agency_id), @@ -1045,17 +1038,41 @@ function rowToAgency(row) { function getStops(db, filters = {}) { const { stopId, stopCode, name, tripId, limit } = filters; if (tripId) { - return getStopsByTrip(db, tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length === 0) return []; + const placeholders = tripIds.map(() => "?").join(", "); + const stmt2 = db.prepare(` + SELECT s.* FROM stops s + INNER JOIN stop_times st ON s.stop_id = st.stop_id + WHERE st.trip_id IN (${placeholders}) + ORDER BY st.stop_sequence + `); + stmt2.bind(tripIds); + const stops2 = []; + while (stmt2.step()) { + const row = stmt2.getAsObject(); + stops2.push(rowToStop(row)); + } + stmt2.free(); + return stops2; } const conditions = []; const params = []; if (stopId) { - conditions.push("stop_id = ?"); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => "?").join(", "); + conditions.push(`stop_id IN (${placeholders})`); + params.push(...stopIds); + } } if (stopCode) { - conditions.push("stop_code = ?"); - params.push(stopCode); + const stopCodes = Array.isArray(stopCode) ? stopCode : [stopCode]; + if (stopCodes.length > 0) { + const placeholders = stopCodes.map(() => "?").join(", "); + conditions.push(`stop_code IN (${placeholders})`); + params.push(...stopCodes); + } } if (name) { conditions.push("stop_name LIKE ?"); @@ -1082,33 +1099,6 @@ function getStops(db, filters = {}) { stmt.free(); return stops; } -function getStopById(db, stopId) { - const stmt = db.prepare("SELECT * FROM stops WHERE stop_id = ?"); - stmt.bind([stopId]); - if (stmt.step()) { - const row = stmt.getAsObject(); - stmt.free(); - return rowToStop(row); - } - stmt.free(); - return null; -} -function getStopsByTrip(db, tripId) { - const stmt = db.prepare(` - SELECT s.* FROM stops s - INNER JOIN stop_times st ON s.stop_id = st.stop_id - WHERE st.trip_id = ? - ORDER BY st.stop_sequence - `); - stmt.bind([tripId]); - const stops = []; - while (stmt.step()) { - const row = stmt.getAsObject(); - stops.push(rowToStop(row)); - } - stmt.free(); - return stops; -} function rowToStop(row) { return { stop_id: String(row.stop_id), @@ -1134,12 +1124,20 @@ function getRoutes(db, filters = {}) { const conditions = []; const params = []; if (routeId) { - conditions.push("route_id = ?"); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => "?").join(", "); + conditions.push(`route_id IN (${placeholders})`); + params.push(...routeIds); + } } if (agencyId) { - conditions.push("agency_id = ?"); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => "?").join(", "); + conditions.push(`agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } let sql = "SELECT * FROM routes"; if (conditions.length > 0) { @@ -1162,17 +1160,6 @@ function getRoutes(db, filters = {}) { stmt.free(); return routes; } -function getRouteById(db, routeId) { - const stmt = db.prepare("SELECT * FROM routes WHERE route_id = ?"); - stmt.bind([routeId]); - if (stmt.step()) { - const row = stmt.getAsObject(); - stmt.free(); - return rowToRoute(row); - } - stmt.free(); - return null; -} function rowToRoute(row) { return { route_id: String(row.route_id), @@ -1364,10 +1351,6 @@ function getVehiclePositions(db, filters = {}, stalenessThreshold = 120) { stmt.free(); return positions; } -function getVehiclePositionByTripId(db, tripId, stalenessThreshold = 120) { - const positions = getVehiclePositions(db, { tripId, limit: 1 }, stalenessThreshold); - return positions.length > 0 ? positions[0] : null; -} function getAllVehiclePositions(db) { const sql = "SELECT * FROM rt_vehicle_positions ORDER BY rt_last_updated DESC"; const stmt = db.prepare(sql); @@ -1437,25 +1420,44 @@ function getTrips(db, filters = {}, stalenessThreshold = 120) { const conditions = []; const params = []; if (tripId) { - conditions.push(needsRoutesJoin ? "t.trip_id = ?" : "trip_id = ?"); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => "?").join(", "); + conditions.push(needsRoutesJoin ? `t.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`); + params.push(...tripIds); + } } if (routeId) { - conditions.push(needsRoutesJoin ? "t.route_id = ?" : "route_id = ?"); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => "?").join(", "); + conditions.push(needsRoutesJoin ? `t.route_id IN (${placeholders})` : `route_id IN (${placeholders})`); + params.push(...routeIds); + } } - if (serviceIds && serviceIds.length > 0) { - const placeholders = serviceIds.map(() => "?").join(", "); - conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`); - params.push(...serviceIds); + if (serviceIds) { + const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds]; + if (serviceIdArray.length > 0) { + const placeholders = serviceIdArray.map(() => "?").join(", "); + conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`); + params.push(...serviceIdArray); + } } if (directionId !== void 0) { - conditions.push(needsRoutesJoin ? "t.direction_id = ?" : "direction_id = ?"); - params.push(directionId); + const directionIds = Array.isArray(directionId) ? directionId : [directionId]; + if (directionIds.length > 0) { + const placeholders = directionIds.map(() => "?").join(", "); + conditions.push(needsRoutesJoin ? `t.direction_id IN (${placeholders})` : `direction_id IN (${placeholders})`); + params.push(...directionIds); + } } if (agencyId) { - conditions.push("r.agency_id = ?"); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => "?").join(", "); + conditions.push(`r.agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } let sql = needsRoutesJoin ? "SELECT t.* FROM trips t INNER JOIN routes r ON t.route_id = r.route_id" : "SELECT * FROM trips"; if (conditions.length > 0) { @@ -1480,17 +1482,6 @@ function getTrips(db, filters = {}, stalenessThreshold = 120) { } return trips; } -function getTripById(db, tripId) { - const stmt = db.prepare("SELECT * FROM trips WHERE trip_id = ?"); - stmt.bind([tripId]); - if (stmt.step()) { - const row = stmt.getAsObject(); - stmt.free(); - return rowToTrip(row); - } - stmt.free(); - return null; -} function rowToTrip(row) { return { trip_id: String(row.trip_id), @@ -1515,7 +1506,7 @@ function mergeRealtimeData2(stopTimes, db, stalenessThreshold) { const placeholders = tripIds.map(() => "?").join(", "); const stmt = db.prepare(` SELECT trip_id, stop_sequence, stop_id, - arrival_delay, departure_delay, schedule_relationship + arrival_delay, arrival_time, departure_delay, departure_time, schedule_relationship FROM rt_stop_time_updates WHERE trip_id IN (${placeholders}) AND rt_last_updated >= ? @@ -1527,7 +1518,9 @@ function mergeRealtimeData2(stopTimes, db, stalenessThreshold) { const key = `${row.trip_id}_${row.stop_sequence}`; rtMap.set(key, { arrival_delay: row.arrival_delay !== null ? Number(row.arrival_delay) : void 0, + arrival_time: row.arrival_time !== null ? Number(row.arrival_time) : void 0, departure_delay: row.departure_delay !== null ? Number(row.departure_delay) : void 0, + departure_time: row.departure_time !== null ? Number(row.departure_time) : void 0, schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : void 0 }); } @@ -1548,29 +1541,52 @@ function getStopTimes(db, filters = {}, stalenessThreshold = 120) { const conditions = []; const params = []; if (tripId) { - conditions.push(needsTripsJoin ? "st.trip_id = ?" : "trip_id = ?"); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => "?").join(", "); + conditions.push(needsTripsJoin ? `st.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`); + params.push(...tripIds); + } } if (stopId) { - conditions.push(needsTripsJoin ? "st.stop_id = ?" : "stop_id = ?"); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => "?").join(", "); + conditions.push(needsTripsJoin ? `st.stop_id IN (${placeholders})` : `stop_id IN (${placeholders})`); + params.push(...stopIds); + } } if (routeId) { - conditions.push("t.route_id = ?"); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => "?").join(", "); + conditions.push(`t.route_id IN (${placeholders})`); + params.push(...routeIds); + } } - if (serviceIds && serviceIds.length > 0) { - const placeholders = serviceIds.map(() => "?").join(", "); - conditions.push(`t.service_id IN (${placeholders})`); - params.push(...serviceIds); + if (serviceIds) { + const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds]; + if (serviceIdArray.length > 0) { + const placeholders = serviceIdArray.map(() => "?").join(", "); + conditions.push(`t.service_id IN (${placeholders})`); + params.push(...serviceIdArray); + } } if (directionId !== void 0) { - conditions.push("t.direction_id = ?"); - params.push(directionId); + const directionIds = Array.isArray(directionId) ? directionId : [directionId]; + if (directionIds.length > 0) { + const placeholders = directionIds.map(() => "?").join(", "); + conditions.push(`t.direction_id IN (${placeholders})`); + params.push(...directionIds); + } } if (agencyId) { - conditions.push("r.agency_id = ?"); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => "?").join(", "); + conditions.push(`r.agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } let sql; if (needsRoutesJoin) { @@ -1603,17 +1619,6 @@ function getStopTimes(db, filters = {}, stalenessThreshold = 120) { } return stopTimes; } -function getStopTimesByTrip(db, tripId) { - const stmt = db.prepare("SELECT * FROM stop_times WHERE trip_id = ? ORDER BY stop_sequence"); - stmt.bind([tripId]); - const stopTimes = []; - while (stmt.step()) { - const row = stmt.getAsObject(); - stopTimes.push(rowToStopTime(row)); - } - stmt.free(); - return stopTimes; -} function rowToStopTime(row) { return { trip_id: String(row.trip_id), @@ -1734,10 +1739,6 @@ function getAlerts(db, filters = {}, stalenessThreshold = 120) { stmt.free(); return alerts; } -function getAlertById(db, alertId, stalenessThreshold = 120) { - const alerts = getAlerts(db, { alertId, limit: 1 }, stalenessThreshold); - return alerts.length > 0 ? alerts[0] : null; -} function getAllAlerts(db) { const sql = "SELECT * FROM rt_alerts ORDER BY rt_last_updated DESC"; const stmt = db.prepare(sql); @@ -1812,10 +1813,6 @@ function getTripUpdates(db, filters = {}, stalenessThreshold = 120) { stmt.free(); return tripUpdates; } -function getTripUpdateByTripId(db, tripId, stalenessThreshold = 120) { - const updates = getTripUpdates(db, { tripId, limit: 1 }, stalenessThreshold); - return updates.length > 0 ? updates[0] : null; -} function getAllTripUpdates(db) { const sql = "SELECT * FROM rt_trip_updates ORDER BY rt_last_updated DESC"; const stmt = db.prepare(sql); @@ -1862,16 +1859,28 @@ function getStopTimeUpdates(db, filters = {}, stalenessThreshold = 120) { const conditions = []; const params = []; if (tripId) { - conditions.push("trip_id = ?"); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => "?").join(", "); + conditions.push(`trip_id IN (${placeholders})`); + params.push(...tripIds); + } } if (stopId) { - conditions.push("stop_id = ?"); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => "?").join(", "); + conditions.push(`stop_id IN (${placeholders})`); + params.push(...stopIds); + } } if (stopSequence !== void 0) { - conditions.push("stop_sequence = ?"); - params.push(stopSequence); + const stopSequences = Array.isArray(stopSequence) ? stopSequence : [stopSequence]; + if (stopSequences.length > 0) { + const placeholders = stopSequences.map(() => "?").join(", "); + conditions.push(`stop_sequence IN (${placeholders})`); + params.push(...stopSequences); + } } const now = Math.floor(Date.now() / 1e3); const staleThreshold = now - stalenessThreshold; @@ -1898,9 +1907,6 @@ function getStopTimeUpdates(db, filters = {}, stalenessThreshold = 120) { stmt.free(); return stopTimeUpdates; } -function getStopTimeUpdatesByTripId(db, tripId, stalenessThreshold = 120) { - return getStopTimeUpdates(db, { tripId }, stalenessThreshold); -} function getAllStopTimeUpdates(db) { const sql = "SELECT * FROM rt_stop_time_updates ORDER BY trip_id, stop_sequence"; const stmt = db.prepare(sql); @@ -2005,45 +2011,27 @@ var GtfsSqlJs = class _GtfsSqlJs { return this.db; } // ==================== Agency Methods ==================== - /** - * Get an agency by its agency_id - */ - getAgencyById(agencyId) { - if (!this.db) throw new Error("Database not initialized"); - return getAgencyById(this.db, agencyId); - } /** * Get agencies with optional filters + * Pass agencyId filter to get a specific agency */ getAgencies(filters) { if (!this.db) throw new Error("Database not initialized"); return getAgencies(this.db, filters); } // ==================== Stop Methods ==================== - /** - * Get a stop by its stop_id - */ - getStopById(stopId) { - if (!this.db) throw new Error("Database not initialized"); - return getStopById(this.db, stopId); - } /** * Get stops with optional filters + * Pass stopId filter to get a specific stop */ getStops(filters) { if (!this.db) throw new Error("Database not initialized"); return getStops(this.db, filters); } // ==================== Route Methods ==================== - /** - * Get a route by its route_id - */ - getRouteById(routeId) { - if (!this.db) throw new Error("Database not initialized"); - return getRouteById(this.db, routeId); - } /** * Get routes with optional filters + * Pass routeId filter to get a specific route */ getRoutes(filters) { if (!this.db) throw new Error("Database not initialized"); @@ -2079,22 +2067,16 @@ var GtfsSqlJs = class _GtfsSqlJs { return getCalendarDatesForDate(this.db, date); } // ==================== Trip Methods ==================== - /** - * Get a trip by its trip_id - */ - getTripById(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getTripById(this.db, tripId); - } /** * Get trips with optional filters + * Pass tripId filter to get a specific trip * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) * @param filters.limit - Limit number of results * * @example @@ -2104,6 +2086,10 @@ var GtfsSqlJs = class _GtfsSqlJs { * @example * // Get all trips for a route going in one direction * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 }); + * + * @example + * // Get a specific trip + * const trips = gtfs.getTrips({ tripId: 'TRIP_123' }); */ getTrips(filters) { if (!this.db) throw new Error("Database not initialized"); @@ -2116,23 +2102,17 @@ var GtfsSqlJs = class _GtfsSqlJs { return getTrips(this.db, finalFilters, this.stalenessThreshold); } // ==================== Stop Time Methods ==================== - /** - * Get stop times for a trip (ordered by stop_sequence) - */ - getStopTimesByTrip(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getStopTimesByTrip(this.db, tripId); - } /** * Get stop times with optional filters * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.stopId - Filter by stop ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.stopId - Filter by stop ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) + * @param filters.includeRealtime - Include realtime data (delay and time fields) * @param filters.limit - Limit number of results * * @example @@ -2146,6 +2126,13 @@ var GtfsSqlJs = class _GtfsSqlJs { * routeId: 'ROUTE_1', * date: '20240115' * }); + * + * @example + * // Get stop times with realtime data + * const stopTimes = gtfs.getStopTimes({ + * tripId: 'TRIP_123', + * includeRealtime: true + * }); */ getStopTimes(filters) { if (!this.db) throw new Error("Database not initialized"); @@ -2203,60 +2190,36 @@ var GtfsSqlJs = class _GtfsSqlJs { } /** * Get alerts with optional filters + * Pass alertId filter to get a specific alert */ getAlerts(filters) { if (!this.db) throw new Error("Database not initialized"); return getAlerts(this.db, filters, this.stalenessThreshold); } - /** - * Get alert by ID - */ - getAlertById(alertId) { - if (!this.db) throw new Error("Database not initialized"); - return getAlertById(this.db, alertId, this.stalenessThreshold); - } /** * Get vehicle positions with optional filters + * Pass tripId filter to get vehicle position for a specific trip */ getVehiclePositions(filters) { if (!this.db) throw new Error("Database not initialized"); return getVehiclePositions(this.db, filters, this.stalenessThreshold); } - /** - * Get vehicle position by trip ID - */ - getVehiclePositionByTripId(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getVehiclePositionByTripId(this.db, tripId, this.stalenessThreshold); - } /** * Get trip updates with optional filters + * Pass tripId filter to get trip update for a specific trip */ getTripUpdates(filters) { if (!this.db) throw new Error("Database not initialized"); return getTripUpdates(this.db, filters, this.stalenessThreshold); } - /** - * Get trip update by trip ID - */ - getTripUpdateByTripId(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getTripUpdateByTripId(this.db, tripId, this.stalenessThreshold); - } /** * Get stop time updates with optional filters + * Pass tripId filter to get stop time updates for a specific trip */ getStopTimeUpdates(filters) { if (!this.db) throw new Error("Database not initialized"); return getStopTimeUpdates(this.db, filters, this.stalenessThreshold); } - /** - * Get stop time updates for a specific trip - */ - getStopTimeUpdatesByTripId(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getStopTimeUpdatesByTripId(this.db, tripId, this.stalenessThreshold); - } // ==================== Debug Export Methods ==================== // These methods export all realtime data without staleness filtering // for debugging purposes diff --git a/dist/index.js.map b/dist/index.js.map index 49b8952..39a6c11 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/index.ts","../src/gtfs-sqljs.ts","../src/schema/schema.ts","../src/loaders/zip-loader.ts","../src/loaders/csv-parser.ts","../src/loaders/data-loader.ts","../src/schema/gtfs-rt-schema.ts","../src/loaders/gtfs-rt-loader.ts","../src/queries/agencies.ts","../src/queries/stops.ts","../src/queries/routes.ts","../src/queries/calendar.ts","../src/queries/rt-vehicle-positions.ts","../src/queries/trips.ts","../src/queries/stop-times.ts","../src/queries/rt-alerts.ts","../src/queries/rt-trip-updates.ts","../src/queries/rt-stop-time-updates.ts","../src/types/gtfs-rt.ts"],"sourcesContent":["/**\n * gtfs-sqljs - Load GTFS data into sql.js SQLite database\n * @author Théophile Helleboid/SysDevRun\n * @license MIT\n */\n\nexport {\n GtfsSqlJs,\n type GtfsSqlJsOptions,\n type AgencyFilters,\n type StopFilters,\n type RouteFilters,\n type TripFilters,\n type StopTimeFilters,\n type AlertFilters,\n type VehiclePositionFilters,\n type TripUpdateFilters,\n type StopTimeUpdateFilters,\n type Alert,\n type VehiclePosition,\n type TripUpdate,\n type StopTimeUpdateWithMetadata,\n type TripWithRealtime,\n type StopTimeWithRealtime\n} from './gtfs-sqljs';\n\n// Export GTFS types\nexport type {\n Agency,\n Stop,\n Route,\n Trip,\n StopTime,\n Calendar,\n CalendarDate,\n FareAttribute,\n FareRule,\n Shape,\n Frequency,\n Transfer,\n Pathway,\n Level,\n FeedInfo,\n Attribution,\n} from './types/gtfs';\n\n// Export GTFS-RT types\nexport type {\n TranslatedString,\n EntitySelector,\n TimeRange,\n Position,\n VehicleDescriptor,\n StopTimeEvent,\n StopTimeUpdate,\n StopTimeRealtime,\n TripRealtime,\n RealtimeConfig\n} from './types/gtfs-rt';\n\n// Export GTFS-RT enums\nexport {\n ScheduleRelationship,\n VehicleStopStatus,\n CongestionLevel,\n OccupancyStatus,\n AlertCause,\n AlertEffect\n} from './types/gtfs-rt';\n\n// Export schema definitions for advanced use\nexport { GTFS_SCHEMA, type TableSchema, type ColumnDefinition, type IndexDefinition } from './schema/schema';\n","/**\n * Main GtfsSqlJs Class\n */\n\nimport initSqlJs, { type Database, type SqlJsStatic } from 'sql.js';\nimport { getAllCreateStatements } from './schema/schema';\nimport { loadGTFSZip } from './loaders/zip-loader';\nimport { loadGTFSData } from './loaders/data-loader';\nimport { createRealtimeTables, clearRealtimeData as clearRTData } from './schema/gtfs-rt-schema';\nimport { loadRealtimeData } from './loaders/gtfs-rt-loader';\n\n// Query methods\nimport { getAgencyById, getAgencies, type AgencyFilters } from './queries/agencies';\nimport { getStopById, getStops, type StopFilters } from './queries/stops';\nimport { getRouteById, getRoutes, type RouteFilters } from './queries/routes';\nimport {\n getActiveServiceIds,\n getCalendarByServiceId,\n getCalendarDates,\n getCalendarDatesForDate,\n} from './queries/calendar';\nimport { getTripById, getTrips, type TripFilters, type TripWithRealtime } from './queries/trips';\nimport { getStopTimesByTrip, getStopTimes, type StopTimeFilters, type StopTimeWithRealtime } from './queries/stop-times';\nimport { getAlerts as getAlertsQuery, getAlertById, getAllAlerts, type AlertFilters } from './queries/rt-alerts';\nimport { getVehiclePositions as getVehiclePositionsQuery, getVehiclePositionByTripId, getAllVehiclePositions, type VehiclePositionFilters } from './queries/rt-vehicle-positions';\nimport { getTripUpdates, getTripUpdateByTripId, getAllTripUpdates, type TripUpdateFilters } from './queries/rt-trip-updates';\nimport { getStopTimeUpdates, getStopTimeUpdatesByTripId, getAllStopTimeUpdates, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata } from './queries/rt-stop-time-updates';\n\n// Types\nimport type { Agency, Stop, Route, Trip, StopTime, Calendar, CalendarDate } from './types/gtfs';\nimport type { Alert, VehiclePosition, TripUpdate } from './types/gtfs-rt';\n\n// Export filter types for users\nexport type { AgencyFilters, StopFilters, RouteFilters, TripFilters, StopTimeFilters, AlertFilters, VehiclePositionFilters, TripUpdateFilters, StopTimeUpdateFilters };\n// Export RT types\nexport type { Alert, VehiclePosition, TripUpdate, StopTimeUpdateWithMetadata, TripWithRealtime, StopTimeWithRealtime };\n\nexport interface GtfsSqlJsOptions {\n /**\n * Path or URL to GTFS ZIP file\n */\n zipPath?: string;\n\n /**\n * Pre-loaded SQLite database as ArrayBuffer\n */\n database?: ArrayBuffer;\n\n /**\n * Optional: Custom SQL.js instance\n */\n SQL?: SqlJsStatic;\n\n /**\n * Optional: Path to SQL.js WASM file (for custom loading)\n */\n locateFile?: (filename: string) => string;\n\n /**\n * Optional: Array of GTFS filenames to skip importing (e.g., ['shapes.txt'])\n * Tables will be created but no data will be imported for these files\n */\n skipFiles?: string[];\n\n /**\n * Optional: Array of GTFS-RT feed URLs for realtime data\n */\n realtimeFeedUrls?: string[];\n\n /**\n * Optional: Staleness threshold in seconds (default: 120)\n * Realtime data older than this will be excluded from queries\n */\n stalenessThreshold?: number;\n}\n\nexport class GtfsSqlJs {\n private db: Database | null = null;\n private SQL: SqlJsStatic | null = null;\n private realtimeFeedUrls: string[] = [];\n private stalenessThreshold: number = 120;\n\n /**\n * Private constructor - use static factory methods instead\n */\n private constructor() {}\n\n /**\n * Create GtfsSqlJs instance from GTFS ZIP file\n */\n static async fromZip(\n zipPath: string,\n options: Omit = {}\n ): Promise {\n const instance = new GtfsSqlJs();\n await instance.initFromZip(zipPath, options);\n return instance;\n }\n\n /**\n * Create GtfsSqlJs instance from existing SQLite database\n */\n static async fromDatabase(\n database: ArrayBuffer,\n options: Omit = {}\n ): Promise {\n const instance = new GtfsSqlJs();\n await instance.initFromDatabase(database, options);\n return instance;\n }\n\n /**\n * Initialize from ZIP file\n */\n private async initFromZip(zipPath: string, options: Omit): Promise {\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Create new database\n this.db = new this.SQL.Database();\n\n // Create GTFS schema\n const createStatements = getAllCreateStatements();\n for (const statement of createStatements) {\n this.db.run(statement);\n }\n\n // Create GTFS-RT tables\n createRealtimeTables(this.db);\n\n // Load GTFS data\n const files = await loadGTFSZip(zipPath);\n await loadGTFSData(this.db, files, options.skipFiles);\n\n // Set RT configuration\n if (options.realtimeFeedUrls) {\n this.realtimeFeedUrls = options.realtimeFeedUrls;\n }\n if (options.stalenessThreshold !== undefined) {\n this.stalenessThreshold = options.stalenessThreshold;\n }\n }\n\n /**\n * Initialize from existing database\n */\n private async initFromDatabase(\n database: ArrayBuffer,\n options: Omit\n ): Promise {\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Load existing database\n this.db = new this.SQL.Database(new Uint8Array(database));\n\n // Ensure RT tables exist (in case loading old database)\n createRealtimeTables(this.db);\n\n // Set RT configuration\n if (options.realtimeFeedUrls) {\n this.realtimeFeedUrls = options.realtimeFeedUrls;\n }\n if (options.stalenessThreshold !== undefined) {\n this.stalenessThreshold = options.stalenessThreshold;\n }\n }\n\n /**\n * Export database to ArrayBuffer\n */\n export(): ArrayBuffer {\n if (!this.db) {\n throw new Error('Database not initialized');\n }\n\n const data = this.db.export();\n // Create a new ArrayBuffer and copy the data to ensure proper type\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n return buffer;\n }\n\n /**\n * Close the database connection\n */\n close(): void {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n\n /**\n * Get direct access to the database (for advanced queries)\n */\n getDatabase(): Database {\n if (!this.db) {\n throw new Error('Database not initialized');\n }\n return this.db;\n }\n\n // ==================== Agency Methods ====================\n\n /**\n * Get an agency by its agency_id\n */\n getAgencyById(agencyId: string): Agency | null {\n if (!this.db) throw new Error('Database not initialized');\n return getAgencyById(this.db, agencyId);\n }\n\n /**\n * Get agencies with optional filters\n */\n getAgencies(filters?: AgencyFilters): Agency[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAgencies(this.db, filters);\n }\n\n // ==================== Stop Methods ====================\n\n /**\n * Get a stop by its stop_id\n */\n getStopById(stopId: string): Stop | null {\n if (!this.db) throw new Error('Database not initialized');\n return getStopById(this.db, stopId);\n }\n\n /**\n * Get stops with optional filters\n */\n getStops(filters?: StopFilters): Stop[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStops(this.db, filters);\n }\n\n // ==================== Route Methods ====================\n\n /**\n * Get a route by its route_id\n */\n getRouteById(routeId: string): Route | null {\n if (!this.db) throw new Error('Database not initialized');\n return getRouteById(this.db, routeId);\n }\n\n /**\n * Get routes with optional filters\n */\n getRoutes(filters?: RouteFilters): Route[] {\n if (!this.db) throw new Error('Database not initialized');\n return getRoutes(this.db, filters);\n }\n\n // ==================== Calendar Methods ====================\n\n /**\n * Get active service IDs for a given date (YYYYMMDD format)\n */\n getActiveServiceIds(date: string): string[] {\n if (!this.db) throw new Error('Database not initialized');\n return getActiveServiceIds(this.db, date);\n }\n\n /**\n * Get calendar entry by service_id\n */\n getCalendarByServiceId(serviceId: string): Calendar | null {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarByServiceId(this.db, serviceId);\n }\n\n /**\n * Get calendar date exceptions for a service\n */\n getCalendarDates(serviceId: string): CalendarDate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarDates(this.db, serviceId);\n }\n\n /**\n * Get calendar date exceptions for a specific date\n */\n getCalendarDatesForDate(date: string): CalendarDate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarDatesForDate(this.db, date);\n }\n\n // ==================== Trip Methods ====================\n\n /**\n * Get a trip by its trip_id\n */\n getTripById(tripId: string): Trip | null {\n if (!this.db) throw new Error('Database not initialized');\n return getTripById(this.db, tripId);\n }\n\n /**\n * Get trips with optional filters\n *\n * @param filters - Optional filters\n * @param filters.tripId - Filter by trip ID\n * @param filters.routeId - Filter by route ID\n * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date\n * @param filters.directionId - Filter by direction ID\n * @param filters.agencyId - Filter by agency ID\n * @param filters.limit - Limit number of results\n *\n * @example\n * // Get all trips for a route on a specific date\n * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', date: '20240115' });\n *\n * @example\n * // Get all trips for a route going in one direction\n * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 });\n */\n getTrips(filters?: TripFilters & { date?: string }): Trip[] {\n if (!this.db) throw new Error('Database not initialized');\n\n // Handle date parameter by converting it to serviceIds\n const { date, ...restFilters } = filters || {};\n const finalFilters = { ...restFilters };\n\n if (date) {\n const serviceIds = getActiveServiceIds(this.db, date);\n finalFilters.serviceIds = serviceIds;\n }\n\n return getTrips(this.db, finalFilters, this.stalenessThreshold);\n }\n\n // ==================== Stop Time Methods ====================\n\n /**\n * Get stop times for a trip (ordered by stop_sequence)\n */\n getStopTimesByTrip(tripId: string): StopTime[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStopTimesByTrip(this.db, tripId);\n }\n\n /**\n * Get stop times with optional filters\n *\n * @param filters - Optional filters\n * @param filters.tripId - Filter by trip ID\n * @param filters.stopId - Filter by stop ID\n * @param filters.routeId - Filter by route ID\n * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date\n * @param filters.directionId - Filter by direction ID\n * @param filters.agencyId - Filter by agency ID\n * @param filters.limit - Limit number of results\n *\n * @example\n * // Get stop times for a specific trip\n * const stopTimes = gtfs.getStopTimes({ tripId: 'TRIP_123' });\n *\n * @example\n * // Get stop times at a stop for a specific route on a date\n * const stopTimes = gtfs.getStopTimes({\n * stopId: 'STOP_123',\n * routeId: 'ROUTE_1',\n * date: '20240115'\n * });\n */\n getStopTimes(filters?: StopTimeFilters & { date?: string }): StopTime[] {\n if (!this.db) throw new Error('Database not initialized');\n\n // Handle date parameter by converting it to serviceIds\n const { date, ...restFilters } = filters || {};\n const finalFilters = { ...restFilters };\n\n if (date) {\n const serviceIds = getActiveServiceIds(this.db, date);\n finalFilters.serviceIds = serviceIds;\n }\n\n return getStopTimes(this.db, finalFilters, this.stalenessThreshold);\n }\n\n // ==================== Realtime Methods ====================\n\n /**\n * Set GTFS-RT feed URLs\n */\n setRealtimeFeedUrls(urls: string[]): void {\n this.realtimeFeedUrls = urls;\n }\n\n /**\n * Get currently configured GTFS-RT feed URLs\n */\n getRealtimeFeedUrls(): string[] {\n return [...this.realtimeFeedUrls];\n }\n\n /**\n * Set staleness threshold in seconds\n */\n setStalenessThreshold(seconds: number): void {\n this.stalenessThreshold = seconds;\n }\n\n /**\n * Get current staleness threshold\n */\n getStalenessThreshold(): number {\n return this.stalenessThreshold;\n }\n\n /**\n * Fetch and load GTFS Realtime data from configured feed URLs or provided URLs\n * @param urls - Optional array of feed URLs. If not provided, uses configured feed URLs\n */\n async fetchRealtimeData(urls?: string[]): Promise {\n if (!this.db) throw new Error('Database not initialized');\n\n const feedUrls = urls || this.realtimeFeedUrls;\n if (feedUrls.length === 0) {\n throw new Error('No realtime feed URLs configured. Use setRealtimeFeedUrls() or pass urls parameter.');\n }\n\n await loadRealtimeData(this.db, feedUrls);\n }\n\n /**\n * Clear all realtime data from the database\n */\n clearRealtimeData(): void {\n if (!this.db) throw new Error('Database not initialized');\n clearRTData(this.db);\n }\n\n /**\n * Get alerts with optional filters\n */\n getAlerts(filters?: AlertFilters): Alert[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAlertsQuery(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get alert by ID\n */\n getAlertById(alertId: string): Alert | null {\n if (!this.db) throw new Error('Database not initialized');\n return getAlertById(this.db, alertId, this.stalenessThreshold);\n }\n\n /**\n * Get vehicle positions with optional filters\n */\n getVehiclePositions(filters?: VehiclePositionFilters): VehiclePosition[] {\n if (!this.db) throw new Error('Database not initialized');\n return getVehiclePositionsQuery(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get vehicle position by trip ID\n */\n getVehiclePositionByTripId(tripId: string): VehiclePosition | null {\n if (!this.db) throw new Error('Database not initialized');\n return getVehiclePositionByTripId(this.db, tripId, this.stalenessThreshold);\n }\n\n /**\n * Get trip updates with optional filters\n */\n getTripUpdates(filters?: TripUpdateFilters): TripUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getTripUpdates(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get trip update by trip ID\n */\n getTripUpdateByTripId(tripId: string): TripUpdate | null {\n if (!this.db) throw new Error('Database not initialized');\n return getTripUpdateByTripId(this.db, tripId, this.stalenessThreshold);\n }\n\n /**\n * Get stop time updates with optional filters\n */\n getStopTimeUpdates(filters?: StopTimeUpdateFilters): import('./types/gtfs-rt').StopTimeUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStopTimeUpdates(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get stop time updates for a specific trip\n */\n getStopTimeUpdatesByTripId(tripId: string): import('./types/gtfs-rt').StopTimeUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStopTimeUpdatesByTripId(this.db, tripId, this.stalenessThreshold);\n }\n\n // ==================== Debug Export Methods ====================\n // These methods export all realtime data without staleness filtering\n // for debugging purposes\n\n /**\n * Export all alerts without staleness filtering (for debugging)\n */\n debugExportAllAlerts(): Alert[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllAlerts(this.db);\n }\n\n /**\n * Export all vehicle positions without staleness filtering (for debugging)\n */\n debugExportAllVehiclePositions(): VehiclePosition[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllVehiclePositions(this.db);\n }\n\n /**\n * Export all trip updates without staleness filtering (for debugging)\n */\n debugExportAllTripUpdates(): TripUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllTripUpdates(this.db);\n }\n\n /**\n * Export all stop time updates without staleness filtering (for debugging)\n * Returns extended type with trip_id and rt_last_updated for debugging purposes\n */\n debugExportAllStopTimeUpdates(): StopTimeUpdateWithMetadata[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllStopTimeUpdates(this.db);\n }\n}\n","/**\n * SQLite Schema Definitions for GTFS Data\n * Matches required/optional fields from GTFS specification\n */\n\nexport interface TableSchema {\n name: string;\n columns: ColumnDefinition[];\n indexes?: IndexDefinition[];\n}\n\nexport interface ColumnDefinition {\n name: string;\n type: 'TEXT' | 'INTEGER' | 'REAL';\n required: boolean;\n primaryKey?: boolean;\n}\n\nexport interface IndexDefinition {\n name: string;\n columns: string[];\n unique?: boolean;\n}\n\nexport const GTFS_SCHEMA: TableSchema[] = [\n {\n name: 'agency',\n columns: [\n { name: 'agency_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'agency_name', type: 'TEXT', required: true },\n { name: 'agency_url', type: 'TEXT', required: true },\n { name: 'agency_timezone', type: 'TEXT', required: true },\n { name: 'agency_lang', type: 'TEXT', required: false },\n { name: 'agency_phone', type: 'TEXT', required: false },\n { name: 'agency_fare_url', type: 'TEXT', required: false },\n { name: 'agency_email', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'stops',\n columns: [\n { name: 'stop_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'stop_name', type: 'TEXT', required: true },\n { name: 'stop_lat', type: 'REAL', required: true },\n { name: 'stop_lon', type: 'REAL', required: true },\n { name: 'stop_code', type: 'TEXT', required: false },\n { name: 'stop_desc', type: 'TEXT', required: false },\n { name: 'zone_id', type: 'TEXT', required: false },\n { name: 'stop_url', type: 'TEXT', required: false },\n { name: 'location_type', type: 'INTEGER', required: false },\n { name: 'parent_station', type: 'TEXT', required: false },\n { name: 'stop_timezone', type: 'TEXT', required: false },\n { name: 'wheelchair_boarding', type: 'INTEGER', required: false },\n { name: 'level_id', type: 'TEXT', required: false },\n { name: 'platform_code', type: 'TEXT', required: false },\n ],\n indexes: [\n { name: 'idx_stops_stop_code', columns: ['stop_code'] },\n { name: 'idx_stops_stop_name', columns: ['stop_name'] },\n { name: 'idx_stops_parent_station', columns: ['parent_station'] },\n ],\n },\n {\n name: 'routes',\n columns: [\n { name: 'route_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'route_short_name', type: 'TEXT', required: true },\n { name: 'route_long_name', type: 'TEXT', required: true },\n { name: 'route_type', type: 'INTEGER', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'route_desc', type: 'TEXT', required: false },\n { name: 'route_url', type: 'TEXT', required: false },\n { name: 'route_color', type: 'TEXT', required: false },\n { name: 'route_text_color', type: 'TEXT', required: false },\n { name: 'route_sort_order', type: 'INTEGER', required: false },\n { name: 'continuous_pickup', type: 'INTEGER', required: false },\n { name: 'continuous_drop_off', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_routes_agency_id', columns: ['agency_id'] },\n ],\n },\n {\n name: 'trips',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'route_id', type: 'TEXT', required: true },\n { name: 'service_id', type: 'TEXT', required: true },\n { name: 'trip_headsign', type: 'TEXT', required: false },\n { name: 'trip_short_name', type: 'TEXT', required: false },\n { name: 'direction_id', type: 'INTEGER', required: false },\n { name: 'block_id', type: 'TEXT', required: false },\n { name: 'shape_id', type: 'TEXT', required: false },\n { name: 'wheelchair_accessible', type: 'INTEGER', required: false },\n { name: 'bikes_allowed', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_trips_route_id', columns: ['route_id'] },\n { name: 'idx_trips_service_id', columns: ['service_id'] },\n { name: 'idx_trips_route_service', columns: ['route_id', 'service_id'] },\n ],\n },\n {\n name: 'stop_times',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true },\n { name: 'arrival_time', type: 'TEXT', required: true },\n { name: 'departure_time', type: 'TEXT', required: true },\n { name: 'stop_id', type: 'TEXT', required: true },\n { name: 'stop_sequence', type: 'INTEGER', required: true },\n { name: 'stop_headsign', type: 'TEXT', required: false },\n { name: 'pickup_type', type: 'INTEGER', required: false },\n { name: 'drop_off_type', type: 'INTEGER', required: false },\n { name: 'continuous_pickup', type: 'INTEGER', required: false },\n { name: 'continuous_drop_off', type: 'INTEGER', required: false },\n { name: 'shape_dist_traveled', type: 'REAL', required: false },\n { name: 'timepoint', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_stop_times_trip_id', columns: ['trip_id'] },\n { name: 'idx_stop_times_stop_id', columns: ['stop_id'] },\n { name: 'idx_stop_times_trip_sequence', columns: ['trip_id', 'stop_sequence'] },\n ],\n },\n {\n name: 'calendar',\n columns: [\n { name: 'service_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'monday', type: 'INTEGER', required: true },\n { name: 'tuesday', type: 'INTEGER', required: true },\n { name: 'wednesday', type: 'INTEGER', required: true },\n { name: 'thursday', type: 'INTEGER', required: true },\n { name: 'friday', type: 'INTEGER', required: true },\n { name: 'saturday', type: 'INTEGER', required: true },\n { name: 'sunday', type: 'INTEGER', required: true },\n { name: 'start_date', type: 'TEXT', required: true },\n { name: 'end_date', type: 'TEXT', required: true },\n ],\n },\n {\n name: 'calendar_dates',\n columns: [\n { name: 'service_id', type: 'TEXT', required: true },\n { name: 'date', type: 'TEXT', required: true },\n { name: 'exception_type', type: 'INTEGER', required: true },\n ],\n indexes: [\n { name: 'idx_calendar_dates_service_id', columns: ['service_id'] },\n { name: 'idx_calendar_dates_date', columns: ['date'] },\n { name: 'idx_calendar_dates_service_date', columns: ['service_id', 'date'] },\n ],\n },\n {\n name: 'fare_attributes',\n columns: [\n { name: 'fare_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'price', type: 'REAL', required: true },\n { name: 'currency_type', type: 'TEXT', required: true },\n { name: 'payment_method', type: 'INTEGER', required: true },\n { name: 'transfers', type: 'INTEGER', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'transfer_duration', type: 'INTEGER', required: false },\n ],\n },\n {\n name: 'fare_rules',\n columns: [\n { name: 'fare_id', type: 'TEXT', required: true },\n { name: 'route_id', type: 'TEXT', required: false },\n { name: 'origin_id', type: 'TEXT', required: false },\n { name: 'destination_id', type: 'TEXT', required: false },\n { name: 'contains_id', type: 'TEXT', required: false },\n ],\n indexes: [\n { name: 'idx_fare_rules_fare_id', columns: ['fare_id'] },\n { name: 'idx_fare_rules_route_id', columns: ['route_id'] },\n ],\n },\n {\n name: 'shapes',\n columns: [\n { name: 'shape_id', type: 'TEXT', required: true },\n { name: 'shape_pt_lat', type: 'REAL', required: true },\n { name: 'shape_pt_lon', type: 'REAL', required: true },\n { name: 'shape_pt_sequence', type: 'INTEGER', required: true },\n { name: 'shape_dist_traveled', type: 'REAL', required: false },\n ],\n indexes: [\n { name: 'idx_shapes_shape_id', columns: ['shape_id'] },\n { name: 'idx_shapes_shape_sequence', columns: ['shape_id', 'shape_pt_sequence'] },\n ],\n },\n {\n name: 'frequencies',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true },\n { name: 'start_time', type: 'TEXT', required: true },\n { name: 'end_time', type: 'TEXT', required: true },\n { name: 'headway_secs', type: 'INTEGER', required: true },\n { name: 'exact_times', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_frequencies_trip_id', columns: ['trip_id'] },\n ],\n },\n {\n name: 'transfers',\n columns: [\n { name: 'from_stop_id', type: 'TEXT', required: true },\n { name: 'to_stop_id', type: 'TEXT', required: true },\n { name: 'transfer_type', type: 'INTEGER', required: true },\n { name: 'min_transfer_time', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_transfers_from_stop_id', columns: ['from_stop_id'] },\n { name: 'idx_transfers_to_stop_id', columns: ['to_stop_id'] },\n ],\n },\n {\n name: 'pathways',\n columns: [\n { name: 'pathway_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'from_stop_id', type: 'TEXT', required: true },\n { name: 'to_stop_id', type: 'TEXT', required: true },\n { name: 'pathway_mode', type: 'INTEGER', required: true },\n { name: 'is_bidirectional', type: 'INTEGER', required: true },\n { name: 'length', type: 'REAL', required: false },\n { name: 'traversal_time', type: 'INTEGER', required: false },\n { name: 'stair_count', type: 'INTEGER', required: false },\n { name: 'max_slope', type: 'REAL', required: false },\n { name: 'min_width', type: 'REAL', required: false },\n { name: 'signposted_as', type: 'TEXT', required: false },\n { name: 'reversed_signposted_as', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'levels',\n columns: [\n { name: 'level_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'level_index', type: 'REAL', required: true },\n { name: 'level_name', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'feed_info',\n columns: [\n { name: 'feed_publisher_name', type: 'TEXT', required: true },\n { name: 'feed_publisher_url', type: 'TEXT', required: true },\n { name: 'feed_lang', type: 'TEXT', required: true },\n { name: 'default_lang', type: 'TEXT', required: false },\n { name: 'feed_start_date', type: 'TEXT', required: false },\n { name: 'feed_end_date', type: 'TEXT', required: false },\n { name: 'feed_version', type: 'TEXT', required: false },\n { name: 'feed_contact_email', type: 'TEXT', required: false },\n { name: 'feed_contact_url', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'attributions',\n columns: [\n { name: 'attribution_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'organization_name', type: 'TEXT', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'route_id', type: 'TEXT', required: false },\n { name: 'trip_id', type: 'TEXT', required: false },\n { name: 'is_producer', type: 'INTEGER', required: false },\n { name: 'is_operator', type: 'INTEGER', required: false },\n { name: 'is_authority', type: 'INTEGER', required: false },\n { name: 'attribution_url', type: 'TEXT', required: false },\n { name: 'attribution_email', type: 'TEXT', required: false },\n { name: 'attribution_phone', type: 'TEXT', required: false },\n ],\n },\n];\n\n/**\n * Generate CREATE TABLE SQL statement from schema definition\n */\nexport function generateCreateTableSQL(schema: TableSchema): string {\n const columns = schema.columns.map((col) => {\n const parts = [col.name, col.type];\n if (col.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n if (col.required && !col.primaryKey) {\n parts.push('NOT NULL');\n }\n return parts.join(' ');\n });\n\n return `CREATE TABLE IF NOT EXISTS ${schema.name} (${columns.join(', ')})`;\n}\n\n/**\n * Generate CREATE INDEX SQL statements from schema definition\n */\nexport function generateCreateIndexSQL(schema: TableSchema): string[] {\n if (!schema.indexes) {\n return [];\n }\n\n return schema.indexes.map((idx) => {\n const unique = idx.unique ? 'UNIQUE ' : '';\n const columns = idx.columns.join(', ');\n return `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name} (${columns})`;\n });\n}\n\n/**\n * Initialize all GTFS tables in the database\n */\nexport function getAllCreateStatements(): string[] {\n const statements: string[] = [];\n\n for (const schema of GTFS_SCHEMA) {\n statements.push(generateCreateTableSQL(schema));\n statements.push(...generateCreateIndexSQL(schema));\n }\n\n return statements;\n}\n","/**\n * ZIP Loader for GTFS files\n */\n\nimport JSZip from 'jszip';\n\nexport interface GTFSFiles {\n [filename: string]: string;\n}\n\n/**\n * Fetch and extract GTFS ZIP file\n * Works in both browser and Node.js environments\n */\nexport async function loadGTFSZip(source: string | ArrayBuffer | Uint8Array): Promise {\n let zipData: ArrayBuffer | Uint8Array;\n\n // If source is a string, treat it as URL or file path\n if (typeof source === 'string') {\n zipData = await fetchZip(source);\n } else {\n zipData = source;\n }\n\n // Load ZIP file\n const zip = await JSZip.loadAsync(zipData);\n\n // Extract all .txt files (GTFS files)\n const files: GTFSFiles = {};\n const filePromises: Promise[] = [];\n\n zip.forEach((relativePath, file) => {\n // Only process .txt files\n if (!file.dir && relativePath.endsWith('.txt')) {\n const fileName = relativePath.split('/').pop() || relativePath;\n filePromises.push(\n file.async('string').then((content) => {\n files[fileName] = content;\n })\n );\n }\n });\n\n await Promise.all(filePromises);\n\n return files;\n}\n\n/**\n * Fetch ZIP file from URL or file path\n */\nasync function fetchZip(source: string): Promise {\n // Check if source is a URL\n const isUrl = source.startsWith('http://') || source.startsWith('https://');\n\n // For URLs, always use fetch\n if (isUrl) {\n if (typeof fetch !== 'undefined') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS ZIP: ${response.status} ${response.statusText}`);\n }\n return await response.arrayBuffer();\n }\n throw new Error('fetch is not available to load URL');\n }\n\n // For non-URLs, try Node.js fs first, fall back to fetch for browser\n // Check if we're in Node.js environment\n const isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n if (isNode) {\n // In Node.js, treat as file path\n try {\n const fs = await import('fs');\n const buffer = await fs.promises.readFile(source);\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n } catch (error) {\n throw new Error(`Failed to read GTFS ZIP file: ${error}`);\n }\n }\n\n // In browser, treat as relative URL and use fetch\n if (typeof fetch !== 'undefined') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS ZIP: ${response.status} ${response.statusText}`);\n }\n return await response.arrayBuffer();\n }\n\n throw new Error('No method available to load ZIP file');\n}\n\n/**\n * Mapping of GTFS file names to database table names\n */\nexport const GTFS_FILE_MAPPING: Record = {\n 'agency.txt': 'agency',\n 'stops.txt': 'stops',\n 'routes.txt': 'routes',\n 'trips.txt': 'trips',\n 'stop_times.txt': 'stop_times',\n 'calendar.txt': 'calendar',\n 'calendar_dates.txt': 'calendar_dates',\n 'fare_attributes.txt': 'fare_attributes',\n 'fare_rules.txt': 'fare_rules',\n 'shapes.txt': 'shapes',\n 'frequencies.txt': 'frequencies',\n 'transfers.txt': 'transfers',\n 'pathways.txt': 'pathways',\n 'levels.txt': 'levels',\n 'feed_info.txt': 'feed_info',\n 'attributions.txt': 'attributions',\n};\n","/**\n * CSV Parser for GTFS files using papaparse\n */\n\nimport Papa from 'papaparse';\n\nexport interface ParsedCSV {\n headers: string[];\n rows: Record[];\n}\n\n/**\n * Parse CSV text into structured data using papaparse\n */\nexport function parseCSV(text: string): ParsedCSV {\n const result = Papa.parse(text, {\n header: true,\n skipEmptyLines: true,\n transformHeader: (header) => header.trim(),\n transform: (value) => value.trim(),\n });\n\n if (result.errors.length > 0) {\n console.warn('CSV parsing warnings:', result.errors);\n }\n\n const headers = result.meta.fields || [];\n const rows = result.data as Record[];\n\n return { headers, rows };\n}\n\n/**\n * Convert parsed row to typed object with proper type conversions\n */\nexport function convertRowTypes(\n row: Record,\n columnTypes: Record\n): Record {\n const result: Record = {};\n\n // Iterate over all expected columns, not just the ones present in the row\n for (const [key, type] of Object.entries(columnTypes)) {\n const value = row[key];\n\n // Handle missing or empty values\n if (value === undefined || value === null || value === '') {\n result[key] = null;\n continue;\n }\n\n if (type === 'INTEGER') {\n const parsed = parseInt(value, 10);\n result[key] = isNaN(parsed) ? null : parsed;\n } else if (type === 'REAL') {\n const parsed = parseFloat(value);\n result[key] = isNaN(parsed) ? null : parsed;\n } else {\n result[key] = value;\n }\n }\n\n return result;\n}\n","/**\n * Data Loader - Loads GTFS data into SQLite database\n */\n\nimport type { Database } from 'sql.js';\nimport { parseCSV, convertRowTypes } from './csv-parser';\nimport { GTFS_SCHEMA, type TableSchema } from '../schema/schema';\nimport type { GTFSFiles } from './zip-loader';\n\n/**\n * Load GTFS files into SQLite database\n * @param skipFiles - Optional array of filenames to skip importing (tables will be created but remain empty)\n */\nexport async function loadGTFSData(db: Database, files: GTFSFiles, skipFiles?: string[]): Promise {\n // Map of file names to table schemas\n const fileToSchema: Map = new Map();\n for (const schema of GTFS_SCHEMA) {\n // Match file names like agency.txt to table name 'agency'\n fileToSchema.set(`${schema.name}.txt`, schema);\n }\n\n // Normalize skipFiles to a Set for faster lookup\n const skipSet = new Set(skipFiles?.map(f => f.toLowerCase()) || []);\n\n // Process each file\n for (const [fileName, content] of Object.entries(files)) {\n const schema = fileToSchema.get(fileName);\n if (!schema) {\n // Skip unknown files\n continue;\n }\n\n // Skip if in skip list\n if (skipSet.has(fileName.toLowerCase())) {\n console.log(`Skipping import of ${fileName} (table ${schema.name} created but empty)`);\n continue;\n }\n\n await loadTableData(db, schema, content);\n }\n}\n\n/**\n * Load data for a single table\n */\nasync function loadTableData(db: Database, schema: TableSchema, csvContent: string): Promise {\n const { headers, rows } = parseCSV(csvContent);\n\n if (rows.length === 0) {\n return;\n }\n\n // Build column type map\n const columnTypes: Record = {};\n for (const col of schema.columns) {\n columnTypes[col.name] = col.type;\n }\n\n // Prepare INSERT statement\n const columns = headers.filter((h) => schema.columns.some((c) => c.name === h));\n const placeholders = columns.map(() => '?').join(', ');\n const insertSQL = `INSERT INTO ${schema.name} (${columns.join(', ')}) VALUES (${placeholders})`;\n\n const stmt = db.prepare(insertSQL);\n\n // Insert each row\n for (const row of rows) {\n const typedRow = convertRowTypes(row, columnTypes);\n const values = columns.map((col) => {\n const value = typedRow[col];\n // Handle undefined, null, or empty values\n return value === null || value === undefined ? null : value;\n });\n\n try {\n stmt.run(values);\n } catch (error) {\n console.error(`Error inserting row into ${schema.name}:`, error);\n console.error('Row data:', row);\n throw error;\n }\n }\n\n stmt.free();\n}\n","import type { Database } from 'sql.js';\n\n/**\n * Create GTFS Realtime tables in the database\n */\nexport function createRealtimeTables(db: Database): void {\n // Alerts table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_alerts (\n id TEXT PRIMARY KEY,\n active_period TEXT, -- JSON array of TimeRange objects\n informed_entity TEXT, -- JSON array of EntitySelector objects\n cause INTEGER,\n effect INTEGER,\n url TEXT, -- JSON TranslatedString\n header_text TEXT, -- JSON TranslatedString\n description_text TEXT, -- JSON TranslatedString\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n // Create index on rt_last_updated for staleness filtering\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_alerts_updated ON rt_alerts(rt_last_updated)');\n\n // Vehicle Positions table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_vehicle_positions (\n trip_id TEXT PRIMARY KEY,\n route_id TEXT,\n vehicle_id TEXT,\n vehicle_label TEXT,\n vehicle_license_plate TEXT,\n latitude REAL,\n longitude REAL,\n bearing REAL,\n odometer REAL,\n speed REAL,\n current_stop_sequence INTEGER,\n stop_id TEXT,\n current_status INTEGER,\n timestamp INTEGER,\n congestion_level INTEGER,\n occupancy_status INTEGER,\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_vehicle_positions_updated ON rt_vehicle_positions(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_vehicle_positions_route ON rt_vehicle_positions(route_id)');\n\n // Trip Updates table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_trip_updates (\n trip_id TEXT PRIMARY KEY,\n route_id TEXT,\n vehicle_id TEXT,\n vehicle_label TEXT,\n vehicle_license_plate TEXT,\n timestamp INTEGER,\n delay INTEGER,\n schedule_relationship INTEGER,\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_trip_updates_updated ON rt_trip_updates(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_trip_updates_route ON rt_trip_updates(route_id)');\n\n // Stop Time Updates table (child of trip updates)\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_stop_time_updates (\n trip_id TEXT NOT NULL,\n stop_sequence INTEGER,\n stop_id TEXT,\n arrival_delay INTEGER,\n arrival_time INTEGER,\n arrival_uncertainty INTEGER,\n departure_delay INTEGER,\n departure_time INTEGER,\n departure_uncertainty INTEGER,\n schedule_relationship INTEGER,\n rt_last_updated INTEGER NOT NULL,\n PRIMARY KEY (trip_id, stop_sequence),\n FOREIGN KEY (trip_id) REFERENCES rt_trip_updates(trip_id) ON DELETE CASCADE\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_stop_time_updates_updated ON rt_stop_time_updates(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_stop_time_updates_stop ON rt_stop_time_updates(stop_id)');\n}\n\n/**\n * Clear all realtime data from the database\n */\nexport function clearRealtimeData(db: Database): void {\n db.run('DELETE FROM rt_alerts');\n db.run('DELETE FROM rt_vehicle_positions');\n db.run('DELETE FROM rt_trip_updates');\n db.run('DELETE FROM rt_stop_time_updates');\n}\n","import type { Database } from 'sql.js';\nimport protobuf from 'protobufjs';\n\n// Types for protobuf decoded objects\ninterface ProtobufTranslation {\n text: string;\n language?: string;\n}\n\ninterface ProtobufTranslatedString {\n translation: ProtobufTranslation[];\n}\n\ninterface ProtobufAlert {\n id: string;\n activePeriod?: unknown[];\n active_period?: unknown[];\n informedEntity?: unknown[];\n informed_entity?: unknown[];\n cause?: number;\n effect?: number;\n url?: ProtobufTranslatedString;\n headerText?: ProtobufTranslatedString;\n header_text?: ProtobufTranslatedString;\n descriptionText?: ProtobufTranslatedString;\n description_text?: ProtobufTranslatedString;\n}\n\ninterface ProtobufTripDescriptor {\n tripId?: string;\n trip_id?: string;\n routeId?: string;\n route_id?: string;\n scheduleRelationship?: number;\n schedule_relationship?: number;\n}\n\ninterface ProtobufVehicleDescriptor {\n id?: string;\n label?: string;\n licensePlate?: string;\n license_plate?: string;\n}\n\ninterface ProtobufPosition {\n latitude?: number;\n longitude?: number;\n bearing?: number;\n odometer?: number;\n speed?: number;\n}\n\ninterface ProtobufVehiclePosition {\n trip?: ProtobufTripDescriptor;\n vehicle?: ProtobufVehicleDescriptor;\n position?: ProtobufPosition;\n currentStopSequence?: number;\n current_stop_sequence?: number;\n stopId?: string;\n stop_id?: string;\n currentStatus?: number;\n current_status?: number;\n timestamp?: number;\n congestionLevel?: number;\n congestion_level?: number;\n occupancyStatus?: number;\n occupancy_status?: number;\n}\n\ninterface ProtobufStopTimeEvent {\n delay?: number;\n time?: number;\n uncertainty?: number;\n}\n\ninterface ProtobufStopTimeUpdate {\n stopSequence?: number;\n stop_sequence?: number;\n stopId?: string;\n stop_id?: string;\n scheduleRelationship?: number;\n schedule_relationship?: number;\n arrival?: ProtobufStopTimeEvent;\n departure?: ProtobufStopTimeEvent;\n}\n\ninterface ProtobufTripUpdate {\n trip?: ProtobufTripDescriptor;\n vehicle?: ProtobufVehicleDescriptor;\n timestamp?: number;\n delay?: number;\n stopTimeUpdate?: ProtobufStopTimeUpdate[];\n stop_time_update?: ProtobufStopTimeUpdate[];\n}\n\n// GTFS Realtime protobuf definition (v2.0)\n// Source: https://github.com/google/transit/blob/master/gtfs-realtime/proto/gtfs-realtime.proto\nconst GTFS_RT_PROTO = `\nsyntax = \"proto2\";\noption java_package = \"com.google.transit.realtime\";\npackage transit_realtime;\n\nmessage FeedMessage {\n required FeedHeader header = 1;\n repeated FeedEntity entity = 2;\n}\n\nmessage FeedHeader {\n required string gtfs_realtime_version = 1;\n enum Incrementality {\n FULL_DATASET = 0;\n DIFFERENTIAL = 1;\n }\n optional Incrementality incrementality = 2 [default = FULL_DATASET];\n optional uint64 timestamp = 3;\n}\n\nmessage FeedEntity {\n required string id = 1;\n optional bool is_deleted = 2 [default = false];\n optional TripUpdate trip_update = 3;\n optional VehiclePosition vehicle = 4;\n optional Alert alert = 5;\n}\n\nmessage TripUpdate {\n required TripDescriptor trip = 1;\n optional VehicleDescriptor vehicle = 3;\n repeated StopTimeUpdate stop_time_update = 2;\n optional uint64 timestamp = 4;\n optional int32 delay = 5;\n\n message StopTimeEvent {\n optional int32 delay = 1;\n optional int64 time = 2;\n optional int32 uncertainty = 3;\n }\n\n message StopTimeUpdate {\n optional uint32 stop_sequence = 1;\n optional string stop_id = 4;\n optional StopTimeEvent arrival = 2;\n optional StopTimeEvent departure = 3;\n enum ScheduleRelationship {\n SCHEDULED = 0;\n SKIPPED = 1;\n NO_DATA = 2;\n }\n optional ScheduleRelationship schedule_relationship = 5 [default = SCHEDULED];\n }\n}\n\nmessage VehiclePosition {\n optional TripDescriptor trip = 1;\n optional VehicleDescriptor vehicle = 8;\n optional Position position = 2;\n optional uint32 current_stop_sequence = 3;\n optional string stop_id = 7;\n enum VehicleStopStatus {\n INCOMING_AT = 0;\n STOPPED_AT = 1;\n IN_TRANSIT_TO = 2;\n }\n optional VehicleStopStatus current_status = 4 [default = IN_TRANSIT_TO];\n optional uint64 timestamp = 5;\n enum CongestionLevel {\n UNKNOWN_CONGESTION_LEVEL = 0;\n RUNNING_SMOOTHLY = 1;\n STOP_AND_GO = 2;\n CONGESTION = 3;\n SEVERE_CONGESTION = 4;\n }\n optional CongestionLevel congestion_level = 6;\n enum OccupancyStatus {\n EMPTY = 0;\n MANY_SEATS_AVAILABLE = 1;\n FEW_SEATS_AVAILABLE = 2;\n STANDING_ROOM_ONLY = 3;\n CRUSHED_STANDING_ROOM_ONLY = 4;\n FULL = 5;\n NOT_ACCEPTING_PASSENGERS = 6;\n }\n optional OccupancyStatus occupancy_status = 9;\n}\n\nmessage Alert {\n repeated TimeRange active_period = 1;\n repeated EntitySelector informed_entity = 5;\n\n enum Cause {\n UNKNOWN_CAUSE = 1;\n OTHER_CAUSE = 2;\n TECHNICAL_PROBLEM = 3;\n STRIKE = 4;\n DEMONSTRATION = 5;\n ACCIDENT = 6;\n HOLIDAY = 7;\n WEATHER = 8;\n MAINTENANCE = 9;\n CONSTRUCTION = 10;\n POLICE_ACTIVITY = 11;\n MEDICAL_EMERGENCY = 12;\n }\n optional Cause cause = 6 [default = UNKNOWN_CAUSE];\n\n enum Effect {\n NO_SERVICE = 1;\n REDUCED_SERVICE = 2;\n SIGNIFICANT_DELAYS = 3;\n DETOUR = 4;\n ADDITIONAL_SERVICE = 5;\n MODIFIED_SERVICE = 6;\n OTHER_EFFECT = 7;\n UNKNOWN_EFFECT = 8;\n STOP_MOVED = 9;\n NO_EFFECT = 10;\n ACCESSIBILITY_ISSUE = 11;\n }\n optional Effect effect = 7 [default = UNKNOWN_EFFECT];\n optional TranslatedString url = 8;\n optional TranslatedString header_text = 10;\n optional TranslatedString description_text = 11;\n}\n\nmessage TimeRange {\n optional uint64 start = 1;\n optional uint64 end = 2;\n}\n\nmessage Position {\n required float latitude = 1;\n required float longitude = 2;\n optional float bearing = 3;\n optional double odometer = 4;\n optional float speed = 5;\n}\n\nmessage TripDescriptor {\n optional string trip_id = 1;\n optional string route_id = 5;\n optional uint32 direction_id = 6;\n optional string start_time = 2;\n optional string start_date = 3;\n enum ScheduleRelationship {\n SCHEDULED = 0;\n ADDED = 1;\n UNSCHEDULED = 2;\n CANCELED = 3;\n }\n optional ScheduleRelationship schedule_relationship = 4;\n}\n\nmessage VehicleDescriptor {\n optional string id = 1;\n optional string label = 2;\n optional string license_plate = 3;\n}\n\nmessage EntitySelector {\n optional string agency_id = 1;\n optional string route_id = 2;\n optional int32 route_type = 3;\n optional TripDescriptor trip = 4;\n optional string stop_id = 5;\n}\n\nmessage TranslatedString {\n message Translation {\n required string text = 1;\n optional string language = 2;\n }\n repeated Translation translation = 1;\n}\n`;\n\n// Fetch protobuf data from URL or local file\n// Uses fetch API (available in modern browsers and Node.js 18+)\nasync function fetchProtobuf(source: string): Promise {\n const isUrl = source.startsWith('http://') || source.startsWith('https://');\n\n if (isUrl) {\n // Fetch from URL\n const response = await fetch(source, {\n headers: {\n 'Accept': 'application/x-protobuf, application/octet-stream'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS-RT feed from ${source}: ${response.status} ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n }\n\n // Read from local file (Node.js only)\n const isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n if (isNode) {\n try {\n const fs = await import('fs');\n const buffer = await fs.promises.readFile(source);\n return new Uint8Array(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));\n } catch (error) {\n throw new Error(`Failed to read GTFS-RT file from ${source}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // In browser, try fetch for relative paths\n const response = await fetch(source, {\n headers: {\n 'Accept': 'application/x-protobuf, application/octet-stream'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS-RT feed from ${source}: ${response.status} ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n}\n\n// Load and parse GTFS-RT protobuf schema\nlet gtfsRtRoot: protobuf.Root | null = null;\n\nfunction loadGtfsRtProto(): protobuf.Root {\n if (!gtfsRtRoot) {\n gtfsRtRoot = protobuf.parse(GTFS_RT_PROTO).root;\n }\n return gtfsRtRoot;\n}\n\n// Convert camelCase object keys to snake_case\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);\n}\n\nfunction convertKeysToSnakeCase(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToSnakeCase);\n }\n if (typeof obj === 'object') {\n const result: Record = {};\n for (const key in obj as Record) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const snakeKey = camelToSnake(key);\n result[snakeKey] = convertKeysToSnakeCase((obj as Record)[key]);\n }\n }\n return result;\n }\n return obj;\n}\n\n// Parse TranslatedString to JSON\nfunction parseTranslatedString(ts: ProtobufTranslatedString | undefined): string | null {\n if (!ts || !ts.translation || ts.translation.length === 0) {\n return null;\n }\n return JSON.stringify({\n translation: ts.translation.map((t) => ({\n text: t.text,\n language: t.language || undefined\n }))\n });\n}\n\n// Insert alerts into database\nfunction insertAlerts(db: Database, alerts: ProtobufAlert[], timestamp: number): void {\n const stmt = db.prepare(`\n INSERT OR REPLACE INTO rt_alerts (\n id, active_period, informed_entity, cause, effect,\n url, header_text, description_text, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const alert of alerts) {\n // Map camelCase protobuf fields to snake_case\n const activePeriod = alert.activePeriod || alert.active_period || [];\n const informedEntity = alert.informedEntity || alert.informed_entity || [];\n const headerText = alert.headerText || alert.header_text;\n const descriptionText = alert.descriptionText || alert.description_text;\n\n // Convert nested objects to snake_case\n const activePeriodSnake = convertKeysToSnakeCase(activePeriod);\n const informedEntitySnake = convertKeysToSnakeCase(informedEntity);\n\n stmt.run([\n alert.id,\n JSON.stringify(activePeriodSnake),\n JSON.stringify(informedEntitySnake),\n alert.cause || null,\n alert.effect || null,\n parseTranslatedString(alert.url),\n parseTranslatedString(headerText),\n parseTranslatedString(descriptionText),\n timestamp\n ]);\n }\n\n stmt.free();\n}\n\n// Insert vehicle positions into database\nfunction insertVehiclePositions(db: Database, positions: ProtobufVehiclePosition[], timestamp: number): void {\n const stmt = db.prepare(`\n INSERT OR REPLACE INTO rt_vehicle_positions (\n trip_id, route_id, vehicle_id, vehicle_label, vehicle_license_plate,\n latitude, longitude, bearing, odometer, speed,\n current_stop_sequence, stop_id, current_status, timestamp,\n congestion_level, occupancy_status, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const vp of positions) {\n const trip = vp.trip;\n if (!trip || !(trip.tripId || trip.trip_id)) continue;\n\n // Map camelCase protobuf fields to snake_case\n const tripId = (trip.tripId || trip.trip_id)!; // Safe: checked above\n const routeId = trip.routeId || trip.route_id;\n const vehicleId = vp.vehicle?.id;\n const vehicleLabel = vp.vehicle?.label;\n const vehicleLicensePlate = vp.vehicle?.licensePlate || vp.vehicle?.license_plate;\n const currentStopSequence = vp.currentStopSequence || vp.current_stop_sequence;\n const stopId = vp.stopId || vp.stop_id;\n const currentStatus = vp.currentStatus || vp.current_status;\n const congestionLevel = vp.congestionLevel || vp.congestion_level;\n const occupancyStatus = vp.occupancyStatus || vp.occupancy_status;\n\n stmt.run([\n tripId,\n routeId || null,\n vehicleId || null,\n vehicleLabel || null,\n vehicleLicensePlate || null,\n vp.position?.latitude || null,\n vp.position?.longitude || null,\n vp.position?.bearing || null,\n vp.position?.odometer || null,\n vp.position?.speed || null,\n currentStopSequence || null,\n stopId || null,\n currentStatus || null,\n vp.timestamp || null,\n congestionLevel || null,\n occupancyStatus || null,\n timestamp\n ]);\n }\n\n stmt.free();\n}\n\n// Insert trip updates into database\nfunction insertTripUpdates(db: Database, updates: ProtobufTripUpdate[], timestamp: number): void {\n const tripStmt = db.prepare(`\n INSERT OR REPLACE INTO rt_trip_updates (\n trip_id, route_id, vehicle_id, vehicle_label, vehicle_license_plate,\n timestamp, delay, schedule_relationship, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const stopTimeStmt = db.prepare(`\n INSERT OR REPLACE INTO rt_stop_time_updates (\n trip_id, stop_sequence, stop_id,\n arrival_delay, arrival_time, arrival_uncertainty,\n departure_delay, departure_time, departure_uncertainty,\n schedule_relationship, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const tu of updates) {\n const trip = tu.trip;\n if (!trip || !(trip.tripId || trip.trip_id)) continue;\n\n // Map camelCase protobuf fields to snake_case\n const tripId = (trip.tripId || trip.trip_id)!; // Safe: checked above\n const routeId = trip.routeId || trip.route_id;\n const vehicleId = tu.vehicle?.id;\n const vehicleLabel = tu.vehicle?.label;\n const vehicleLicensePlate = tu.vehicle?.licensePlate || tu.vehicle?.license_plate;\n const scheduleRelationship = trip.scheduleRelationship || trip.schedule_relationship;\n const stopTimeUpdate = tu.stopTimeUpdate || tu.stop_time_update;\n\n // Insert trip update\n tripStmt.run([\n tripId,\n routeId || null,\n vehicleId || null,\n vehicleLabel || null,\n vehicleLicensePlate || null,\n tu.timestamp || null,\n tu.delay || null,\n scheduleRelationship || null,\n timestamp\n ]);\n\n // Insert stop time updates\n if (stopTimeUpdate) {\n for (const stu of stopTimeUpdate) {\n const stopSequence = stu.stopSequence || stu.stop_sequence;\n const stopId = stu.stopId || stu.stop_id;\n const scheduleRel = stu.scheduleRelationship || stu.schedule_relationship;\n\n stopTimeStmt.run([\n tripId,\n stopSequence || null,\n stopId || null,\n stu.arrival?.delay || null,\n stu.arrival?.time || null,\n stu.arrival?.uncertainty || null,\n stu.departure?.delay || null,\n stu.departure?.time || null,\n stu.departure?.uncertainty || null,\n scheduleRel || null,\n timestamp\n ]);\n }\n }\n }\n\n tripStmt.free();\n stopTimeStmt.free();\n}\n\n/**\n * Fetch and load GTFS Realtime data from multiple feed URLs\n * All feeds are fetched in parallel. If any feed fails, an error is thrown.\n */\nexport async function loadRealtimeData(db: Database, feedUrls: string[]): Promise {\n const root = loadGtfsRtProto();\n const FeedMessage = root.lookupType('transit_realtime.FeedMessage');\n\n // Fetch all feeds in parallel\n const fetchPromises = feedUrls.map(async (url) => {\n try {\n const data = await fetchProtobuf(url);\n const message = FeedMessage.decode(data);\n return FeedMessage.toObject(message, {\n longs: Number,\n enums: Number,\n bytes: String,\n defaults: false,\n arrays: true,\n objects: true,\n oneofs: true\n });\n } catch (error) {\n throw new Error(`Failed to fetch or parse GTFS-RT feed from ${url}: ${error instanceof Error ? error.message : String(error)}`);\n }\n });\n\n // Wait for all feeds - if any fails, this will throw\n const feeds = await Promise.all(fetchPromises);\n\n // Current timestamp for staleness tracking\n const now = Math.floor(Date.now() / 1000);\n\n // Collect all entities by type\n const allAlerts: ProtobufAlert[] = [];\n const allVehiclePositions: ProtobufVehiclePosition[] = [];\n const allTripUpdates: ProtobufTripUpdate[] = [];\n\n for (const feed of feeds) {\n if (!feed.entity) continue;\n\n for (const entity of feed.entity) {\n if (entity.alert) {\n allAlerts.push({ id: entity.id, ...entity.alert });\n }\n if (entity.vehicle) {\n allVehiclePositions.push(entity.vehicle);\n }\n if (entity.tripUpdate) {\n allTripUpdates.push(entity.tripUpdate);\n }\n }\n }\n\n // Insert into database\n if (allAlerts.length > 0) {\n insertAlerts(db, allAlerts, now);\n }\n if (allVehiclePositions.length > 0) {\n insertVehiclePositions(db, allVehiclePositions, now);\n }\n if (allTripUpdates.length > 0) {\n insertTripUpdates(db, allTripUpdates, now);\n }\n}\n","/**\n * Agency Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Agency } from '../types/gtfs';\n\nexport interface AgencyFilters {\n agencyId?: string;\n limit?: number;\n}\n\n/**\n * Get agencies with optional filters\n */\nexport function getAgencies(db: Database, filters: AgencyFilters = {}): Agency[] {\n const { agencyId, limit } = filters;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (agencyId) {\n conditions.push('agency_id = ?');\n params.push(agencyId);\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM agency';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY agency_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const agencies: Agency[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n agencies.push(rowToAgency(row));\n }\n\n stmt.free();\n return agencies;\n}\n\n/**\n * Get an agency by its agency_id\n */\nexport function getAgencyById(db: Database, agencyId: string): Agency | null {\n const stmt = db.prepare('SELECT * FROM agency WHERE agency_id = ?');\n stmt.bind([agencyId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToAgency(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get all agencies\n */\nexport function getAllAgencies(db: Database, limit?: number): Agency[] {\n const sql = limit\n ? `SELECT * FROM agency ORDER BY agency_name LIMIT ${limit}`\n : 'SELECT * FROM agency ORDER BY agency_name';\n\n const stmt = db.prepare(sql);\n\n const agencies: Agency[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n agencies.push(rowToAgency(row));\n }\n\n stmt.free();\n return agencies;\n}\n\n/**\n * Convert database row to Agency object\n */\nfunction rowToAgency(row: Record): Agency {\n return {\n agency_id: String(row.agency_id),\n agency_name: String(row.agency_name),\n agency_url: String(row.agency_url),\n agency_timezone: String(row.agency_timezone),\n agency_lang: row.agency_lang ? String(row.agency_lang) : undefined,\n agency_phone: row.agency_phone ? String(row.agency_phone) : undefined,\n agency_fare_url: row.agency_fare_url ? String(row.agency_fare_url) : undefined,\n agency_email: row.agency_email ? String(row.agency_email) : undefined,\n };\n}\n","/**\n * Stop Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Stop } from '../types/gtfs';\n\nexport interface StopFilters {\n stopId?: string;\n stopCode?: string;\n name?: string;\n tripId?: string;\n limit?: number;\n}\n\n/**\n * Get stops with optional filters\n */\nexport function getStops(db: Database, filters: StopFilters = {}): Stop[] {\n const { stopId, stopCode, name, tripId, limit } = filters;\n\n // Handle special case: get stops by trip (requires JOIN)\n if (tripId) {\n return getStopsByTrip(db, tripId);\n }\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (stopId) {\n conditions.push('stop_id = ?');\n params.push(stopId);\n }\n\n if (stopCode) {\n conditions.push('stop_code = ?');\n params.push(stopCode);\n }\n\n if (name) {\n conditions.push('stop_name LIKE ?');\n params.push(`%${name}%`);\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM stops';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY stop_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Get a stop by its stop_id\n */\nexport function getStopById(db: Database, stopId: string): Stop | null {\n const stmt = db.prepare('SELECT * FROM stops WHERE stop_id = ?');\n stmt.bind([stopId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToStop(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get a stop by its stop_code\n */\nexport function getStopByCode(db: Database, stopCode: string): Stop | null {\n const stmt = db.prepare('SELECT * FROM stops WHERE stop_code = ?');\n stmt.bind([stopCode]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToStop(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Search stops by name (case-insensitive, partial match)\n */\nexport function searchStopsByName(db: Database, name: string, limit = 50): Stop[] {\n const stmt = db.prepare(\n 'SELECT * FROM stops WHERE stop_name LIKE ? ORDER BY stop_name LIMIT ?'\n );\n stmt.bind([`%${name}%`, limit]);\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Get all stops\n */\nexport function getAllStops(db: Database, limit?: number): Stop[] {\n const sql = limit\n ? `SELECT * FROM stops ORDER BY stop_name LIMIT ${limit}`\n : 'SELECT * FROM stops ORDER BY stop_name';\n\n const stmt = db.prepare(sql);\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Get stops for a given trip (ordered by stop_sequence)\n */\nexport function getStopsByTrip(db: Database, tripId: string): Stop[] {\n const stmt = db.prepare(`\n SELECT s.* FROM stops s\n INNER JOIN stop_times st ON s.stop_id = st.stop_id\n WHERE st.trip_id = ?\n ORDER BY st.stop_sequence\n `);\n stmt.bind([tripId]);\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Convert database row to Stop object\n */\nfunction rowToStop(row: Record): Stop {\n return {\n stop_id: String(row.stop_id),\n stop_name: String(row.stop_name),\n stop_lat: Number(row.stop_lat),\n stop_lon: Number(row.stop_lon),\n stop_code: row.stop_code ? String(row.stop_code) : undefined,\n stop_desc: row.stop_desc ? String(row.stop_desc) : undefined,\n zone_id: row.zone_id ? String(row.zone_id) : undefined,\n stop_url: row.stop_url ? String(row.stop_url) : undefined,\n location_type: row.location_type !== null ? Number(row.location_type) : undefined,\n parent_station: row.parent_station ? String(row.parent_station) : undefined,\n stop_timezone: row.stop_timezone ? String(row.stop_timezone) : undefined,\n wheelchair_boarding: row.wheelchair_boarding !== null ? Number(row.wheelchair_boarding) : undefined,\n level_id: row.level_id ? String(row.level_id) : undefined,\n platform_code: row.platform_code ? String(row.platform_code) : undefined,\n };\n}\n","/**\n * Route Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Route } from '../types/gtfs';\n\nexport interface RouteFilters {\n routeId?: string;\n agencyId?: string;\n limit?: number;\n}\n\n/**\n * Get routes with optional filters\n */\nexport function getRoutes(db: Database, filters: RouteFilters = {}): Route[] {\n const { routeId, agencyId, limit } = filters;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n if (agencyId) {\n conditions.push('agency_id = ?');\n params.push(agencyId);\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM routes';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY route_short_name, route_long_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const routes: Route[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n routes.push(rowToRoute(row));\n }\n\n stmt.free();\n return routes;\n}\n\n/**\n * Get a route by its route_id\n */\nexport function getRouteById(db: Database, routeId: string): Route | null {\n const stmt = db.prepare('SELECT * FROM routes WHERE route_id = ?');\n stmt.bind([routeId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToRoute(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get all routes\n */\nexport function getAllRoutes(db: Database, limit?: number): Route[] {\n const sql = limit\n ? `SELECT * FROM routes ORDER BY route_short_name, route_long_name LIMIT ${limit}`\n : 'SELECT * FROM routes ORDER BY route_short_name, route_long_name';\n\n const stmt = db.prepare(sql);\n\n const routes: Route[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n routes.push(rowToRoute(row));\n }\n\n stmt.free();\n return routes;\n}\n\n/**\n * Get routes by agency\n */\nexport function getRoutesByAgency(db: Database, agencyId: string): Route[] {\n const stmt = db.prepare(\n 'SELECT * FROM routes WHERE agency_id = ? ORDER BY route_short_name, route_long_name'\n );\n stmt.bind([agencyId]);\n\n const routes: Route[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n routes.push(rowToRoute(row));\n }\n\n stmt.free();\n return routes;\n}\n\n/**\n * Convert database row to Route object\n */\nfunction rowToRoute(row: Record): Route {\n return {\n route_id: String(row.route_id),\n route_short_name: String(row.route_short_name),\n route_long_name: String(row.route_long_name),\n route_type: Number(row.route_type),\n agency_id: row.agency_id ? String(row.agency_id) : undefined,\n route_desc: row.route_desc ? String(row.route_desc) : undefined,\n route_url: row.route_url ? String(row.route_url) : undefined,\n route_color: row.route_color ? String(row.route_color) : undefined,\n route_text_color: row.route_text_color ? String(row.route_text_color) : undefined,\n route_sort_order: row.route_sort_order !== null ? Number(row.route_sort_order) : undefined,\n continuous_pickup: row.continuous_pickup !== null ? Number(row.continuous_pickup) : undefined,\n continuous_drop_off: row.continuous_drop_off !== null ? Number(row.continuous_drop_off) : undefined,\n };\n}\n","/**\n * Calendar Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Calendar, CalendarDate } from '../types/gtfs';\n\n/**\n * Get active service IDs for a given date\n */\nexport function getActiveServiceIds(db: Database, date: string): string[] {\n const serviceIds = new Set();\n\n // Parse date (format: YYYYMMDD)\n const year = parseInt(date.substring(0, 4));\n const month = parseInt(date.substring(4, 6));\n const day = parseInt(date.substring(6, 8));\n const dateObj = new Date(year, month - 1, day);\n const dayOfWeek = dateObj.getDay(); // 0 = Sunday, 1 = Monday, etc.\n\n // Map day of week to GTFS calendar field\n const dayFields = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];\n const dayField = dayFields[dayOfWeek];\n\n // Check calendar.txt for regular service\n const calendarStmt = db.prepare(\n `SELECT service_id FROM calendar\n WHERE ${dayField} = 1\n AND start_date <= ?\n AND end_date >= ?`\n );\n calendarStmt.bind([date, date]);\n\n while (calendarStmt.step()) {\n const row = calendarStmt.getAsObject() as { service_id: string };\n serviceIds.add(row.service_id);\n }\n calendarStmt.free();\n\n // Check calendar_dates.txt for exceptions\n const exceptionsStmt = db.prepare('SELECT service_id, exception_type FROM calendar_dates WHERE date = ?');\n exceptionsStmt.bind([date]);\n\n while (exceptionsStmt.step()) {\n const row = exceptionsStmt.getAsObject() as { service_id: string; exception_type: number };\n if (row.exception_type === 1) {\n // Service added\n serviceIds.add(row.service_id);\n } else if (row.exception_type === 2) {\n // Service removed\n serviceIds.delete(row.service_id);\n }\n }\n exceptionsStmt.free();\n\n return Array.from(serviceIds);\n}\n\n/**\n * Get calendar entry by service_id\n */\nexport function getCalendarByServiceId(db: Database, serviceId: string): Calendar | null {\n const stmt = db.prepare('SELECT * FROM calendar WHERE service_id = ?');\n stmt.bind([serviceId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToCalendar(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get calendar date exceptions for a service\n */\nexport function getCalendarDates(db: Database, serviceId: string): CalendarDate[] {\n const stmt = db.prepare('SELECT * FROM calendar_dates WHERE service_id = ? ORDER BY date');\n stmt.bind([serviceId]);\n\n const dates: CalendarDate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n dates.push(rowToCalendarDate(row));\n }\n\n stmt.free();\n return dates;\n}\n\n/**\n * Get calendar date exceptions for a specific date\n */\nexport function getCalendarDatesForDate(db: Database, date: string): CalendarDate[] {\n const stmt = db.prepare('SELECT * FROM calendar_dates WHERE date = ?');\n stmt.bind([date]);\n\n const dates: CalendarDate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n dates.push(rowToCalendarDate(row));\n }\n\n stmt.free();\n return dates;\n}\n\n/**\n * Convert database row to Calendar object\n */\nfunction rowToCalendar(row: Record): Calendar {\n return {\n service_id: String(row.service_id),\n monday: Number(row.monday),\n tuesday: Number(row.tuesday),\n wednesday: Number(row.wednesday),\n thursday: Number(row.thursday),\n friday: Number(row.friday),\n saturday: Number(row.saturday),\n sunday: Number(row.sunday),\n start_date: String(row.start_date),\n end_date: String(row.end_date),\n };\n}\n\n/**\n * Convert database row to CalendarDate object\n */\nfunction rowToCalendarDate(row: Record): CalendarDate {\n return {\n service_id: String(row.service_id),\n date: String(row.date),\n exception_type: Number(row.exception_type),\n };\n}\n","import type { Database} from 'sql.js';\nimport type { VehiclePosition } from '../types/gtfs-rt';\nimport type { VehiclePositionFilters } from '../types/gtfs-rt';\n\nexport type { VehiclePositionFilters };\n\n/**\n * Parse vehicle position from database row\n */\nexport function parseVehiclePosition(row: Record): VehiclePosition {\n const vp: VehiclePosition = {\n trip_id: String(row.trip_id),\n route_id: row.route_id ? String(row.route_id) : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n\n // Vehicle descriptor\n if (row.vehicle_id || row.vehicle_label || row.vehicle_license_plate) {\n vp.vehicle = {\n id: row.vehicle_id ? String(row.vehicle_id) : undefined,\n label: row.vehicle_label ? String(row.vehicle_label) : undefined,\n license_plate: row.vehicle_license_plate ? String(row.vehicle_license_plate) : undefined\n };\n }\n\n // Position\n if (row.latitude !== null && row.longitude !== null) {\n vp.position = {\n latitude: Number(row.latitude),\n longitude: Number(row.longitude),\n bearing: row.bearing !== null ? Number(row.bearing) : undefined,\n odometer: row.odometer !== null ? Number(row.odometer) : undefined,\n speed: row.speed !== null ? Number(row.speed) : undefined\n };\n }\n\n // Other fields\n if (row.current_stop_sequence !== null) {\n vp.current_stop_sequence = Number(row.current_stop_sequence);\n }\n if (row.stop_id) {\n vp.stop_id = String(row.stop_id);\n }\n if (row.current_status !== null) {\n vp.current_status = Number(row.current_status);\n }\n if (row.timestamp !== null) {\n vp.timestamp = Number(row.timestamp);\n }\n if (row.congestion_level !== null) {\n vp.congestion_level = Number(row.congestion_level);\n }\n if (row.occupancy_status !== null) {\n vp.occupancy_status = Number(row.occupancy_status);\n }\n\n return vp;\n}\n\n/**\n * Get vehicle positions with optional filters\n */\nexport function getVehiclePositions(\n db: Database,\n filters: VehiclePositionFilters = {},\n stalenessThreshold: number = 120\n): VehiclePosition[] {\n const { tripId, routeId, vehicleId, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by route_id\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n // Filter by vehicle_id\n if (vehicleId) {\n conditions.push('vehicle_id = ?');\n params.push(vehicleId);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_vehicle_positions';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const positions: VehiclePosition[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n positions.push(parseVehiclePosition(row));\n }\n\n stmt.free();\n return positions;\n}\n\n/**\n * Get vehicle position by trip ID\n */\nexport function getVehiclePositionByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): VehiclePosition | null {\n const positions = getVehiclePositions(db, { tripId, limit: 1 }, stalenessThreshold);\n return positions.length > 0 ? positions[0] : null;\n}\n\n/**\n * Get all vehicle positions without staleness filtering (for debugging)\n */\nexport function getAllVehiclePositions(db: Database): VehiclePosition[] {\n const sql = 'SELECT * FROM rt_vehicle_positions ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const positions: VehiclePosition[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n positions.push(parseVehiclePosition(row));\n }\n\n stmt.free();\n return positions;\n}\n","/**\n * Trip Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Trip } from '../types/gtfs';\nimport type { TripRealtime, VehiclePosition } from '../types/gtfs-rt';\nimport { parseVehiclePosition } from './rt-vehicle-positions';\n\nexport interface TripFilters {\n tripId?: string;\n routeId?: string;\n serviceIds?: string[];\n directionId?: number;\n agencyId?: string;\n includeRealtime?: boolean;\n limit?: number;\n}\n\nexport interface TripWithRealtime extends Trip {\n realtime?: TripRealtime;\n}\n\n/**\n * Merge realtime data with trips\n */\nfunction mergeRealtimeData(\n trips: Trip[],\n db: Database,\n stalenessThreshold: number\n): TripWithRealtime[] {\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n\n const tripIds = trips.map(t => t.trip_id);\n if (tripIds.length === 0) return trips;\n\n const placeholders = tripIds.map(() => '?').join(', ');\n\n // Get vehicle positions\n const vpStmt = db.prepare(`\n SELECT * FROM rt_vehicle_positions\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n vpStmt.bind([...tripIds, staleThreshold]);\n\n const vpMap = new Map();\n while (vpStmt.step()) {\n const row = vpStmt.getAsObject() as Record;\n const vp = parseVehiclePosition(row);\n vpMap.set(vp.trip_id, vp);\n }\n vpStmt.free();\n\n // Get trip updates\n const tuStmt = db.prepare(`\n SELECT * FROM rt_trip_updates\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n tuStmt.bind([...tripIds, staleThreshold]);\n\n const tuMap = new Map();\n while (tuStmt.step()) {\n const row = tuStmt.getAsObject() as Record;\n const tripId = String(row.trip_id);\n tuMap.set(tripId, {\n delay: row.delay !== null ? Number(row.delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n });\n }\n tuStmt.free();\n\n // Merge realtime data\n return trips.map((trip): TripWithRealtime => {\n const vp = vpMap.get(trip.trip_id);\n const tu = tuMap.get(trip.trip_id);\n\n if (!vp && !tu) {\n return { ...trip, realtime: { vehicle_position: null, trip_update: null } };\n }\n\n return {\n ...trip,\n realtime: {\n vehicle_position: vp || null,\n trip_update: tu || null\n }\n };\n });\n}\n\n/**\n * Get trips with optional filters\n */\nexport function getTrips(\n db: Database,\n filters: TripFilters = {},\n stalenessThreshold: number = 120\n): Trip[] | TripWithRealtime[] {\n const { tripId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit } = filters;\n\n // Determine if we need to join with routes table\n const needsRoutesJoin = agencyId !== undefined;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (tripId) {\n conditions.push(needsRoutesJoin ? 't.trip_id = ?' : 'trip_id = ?');\n params.push(tripId);\n }\n\n if (routeId) {\n conditions.push(needsRoutesJoin ? 't.route_id = ?' : 'route_id = ?');\n params.push(routeId);\n }\n\n if (serviceIds && serviceIds.length > 0) {\n const placeholders = serviceIds.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`);\n params.push(...serviceIds);\n }\n\n if (directionId !== undefined) {\n conditions.push(needsRoutesJoin ? 't.direction_id = ?' : 'direction_id = ?');\n params.push(directionId);\n }\n\n if (agencyId) {\n conditions.push('r.agency_id = ?');\n params.push(agencyId);\n }\n\n // Build SQL query\n let sql = needsRoutesJoin\n ? 'SELECT t.* FROM trips t INNER JOIN routes r ON t.route_id = r.route_id'\n : 'SELECT * FROM trips';\n\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n\n // Merge realtime data if requested\n if (includeRealtime) {\n return mergeRealtimeData(trips, db, stalenessThreshold);\n }\n\n return trips;\n}\n\n/**\n * Get a trip by its trip_id\n */\nexport function getTripById(db: Database, tripId: string): Trip | null {\n const stmt = db.prepare('SELECT * FROM trips WHERE trip_id = ?');\n stmt.bind([tripId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToTrip(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get trips for a route\n */\nexport function getTripsByRoute(db: Database, routeId: string): Trip[] {\n const stmt = db.prepare('SELECT * FROM trips WHERE route_id = ?');\n stmt.bind([routeId]);\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n return trips;\n}\n\n/**\n * Get trips for a route and service (active on a given date)\n */\nexport function getTripsByRouteAndService(\n db: Database,\n routeId: string,\n serviceIds: string[]\n): Trip[] {\n if (serviceIds.length === 0) {\n return [];\n }\n\n const placeholders = serviceIds.map(() => '?').join(', ');\n const stmt = db.prepare(\n `SELECT * FROM trips WHERE route_id = ? AND service_id IN (${placeholders})`\n );\n stmt.bind([routeId, ...serviceIds]);\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n return trips;\n}\n\n/**\n * Get trips for a route, service, and direction\n */\nexport function getTripsByRouteServiceAndDirection(\n db: Database,\n routeId: string,\n serviceIds: string[],\n directionId: number\n): Trip[] {\n if (serviceIds.length === 0) {\n return [];\n }\n\n const placeholders = serviceIds.map(() => '?').join(', ');\n const stmt = db.prepare(\n `SELECT * FROM trips WHERE route_id = ? AND service_id IN (${placeholders}) AND direction_id = ?`\n );\n stmt.bind([routeId, ...serviceIds, directionId]);\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n return trips;\n}\n\n/**\n * Get all trips for a service (active on a given date)\n */\nexport function getTripsByService(db: Database, serviceIds: string[]): Trip[] {\n if (serviceIds.length === 0) {\n return [];\n }\n\n const placeholders = serviceIds.map(() => '?').join(', ');\n const stmt = db.prepare(`SELECT * FROM trips WHERE service_id IN (${placeholders})`);\n stmt.bind(serviceIds);\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n return trips;\n}\n\n/**\n * Convert database row to Trip object\n */\nfunction rowToTrip(row: Record): Trip {\n return {\n trip_id: String(row.trip_id),\n route_id: String(row.route_id),\n service_id: String(row.service_id),\n trip_headsign: row.trip_headsign ? String(row.trip_headsign) : undefined,\n trip_short_name: row.trip_short_name ? String(row.trip_short_name) : undefined,\n direction_id: row.direction_id !== null ? Number(row.direction_id) : undefined,\n block_id: row.block_id ? String(row.block_id) : undefined,\n shape_id: row.shape_id ? String(row.shape_id) : undefined,\n wheelchair_accessible: row.wheelchair_accessible !== null ? Number(row.wheelchair_accessible) : undefined,\n bikes_allowed: row.bikes_allowed !== null ? Number(row.bikes_allowed) : undefined,\n };\n}\n","/**\n * Stop Time Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { StopTime } from '../types/gtfs';\nimport type { StopTimeRealtime } from '../types/gtfs-rt';\n\nexport interface StopTimeFilters {\n tripId?: string;\n stopId?: string;\n routeId?: string;\n serviceIds?: string[];\n directionId?: number;\n agencyId?: string;\n includeRealtime?: boolean;\n limit?: number;\n}\n\nexport interface StopTimeWithRealtime extends StopTime {\n realtime?: StopTimeRealtime;\n}\n\n/**\n * Merge realtime data with stop times\n */\nfunction mergeRealtimeData(\n stopTimes: StopTime[],\n db: Database,\n stalenessThreshold: number\n): StopTimeWithRealtime[] {\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n\n // Build map of trip_id -> stop times for this query\n const tripIds = Array.from(new Set(stopTimes.map(st => st.trip_id)));\n if (tripIds.length === 0) return stopTimes;\n\n const placeholders = tripIds.map(() => '?').join(', ');\n const stmt = db.prepare(`\n SELECT trip_id, stop_sequence, stop_id,\n arrival_delay, departure_delay, schedule_relationship\n FROM rt_stop_time_updates\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n stmt.bind([...tripIds, staleThreshold]);\n\n // Build map of trip_id+stop_sequence -> RT data\n const rtMap = new Map();\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n const key = `${row.trip_id}_${row.stop_sequence}`;\n rtMap.set(key, {\n arrival_delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,\n departure_delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n });\n }\n stmt.free();\n\n // Merge RT data with stop times\n return stopTimes.map((st): StopTimeWithRealtime => {\n const key = `${st.trip_id}_${st.stop_sequence}`;\n const rtData = rtMap.get(key);\n\n if (rtData) {\n return { ...st, realtime: rtData };\n }\n return st;\n });\n}\n\n/**\n * Get stop times with optional filters\n */\nexport function getStopTimes(\n db: Database,\n filters: StopTimeFilters = {},\n stalenessThreshold: number = 120\n): StopTime[] | StopTimeWithRealtime[] {\n const { tripId, stopId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit } = filters;\n\n // Determine if we need to join with trips and/or routes table\n const needsTripsJoin = routeId || serviceIds || directionId !== undefined || agencyId !== undefined;\n const needsRoutesJoin = agencyId !== undefined;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (tripId) {\n conditions.push(needsTripsJoin ? 'st.trip_id = ?' : 'trip_id = ?');\n params.push(tripId);\n }\n\n if (stopId) {\n conditions.push(needsTripsJoin ? 'st.stop_id = ?' : 'stop_id = ?');\n params.push(stopId);\n }\n\n if (routeId) {\n conditions.push('t.route_id = ?');\n params.push(routeId);\n }\n\n if (serviceIds && serviceIds.length > 0) {\n const placeholders = serviceIds.map(() => '?').join(', ');\n conditions.push(`t.service_id IN (${placeholders})`);\n params.push(...serviceIds);\n }\n\n if (directionId !== undefined) {\n conditions.push('t.direction_id = ?');\n params.push(directionId);\n }\n\n if (agencyId) {\n conditions.push('r.agency_id = ?');\n params.push(agencyId);\n }\n\n // Build SQL query\n let sql: string;\n if (needsRoutesJoin) {\n sql = 'SELECT st.* FROM stop_times st INNER JOIN trips t ON st.trip_id = t.trip_id INNER JOIN routes r ON t.route_id = r.route_id';\n } else if (needsTripsJoin) {\n sql = 'SELECT st.* FROM stop_times st INNER JOIN trips t ON st.trip_id = t.trip_id';\n } else {\n sql = 'SELECT * FROM stop_times';\n }\n\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n\n // Order by stop_sequence if filtering by trip, otherwise by arrival_time\n sql += tripId ? ' ORDER BY stop_sequence' : ' ORDER BY arrival_time';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n\n // Merge realtime data if requested\n if (includeRealtime) {\n return mergeRealtimeData(stopTimes, db, stalenessThreshold);\n }\n\n return stopTimes;\n}\n\n/**\n * Get stop times for a trip\n */\nexport function getStopTimesByTrip(db: Database, tripId: string): StopTime[] {\n const stmt = db.prepare('SELECT * FROM stop_times WHERE trip_id = ? ORDER BY stop_sequence');\n stmt.bind([tripId]);\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n return stopTimes;\n}\n\n/**\n * Get stop times for a stop\n */\nexport function getStopTimesByStop(db: Database, stopId: string, limit = 100): StopTime[] {\n const stmt = db.prepare(\n 'SELECT * FROM stop_times WHERE stop_id = ? ORDER BY arrival_time LIMIT ?'\n );\n stmt.bind([stopId, limit]);\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n return stopTimes;\n}\n\n/**\n * Get stop times for a stop and specific trips (filtered by route/date)\n */\nexport function getStopTimesByStopAndTrips(\n db: Database,\n stopId: string,\n tripIds: string[]\n): StopTime[] {\n if (tripIds.length === 0) {\n return [];\n }\n\n const placeholders = tripIds.map(() => '?').join(', ');\n const stmt = db.prepare(\n `SELECT * FROM stop_times\n WHERE stop_id = ? AND trip_id IN (${placeholders})\n ORDER BY arrival_time`\n );\n stmt.bind([stopId, ...tripIds]);\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n return stopTimes;\n}\n\n/**\n * Get stop times for a stop, route, and direction on a specific date\n */\nexport function getStopTimesForStopRouteDirection(\n db: Database,\n stopId: string,\n routeId: string,\n serviceIds: string[],\n directionId?: number\n): StopTime[] {\n if (serviceIds.length === 0) {\n return [];\n }\n\n const placeholders = serviceIds.map(() => '?').join(', ');\n\n let sql = `\n SELECT st.* FROM stop_times st\n INNER JOIN trips t ON st.trip_id = t.trip_id\n WHERE st.stop_id = ?\n AND t.route_id = ?\n AND t.service_id IN (${placeholders})\n `;\n\n const params: (string | number)[] = [stopId, routeId, ...serviceIds];\n\n if (directionId !== undefined) {\n sql += ' AND t.direction_id = ?';\n params.push(directionId);\n }\n\n sql += ' ORDER BY st.arrival_time';\n\n const stmt = db.prepare(sql);\n stmt.bind(params);\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n return stopTimes;\n}\n\n/**\n * Convert database row to StopTime object\n */\nfunction rowToStopTime(row: Record): StopTime {\n return {\n trip_id: String(row.trip_id),\n arrival_time: String(row.arrival_time),\n departure_time: String(row.departure_time),\n stop_id: String(row.stop_id),\n stop_sequence: Number(row.stop_sequence),\n stop_headsign: row.stop_headsign ? String(row.stop_headsign) : undefined,\n pickup_type: row.pickup_type !== null ? Number(row.pickup_type) : undefined,\n drop_off_type: row.drop_off_type !== null ? Number(row.drop_off_type) : undefined,\n continuous_pickup: row.continuous_pickup !== null ? Number(row.continuous_pickup) : undefined,\n continuous_drop_off: row.continuous_drop_off !== null ? Number(row.continuous_drop_off) : undefined,\n shape_dist_traveled: row.shape_dist_traveled !== null ? Number(row.shape_dist_traveled) : undefined,\n timepoint: row.timepoint !== null ? Number(row.timepoint) : undefined,\n };\n}\n","import type { Database } from 'sql.js';\nimport type { Alert, EntitySelector, TimeRange, TranslatedString } from '../types/gtfs-rt';\nimport type { AlertFilters } from '../types/gtfs-rt';\n\nexport type { AlertFilters };\n\n/**\n * Parse JSON fields from database\n */\nfunction parseAlert(row: Record): Alert {\n return {\n id: String(row.id),\n active_period: row.active_period ? JSON.parse(String(row.active_period)) as TimeRange[] : [],\n informed_entity: row.informed_entity ? JSON.parse(String(row.informed_entity)) as EntitySelector[] : [],\n cause: row.cause ? Number(row.cause) : undefined,\n effect: row.effect ? Number(row.effect) : undefined,\n url: row.url ? JSON.parse(String(row.url)) as TranslatedString : undefined,\n header_text: row.header_text ? JSON.parse(String(row.header_text)) as TranslatedString : undefined,\n description_text: row.description_text ? JSON.parse(String(row.description_text)) as TranslatedString : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n}\n\n/**\n * Check if an alert is currently active based on its active_period\n */\nfunction isAlertActive(alert: Alert, now: number): boolean {\n // If no active periods defined, assume always active\n if (!alert.active_period || alert.active_period.length === 0) {\n return true;\n }\n\n // Check if current time falls within any active period\n for (const period of alert.active_period) {\n const start = period.start || 0;\n const end = period.end || Number.MAX_SAFE_INTEGER;\n\n if (now >= start && now <= end) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if an alert affects the specified entity\n */\nfunction alertAffectsEntity(alert: Alert, filters: AlertFilters): boolean {\n if (!alert.informed_entity || alert.informed_entity.length === 0) {\n return true; // No specific entities, assume it affects all\n }\n\n for (const entity of alert.informed_entity) {\n // Check route_id\n if (filters.routeId && entity.route_id === filters.routeId) {\n return true;\n }\n\n // Check stop_id\n if (filters.stopId && entity.stop_id === filters.stopId) {\n return true;\n }\n\n // Check trip_id\n if (filters.tripId && entity.trip?.trip_id === filters.tripId) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Get alerts with optional filters\n */\nexport function getAlerts(db: Database, filters: AlertFilters = {}, stalenessThreshold: number = 120): Alert[] {\n const {\n alertId,\n activeOnly,\n routeId,\n stopId,\n tripId,\n cause,\n effect,\n limit\n } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by alert ID\n if (alertId) {\n conditions.push('id = ?');\n params.push(alertId);\n }\n\n // Filter by cause\n if (cause !== undefined) {\n conditions.push('cause = ?');\n params.push(cause);\n }\n\n // Filter by effect\n if (effect !== undefined) {\n conditions.push('effect = ?');\n params.push(effect);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_alerts';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const alerts: Alert[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n const alert = parseAlert(row);\n\n // Apply activeOnly filter in application code\n if (activeOnly && !isAlertActive(alert, now)) {\n continue;\n }\n\n // Apply entity filters in application code (since informed_entity is JSON)\n if (routeId || stopId || tripId) {\n if (!alertAffectsEntity(alert, filters)) {\n continue;\n }\n }\n\n alerts.push(alert);\n }\n\n stmt.free();\n return alerts;\n}\n\n/**\n * Get alert by ID\n */\nexport function getAlertById(db: Database, alertId: string, stalenessThreshold: number = 120): Alert | null {\n const alerts = getAlerts(db, { alertId, limit: 1 }, stalenessThreshold);\n return alerts.length > 0 ? alerts[0] : null;\n}\n\n/**\n * Get all alerts without staleness filtering (for debugging)\n */\nexport function getAllAlerts(db: Database): Alert[] {\n const sql = 'SELECT * FROM rt_alerts ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const alerts: Alert[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n alerts.push(parseAlert(row));\n }\n\n stmt.free();\n return alerts;\n}\n","import type { Database } from 'sql.js';\nimport type { TripUpdate } from '../types/gtfs-rt';\n\nexport interface TripUpdateFilters {\n tripId?: string;\n routeId?: string;\n vehicleId?: string;\n limit?: number;\n}\n\n/**\n * Parse trip update from database row\n */\nexport function parseTripUpdate(row: Record): TripUpdate {\n const tu: TripUpdate = {\n trip_id: String(row.trip_id),\n route_id: row.route_id ? String(row.route_id) : undefined,\n stop_time_update: [], // Will be populated separately\n timestamp: row.timestamp !== null ? Number(row.timestamp) : undefined,\n delay: row.delay !== null ? Number(row.delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n\n // Vehicle descriptor\n if (row.vehicle_id || row.vehicle_label || row.vehicle_license_plate) {\n tu.vehicle = {\n id: row.vehicle_id ? String(row.vehicle_id) : undefined,\n label: row.vehicle_label ? String(row.vehicle_label) : undefined,\n license_plate: row.vehicle_license_plate ? String(row.vehicle_license_plate) : undefined\n };\n }\n\n return tu;\n}\n\n/**\n * Get trip updates with optional filters\n */\nexport function getTripUpdates(\n db: Database,\n filters: TripUpdateFilters = {},\n stalenessThreshold: number = 120\n): TripUpdate[] {\n const { tripId, routeId, vehicleId, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by route_id\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n // Filter by vehicle_id\n if (vehicleId) {\n conditions.push('vehicle_id = ?');\n params.push(vehicleId);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_trip_updates';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const tripUpdates: TripUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n tripUpdates.push(parseTripUpdate(row));\n }\n\n stmt.free();\n return tripUpdates;\n}\n\n/**\n * Get trip update by trip ID\n */\nexport function getTripUpdateByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): TripUpdate | null {\n const updates = getTripUpdates(db, { tripId, limit: 1 }, stalenessThreshold);\n return updates.length > 0 ? updates[0] : null;\n}\n\n/**\n * Get all trip updates without staleness filtering (for debugging)\n */\nexport function getAllTripUpdates(db: Database): TripUpdate[] {\n const sql = 'SELECT * FROM rt_trip_updates ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const tripUpdates: TripUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n tripUpdates.push(parseTripUpdate(row));\n }\n\n stmt.free();\n return tripUpdates;\n}\n","import type { Database } from 'sql.js';\nimport type { StopTimeUpdate } from '../types/gtfs-rt';\n\nexport interface StopTimeUpdateFilters {\n tripId?: string;\n stopId?: string;\n stopSequence?: number;\n limit?: number;\n}\n\n/**\n * Parse stop time update from database row\n */\nfunction parseStopTimeUpdate(row: Record): StopTimeUpdate {\n const stu: StopTimeUpdate = {\n stop_sequence: row.stop_sequence !== null ? Number(row.stop_sequence) : undefined,\n stop_id: row.stop_id ? String(row.stop_id) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n };\n\n // Arrival event\n if (row.arrival_delay !== null || row.arrival_time !== null || row.arrival_uncertainty !== null) {\n stu.arrival = {\n delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,\n time: row.arrival_time !== null ? Number(row.arrival_time) : undefined,\n uncertainty: row.arrival_uncertainty !== null ? Number(row.arrival_uncertainty) : undefined\n };\n }\n\n // Departure event\n if (row.departure_delay !== null || row.departure_time !== null || row.departure_uncertainty !== null) {\n stu.departure = {\n delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,\n time: row.departure_time !== null ? Number(row.departure_time) : undefined,\n uncertainty: row.departure_uncertainty !== null ? Number(row.departure_uncertainty) : undefined\n };\n }\n\n return stu;\n}\n\n/**\n * Extended StopTimeUpdate with trip_id and rt_last_updated for debugging\n */\nexport interface StopTimeUpdateWithMetadata extends StopTimeUpdate {\n trip_id: string;\n rt_last_updated: number;\n}\n\n/**\n * Parse stop time update with metadata from database row\n */\nfunction parseStopTimeUpdateWithMetadata(row: Record): StopTimeUpdateWithMetadata {\n const stu = parseStopTimeUpdate(row) as StopTimeUpdateWithMetadata;\n stu.trip_id = String(row.trip_id);\n stu.rt_last_updated = Number(row.rt_last_updated);\n return stu;\n}\n\n/**\n * Get stop time updates with optional filters\n */\nexport function getStopTimeUpdates(\n db: Database,\n filters: StopTimeUpdateFilters = {},\n stalenessThreshold: number = 120\n): StopTimeUpdate[] {\n const { tripId, stopId, stopSequence, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by stop_id\n if (stopId) {\n conditions.push('stop_id = ?');\n params.push(stopId);\n }\n\n // Filter by stop_sequence\n if (stopSequence !== undefined) {\n conditions.push('stop_sequence = ?');\n params.push(stopSequence);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_stop_time_updates';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY trip_id, stop_sequence';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stopTimeUpdates: StopTimeUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimeUpdates.push(parseStopTimeUpdate(row));\n }\n\n stmt.free();\n return stopTimeUpdates;\n}\n\n/**\n * Get stop time updates for a specific trip\n */\nexport function getStopTimeUpdatesByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): StopTimeUpdate[] {\n return getStopTimeUpdates(db, { tripId }, stalenessThreshold);\n}\n\n/**\n * Get all stop time updates without staleness filtering (for debugging)\n * Returns extended type with trip_id and rt_last_updated for debugging purposes\n */\nexport function getAllStopTimeUpdates(db: Database): StopTimeUpdateWithMetadata[] {\n const sql = 'SELECT * FROM rt_stop_time_updates ORDER BY trip_id, stop_sequence';\n const stmt = db.prepare(sql);\n\n const stopTimeUpdates: StopTimeUpdateWithMetadata[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimeUpdates.push(parseStopTimeUpdateWithMetadata(row));\n }\n\n stmt.free();\n return stopTimeUpdates;\n}\n","/**\n * GTFS Realtime TypeScript types\n * Based on GTFS Realtime v2.0 specification\n */\n\n// Enum types from GTFS-RT specification\n\nexport enum ScheduleRelationship {\n SCHEDULED = 0,\n ADDED = 1,\n UNSCHEDULED = 2,\n CANCELED = 3,\n SKIPPED = 4,\n NO_DATA = 5\n}\n\nexport enum VehicleStopStatus {\n INCOMING_AT = 0,\n STOPPED_AT = 1,\n IN_TRANSIT_TO = 2\n}\n\nexport enum CongestionLevel {\n UNKNOWN_CONGESTION_LEVEL = 0,\n RUNNING_SMOOTHLY = 1,\n STOP_AND_GO = 2,\n CONGESTION = 3,\n SEVERE_CONGESTION = 4\n}\n\nexport enum OccupancyStatus {\n EMPTY = 0,\n MANY_SEATS_AVAILABLE = 1,\n FEW_SEATS_AVAILABLE = 2,\n STANDING_ROOM_ONLY = 3,\n CRUSHED_STANDING_ROOM_ONLY = 4,\n FULL = 5,\n NOT_ACCEPTING_PASSENGERS = 6\n}\n\nexport enum AlertCause {\n UNKNOWN_CAUSE = 1,\n OTHER_CAUSE = 2,\n TECHNICAL_PROBLEM = 3,\n STRIKE = 4,\n DEMONSTRATION = 5,\n ACCIDENT = 6,\n HOLIDAY = 7,\n WEATHER = 8,\n MAINTENANCE = 9,\n CONSTRUCTION = 10,\n POLICE_ACTIVITY = 11,\n MEDICAL_EMERGENCY = 12\n}\n\nexport enum AlertEffect {\n NO_SERVICE = 1,\n REDUCED_SERVICE = 2,\n SIGNIFICANT_DELAYS = 3,\n DETOUR = 4,\n ADDITIONAL_SERVICE = 5,\n MODIFIED_SERVICE = 6,\n OTHER_EFFECT = 7,\n UNKNOWN_EFFECT = 8,\n STOP_MOVED = 9,\n NO_EFFECT = 10,\n ACCESSIBILITY_ISSUE = 11\n}\n\n// Main GTFS-RT types\n\nexport interface TranslatedString {\n translation: Array<{\n text: string;\n language?: string;\n }>;\n}\n\nexport interface EntitySelector {\n agency_id?: string;\n route_id?: string;\n route_type?: number;\n trip?: {\n trip_id?: string;\n route_id?: string;\n direction_id?: number;\n start_time?: string;\n start_date?: string;\n schedule_relationship?: ScheduleRelationship;\n };\n stop_id?: string;\n}\n\nexport interface TimeRange {\n start?: number; // UNIX timestamp\n end?: number; // UNIX timestamp\n}\n\nexport interface Alert {\n id: string;\n active_period: TimeRange[];\n informed_entity: EntitySelector[];\n cause?: AlertCause;\n effect?: AlertEffect;\n url?: TranslatedString;\n header_text?: TranslatedString;\n description_text?: TranslatedString;\n rt_last_updated: number; // UNIX timestamp\n}\n\nexport interface Position {\n latitude: number;\n longitude: number;\n bearing?: number;\n odometer?: number;\n speed?: number;\n}\n\nexport interface VehicleDescriptor {\n id?: string;\n label?: string;\n license_plate?: string;\n}\n\nexport interface VehiclePosition {\n trip_id: string;\n route_id?: string;\n vehicle?: VehicleDescriptor;\n position?: Position;\n current_stop_sequence?: number;\n stop_id?: string;\n current_status?: VehicleStopStatus;\n timestamp?: number;\n congestion_level?: CongestionLevel;\n occupancy_status?: OccupancyStatus;\n rt_last_updated: number; // UNIX timestamp\n}\n\nexport interface StopTimeEvent {\n delay?: number; // seconds\n time?: number; // UNIX timestamp (absolute time)\n uncertainty?: number;\n}\n\nexport interface StopTimeUpdate {\n stop_sequence?: number;\n stop_id?: string;\n arrival?: StopTimeEvent;\n departure?: StopTimeEvent;\n schedule_relationship?: ScheduleRelationship;\n}\n\nexport interface TripUpdate {\n trip_id: string;\n route_id?: string;\n vehicle?: VehicleDescriptor;\n stop_time_update: StopTimeUpdate[];\n timestamp?: number;\n delay?: number;\n schedule_relationship?: ScheduleRelationship;\n rt_last_updated: number; // UNIX timestamp\n}\n\n// Enhanced types for queries with realtime data\n\nexport interface StopTimeRealtime {\n arrival_delay?: number; // seconds\n departure_delay?: number; // seconds\n schedule_relationship?: ScheduleRelationship;\n}\n\nexport interface TripRealtime {\n vehicle_position?: VehiclePosition | null;\n trip_update?: {\n delay?: number;\n schedule_relationship?: ScheduleRelationship;\n } | null;\n}\n\n// Filter types for realtime queries\n\nexport interface AlertFilters {\n alertId?: string;\n activeOnly?: boolean;\n routeId?: string;\n stopId?: string;\n tripId?: string;\n cause?: AlertCause;\n effect?: AlertEffect;\n limit?: number;\n}\n\nexport interface VehiclePositionFilters {\n tripId?: string;\n routeId?: string;\n vehicleId?: string;\n limit?: number;\n}\n\n// Configuration\n\nexport interface RealtimeConfig {\n feedUrls?: string[];\n stalenessThreshold?: number; // seconds, default 120\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIA,iBAA2D;;;ACoBpD,IAAM,cAA6B;AAAA,EACxC;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MACpE,EAAE,MAAM,eAAe,MAAM,QAAQ,UAAU,KAAK;AAAA,MACpD,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,KAAK;AAAA,MACnD,EAAE,MAAM,mBAAmB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACxD,EAAE,MAAM,eAAe,MAAM,QAAQ,UAAU,MAAM;AAAA,MACrD,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACtD,EAAE,MAAM,mBAAmB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACzD,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,MAAM;AAAA,IACxD;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MAClE,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,KAAK;AAAA,MAClD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,KAAK;AAAA,MACjD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,KAAK;AAAA,MACjD,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,MAAM;AAAA,MACjD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,MAAM;AAAA,MAClD,EAAE,MAAM,iBAAiB,MAAM,WAAW,UAAU,MAAM;AAAA,MAC1D,EAAE,MAAM,kBAAkB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACxD,EAAE,MAAM,iBAAiB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACvD,EAAE,MAAM,uBAAuB,MAAM,WAAW,UAAU,MAAM;AAAA,MAChE,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,MAAM;AAAA,MAClD,EAAE,MAAM,iBAAiB,MAAM,QAAQ,UAAU,MAAM;AAAA,IACzD;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,uBAAuB,SAAS,CAAC,WAAW,EAAE;AAAA,MACtD,EAAE,MAAM,uBAAuB,SAAS,CAAC,WAAW,EAAE;AAAA,MACtD,EAAE,MAAM,4BAA4B,SAAS,CAAC,gBAAgB,EAAE;AAAA,IAClE;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MACnE,EAAE,MAAM,oBAAoB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACzD,EAAE,MAAM,mBAAmB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACxD,EAAE,MAAM,cAAc,MAAM,WAAW,UAAU,KAAK;AAAA,MACtD,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,MAAM;AAAA,MACpD,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,eAAe,MAAM,QAAQ,UAAU,MAAM;AAAA,MACrD,EAAE,MAAM,oBAAoB,MAAM,QAAQ,UAAU,MAAM;AAAA,MAC1D,EAAE,MAAM,oBAAoB,MAAM,WAAW,UAAU,MAAM;AAAA,MAC7D,EAAE,MAAM,qBAAqB,MAAM,WAAW,UAAU,MAAM;AAAA,MAC9D,EAAE,MAAM,uBAAuB,MAAM,WAAW,UAAU,MAAM;AAAA,IAClE;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,wBAAwB,SAAS,CAAC,WAAW,EAAE;AAAA,IACzD;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MAClE,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,KAAK;AAAA,MACjD,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,KAAK;AAAA,MACnD,EAAE,MAAM,iBAAiB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACvD,EAAE,MAAM,mBAAmB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACzD,EAAE,MAAM,gBAAgB,MAAM,WAAW,UAAU,MAAM;AAAA,MACzD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,MAAM;AAAA,MAClD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,MAAM;AAAA,MAClD,EAAE,MAAM,yBAAyB,MAAM,WAAW,UAAU,MAAM;AAAA,MAClE,EAAE,MAAM,iBAAiB,MAAM,WAAW,UAAU,MAAM;AAAA,IAC5D;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,sBAAsB,SAAS,CAAC,UAAU,EAAE;AAAA,MACpD,EAAE,MAAM,wBAAwB,SAAS,CAAC,YAAY,EAAE;AAAA,MACxD,EAAE,MAAM,2BAA2B,SAAS,CAAC,YAAY,YAAY,EAAE;AAAA,IACzE;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,KAAK;AAAA,MAChD,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACrD,EAAE,MAAM,kBAAkB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACvD,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,KAAK;AAAA,MAChD,EAAE,MAAM,iBAAiB,MAAM,WAAW,UAAU,KAAK;AAAA,MACzD,EAAE,MAAM,iBAAiB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACvD,EAAE,MAAM,eAAe,MAAM,WAAW,UAAU,MAAM;AAAA,MACxD,EAAE,MAAM,iBAAiB,MAAM,WAAW,UAAU,MAAM;AAAA,MAC1D,EAAE,MAAM,qBAAqB,MAAM,WAAW,UAAU,MAAM;AAAA,MAC9D,EAAE,MAAM,uBAAuB,MAAM,WAAW,UAAU,MAAM;AAAA,MAChE,EAAE,MAAM,uBAAuB,MAAM,QAAQ,UAAU,MAAM;AAAA,MAC7D,EAAE,MAAM,aAAa,MAAM,WAAW,UAAU,MAAM;AAAA,IACxD;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,0BAA0B,SAAS,CAAC,SAAS,EAAE;AAAA,MACvD,EAAE,MAAM,0BAA0B,SAAS,CAAC,SAAS,EAAE;AAAA,MACvD,EAAE,MAAM,gCAAgC,SAAS,CAAC,WAAW,eAAe,EAAE;AAAA,IAChF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MACrE,EAAE,MAAM,UAAU,MAAM,WAAW,UAAU,KAAK;AAAA,MAClD,EAAE,MAAM,WAAW,MAAM,WAAW,UAAU,KAAK;AAAA,MACnD,EAAE,MAAM,aAAa,MAAM,WAAW,UAAU,KAAK;AAAA,MACrD,EAAE,MAAM,YAAY,MAAM,WAAW,UAAU,KAAK;AAAA,MACpD,EAAE,MAAM,UAAU,MAAM,WAAW,UAAU,KAAK;AAAA,MAClD,EAAE,MAAM,YAAY,MAAM,WAAW,UAAU,KAAK;AAAA,MACpD,EAAE,MAAM,UAAU,MAAM,WAAW,UAAU,KAAK;AAAA,MAClD,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,KAAK;AAAA,MACnD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,KAAK;AAAA,IACnD;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,KAAK;AAAA,MACnD,EAAE,MAAM,QAAQ,MAAM,QAAQ,UAAU,KAAK;AAAA,MAC7C,EAAE,MAAM,kBAAkB,MAAM,WAAW,UAAU,KAAK;AAAA,IAC5D;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,iCAAiC,SAAS,CAAC,YAAY,EAAE;AAAA,MACjE,EAAE,MAAM,2BAA2B,SAAS,CAAC,MAAM,EAAE;AAAA,MACrD,EAAE,MAAM,mCAAmC,SAAS,CAAC,cAAc,MAAM,EAAE;AAAA,IAC7E;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MAClE,EAAE,MAAM,SAAS,MAAM,QAAQ,UAAU,KAAK;AAAA,MAC9C,EAAE,MAAM,iBAAiB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACtD,EAAE,MAAM,kBAAkB,MAAM,WAAW,UAAU,KAAK;AAAA,MAC1D,EAAE,MAAM,aAAa,MAAM,WAAW,UAAU,KAAK;AAAA,MACrD,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,qBAAqB,MAAM,WAAW,UAAU,MAAM;AAAA,IAChE;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,KAAK;AAAA,MAChD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,MAAM;AAAA,MAClD,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,kBAAkB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACxD,EAAE,MAAM,eAAe,MAAM,QAAQ,UAAU,MAAM;AAAA,IACvD;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,0BAA0B,SAAS,CAAC,SAAS,EAAE;AAAA,MACvD,EAAE,MAAM,2BAA2B,SAAS,CAAC,UAAU,EAAE;AAAA,IAC3D;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,KAAK;AAAA,MACjD,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACrD,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACrD,EAAE,MAAM,qBAAqB,MAAM,WAAW,UAAU,KAAK;AAAA,MAC7D,EAAE,MAAM,uBAAuB,MAAM,QAAQ,UAAU,MAAM;AAAA,IAC/D;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,uBAAuB,SAAS,CAAC,UAAU,EAAE;AAAA,MACrD,EAAE,MAAM,6BAA6B,SAAS,CAAC,YAAY,mBAAmB,EAAE;AAAA,IAClF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,KAAK;AAAA,MAChD,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,KAAK;AAAA,MACnD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,KAAK;AAAA,MACjD,EAAE,MAAM,gBAAgB,MAAM,WAAW,UAAU,KAAK;AAAA,MACxD,EAAE,MAAM,eAAe,MAAM,WAAW,UAAU,MAAM;AAAA,IAC1D;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,2BAA2B,SAAS,CAAC,SAAS,EAAE;AAAA,IAC1D;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACrD,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,KAAK;AAAA,MACnD,EAAE,MAAM,iBAAiB,MAAM,WAAW,UAAU,KAAK;AAAA,MACzD,EAAE,MAAM,qBAAqB,MAAM,WAAW,UAAU,MAAM;AAAA,IAChE;AAAA,IACA,SAAS;AAAA,MACP,EAAE,MAAM,8BAA8B,SAAS,CAAC,cAAc,EAAE;AAAA,MAChE,EAAE,MAAM,4BAA4B,SAAS,CAAC,YAAY,EAAE;AAAA,IAC9D;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MACrE,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,KAAK;AAAA,MACrD,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,KAAK;AAAA,MACnD,EAAE,MAAM,gBAAgB,MAAM,WAAW,UAAU,KAAK;AAAA,MACxD,EAAE,MAAM,oBAAoB,MAAM,WAAW,UAAU,KAAK;AAAA,MAC5D,EAAE,MAAM,UAAU,MAAM,QAAQ,UAAU,MAAM;AAAA,MAChD,EAAE,MAAM,kBAAkB,MAAM,WAAW,UAAU,MAAM;AAAA,MAC3D,EAAE,MAAM,eAAe,MAAM,WAAW,UAAU,MAAM;AAAA,MACxD,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,iBAAiB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACvD,EAAE,MAAM,0BAA0B,MAAM,QAAQ,UAAU,MAAM;AAAA,IAClE;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MACnE,EAAE,MAAM,eAAe,MAAM,QAAQ,UAAU,KAAK;AAAA,MACpD,EAAE,MAAM,cAAc,MAAM,QAAQ,UAAU,MAAM;AAAA,IACtD;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,uBAAuB,MAAM,QAAQ,UAAU,KAAK;AAAA,MAC5D,EAAE,MAAM,sBAAsB,MAAM,QAAQ,UAAU,KAAK;AAAA,MAC3D,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,KAAK;AAAA,MAClD,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACtD,EAAE,MAAM,mBAAmB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACzD,EAAE,MAAM,iBAAiB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACvD,EAAE,MAAM,gBAAgB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACtD,EAAE,MAAM,sBAAsB,MAAM,QAAQ,UAAU,MAAM;AAAA,MAC5D,EAAE,MAAM,oBAAoB,MAAM,QAAQ,UAAU,MAAM;AAAA,IAC5D;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,MACP,EAAE,MAAM,kBAAkB,MAAM,QAAQ,UAAU,MAAM,YAAY,KAAK;AAAA,MACzE,EAAE,MAAM,qBAAqB,MAAM,QAAQ,UAAU,KAAK;AAAA,MAC1D,EAAE,MAAM,aAAa,MAAM,QAAQ,UAAU,MAAM;AAAA,MACnD,EAAE,MAAM,YAAY,MAAM,QAAQ,UAAU,MAAM;AAAA,MAClD,EAAE,MAAM,WAAW,MAAM,QAAQ,UAAU,MAAM;AAAA,MACjD,EAAE,MAAM,eAAe,MAAM,WAAW,UAAU,MAAM;AAAA,MACxD,EAAE,MAAM,eAAe,MAAM,WAAW,UAAU,MAAM;AAAA,MACxD,EAAE,MAAM,gBAAgB,MAAM,WAAW,UAAU,MAAM;AAAA,MACzD,EAAE,MAAM,mBAAmB,MAAM,QAAQ,UAAU,MAAM;AAAA,MACzD,EAAE,MAAM,qBAAqB,MAAM,QAAQ,UAAU,MAAM;AAAA,MAC3D,EAAE,MAAM,qBAAqB,MAAM,QAAQ,UAAU,MAAM;AAAA,IAC7D;AAAA,EACF;AACF;AAKO,SAAS,uBAAuB,QAA6B;AAClE,QAAM,UAAU,OAAO,QAAQ,IAAI,CAAC,QAAQ;AAC1C,UAAM,QAAQ,CAAC,IAAI,MAAM,IAAI,IAAI;AACjC,QAAI,IAAI,YAAY;AAClB,YAAM,KAAK,aAAa;AAAA,IAC1B;AACA,QAAI,IAAI,YAAY,CAAC,IAAI,YAAY;AACnC,YAAM,KAAK,UAAU;AAAA,IACvB;AACA,WAAO,MAAM,KAAK,GAAG;AAAA,EACvB,CAAC;AAED,SAAO,8BAA8B,OAAO,IAAI,KAAK,QAAQ,KAAK,IAAI,CAAC;AACzE;AAKO,SAAS,uBAAuB,QAA+B;AACpE,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,OAAO,QAAQ,IAAI,CAAC,QAAQ;AACjC,UAAM,SAAS,IAAI,SAAS,YAAY;AACxC,UAAM,UAAU,IAAI,QAAQ,KAAK,IAAI;AACrC,WAAO,UAAU,MAAM,uBAAuB,IAAI,IAAI,OAAO,OAAO,IAAI,KAAK,OAAO;AAAA,EACtF,CAAC;AACH;AAKO,SAAS,yBAAmC;AACjD,QAAM,aAAuB,CAAC;AAE9B,aAAW,UAAU,aAAa;AAChC,eAAW,KAAK,uBAAuB,MAAM,CAAC;AAC9C,eAAW,KAAK,GAAG,uBAAuB,MAAM,CAAC;AAAA,EACnD;AAEA,SAAO;AACT;;;AC5TA,mBAAkB;AAUlB,eAAsB,YAAY,QAA+D;AAC/F,MAAI;AAGJ,MAAI,OAAO,WAAW,UAAU;AAC9B,cAAU,MAAM,SAAS,MAAM;AAAA,EACjC,OAAO;AACL,cAAU;AAAA,EACZ;AAGA,QAAM,MAAM,MAAM,aAAAA,QAAM,UAAU,OAAO;AAGzC,QAAM,QAAmB,CAAC;AAC1B,QAAM,eAAgC,CAAC;AAEvC,MAAI,QAAQ,CAAC,cAAc,SAAS;AAElC,QAAI,CAAC,KAAK,OAAO,aAAa,SAAS,MAAM,GAAG;AAC9C,YAAM,WAAW,aAAa,MAAM,GAAG,EAAE,IAAI,KAAK;AAClD,mBAAa;AAAA,QACX,KAAK,MAAM,QAAQ,EAAE,KAAK,CAAC,YAAY;AACrC,gBAAM,QAAQ,IAAI;AAAA,QACpB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,QAAQ,IAAI,YAAY;AAE9B,SAAO;AACT;AAKA,eAAe,SAAS,QAAsC;AAE5D,QAAM,QAAQ,OAAO,WAAW,SAAS,KAAK,OAAO,WAAW,UAAU;AAG1E,MAAI,OAAO;AACT,QAAI,OAAO,UAAU,aAAa;AAChC,YAAM,WAAW,MAAM,MAAM,MAAM;AACnC,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,MACvF;AACA,aAAO,MAAM,SAAS,YAAY;AAAA,IACpC;AACA,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAIA,QAAM,SAAS,OAAO,YAAY,eACnB,QAAQ,YAAY,QACpB,QAAQ,SAAS,QAAQ;AAExC,MAAI,QAAQ;AAEV,QAAI;AACF,YAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,YAAM,SAAS,MAAM,GAAG,SAAS,SAAS,MAAM;AAChD,aAAO,OAAO,OAAO,MAAM,OAAO,YAAY,OAAO,aAAa,OAAO,UAAU;AAAA,IACrF,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,iCAAiC,KAAK,EAAE;AAAA,IAC1D;AAAA,EACF;AAGA,MAAI,OAAO,UAAU,aAAa;AAChC,UAAM,WAAW,MAAM,MAAM,MAAM;AACnC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,IACvF;AACA,WAAO,MAAM,SAAS,YAAY;AAAA,EACpC;AAEA,QAAM,IAAI,MAAM,sCAAsC;AACxD;;;AC1FA,uBAAiB;AAUV,SAAS,SAAS,MAAyB;AAChD,QAAM,SAAS,iBAAAC,QAAK,MAAM,MAAM;AAAA,IAC9B,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,iBAAiB,CAAC,WAAW,OAAO,KAAK;AAAA,IACzC,WAAW,CAAC,UAAU,MAAM,KAAK;AAAA,EACnC,CAAC;AAED,MAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,YAAQ,KAAK,yBAAyB,OAAO,MAAM;AAAA,EACrD;AAEA,QAAM,UAAU,OAAO,KAAK,UAAU,CAAC;AACvC,QAAM,OAAO,OAAO;AAEpB,SAAO,EAAE,SAAS,KAAK;AACzB;AAKO,SAAS,gBACd,KACA,aACwC;AACxC,QAAM,SAAiD,CAAC;AAGxD,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,WAAW,GAAG;AACrD,UAAM,QAAQ,IAAI,GAAG;AAGrB,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AACzD,aAAO,GAAG,IAAI;AACd;AAAA,IACF;AAEA,QAAI,SAAS,WAAW;AACtB,YAAM,SAAS,SAAS,OAAO,EAAE;AACjC,aAAO,GAAG,IAAI,MAAM,MAAM,IAAI,OAAO;AAAA,IACvC,WAAW,SAAS,QAAQ;AAC1B,YAAM,SAAS,WAAW,KAAK;AAC/B,aAAO,GAAG,IAAI,MAAM,MAAM,IAAI,OAAO;AAAA,IACvC,OAAO;AACL,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;;;AClDA,eAAsB,aAAa,IAAc,OAAkB,WAAqC;AAEtG,QAAM,eAAyC,oBAAI,IAAI;AACvD,aAAW,UAAU,aAAa;AAEhC,iBAAa,IAAI,GAAG,OAAO,IAAI,QAAQ,MAAM;AAAA,EAC/C;AAGA,QAAM,UAAU,IAAI,IAAI,WAAW,IAAI,OAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;AAGlE,aAAW,CAAC,UAAU,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AACvD,UAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,QAAI,CAAC,QAAQ;AAEX;AAAA,IACF;AAGA,QAAI,QAAQ,IAAI,SAAS,YAAY,CAAC,GAAG;AACvC,cAAQ,IAAI,sBAAsB,QAAQ,WAAW,OAAO,IAAI,qBAAqB;AACrF;AAAA,IACF;AAEA,UAAM,cAAc,IAAI,QAAQ,OAAO;AAAA,EACzC;AACF;AAKA,eAAe,cAAc,IAAc,QAAqB,YAAmC;AACjG,QAAM,EAAE,SAAS,KAAK,IAAI,SAAS,UAAU;AAE7C,MAAI,KAAK,WAAW,GAAG;AACrB;AAAA,EACF;AAGA,QAAM,cAA2D,CAAC;AAClE,aAAW,OAAO,OAAO,SAAS;AAChC,gBAAY,IAAI,IAAI,IAAI,IAAI;AAAA,EAC9B;AAGA,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC9E,QAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,QAAM,YAAY,eAAe,OAAO,IAAI,KAAK,QAAQ,KAAK,IAAI,CAAC,aAAa,YAAY;AAE5F,QAAM,OAAO,GAAG,QAAQ,SAAS;AAGjC,aAAW,OAAO,MAAM;AACtB,UAAM,WAAW,gBAAgB,KAAK,WAAW;AACjD,UAAM,SAAS,QAAQ,IAAI,CAAC,QAAQ;AAClC,YAAM,QAAQ,SAAS,GAAG;AAE1B,aAAO,UAAU,QAAQ,UAAU,SAAY,OAAO;AAAA,IACxD,CAAC;AAED,QAAI;AACF,WAAK,IAAI,MAAM;AAAA,IACjB,SAAS,OAAO;AACd,cAAQ,MAAM,4BAA4B,OAAO,IAAI,KAAK,KAAK;AAC/D,cAAQ,MAAM,aAAa,GAAG;AAC9B,YAAM;AAAA,IACR;AAAA,EACF;AAEA,OAAK,KAAK;AACZ;;;AC/EO,SAAS,qBAAqB,IAAoB;AAEvD,KAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAYN;AAGD,KAAG,IAAI,gFAAgF;AAGvF,KAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAoBN;AAED,KAAG,IAAI,sGAAsG;AAC7G,KAAG,IAAI,6FAA6F;AAGpG,KAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAYN;AAED,KAAG,IAAI,4FAA4F;AACnG,KAAG,IAAI,mFAAmF;AAG1F,KAAG,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAgBN;AAED,KAAG,IAAI,sGAAsG;AAC7G,KAAG,IAAI,2FAA2F;AACpG;AAKO,SAAS,kBAAkB,IAAoB;AACpD,KAAG,IAAI,uBAAuB;AAC9B,KAAG,IAAI,kCAAkC;AACzC,KAAG,IAAI,6BAA6B;AACpC,KAAG,IAAI,kCAAkC;AAC3C;;;AClGA,wBAAqB;AAgGrB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoLtB,eAAe,cAAc,QAAqC;AAChE,QAAM,QAAQ,OAAO,WAAW,SAAS,KAAK,OAAO,WAAW,UAAU;AAE1E,MAAI,OAAO;AAET,UAAMC,YAAW,MAAM,MAAM,QAAQ;AAAA,MACnC,SAAS;AAAA,QACP,UAAU;AAAA,MACZ;AAAA,IACF,CAAC;AAED,QAAI,CAACA,UAAS,IAAI;AAChB,YAAM,IAAI,MAAM,qCAAqC,MAAM,KAAKA,UAAS,MAAM,IAAIA,UAAS,UAAU,EAAE;AAAA,IAC1G;AAEA,UAAMC,eAAc,MAAMD,UAAS,YAAY;AAC/C,WAAO,IAAI,WAAWC,YAAW;AAAA,EACnC;AAGA,QAAM,SAAS,OAAO,YAAY,eACnB,QAAQ,YAAY,QACpB,QAAQ,SAAS,QAAQ;AAExC,MAAI,QAAQ;AACV,QAAI;AACF,YAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,YAAM,SAAS,MAAM,GAAG,SAAS,SAAS,MAAM;AAChD,aAAO,IAAI,WAAW,OAAO,OAAO,MAAM,OAAO,YAAY,OAAO,aAAa,OAAO,UAAU,CAAC;AAAA,IACrG,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,oCAAoC,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,IACzH;AAAA,EACF;AAGA,QAAM,WAAW,MAAM,MAAM,QAAQ;AAAA,IACnC,SAAS;AAAA,MACP,UAAU;AAAA,IACZ;AAAA,EACF,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,qCAAqC,MAAM,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,EAC1G;AAEA,QAAM,cAAc,MAAM,SAAS,YAAY;AAC/C,SAAO,IAAI,WAAW,WAAW;AACnC;AAGA,IAAI,aAAmC;AAEvC,SAAS,kBAAiC;AACxC,MAAI,CAAC,YAAY;AACf,iBAAa,kBAAAC,QAAS,MAAM,aAAa,EAAE;AAAA,EAC7C;AACA,SAAO;AACT;AAGA,SAAS,aAAa,KAAqB;AACzC,SAAO,IAAI,QAAQ,UAAU,YAAU,IAAI,OAAO,YAAY,CAAC,EAAE;AACnE;AAEA,SAAS,uBAAuB,KAAuB;AACrD,MAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI,IAAI,sBAAsB;AAAA,EACvC;AACA,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,SAAkC,CAAC;AACzC,eAAW,OAAO,KAAgC;AAChD,UAAI,OAAO,UAAU,eAAe,KAAK,KAAK,GAAG,GAAG;AAClD,cAAM,WAAW,aAAa,GAAG;AACjC,eAAO,QAAQ,IAAI,uBAAwB,IAAgC,GAAG,CAAC;AAAA,MACjF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,SAAS,sBAAsB,IAAyD;AACtF,MAAI,CAAC,MAAM,CAAC,GAAG,eAAe,GAAG,YAAY,WAAW,GAAG;AACzD,WAAO;AAAA,EACT;AACA,SAAO,KAAK,UAAU;AAAA,IACpB,aAAa,GAAG,YAAY,IAAI,CAAC,OAAO;AAAA,MACtC,MAAM,EAAE;AAAA,MACR,UAAU,EAAE,YAAY;AAAA,IAC1B,EAAE;AAAA,EACJ,CAAC;AACH;AAGA,SAAS,aAAa,IAAc,QAAyB,WAAyB;AACpF,QAAM,OAAO,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,GAKvB;AAED,aAAW,SAAS,QAAQ;AAE1B,UAAM,eAAe,MAAM,gBAAgB,MAAM,iBAAiB,CAAC;AACnE,UAAM,iBAAiB,MAAM,kBAAkB,MAAM,mBAAmB,CAAC;AACzE,UAAM,aAAa,MAAM,cAAc,MAAM;AAC7C,UAAM,kBAAkB,MAAM,mBAAmB,MAAM;AAGvD,UAAM,oBAAoB,uBAAuB,YAAY;AAC7D,UAAM,sBAAsB,uBAAuB,cAAc;AAEjE,SAAK,IAAI;AAAA,MACP,MAAM;AAAA,MACN,KAAK,UAAU,iBAAiB;AAAA,MAChC,KAAK,UAAU,mBAAmB;AAAA,MAClC,MAAM,SAAS;AAAA,MACf,MAAM,UAAU;AAAA,MAChB,sBAAsB,MAAM,GAAG;AAAA,MAC/B,sBAAsB,UAAU;AAAA,MAChC,sBAAsB,eAAe;AAAA,MACrC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,OAAK,KAAK;AACZ;AAGA,SAAS,uBAAuB,IAAc,WAAsC,WAAyB;AAC3G,QAAM,OAAO,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOvB;AAED,aAAW,MAAM,WAAW;AAC1B,UAAM,OAAO,GAAG;AAChB,QAAI,CAAC,QAAQ,EAAE,KAAK,UAAU,KAAK,SAAU;AAG7C,UAAM,SAAU,KAAK,UAAU,KAAK;AACpC,UAAM,UAAU,KAAK,WAAW,KAAK;AACrC,UAAM,YAAY,GAAG,SAAS;AAC9B,UAAM,eAAe,GAAG,SAAS;AACjC,UAAM,sBAAsB,GAAG,SAAS,gBAAgB,GAAG,SAAS;AACpE,UAAM,sBAAsB,GAAG,uBAAuB,GAAG;AACzD,UAAM,SAAS,GAAG,UAAU,GAAG;AAC/B,UAAM,gBAAgB,GAAG,iBAAiB,GAAG;AAC7C,UAAM,kBAAkB,GAAG,mBAAmB,GAAG;AACjD,UAAM,kBAAkB,GAAG,mBAAmB,GAAG;AAEjD,SAAK,IAAI;AAAA,MACP;AAAA,MACA,WAAW;AAAA,MACX,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,uBAAuB;AAAA,MACvB,GAAG,UAAU,YAAY;AAAA,MACzB,GAAG,UAAU,aAAa;AAAA,MAC1B,GAAG,UAAU,WAAW;AAAA,MACxB,GAAG,UAAU,YAAY;AAAA,MACzB,GAAG,UAAU,SAAS;AAAA,MACtB,uBAAuB;AAAA,MACvB,UAAU;AAAA,MACV,iBAAiB;AAAA,MACjB,GAAG,aAAa;AAAA,MAChB,mBAAmB;AAAA,MACnB,mBAAmB;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,OAAK,KAAK;AACZ;AAGA,SAAS,kBAAkB,IAAc,SAA+B,WAAyB;AAC/F,QAAM,WAAW,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,GAK3B;AAED,QAAM,eAAe,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAO/B;AAED,aAAW,MAAM,SAAS;AACxB,UAAM,OAAO,GAAG;AAChB,QAAI,CAAC,QAAQ,EAAE,KAAK,UAAU,KAAK,SAAU;AAG7C,UAAM,SAAU,KAAK,UAAU,KAAK;AACpC,UAAM,UAAU,KAAK,WAAW,KAAK;AACrC,UAAM,YAAY,GAAG,SAAS;AAC9B,UAAM,eAAe,GAAG,SAAS;AACjC,UAAM,sBAAsB,GAAG,SAAS,gBAAgB,GAAG,SAAS;AACpE,UAAM,uBAAuB,KAAK,wBAAwB,KAAK;AAC/D,UAAM,iBAAiB,GAAG,kBAAkB,GAAG;AAG/C,aAAS,IAAI;AAAA,MACX;AAAA,MACA,WAAW;AAAA,MACX,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,uBAAuB;AAAA,MACvB,GAAG,aAAa;AAAA,MAChB,GAAG,SAAS;AAAA,MACZ,wBAAwB;AAAA,MACxB;AAAA,IACF,CAAC;AAGD,QAAI,gBAAgB;AAClB,iBAAW,OAAO,gBAAgB;AAChC,cAAM,eAAe,IAAI,gBAAgB,IAAI;AAC7C,cAAM,SAAS,IAAI,UAAU,IAAI;AACjC,cAAM,cAAc,IAAI,wBAAwB,IAAI;AAEpD,qBAAa,IAAI;AAAA,UACf;AAAA,UACA,gBAAgB;AAAA,UAChB,UAAU;AAAA,UACV,IAAI,SAAS,SAAS;AAAA,UACtB,IAAI,SAAS,QAAQ;AAAA,UACrB,IAAI,SAAS,eAAe;AAAA,UAC5B,IAAI,WAAW,SAAS;AAAA,UACxB,IAAI,WAAW,QAAQ;AAAA,UACvB,IAAI,WAAW,eAAe;AAAA,UAC9B,eAAe;AAAA,UACf;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,WAAS,KAAK;AACd,eAAa,KAAK;AACpB;AAMA,eAAsB,iBAAiB,IAAc,UAAmC;AACtF,QAAM,OAAO,gBAAgB;AAC7B,QAAM,cAAc,KAAK,WAAW,8BAA8B;AAGlE,QAAM,gBAAgB,SAAS,IAAI,OAAO,QAAQ;AAChD,QAAI;AACF,YAAM,OAAO,MAAM,cAAc,GAAG;AACpC,YAAM,UAAU,YAAY,OAAO,IAAI;AACvC,aAAO,YAAY,SAAS,SAAS;AAAA,QACnC,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,QACP,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,QAAQ;AAAA,MACV,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,8CAA8C,GAAG,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,IAChI;AAAA,EACF,CAAC;AAGD,QAAM,QAAQ,MAAM,QAAQ,IAAI,aAAa;AAG7C,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAGxC,QAAM,YAA6B,CAAC;AACpC,QAAM,sBAAiD,CAAC;AACxD,QAAM,iBAAuC,CAAC;AAE9C,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,KAAK,OAAQ;AAElB,eAAW,UAAU,KAAK,QAAQ;AAChC,UAAI,OAAO,OAAO;AAChB,kBAAU,KAAK,EAAE,IAAI,OAAO,IAAI,GAAG,OAAO,MAAM,CAAC;AAAA,MACnD;AACA,UAAI,OAAO,SAAS;AAClB,4BAAoB,KAAK,OAAO,OAAO;AAAA,MACzC;AACA,UAAI,OAAO,YAAY;AACrB,uBAAe,KAAK,OAAO,UAAU;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAGA,MAAI,UAAU,SAAS,GAAG;AACxB,iBAAa,IAAI,WAAW,GAAG;AAAA,EACjC;AACA,MAAI,oBAAoB,SAAS,GAAG;AAClC,2BAAuB,IAAI,qBAAqB,GAAG;AAAA,EACrD;AACA,MAAI,eAAe,SAAS,GAAG;AAC7B,sBAAkB,IAAI,gBAAgB,GAAG;AAAA,EAC3C;AACF;;;ACtkBO,SAAS,YAAY,IAAc,UAAyB,CAAC,GAAa;AAC/E,QAAM,EAAE,UAAU,MAAM,IAAI;AAG5B,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAErC,MAAI,UAAU;AACZ,eAAW,KAAK,eAAe;AAC/B,WAAO,KAAK,QAAQ;AAAA,EACtB;AAGA,MAAI,MAAM;AACV,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AACA,SAAO;AACP,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,WAAqB,CAAC;AAC5B,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,aAAS,KAAK,YAAY,GAAG,CAAC;AAAA,EAChC;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,cAAc,IAAc,UAAiC;AAC3E,QAAM,OAAO,GAAG,QAAQ,0CAA0C;AAClE,OAAK,KAAK,CAAC,QAAQ,CAAC;AAEpB,MAAI,KAAK,KAAK,GAAG;AACf,UAAM,MAAM,KAAK,YAAY;AAC7B,SAAK,KAAK;AACV,WAAO,YAAY,GAAG;AAAA,EACxB;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAyBA,SAAS,YAAY,KAAsC;AACzD,SAAO;AAAA,IACL,WAAW,OAAO,IAAI,SAAS;AAAA,IAC/B,aAAa,OAAO,IAAI,WAAW;AAAA,IACnC,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,iBAAiB,OAAO,IAAI,eAAe;AAAA,IAC3C,aAAa,IAAI,cAAc,OAAO,IAAI,WAAW,IAAI;AAAA,IACzD,cAAc,IAAI,eAAe,OAAO,IAAI,YAAY,IAAI;AAAA,IAC5D,iBAAiB,IAAI,kBAAkB,OAAO,IAAI,eAAe,IAAI;AAAA,IACrE,cAAc,IAAI,eAAe,OAAO,IAAI,YAAY,IAAI;AAAA,EAC9D;AACF;;;ACtFO,SAAS,SAAS,IAAc,UAAuB,CAAC,GAAW;AACxE,QAAM,EAAE,QAAQ,UAAU,MAAM,QAAQ,MAAM,IAAI;AAGlD,MAAI,QAAQ;AACV,WAAO,eAAe,IAAI,MAAM;AAAA,EAClC;AAGA,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAErC,MAAI,QAAQ;AACV,eAAW,KAAK,aAAa;AAC7B,WAAO,KAAK,MAAM;AAAA,EACpB;AAEA,MAAI,UAAU;AACZ,eAAW,KAAK,eAAe;AAC/B,WAAO,KAAK,QAAQ;AAAA,EACtB;AAEA,MAAI,MAAM;AACR,eAAW,KAAK,kBAAkB;AAClC,WAAO,KAAK,IAAI,IAAI,GAAG;AAAA,EACzB;AAGA,MAAI,MAAM;AACV,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AACA,SAAO;AACP,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,QAAgB,CAAC;AACvB,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,KAAK,UAAU,GAAG,CAAC;AAAA,EAC3B;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,YAAY,IAAc,QAA6B;AACrE,QAAM,OAAO,GAAG,QAAQ,uCAAuC;AAC/D,OAAK,KAAK,CAAC,MAAM,CAAC;AAElB,MAAI,KAAK,KAAK,GAAG;AACf,UAAM,MAAM,KAAK,YAAY;AAC7B,SAAK,KAAK;AACV,WAAO,UAAU,GAAG;AAAA,EACtB;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AA6DO,SAAS,eAAe,IAAc,QAAwB;AACnE,QAAM,OAAO,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,GAKvB;AACD,OAAK,KAAK,CAAC,MAAM,CAAC;AAElB,QAAM,QAAgB,CAAC;AACvB,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,KAAK,UAAU,GAAG,CAAC;AAAA,EAC3B;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKA,SAAS,UAAU,KAAoC;AACrD,SAAO;AAAA,IACL,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,WAAW,OAAO,IAAI,SAAS;AAAA,IAC/B,UAAU,OAAO,IAAI,QAAQ;AAAA,IAC7B,UAAU,OAAO,IAAI,QAAQ;AAAA,IAC7B,WAAW,IAAI,YAAY,OAAO,IAAI,SAAS,IAAI;AAAA,IACnD,WAAW,IAAI,YAAY,OAAO,IAAI,SAAS,IAAI;AAAA,IACnD,SAAS,IAAI,UAAU,OAAO,IAAI,OAAO,IAAI;AAAA,IAC7C,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AAAA,IAChD,eAAe,IAAI,kBAAkB,OAAO,OAAO,IAAI,aAAa,IAAI;AAAA,IACxE,gBAAgB,IAAI,iBAAiB,OAAO,IAAI,cAAc,IAAI;AAAA,IAClE,eAAe,IAAI,gBAAgB,OAAO,IAAI,aAAa,IAAI;AAAA,IAC/D,qBAAqB,IAAI,wBAAwB,OAAO,OAAO,IAAI,mBAAmB,IAAI;AAAA,IAC1F,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AAAA,IAChD,eAAe,IAAI,gBAAgB,OAAO,IAAI,aAAa,IAAI;AAAA,EACjE;AACF;;;AC1KO,SAAS,UAAU,IAAc,UAAwB,CAAC,GAAY;AAC3E,QAAM,EAAE,SAAS,UAAU,MAAM,IAAI;AAGrC,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAErC,MAAI,SAAS;AACX,eAAW,KAAK,cAAc;AAC9B,WAAO,KAAK,OAAO;AAAA,EACrB;AAEA,MAAI,UAAU;AACZ,eAAW,KAAK,eAAe;AAC/B,WAAO,KAAK,QAAQ;AAAA,EACtB;AAGA,MAAI,MAAM;AACV,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AACA,SAAO;AACP,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,SAAkB,CAAC;AACzB,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,WAAO,KAAK,WAAW,GAAG,CAAC;AAAA,EAC7B;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,aAAa,IAAc,SAA+B;AACxE,QAAM,OAAO,GAAG,QAAQ,yCAAyC;AACjE,OAAK,KAAK,CAAC,OAAO,CAAC;AAEnB,MAAI,KAAK,KAAK,GAAG;AACf,UAAM,MAAM,KAAK,YAAY;AAC7B,SAAK,KAAK;AACV,WAAO,WAAW,GAAG;AAAA,EACvB;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AA4CA,SAAS,WAAW,KAAqC;AACvD,SAAO;AAAA,IACL,UAAU,OAAO,IAAI,QAAQ;AAAA,IAC7B,kBAAkB,OAAO,IAAI,gBAAgB;AAAA,IAC7C,iBAAiB,OAAO,IAAI,eAAe;AAAA,IAC3C,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,WAAW,IAAI,YAAY,OAAO,IAAI,SAAS,IAAI;AAAA,IACnD,YAAY,IAAI,aAAa,OAAO,IAAI,UAAU,IAAI;AAAA,IACtD,WAAW,IAAI,YAAY,OAAO,IAAI,SAAS,IAAI;AAAA,IACnD,aAAa,IAAI,cAAc,OAAO,IAAI,WAAW,IAAI;AAAA,IACzD,kBAAkB,IAAI,mBAAmB,OAAO,IAAI,gBAAgB,IAAI;AAAA,IACxE,kBAAkB,IAAI,qBAAqB,OAAO,OAAO,IAAI,gBAAgB,IAAI;AAAA,IACjF,mBAAmB,IAAI,sBAAsB,OAAO,OAAO,IAAI,iBAAiB,IAAI;AAAA,IACpF,qBAAqB,IAAI,wBAAwB,OAAO,OAAO,IAAI,mBAAmB,IAAI;AAAA,EAC5F;AACF;;;AC3HO,SAAS,oBAAoB,IAAc,MAAwB;AACxE,QAAM,aAAa,oBAAI,IAAY;AAGnC,QAAM,OAAO,SAAS,KAAK,UAAU,GAAG,CAAC,CAAC;AAC1C,QAAM,QAAQ,SAAS,KAAK,UAAU,GAAG,CAAC,CAAC;AAC3C,QAAM,MAAM,SAAS,KAAK,UAAU,GAAG,CAAC,CAAC;AACzC,QAAM,UAAU,IAAI,KAAK,MAAM,QAAQ,GAAG,GAAG;AAC7C,QAAM,YAAY,QAAQ,OAAO;AAGjC,QAAM,YAAY,CAAC,UAAU,UAAU,WAAW,aAAa,YAAY,UAAU,UAAU;AAC/F,QAAM,WAAW,UAAU,SAAS;AAGpC,QAAM,eAAe,GAAG;AAAA,IACtB;AAAA,aACS,QAAQ;AAAA;AAAA;AAAA,EAGnB;AACA,eAAa,KAAK,CAAC,MAAM,IAAI,CAAC;AAE9B,SAAO,aAAa,KAAK,GAAG;AAC1B,UAAM,MAAM,aAAa,YAAY;AACrC,eAAW,IAAI,IAAI,UAAU;AAAA,EAC/B;AACA,eAAa,KAAK;AAGlB,QAAM,iBAAiB,GAAG,QAAQ,sEAAsE;AACxG,iBAAe,KAAK,CAAC,IAAI,CAAC;AAE1B,SAAO,eAAe,KAAK,GAAG;AAC5B,UAAM,MAAM,eAAe,YAAY;AACvC,QAAI,IAAI,mBAAmB,GAAG;AAE5B,iBAAW,IAAI,IAAI,UAAU;AAAA,IAC/B,WAAW,IAAI,mBAAmB,GAAG;AAEnC,iBAAW,OAAO,IAAI,UAAU;AAAA,IAClC;AAAA,EACF;AACA,iBAAe,KAAK;AAEpB,SAAO,MAAM,KAAK,UAAU;AAC9B;AAKO,SAAS,uBAAuB,IAAc,WAAoC;AACvF,QAAM,OAAO,GAAG,QAAQ,6CAA6C;AACrE,OAAK,KAAK,CAAC,SAAS,CAAC;AAErB,MAAI,KAAK,KAAK,GAAG;AACf,UAAM,MAAM,KAAK,YAAY;AAC7B,SAAK,KAAK;AACV,WAAO,cAAc,GAAG;AAAA,EAC1B;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,iBAAiB,IAAc,WAAmC;AAChF,QAAM,OAAO,GAAG,QAAQ,iEAAiE;AACzF,OAAK,KAAK,CAAC,SAAS,CAAC;AAErB,QAAM,QAAwB,CAAC;AAC/B,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,KAAK,kBAAkB,GAAG,CAAC;AAAA,EACnC;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,wBAAwB,IAAc,MAA8B;AAClF,QAAM,OAAO,GAAG,QAAQ,6CAA6C;AACrE,OAAK,KAAK,CAAC,IAAI,CAAC;AAEhB,QAAM,QAAwB,CAAC;AAC/B,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,KAAK,kBAAkB,GAAG,CAAC;AAAA,EACnC;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKA,SAAS,cAAc,KAAwC;AAC7D,SAAO;AAAA,IACL,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,WAAW,OAAO,IAAI,SAAS;AAAA,IAC/B,UAAU,OAAO,IAAI,QAAQ;AAAA,IAC7B,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,UAAU,OAAO,IAAI,QAAQ;AAAA,IAC7B,QAAQ,OAAO,IAAI,MAAM;AAAA,IACzB,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,UAAU,OAAO,IAAI,QAAQ;AAAA,EAC/B;AACF;AAKA,SAAS,kBAAkB,KAA4C;AACrE,SAAO;AAAA,IACL,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,MAAM,OAAO,IAAI,IAAI;AAAA,IACrB,gBAAgB,OAAO,IAAI,cAAc;AAAA,EAC3C;AACF;;;AC/HO,SAAS,qBAAqB,KAA+C;AAClF,QAAM,KAAsB;AAAA,IAC1B,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AAAA,IAChD,iBAAiB,OAAO,IAAI,eAAe;AAAA,EAC7C;AAGA,MAAI,IAAI,cAAc,IAAI,iBAAiB,IAAI,uBAAuB;AACpE,OAAG,UAAU;AAAA,MACX,IAAI,IAAI,aAAa,OAAO,IAAI,UAAU,IAAI;AAAA,MAC9C,OAAO,IAAI,gBAAgB,OAAO,IAAI,aAAa,IAAI;AAAA,MACvD,eAAe,IAAI,wBAAwB,OAAO,IAAI,qBAAqB,IAAI;AAAA,IACjF;AAAA,EACF;AAGA,MAAI,IAAI,aAAa,QAAQ,IAAI,cAAc,MAAM;AACnD,OAAG,WAAW;AAAA,MACZ,UAAU,OAAO,IAAI,QAAQ;AAAA,MAC7B,WAAW,OAAO,IAAI,SAAS;AAAA,MAC/B,SAAS,IAAI,YAAY,OAAO,OAAO,IAAI,OAAO,IAAI;AAAA,MACtD,UAAU,IAAI,aAAa,OAAO,OAAO,IAAI,QAAQ,IAAI;AAAA,MACzD,OAAO,IAAI,UAAU,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,IAClD;AAAA,EACF;AAGA,MAAI,IAAI,0BAA0B,MAAM;AACtC,OAAG,wBAAwB,OAAO,IAAI,qBAAqB;AAAA,EAC7D;AACA,MAAI,IAAI,SAAS;AACf,OAAG,UAAU,OAAO,IAAI,OAAO;AAAA,EACjC;AACA,MAAI,IAAI,mBAAmB,MAAM;AAC/B,OAAG,iBAAiB,OAAO,IAAI,cAAc;AAAA,EAC/C;AACA,MAAI,IAAI,cAAc,MAAM;AAC1B,OAAG,YAAY,OAAO,IAAI,SAAS;AAAA,EACrC;AACA,MAAI,IAAI,qBAAqB,MAAM;AACjC,OAAG,mBAAmB,OAAO,IAAI,gBAAgB;AAAA,EACnD;AACA,MAAI,IAAI,qBAAqB,MAAM;AACjC,OAAG,mBAAmB,OAAO,IAAI,gBAAgB;AAAA,EACnD;AAEA,SAAO;AACT;AAKO,SAAS,oBACd,IACA,UAAkC,CAAC,GACnC,qBAA6B,KACV;AACnB,QAAM,EAAE,QAAQ,SAAS,WAAW,MAAM,IAAI;AAE9C,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAGrC,MAAI,QAAQ;AACV,eAAW,KAAK,aAAa;AAC7B,WAAO,KAAK,MAAM;AAAA,EACpB;AAGA,MAAI,SAAS;AACX,eAAW,KAAK,cAAc;AAC9B,WAAO,KAAK,OAAO;AAAA,EACrB;AAGA,MAAI,WAAW;AACb,eAAW,KAAK,gBAAgB;AAChC,WAAO,KAAK,SAAS;AAAA,EACvB;AAGA,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,iBAAiB,MAAM;AAC7B,aAAW,KAAK,sBAAsB;AACtC,SAAO,KAAK,cAAc;AAG1B,MAAI,MAAM;AACV,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AACA,SAAO;AAEP,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAGA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,YAA+B,CAAC;AACtC,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,cAAU,KAAK,qBAAqB,GAAG,CAAC;AAAA,EAC1C;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,2BACd,IACA,QACA,qBAA6B,KACL;AACxB,QAAM,YAAY,oBAAoB,IAAI,EAAE,QAAQ,OAAO,EAAE,GAAG,kBAAkB;AAClF,SAAO,UAAU,SAAS,IAAI,UAAU,CAAC,IAAI;AAC/C;AAKO,SAAS,uBAAuB,IAAiC;AACtE,QAAM,MAAM;AACZ,QAAM,OAAO,GAAG,QAAQ,GAAG;AAE3B,QAAM,YAA+B,CAAC;AACtC,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,cAAU,KAAK,qBAAqB,GAAG,CAAC;AAAA,EAC1C;AAEA,OAAK,KAAK;AACV,SAAO;AACT;;;AC7HA,SAAS,kBACP,OACA,IACA,oBACoB;AACpB,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,iBAAiB,MAAM;AAE7B,QAAM,UAAU,MAAM,IAAI,OAAK,EAAE,OAAO;AACxC,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AAGrD,QAAM,SAAS,GAAG,QAAQ;AAAA;AAAA,wBAEJ,YAAY;AAAA;AAAA,GAEjC;AACD,SAAO,KAAK,CAAC,GAAG,SAAS,cAAc,CAAC;AAExC,QAAM,QAAQ,oBAAI,IAA6B;AAC/C,SAAO,OAAO,KAAK,GAAG;AACpB,UAAM,MAAM,OAAO,YAAY;AAC/B,UAAM,KAAK,qBAAqB,GAAG;AACnC,UAAM,IAAI,GAAG,SAAS,EAAE;AAAA,EAC1B;AACA,SAAO,KAAK;AAGZ,QAAM,SAAS,GAAG,QAAQ;AAAA;AAAA,wBAEJ,YAAY;AAAA;AAAA,GAEjC;AACD,SAAO,KAAK,CAAC,GAAG,SAAS,cAAc,CAAC;AAExC,QAAM,QAAQ,oBAAI,IAAgE;AAClF,SAAO,OAAO,KAAK,GAAG;AACpB,UAAM,MAAM,OAAO,YAAY;AAC/B,UAAM,SAAS,OAAO,IAAI,OAAO;AACjC,UAAM,IAAI,QAAQ;AAAA,MAChB,OAAO,IAAI,UAAU,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,MAChD,uBAAuB,IAAI,0BAA0B,OAAO,OAAO,IAAI,qBAAqB,IAAI;AAAA,IAClG,CAAC;AAAA,EACH;AACA,SAAO,KAAK;AAGZ,SAAO,MAAM,IAAI,CAAC,SAA2B;AAC3C,UAAM,KAAK,MAAM,IAAI,KAAK,OAAO;AACjC,UAAM,KAAK,MAAM,IAAI,KAAK,OAAO;AAEjC,QAAI,CAAC,MAAM,CAAC,IAAI;AACd,aAAO,EAAE,GAAG,MAAM,UAAU,EAAE,kBAAkB,MAAM,aAAa,KAAK,EAAE;AAAA,IAC5E;AAEA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU;AAAA,QACR,kBAAkB,MAAM;AAAA,QACxB,aAAa,MAAM;AAAA,MACrB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAKO,SAAS,SACd,IACA,UAAuB,CAAC,GACxB,qBAA6B,KACA;AAC7B,QAAM,EAAE,QAAQ,SAAS,YAAY,aAAa,UAAU,iBAAiB,MAAM,IAAI;AAGvF,QAAM,kBAAkB,aAAa;AAGrC,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAErC,MAAI,QAAQ;AACV,eAAW,KAAK,kBAAkB,kBAAkB,aAAa;AACjE,WAAO,KAAK,MAAM;AAAA,EACpB;AAEA,MAAI,SAAS;AACX,eAAW,KAAK,kBAAkB,mBAAmB,cAAc;AACnE,WAAO,KAAK,OAAO;AAAA,EACrB;AAEA,MAAI,cAAc,WAAW,SAAS,GAAG;AACvC,UAAM,eAAe,WAAW,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACxD,eAAW,KAAK,kBAAkB,oBAAoB,YAAY,MAAM,kBAAkB,YAAY,GAAG;AACzG,WAAO,KAAK,GAAG,UAAU;AAAA,EAC3B;AAEA,MAAI,gBAAgB,QAAW;AAC7B,eAAW,KAAK,kBAAkB,uBAAuB,kBAAkB;AAC3E,WAAO,KAAK,WAAW;AAAA,EACzB;AAEA,MAAI,UAAU;AACZ,eAAW,KAAK,iBAAiB;AACjC,WAAO,KAAK,QAAQ;AAAA,EACtB;AAGA,MAAI,MAAM,kBACN,2EACA;AAEJ,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AACA,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,QAAgB,CAAC;AACvB,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,KAAK,UAAU,GAAG,CAAC;AAAA,EAC3B;AAEA,OAAK,KAAK;AAGV,MAAI,iBAAiB;AACnB,WAAO,kBAAkB,OAAO,IAAI,kBAAkB;AAAA,EACxD;AAEA,SAAO;AACT;AAKO,SAAS,YAAY,IAAc,QAA6B;AACrE,QAAM,OAAO,GAAG,QAAQ,uCAAuC;AAC/D,OAAK,KAAK,CAAC,MAAM,CAAC;AAElB,MAAI,KAAK,KAAK,GAAG;AACf,UAAM,MAAM,KAAK,YAAY;AAC7B,SAAK,KAAK;AACV,WAAO,UAAU,GAAG;AAAA,EACtB;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAqGA,SAAS,UAAU,KAAoC;AACrD,SAAO;AAAA,IACL,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,UAAU,OAAO,IAAI,QAAQ;AAAA,IAC7B,YAAY,OAAO,IAAI,UAAU;AAAA,IACjC,eAAe,IAAI,gBAAgB,OAAO,IAAI,aAAa,IAAI;AAAA,IAC/D,iBAAiB,IAAI,kBAAkB,OAAO,IAAI,eAAe,IAAI;AAAA,IACrE,cAAc,IAAI,iBAAiB,OAAO,OAAO,IAAI,YAAY,IAAI;AAAA,IACrE,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AAAA,IAChD,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AAAA,IAChD,uBAAuB,IAAI,0BAA0B,OAAO,OAAO,IAAI,qBAAqB,IAAI;AAAA,IAChG,eAAe,IAAI,kBAAkB,OAAO,OAAO,IAAI,aAAa,IAAI;AAAA,EAC1E;AACF;;;ACjRA,SAASC,mBACP,WACA,IACA,oBACwB;AACxB,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,iBAAiB,MAAM;AAG7B,QAAM,UAAU,MAAM,KAAK,IAAI,IAAI,UAAU,IAAI,QAAM,GAAG,OAAO,CAAC,CAAC;AACnE,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,QAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,QAAM,OAAO,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA,wBAIF,YAAY;AAAA;AAAA,GAEjC;AACD,OAAK,KAAK,CAAC,GAAG,SAAS,cAAc,CAAC;AAGtC,QAAM,QAAQ,oBAAI,IAA8B;AAChD,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,MAAM,GAAG,IAAI,OAAO,IAAI,IAAI,aAAa;AAC/C,UAAM,IAAI,KAAK;AAAA,MACb,eAAe,IAAI,kBAAkB,OAAO,OAAO,IAAI,aAAa,IAAI;AAAA,MACxE,iBAAiB,IAAI,oBAAoB,OAAO,OAAO,IAAI,eAAe,IAAI;AAAA,MAC9E,uBAAuB,IAAI,0BAA0B,OAAO,OAAO,IAAI,qBAAqB,IAAI;AAAA,IAClG,CAAC;AAAA,EACH;AACA,OAAK,KAAK;AAGV,SAAO,UAAU,IAAI,CAAC,OAA6B;AACjD,UAAM,MAAM,GAAG,GAAG,OAAO,IAAI,GAAG,aAAa;AAC7C,UAAM,SAAS,MAAM,IAAI,GAAG;AAE5B,QAAI,QAAQ;AACV,aAAO,EAAE,GAAG,IAAI,UAAU,OAAO;AAAA,IACnC;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAKO,SAAS,aACd,IACA,UAA2B,CAAC,GAC5B,qBAA6B,KACQ;AACrC,QAAM,EAAE,QAAQ,QAAQ,SAAS,YAAY,aAAa,UAAU,iBAAiB,MAAM,IAAI;AAG/F,QAAM,iBAAiB,WAAW,cAAc,gBAAgB,UAAa,aAAa;AAC1F,QAAM,kBAAkB,aAAa;AAGrC,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAErC,MAAI,QAAQ;AACV,eAAW,KAAK,iBAAiB,mBAAmB,aAAa;AACjE,WAAO,KAAK,MAAM;AAAA,EACpB;AAEA,MAAI,QAAQ;AACV,eAAW,KAAK,iBAAiB,mBAAmB,aAAa;AACjE,WAAO,KAAK,MAAM;AAAA,EACpB;AAEA,MAAI,SAAS;AACX,eAAW,KAAK,gBAAgB;AAChC,WAAO,KAAK,OAAO;AAAA,EACrB;AAEA,MAAI,cAAc,WAAW,SAAS,GAAG;AACvC,UAAM,eAAe,WAAW,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACxD,eAAW,KAAK,oBAAoB,YAAY,GAAG;AACnD,WAAO,KAAK,GAAG,UAAU;AAAA,EAC3B;AAEA,MAAI,gBAAgB,QAAW;AAC7B,eAAW,KAAK,oBAAoB;AACpC,WAAO,KAAK,WAAW;AAAA,EACzB;AAEA,MAAI,UAAU;AACZ,eAAW,KAAK,iBAAiB;AACjC,WAAO,KAAK,QAAQ;AAAA,EACtB;AAGA,MAAI;AACJ,MAAI,iBAAiB;AACnB,UAAM;AAAA,EACR,WAAW,gBAAgB;AACzB,UAAM;AAAA,EACR,OAAO;AACL,UAAM;AAAA,EACR;AAEA,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AAGA,SAAO,SAAS,4BAA4B;AAE5C,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,YAAwB,CAAC;AAC/B,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,cAAU,KAAK,cAAc,GAAG,CAAC;AAAA,EACnC;AAEA,OAAK,KAAK;AAGV,MAAI,iBAAiB;AACnB,WAAOA,mBAAkB,WAAW,IAAI,kBAAkB;AAAA,EAC5D;AAEA,SAAO;AACT;AAKO,SAAS,mBAAmB,IAAc,QAA4B;AAC3E,QAAM,OAAO,GAAG,QAAQ,mEAAmE;AAC3F,OAAK,KAAK,CAAC,MAAM,CAAC;AAElB,QAAM,YAAwB,CAAC;AAC/B,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,cAAU,KAAK,cAAc,GAAG,CAAC;AAAA,EACnC;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAoGA,SAAS,cAAc,KAAwC;AAC7D,SAAO;AAAA,IACL,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,cAAc,OAAO,IAAI,YAAY;AAAA,IACrC,gBAAgB,OAAO,IAAI,cAAc;AAAA,IACzC,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,eAAe,OAAO,IAAI,aAAa;AAAA,IACvC,eAAe,IAAI,gBAAgB,OAAO,IAAI,aAAa,IAAI;AAAA,IAC/D,aAAa,IAAI,gBAAgB,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IAClE,eAAe,IAAI,kBAAkB,OAAO,OAAO,IAAI,aAAa,IAAI;AAAA,IACxE,mBAAmB,IAAI,sBAAsB,OAAO,OAAO,IAAI,iBAAiB,IAAI;AAAA,IACpF,qBAAqB,IAAI,wBAAwB,OAAO,OAAO,IAAI,mBAAmB,IAAI;AAAA,IAC1F,qBAAqB,IAAI,wBAAwB,OAAO,OAAO,IAAI,mBAAmB,IAAI;AAAA,IAC1F,WAAW,IAAI,cAAc,OAAO,OAAO,IAAI,SAAS,IAAI;AAAA,EAC9D;AACF;;;AC9RA,SAAS,WAAW,KAAqC;AACvD,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,eAAe,IAAI,gBAAgB,KAAK,MAAM,OAAO,IAAI,aAAa,CAAC,IAAmB,CAAC;AAAA,IAC3F,iBAAiB,IAAI,kBAAkB,KAAK,MAAM,OAAO,IAAI,eAAe,CAAC,IAAwB,CAAC;AAAA,IACtG,OAAO,IAAI,QAAQ,OAAO,IAAI,KAAK,IAAI;AAAA,IACvC,QAAQ,IAAI,SAAS,OAAO,IAAI,MAAM,IAAI;AAAA,IAC1C,KAAK,IAAI,MAAM,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,IAAwB;AAAA,IACjE,aAAa,IAAI,cAAc,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,IAAwB;AAAA,IACzF,kBAAkB,IAAI,mBAAmB,KAAK,MAAM,OAAO,IAAI,gBAAgB,CAAC,IAAwB;AAAA,IACxG,iBAAiB,OAAO,IAAI,eAAe;AAAA,EAC7C;AACF;AAKA,SAAS,cAAc,OAAc,KAAsB;AAEzD,MAAI,CAAC,MAAM,iBAAiB,MAAM,cAAc,WAAW,GAAG;AAC5D,WAAO;AAAA,EACT;AAGA,aAAW,UAAU,MAAM,eAAe;AACxC,UAAM,QAAQ,OAAO,SAAS;AAC9B,UAAM,MAAM,OAAO,OAAO,OAAO;AAEjC,QAAI,OAAO,SAAS,OAAO,KAAK;AAC9B,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,mBAAmB,OAAc,SAAgC;AACxE,MAAI,CAAC,MAAM,mBAAmB,MAAM,gBAAgB,WAAW,GAAG;AAChE,WAAO;AAAA,EACT;AAEA,aAAW,UAAU,MAAM,iBAAiB;AAE1C,QAAI,QAAQ,WAAW,OAAO,aAAa,QAAQ,SAAS;AAC1D,aAAO;AAAA,IACT;AAGA,QAAI,QAAQ,UAAU,OAAO,YAAY,QAAQ,QAAQ;AACvD,aAAO;AAAA,IACT;AAGA,QAAI,QAAQ,UAAU,OAAO,MAAM,YAAY,QAAQ,QAAQ;AAC7D,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,UAAU,IAAc,UAAwB,CAAC,GAAG,qBAA6B,KAAc;AAC7G,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAGrC,MAAI,SAAS;AACX,eAAW,KAAK,QAAQ;AACxB,WAAO,KAAK,OAAO;AAAA,EACrB;AAGA,MAAI,UAAU,QAAW;AACvB,eAAW,KAAK,WAAW;AAC3B,WAAO,KAAK,KAAK;AAAA,EACnB;AAGA,MAAI,WAAW,QAAW;AACxB,eAAW,KAAK,YAAY;AAC5B,WAAO,KAAK,MAAM;AAAA,EACpB;AAGA,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,iBAAiB,MAAM;AAC7B,aAAW,KAAK,sBAAsB;AACtC,SAAO,KAAK,cAAc;AAG1B,MAAI,MAAM;AACV,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AACA,SAAO;AAEP,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAGA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,SAAkB,CAAC;AACzB,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,QAAQ,WAAW,GAAG;AAG5B,QAAI,cAAc,CAAC,cAAc,OAAO,GAAG,GAAG;AAC5C;AAAA,IACF;AAGA,QAAI,WAAW,UAAU,QAAQ;AAC/B,UAAI,CAAC,mBAAmB,OAAO,OAAO,GAAG;AACvC;AAAA,MACF;AAAA,IACF;AAEA,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,aAAa,IAAc,SAAiB,qBAA6B,KAAmB;AAC1G,QAAM,SAAS,UAAU,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,kBAAkB;AACtE,SAAO,OAAO,SAAS,IAAI,OAAO,CAAC,IAAI;AACzC;AAKO,SAAS,aAAa,IAAuB;AAClD,QAAM,MAAM;AACZ,QAAM,OAAO,GAAG,QAAQ,GAAG;AAE3B,QAAM,SAAkB,CAAC;AACzB,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,WAAO,KAAK,WAAW,GAAG,CAAC;AAAA,EAC7B;AAEA,OAAK,KAAK;AACV,SAAO;AACT;;;ACvKO,SAAS,gBAAgB,KAA0C;AACxE,QAAM,KAAiB;AAAA,IACrB,SAAS,OAAO,IAAI,OAAO;AAAA,IAC3B,UAAU,IAAI,WAAW,OAAO,IAAI,QAAQ,IAAI;AAAA,IAChD,kBAAkB,CAAC;AAAA;AAAA,IACnB,WAAW,IAAI,cAAc,OAAO,OAAO,IAAI,SAAS,IAAI;AAAA,IAC5D,OAAO,IAAI,UAAU,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,IAChD,uBAAuB,IAAI,0BAA0B,OAAO,OAAO,IAAI,qBAAqB,IAAI;AAAA,IAChG,iBAAiB,OAAO,IAAI,eAAe;AAAA,EAC7C;AAGA,MAAI,IAAI,cAAc,IAAI,iBAAiB,IAAI,uBAAuB;AACpE,OAAG,UAAU;AAAA,MACX,IAAI,IAAI,aAAa,OAAO,IAAI,UAAU,IAAI;AAAA,MAC9C,OAAO,IAAI,gBAAgB,OAAO,IAAI,aAAa,IAAI;AAAA,MACvD,eAAe,IAAI,wBAAwB,OAAO,IAAI,qBAAqB,IAAI;AAAA,IACjF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,eACd,IACA,UAA6B,CAAC,GAC9B,qBAA6B,KACf;AACd,QAAM,EAAE,QAAQ,SAAS,WAAW,MAAM,IAAI;AAE9C,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAGrC,MAAI,QAAQ;AACV,eAAW,KAAK,aAAa;AAC7B,WAAO,KAAK,MAAM;AAAA,EACpB;AAGA,MAAI,SAAS;AACX,eAAW,KAAK,cAAc;AAC9B,WAAO,KAAK,OAAO;AAAA,EACrB;AAGA,MAAI,WAAW;AACb,eAAW,KAAK,gBAAgB;AAChC,WAAO,KAAK,SAAS;AAAA,EACvB;AAGA,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,iBAAiB,MAAM;AAC7B,aAAW,KAAK,sBAAsB;AACtC,SAAO,KAAK,cAAc;AAG1B,MAAI,MAAM;AACV,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AACA,SAAO;AAEP,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAGA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,cAA4B,CAAC;AACnC,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,gBAAY,KAAK,gBAAgB,GAAG,CAAC;AAAA,EACvC;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,sBACd,IACA,QACA,qBAA6B,KACV;AACnB,QAAM,UAAU,eAAe,IAAI,EAAE,QAAQ,OAAO,EAAE,GAAG,kBAAkB;AAC3E,SAAO,QAAQ,SAAS,IAAI,QAAQ,CAAC,IAAI;AAC3C;AAKO,SAAS,kBAAkB,IAA4B;AAC5D,QAAM,MAAM;AACZ,QAAM,OAAO,GAAG,QAAQ,GAAG;AAE3B,QAAM,cAA4B,CAAC;AACnC,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,gBAAY,KAAK,gBAAgB,GAAG,CAAC;AAAA,EACvC;AAEA,OAAK,KAAK;AACV,SAAO;AACT;;;ACnHA,SAAS,oBAAoB,KAA8C;AACzE,QAAM,MAAsB;AAAA,IAC1B,eAAe,IAAI,kBAAkB,OAAO,OAAO,IAAI,aAAa,IAAI;AAAA,IACxE,SAAS,IAAI,UAAU,OAAO,IAAI,OAAO,IAAI;AAAA,IAC7C,uBAAuB,IAAI,0BAA0B,OAAO,OAAO,IAAI,qBAAqB,IAAI;AAAA,EAClG;AAGA,MAAI,IAAI,kBAAkB,QAAQ,IAAI,iBAAiB,QAAQ,IAAI,wBAAwB,MAAM;AAC/F,QAAI,UAAU;AAAA,MACZ,OAAO,IAAI,kBAAkB,OAAO,OAAO,IAAI,aAAa,IAAI;AAAA,MAChE,MAAM,IAAI,iBAAiB,OAAO,OAAO,IAAI,YAAY,IAAI;AAAA,MAC7D,aAAa,IAAI,wBAAwB,OAAO,OAAO,IAAI,mBAAmB,IAAI;AAAA,IACpF;AAAA,EACF;AAGA,MAAI,IAAI,oBAAoB,QAAQ,IAAI,mBAAmB,QAAQ,IAAI,0BAA0B,MAAM;AACrG,QAAI,YAAY;AAAA,MACd,OAAO,IAAI,oBAAoB,OAAO,OAAO,IAAI,eAAe,IAAI;AAAA,MACpE,MAAM,IAAI,mBAAmB,OAAO,OAAO,IAAI,cAAc,IAAI;AAAA,MACjE,aAAa,IAAI,0BAA0B,OAAO,OAAO,IAAI,qBAAqB,IAAI;AAAA,IACxF;AAAA,EACF;AAEA,SAAO;AACT;AAaA,SAAS,gCAAgC,KAA0D;AACjG,QAAM,MAAM,oBAAoB,GAAG;AACnC,MAAI,UAAU,OAAO,IAAI,OAAO;AAChC,MAAI,kBAAkB,OAAO,IAAI,eAAe;AAChD,SAAO;AACT;AAKO,SAAS,mBACd,IACA,UAAiC,CAAC,GAClC,qBAA6B,KACX;AAClB,QAAM,EAAE,QAAQ,QAAQ,cAAc,MAAM,IAAI;AAEhD,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAGrC,MAAI,QAAQ;AACV,eAAW,KAAK,aAAa;AAC7B,WAAO,KAAK,MAAM;AAAA,EACpB;AAGA,MAAI,QAAQ;AACV,eAAW,KAAK,aAAa;AAC7B,WAAO,KAAK,MAAM;AAAA,EACpB;AAGA,MAAI,iBAAiB,QAAW;AAC9B,eAAW,KAAK,mBAAmB;AACnC,WAAO,KAAK,YAAY;AAAA,EAC1B;AAGA,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,iBAAiB,MAAM;AAC7B,aAAW,KAAK,sBAAsB;AACtC,SAAO,KAAK,cAAc;AAG1B,MAAI,MAAM;AACV,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,YAAY,WAAW,KAAK,OAAO;AAAA,EAC5C;AACA,SAAO;AAEP,MAAI,OAAO;AACT,WAAO;AACP,WAAO,KAAK,KAAK;AAAA,EACnB;AAGA,QAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,MAAI,OAAO,SAAS,GAAG;AACrB,SAAK,KAAK,MAAM;AAAA,EAClB;AAEA,QAAM,kBAAoC,CAAC;AAC3C,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,oBAAgB,KAAK,oBAAoB,GAAG,CAAC;AAAA,EAC/C;AAEA,OAAK,KAAK;AACV,SAAO;AACT;AAKO,SAAS,2BACd,IACA,QACA,qBAA6B,KACX;AAClB,SAAO,mBAAmB,IAAI,EAAE,OAAO,GAAG,kBAAkB;AAC9D;AAMO,SAAS,sBAAsB,IAA4C;AAChF,QAAM,MAAM;AACZ,QAAM,OAAO,GAAG,QAAQ,GAAG;AAE3B,QAAM,kBAAgD,CAAC;AACvD,SAAO,KAAK,KAAK,GAAG;AAClB,UAAM,MAAM,KAAK,YAAY;AAC7B,oBAAgB,KAAK,gCAAgC,GAAG,CAAC;AAAA,EAC3D;AAEA,OAAK,KAAK;AACV,SAAO;AACT;;;AhB3EO,IAAM,YAAN,MAAM,WAAU;AAAA;AAAA;AAAA;AAAA,EASb,cAAc;AARtB,SAAQ,KAAsB;AAC9B,SAAQ,MAA0B;AAClC,SAAQ,mBAA6B,CAAC;AACtC,SAAQ,qBAA6B;AAAA,EAKd;AAAA;AAAA;AAAA;AAAA,EAKvB,aAAa,QACX,SACA,UAA0D,CAAC,GACvC;AACpB,UAAM,WAAW,IAAI,WAAU;AAC/B,UAAM,SAAS,YAAY,SAAS,OAAO;AAC3C,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,aACX,UACA,UAA0D,CAAC,GACvC;AACpB,UAAM,WAAW,IAAI,WAAU;AAC/B,UAAM,SAAS,iBAAiB,UAAU,OAAO;AACjD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAAY,SAAiB,SAAwE;AAEjH,SAAK,MAAM,QAAQ,OAAQ,UAAM,WAAAC,SAAU,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC,CAAC;AAGvG,SAAK,KAAK,IAAI,KAAK,IAAI,SAAS;AAGhC,UAAM,mBAAmB,uBAAuB;AAChD,eAAW,aAAa,kBAAkB;AACxC,WAAK,GAAG,IAAI,SAAS;AAAA,IACvB;AAGA,yBAAqB,KAAK,EAAE;AAG5B,UAAM,QAAQ,MAAM,YAAY,OAAO;AACvC,UAAM,aAAa,KAAK,IAAI,OAAO,QAAQ,SAAS;AAGpD,QAAI,QAAQ,kBAAkB;AAC5B,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AACA,QAAI,QAAQ,uBAAuB,QAAW;AAC5C,WAAK,qBAAqB,QAAQ;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBACZ,UACA,SACe;AAEf,SAAK,MAAM,QAAQ,OAAQ,UAAM,WAAAA,SAAU,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC,CAAC;AAGvG,SAAK,KAAK,IAAI,KAAK,IAAI,SAAS,IAAI,WAAW,QAAQ,CAAC;AAGxD,yBAAqB,KAAK,EAAE;AAG5B,QAAI,QAAQ,kBAAkB;AAC5B,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AACA,QAAI,QAAQ,uBAAuB,QAAW;AAC5C,WAAK,qBAAqB,QAAQ;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,SAAsB;AACpB,QAAI,CAAC,KAAK,IAAI;AACZ,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,OAAO,KAAK,GAAG,OAAO;AAE5B,UAAM,SAAS,IAAI,YAAY,KAAK,MAAM;AAC1C,QAAI,WAAW,MAAM,EAAE,IAAI,IAAI;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAwB;AACtB,QAAI,CAAC,KAAK,IAAI;AACZ,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,UAAiC;AAC7C,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,cAAc,KAAK,IAAI,QAAQ;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,SAAmC;AAC7C,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,YAAY,KAAK,IAAI,OAAO;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,QAA6B;AACvC,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,YAAY,KAAK,IAAI,MAAM;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,SAA+B;AACtC,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,SAAS,KAAK,IAAI,OAAO;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,SAA+B;AAC1C,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,aAAa,KAAK,IAAI,OAAO;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAiC;AACzC,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,UAAU,KAAK,IAAI,OAAO;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoB,MAAwB;AAC1C,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,oBAAoB,KAAK,IAAI,IAAI;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAuB,WAAoC;AACzD,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,uBAAuB,KAAK,IAAI,SAAS;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,WAAmC;AAClD,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,iBAAiB,KAAK,IAAI,SAAS;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,wBAAwB,MAA8B;AACpD,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,wBAAwB,KAAK,IAAI,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,QAA6B;AACvC,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,YAAY,KAAK,IAAI,MAAM;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,SAAS,SAAmD;AAC1D,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AAGxD,UAAM,EAAE,MAAM,GAAG,YAAY,IAAI,WAAW,CAAC;AAC7C,UAAM,eAAe,EAAE,GAAG,YAAY;AAEtC,QAAI,MAAM;AACR,YAAM,aAAa,oBAAoB,KAAK,IAAI,IAAI;AACpD,mBAAa,aAAa;AAAA,IAC5B;AAEA,WAAO,SAAS,KAAK,IAAI,cAAc,KAAK,kBAAkB;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmB,QAA4B;AAC7C,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,mBAAmB,KAAK,IAAI,MAAM;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,aAAa,SAA2D;AACtE,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AAGxD,UAAM,EAAE,MAAM,GAAG,YAAY,IAAI,WAAW,CAAC;AAC7C,UAAM,eAAe,EAAE,GAAG,YAAY;AAEtC,QAAI,MAAM;AACR,YAAM,aAAa,oBAAoB,KAAK,IAAI,IAAI;AACpD,mBAAa,aAAa;AAAA,IAC5B;AAEA,WAAO,aAAa,KAAK,IAAI,cAAc,KAAK,kBAAkB;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoB,MAAsB;AACxC,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAgC;AAC9B,WAAO,CAAC,GAAG,KAAK,gBAAgB;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAsB,SAAuB;AAC3C,SAAK,qBAAqB;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,wBAAgC;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAkB,MAAgC;AACtD,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AAExD,UAAM,WAAW,QAAQ,KAAK;AAC9B,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI,MAAM,qFAAqF;AAAA,IACvG;AAEA,UAAM,iBAAiB,KAAK,IAAI,QAAQ;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,oBAA0B;AACxB,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,sBAAY,KAAK,EAAE;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAiC;AACzC,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,UAAe,KAAK,IAAI,SAAS,KAAK,kBAAkB;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,SAA+B;AAC1C,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,aAAa,KAAK,IAAI,SAAS,KAAK,kBAAkB;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,SAAqD;AACvE,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,oBAAyB,KAAK,IAAI,SAAS,KAAK,kBAAkB;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAKA,2BAA2B,QAAwC;AACjE,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,2BAA2B,KAAK,IAAI,QAAQ,KAAK,kBAAkB;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,SAA2C;AACxD,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,eAAe,KAAK,IAAI,SAAS,KAAK,kBAAkB;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAsB,QAAmC;AACvD,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,sBAAsB,KAAK,IAAI,QAAQ,KAAK,kBAAkB;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,SAA6E;AAC9F,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,mBAAmB,KAAK,IAAI,SAAS,KAAK,kBAAkB;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKA,2BAA2B,QAA4D;AACrF,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,2BAA2B,KAAK,IAAI,QAAQ,KAAK,kBAAkB;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,uBAAgC;AAC9B,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,aAAa,KAAK,EAAE;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,iCAAoD;AAClD,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,uBAAuB,KAAK,EAAE;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,4BAA0C;AACxC,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,kBAAkB,KAAK,EAAE;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gCAA8D;AAC5D,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,sBAAsB,KAAK,EAAE;AAAA,EACtC;AACF;;;AiBlhBO,IAAK,uBAAL,kBAAKC,0BAAL;AACL,EAAAA,4CAAA,eAAY,KAAZ;AACA,EAAAA,4CAAA,WAAQ,KAAR;AACA,EAAAA,4CAAA,iBAAc,KAAd;AACA,EAAAA,4CAAA,cAAW,KAAX;AACA,EAAAA,4CAAA,aAAU,KAAV;AACA,EAAAA,4CAAA,aAAU,KAAV;AANU,SAAAA;AAAA,GAAA;AASL,IAAK,oBAAL,kBAAKC,uBAAL;AACL,EAAAA,sCAAA,iBAAc,KAAd;AACA,EAAAA,sCAAA,gBAAa,KAAb;AACA,EAAAA,sCAAA,mBAAgB,KAAhB;AAHU,SAAAA;AAAA,GAAA;AAML,IAAK,kBAAL,kBAAKC,qBAAL;AACL,EAAAA,kCAAA,8BAA2B,KAA3B;AACA,EAAAA,kCAAA,sBAAmB,KAAnB;AACA,EAAAA,kCAAA,iBAAc,KAAd;AACA,EAAAA,kCAAA,gBAAa,KAAb;AACA,EAAAA,kCAAA,uBAAoB,KAApB;AALU,SAAAA;AAAA,GAAA;AAQL,IAAK,kBAAL,kBAAKC,qBAAL;AACL,EAAAA,kCAAA,WAAQ,KAAR;AACA,EAAAA,kCAAA,0BAAuB,KAAvB;AACA,EAAAA,kCAAA,yBAAsB,KAAtB;AACA,EAAAA,kCAAA,wBAAqB,KAArB;AACA,EAAAA,kCAAA,gCAA6B,KAA7B;AACA,EAAAA,kCAAA,UAAO,KAAP;AACA,EAAAA,kCAAA,8BAA2B,KAA3B;AAPU,SAAAA;AAAA,GAAA;AAUL,IAAK,aAAL,kBAAKC,gBAAL;AACL,EAAAA,wBAAA,mBAAgB,KAAhB;AACA,EAAAA,wBAAA,iBAAc,KAAd;AACA,EAAAA,wBAAA,uBAAoB,KAApB;AACA,EAAAA,wBAAA,YAAS,KAAT;AACA,EAAAA,wBAAA,mBAAgB,KAAhB;AACA,EAAAA,wBAAA,cAAW,KAAX;AACA,EAAAA,wBAAA,aAAU,KAAV;AACA,EAAAA,wBAAA,aAAU,KAAV;AACA,EAAAA,wBAAA,iBAAc,KAAd;AACA,EAAAA,wBAAA,kBAAe,MAAf;AACA,EAAAA,wBAAA,qBAAkB,MAAlB;AACA,EAAAA,wBAAA,uBAAoB,MAApB;AAZU,SAAAA;AAAA,GAAA;AAeL,IAAK,cAAL,kBAAKC,iBAAL;AACL,EAAAA,0BAAA,gBAAa,KAAb;AACA,EAAAA,0BAAA,qBAAkB,KAAlB;AACA,EAAAA,0BAAA,wBAAqB,KAArB;AACA,EAAAA,0BAAA,YAAS,KAAT;AACA,EAAAA,0BAAA,wBAAqB,KAArB;AACA,EAAAA,0BAAA,sBAAmB,KAAnB;AACA,EAAAA,0BAAA,kBAAe,KAAf;AACA,EAAAA,0BAAA,oBAAiB,KAAjB;AACA,EAAAA,0BAAA,gBAAa,KAAb;AACA,EAAAA,0BAAA,eAAY,MAAZ;AACA,EAAAA,0BAAA,yBAAsB,MAAtB;AAXU,SAAAA;AAAA,GAAA;","names":["JSZip","Papa","response","arrayBuffer","protobuf","mergeRealtimeData","initSqlJs","ScheduleRelationship","VehicleStopStatus","CongestionLevel","OccupancyStatus","AlertCause","AlertEffect"]} \ No newline at end of file +{"version":3,"sources":["../src/index.ts","../src/gtfs-sqljs.ts","../src/schema/schema.ts","../src/loaders/zip-loader.ts","../src/loaders/csv-parser.ts","../src/loaders/data-loader.ts","../src/schema/gtfs-rt-schema.ts","../src/loaders/gtfs-rt-loader.ts","../src/queries/agencies.ts","../src/queries/stops.ts","../src/queries/routes.ts","../src/queries/calendar.ts","../src/queries/rt-vehicle-positions.ts","../src/queries/trips.ts","../src/queries/stop-times.ts","../src/queries/rt-alerts.ts","../src/queries/rt-trip-updates.ts","../src/queries/rt-stop-time-updates.ts","../src/types/gtfs-rt.ts"],"sourcesContent":["/**\n * gtfs-sqljs - Load GTFS data into sql.js SQLite database\n * @author Théophile Helleboid/SysDevRun\n * @license MIT\n */\n\nexport {\n GtfsSqlJs,\n type GtfsSqlJsOptions,\n type AgencyFilters,\n type StopFilters,\n type RouteFilters,\n type TripFilters,\n type StopTimeFilters,\n type AlertFilters,\n type VehiclePositionFilters,\n type TripUpdateFilters,\n type StopTimeUpdateFilters,\n type Alert,\n type VehiclePosition,\n type TripUpdate,\n type StopTimeUpdateWithMetadata,\n type TripWithRealtime,\n type StopTimeWithRealtime\n} from './gtfs-sqljs';\n\n// Export GTFS types\nexport type {\n Agency,\n Stop,\n Route,\n Trip,\n StopTime,\n Calendar,\n CalendarDate,\n FareAttribute,\n FareRule,\n Shape,\n Frequency,\n Transfer,\n Pathway,\n Level,\n FeedInfo,\n Attribution,\n} from './types/gtfs';\n\n// Export GTFS-RT types\nexport type {\n TranslatedString,\n EntitySelector,\n TimeRange,\n Position,\n VehicleDescriptor,\n StopTimeEvent,\n StopTimeUpdate,\n StopTimeRealtime,\n TripRealtime,\n RealtimeConfig\n} from './types/gtfs-rt';\n\n// Export GTFS-RT enums\nexport {\n ScheduleRelationship,\n VehicleStopStatus,\n CongestionLevel,\n OccupancyStatus,\n AlertCause,\n AlertEffect\n} from './types/gtfs-rt';\n\n// Export schema definitions for advanced use\nexport { GTFS_SCHEMA, type TableSchema, type ColumnDefinition, type IndexDefinition } from './schema/schema';\n","/**\n * Main GtfsSqlJs Class\n */\n\nimport initSqlJs, { type Database, type SqlJsStatic } from 'sql.js';\nimport { getAllCreateStatements } from './schema/schema';\nimport { loadGTFSZip } from './loaders/zip-loader';\nimport { loadGTFSData } from './loaders/data-loader';\nimport { createRealtimeTables, clearRealtimeData as clearRTData } from './schema/gtfs-rt-schema';\nimport { loadRealtimeData } from './loaders/gtfs-rt-loader';\n\n// Query methods\nimport { getAgencies, type AgencyFilters } from './queries/agencies';\nimport { getStops, type StopFilters } from './queries/stops';\nimport { getRoutes, type RouteFilters } from './queries/routes';\nimport {\n getActiveServiceIds,\n getCalendarByServiceId,\n getCalendarDates,\n getCalendarDatesForDate,\n} from './queries/calendar';\nimport { getTrips, type TripFilters, type TripWithRealtime } from './queries/trips';\nimport { getStopTimes, type StopTimeFilters, type StopTimeWithRealtime } from './queries/stop-times';\nimport { getAlerts as getAlertsQuery, getAllAlerts, type AlertFilters } from './queries/rt-alerts';\nimport { getVehiclePositions as getVehiclePositionsQuery, getAllVehiclePositions, type VehiclePositionFilters } from './queries/rt-vehicle-positions';\nimport { getTripUpdates, getAllTripUpdates, type TripUpdateFilters } from './queries/rt-trip-updates';\nimport { getStopTimeUpdates, getAllStopTimeUpdates, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata } from './queries/rt-stop-time-updates';\n\n// Types\nimport type { Agency, Stop, Route, Trip, StopTime, Calendar, CalendarDate } from './types/gtfs';\nimport type { Alert, VehiclePosition, TripUpdate } from './types/gtfs-rt';\n\n// Export filter types for users\nexport type { AgencyFilters, StopFilters, RouteFilters, TripFilters, StopTimeFilters, AlertFilters, VehiclePositionFilters, TripUpdateFilters, StopTimeUpdateFilters };\n// Export RT types\nexport type { Alert, VehiclePosition, TripUpdate, StopTimeUpdateWithMetadata, TripWithRealtime, StopTimeWithRealtime };\n\nexport interface GtfsSqlJsOptions {\n /**\n * Path or URL to GTFS ZIP file\n */\n zipPath?: string;\n\n /**\n * Pre-loaded SQLite database as ArrayBuffer\n */\n database?: ArrayBuffer;\n\n /**\n * Optional: Custom SQL.js instance\n */\n SQL?: SqlJsStatic;\n\n /**\n * Optional: Path to SQL.js WASM file (for custom loading)\n */\n locateFile?: (filename: string) => string;\n\n /**\n * Optional: Array of GTFS filenames to skip importing (e.g., ['shapes.txt'])\n * Tables will be created but no data will be imported for these files\n */\n skipFiles?: string[];\n\n /**\n * Optional: Array of GTFS-RT feed URLs for realtime data\n */\n realtimeFeedUrls?: string[];\n\n /**\n * Optional: Staleness threshold in seconds (default: 120)\n * Realtime data older than this will be excluded from queries\n */\n stalenessThreshold?: number;\n}\n\nexport class GtfsSqlJs {\n private db: Database | null = null;\n private SQL: SqlJsStatic | null = null;\n private realtimeFeedUrls: string[] = [];\n private stalenessThreshold: number = 120;\n\n /**\n * Private constructor - use static factory methods instead\n */\n private constructor() {}\n\n /**\n * Create GtfsSqlJs instance from GTFS ZIP file\n */\n static async fromZip(\n zipPath: string,\n options: Omit = {}\n ): Promise {\n const instance = new GtfsSqlJs();\n await instance.initFromZip(zipPath, options);\n return instance;\n }\n\n /**\n * Create GtfsSqlJs instance from existing SQLite database\n */\n static async fromDatabase(\n database: ArrayBuffer,\n options: Omit = {}\n ): Promise {\n const instance = new GtfsSqlJs();\n await instance.initFromDatabase(database, options);\n return instance;\n }\n\n /**\n * Initialize from ZIP file\n */\n private async initFromZip(zipPath: string, options: Omit): Promise {\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Create new database\n this.db = new this.SQL.Database();\n\n // Create GTFS schema\n const createStatements = getAllCreateStatements();\n for (const statement of createStatements) {\n this.db.run(statement);\n }\n\n // Create GTFS-RT tables\n createRealtimeTables(this.db);\n\n // Load GTFS data\n const files = await loadGTFSZip(zipPath);\n await loadGTFSData(this.db, files, options.skipFiles);\n\n // Set RT configuration\n if (options.realtimeFeedUrls) {\n this.realtimeFeedUrls = options.realtimeFeedUrls;\n }\n if (options.stalenessThreshold !== undefined) {\n this.stalenessThreshold = options.stalenessThreshold;\n }\n }\n\n /**\n * Initialize from existing database\n */\n private async initFromDatabase(\n database: ArrayBuffer,\n options: Omit\n ): Promise {\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Load existing database\n this.db = new this.SQL.Database(new Uint8Array(database));\n\n // Ensure RT tables exist (in case loading old database)\n createRealtimeTables(this.db);\n\n // Set RT configuration\n if (options.realtimeFeedUrls) {\n this.realtimeFeedUrls = options.realtimeFeedUrls;\n }\n if (options.stalenessThreshold !== undefined) {\n this.stalenessThreshold = options.stalenessThreshold;\n }\n }\n\n /**\n * Export database to ArrayBuffer\n */\n export(): ArrayBuffer {\n if (!this.db) {\n throw new Error('Database not initialized');\n }\n\n const data = this.db.export();\n // Create a new ArrayBuffer and copy the data to ensure proper type\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n return buffer;\n }\n\n /**\n * Close the database connection\n */\n close(): void {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n\n /**\n * Get direct access to the database (for advanced queries)\n */\n getDatabase(): Database {\n if (!this.db) {\n throw new Error('Database not initialized');\n }\n return this.db;\n }\n\n // ==================== Agency Methods ====================\n\n /**\n * Get agencies with optional filters\n * Pass agencyId filter to get a specific agency\n */\n getAgencies(filters?: AgencyFilters): Agency[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAgencies(this.db, filters);\n }\n\n // ==================== Stop Methods ====================\n\n /**\n * Get stops with optional filters\n * Pass stopId filter to get a specific stop\n */\n getStops(filters?: StopFilters): Stop[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStops(this.db, filters);\n }\n\n // ==================== Route Methods ====================\n\n /**\n * Get routes with optional filters\n * Pass routeId filter to get a specific route\n */\n getRoutes(filters?: RouteFilters): Route[] {\n if (!this.db) throw new Error('Database not initialized');\n return getRoutes(this.db, filters);\n }\n\n // ==================== Calendar Methods ====================\n\n /**\n * Get active service IDs for a given date (YYYYMMDD format)\n */\n getActiveServiceIds(date: string): string[] {\n if (!this.db) throw new Error('Database not initialized');\n return getActiveServiceIds(this.db, date);\n }\n\n /**\n * Get calendar entry by service_id\n */\n getCalendarByServiceId(serviceId: string): Calendar | null {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarByServiceId(this.db, serviceId);\n }\n\n /**\n * Get calendar date exceptions for a service\n */\n getCalendarDates(serviceId: string): CalendarDate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarDates(this.db, serviceId);\n }\n\n /**\n * Get calendar date exceptions for a specific date\n */\n getCalendarDatesForDate(date: string): CalendarDate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarDatesForDate(this.db, date);\n }\n\n // ==================== Trip Methods ====================\n\n /**\n * Get trips with optional filters\n * Pass tripId filter to get a specific trip\n *\n * @param filters - Optional filters\n * @param filters.tripId - Filter by trip ID (single value or array)\n * @param filters.routeId - Filter by route ID (single value or array)\n * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date\n * @param filters.directionId - Filter by direction ID (single value or array)\n * @param filters.agencyId - Filter by agency ID (single value or array)\n * @param filters.limit - Limit number of results\n *\n * @example\n * // Get all trips for a route on a specific date\n * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', date: '20240115' });\n *\n * @example\n * // Get all trips for a route going in one direction\n * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 });\n *\n * @example\n * // Get a specific trip\n * const trips = gtfs.getTrips({ tripId: 'TRIP_123' });\n */\n getTrips(filters?: TripFilters & { date?: string }): Trip[] {\n if (!this.db) throw new Error('Database not initialized');\n\n // Handle date parameter by converting it to serviceIds\n const { date, ...restFilters } = filters || {};\n const finalFilters = { ...restFilters };\n\n if (date) {\n const serviceIds = getActiveServiceIds(this.db, date);\n finalFilters.serviceIds = serviceIds;\n }\n\n return getTrips(this.db, finalFilters, this.stalenessThreshold);\n }\n\n // ==================== Stop Time Methods ====================\n\n /**\n * Get stop times with optional filters\n *\n * @param filters - Optional filters\n * @param filters.tripId - Filter by trip ID (single value or array)\n * @param filters.stopId - Filter by stop ID (single value or array)\n * @param filters.routeId - Filter by route ID (single value or array)\n * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date\n * @param filters.directionId - Filter by direction ID (single value or array)\n * @param filters.agencyId - Filter by agency ID (single value or array)\n * @param filters.includeRealtime - Include realtime data (delay and time fields)\n * @param filters.limit - Limit number of results\n *\n * @example\n * // Get stop times for a specific trip\n * const stopTimes = gtfs.getStopTimes({ tripId: 'TRIP_123' });\n *\n * @example\n * // Get stop times at a stop for a specific route on a date\n * const stopTimes = gtfs.getStopTimes({\n * stopId: 'STOP_123',\n * routeId: 'ROUTE_1',\n * date: '20240115'\n * });\n *\n * @example\n * // Get stop times with realtime data\n * const stopTimes = gtfs.getStopTimes({\n * tripId: 'TRIP_123',\n * includeRealtime: true\n * });\n */\n getStopTimes(filters?: StopTimeFilters & { date?: string }): StopTime[] {\n if (!this.db) throw new Error('Database not initialized');\n\n // Handle date parameter by converting it to serviceIds\n const { date, ...restFilters } = filters || {};\n const finalFilters = { ...restFilters };\n\n if (date) {\n const serviceIds = getActiveServiceIds(this.db, date);\n finalFilters.serviceIds = serviceIds;\n }\n\n return getStopTimes(this.db, finalFilters, this.stalenessThreshold);\n }\n\n // ==================== Realtime Methods ====================\n\n /**\n * Set GTFS-RT feed URLs\n */\n setRealtimeFeedUrls(urls: string[]): void {\n this.realtimeFeedUrls = urls;\n }\n\n /**\n * Get currently configured GTFS-RT feed URLs\n */\n getRealtimeFeedUrls(): string[] {\n return [...this.realtimeFeedUrls];\n }\n\n /**\n * Set staleness threshold in seconds\n */\n setStalenessThreshold(seconds: number): void {\n this.stalenessThreshold = seconds;\n }\n\n /**\n * Get current staleness threshold\n */\n getStalenessThreshold(): number {\n return this.stalenessThreshold;\n }\n\n /**\n * Fetch and load GTFS Realtime data from configured feed URLs or provided URLs\n * @param urls - Optional array of feed URLs. If not provided, uses configured feed URLs\n */\n async fetchRealtimeData(urls?: string[]): Promise {\n if (!this.db) throw new Error('Database not initialized');\n\n const feedUrls = urls || this.realtimeFeedUrls;\n if (feedUrls.length === 0) {\n throw new Error('No realtime feed URLs configured. Use setRealtimeFeedUrls() or pass urls parameter.');\n }\n\n await loadRealtimeData(this.db, feedUrls);\n }\n\n /**\n * Clear all realtime data from the database\n */\n clearRealtimeData(): void {\n if (!this.db) throw new Error('Database not initialized');\n clearRTData(this.db);\n }\n\n /**\n * Get alerts with optional filters\n * Pass alertId filter to get a specific alert\n */\n getAlerts(filters?: AlertFilters): Alert[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAlertsQuery(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get vehicle positions with optional filters\n * Pass tripId filter to get vehicle position for a specific trip\n */\n getVehiclePositions(filters?: VehiclePositionFilters): VehiclePosition[] {\n if (!this.db) throw new Error('Database not initialized');\n return getVehiclePositionsQuery(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get trip updates with optional filters\n * Pass tripId filter to get trip update for a specific trip\n */\n getTripUpdates(filters?: TripUpdateFilters): TripUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getTripUpdates(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get stop time updates with optional filters\n * Pass tripId filter to get stop time updates for a specific trip\n */\n getStopTimeUpdates(filters?: StopTimeUpdateFilters): import('./types/gtfs-rt').StopTimeUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStopTimeUpdates(this.db, filters, this.stalenessThreshold);\n }\n\n // ==================== Debug Export Methods ====================\n // These methods export all realtime data without staleness filtering\n // for debugging purposes\n\n /**\n * Export all alerts without staleness filtering (for debugging)\n */\n debugExportAllAlerts(): Alert[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllAlerts(this.db);\n }\n\n /**\n * Export all vehicle positions without staleness filtering (for debugging)\n */\n debugExportAllVehiclePositions(): VehiclePosition[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllVehiclePositions(this.db);\n }\n\n /**\n * Export all trip updates without staleness filtering (for debugging)\n */\n debugExportAllTripUpdates(): TripUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllTripUpdates(this.db);\n }\n\n /**\n * Export all stop time updates without staleness filtering (for debugging)\n * Returns extended type with trip_id and rt_last_updated for debugging purposes\n */\n debugExportAllStopTimeUpdates(): StopTimeUpdateWithMetadata[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllStopTimeUpdates(this.db);\n }\n}\n","/**\n * SQLite Schema Definitions for GTFS Data\n * Matches required/optional fields from GTFS specification\n */\n\nexport interface TableSchema {\n name: string;\n columns: ColumnDefinition[];\n indexes?: IndexDefinition[];\n}\n\nexport interface ColumnDefinition {\n name: string;\n type: 'TEXT' | 'INTEGER' | 'REAL';\n required: boolean;\n primaryKey?: boolean;\n}\n\nexport interface IndexDefinition {\n name: string;\n columns: string[];\n unique?: boolean;\n}\n\nexport const GTFS_SCHEMA: TableSchema[] = [\n {\n name: 'agency',\n columns: [\n { name: 'agency_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'agency_name', type: 'TEXT', required: true },\n { name: 'agency_url', type: 'TEXT', required: true },\n { name: 'agency_timezone', type: 'TEXT', required: true },\n { name: 'agency_lang', type: 'TEXT', required: false },\n { name: 'agency_phone', type: 'TEXT', required: false },\n { name: 'agency_fare_url', type: 'TEXT', required: false },\n { name: 'agency_email', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'stops',\n columns: [\n { name: 'stop_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'stop_name', type: 'TEXT', required: true },\n { name: 'stop_lat', type: 'REAL', required: true },\n { name: 'stop_lon', type: 'REAL', required: true },\n { name: 'stop_code', type: 'TEXT', required: false },\n { name: 'stop_desc', type: 'TEXT', required: false },\n { name: 'zone_id', type: 'TEXT', required: false },\n { name: 'stop_url', type: 'TEXT', required: false },\n { name: 'location_type', type: 'INTEGER', required: false },\n { name: 'parent_station', type: 'TEXT', required: false },\n { name: 'stop_timezone', type: 'TEXT', required: false },\n { name: 'wheelchair_boarding', type: 'INTEGER', required: false },\n { name: 'level_id', type: 'TEXT', required: false },\n { name: 'platform_code', type: 'TEXT', required: false },\n ],\n indexes: [\n { name: 'idx_stops_stop_code', columns: ['stop_code'] },\n { name: 'idx_stops_stop_name', columns: ['stop_name'] },\n { name: 'idx_stops_parent_station', columns: ['parent_station'] },\n ],\n },\n {\n name: 'routes',\n columns: [\n { name: 'route_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'route_short_name', type: 'TEXT', required: true },\n { name: 'route_long_name', type: 'TEXT', required: true },\n { name: 'route_type', type: 'INTEGER', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'route_desc', type: 'TEXT', required: false },\n { name: 'route_url', type: 'TEXT', required: false },\n { name: 'route_color', type: 'TEXT', required: false },\n { name: 'route_text_color', type: 'TEXT', required: false },\n { name: 'route_sort_order', type: 'INTEGER', required: false },\n { name: 'continuous_pickup', type: 'INTEGER', required: false },\n { name: 'continuous_drop_off', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_routes_agency_id', columns: ['agency_id'] },\n ],\n },\n {\n name: 'trips',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'route_id', type: 'TEXT', required: true },\n { name: 'service_id', type: 'TEXT', required: true },\n { name: 'trip_headsign', type: 'TEXT', required: false },\n { name: 'trip_short_name', type: 'TEXT', required: false },\n { name: 'direction_id', type: 'INTEGER', required: false },\n { name: 'block_id', type: 'TEXT', required: false },\n { name: 'shape_id', type: 'TEXT', required: false },\n { name: 'wheelchair_accessible', type: 'INTEGER', required: false },\n { name: 'bikes_allowed', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_trips_route_id', columns: ['route_id'] },\n { name: 'idx_trips_service_id', columns: ['service_id'] },\n { name: 'idx_trips_route_service', columns: ['route_id', 'service_id'] },\n ],\n },\n {\n name: 'stop_times',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true },\n { name: 'arrival_time', type: 'TEXT', required: true },\n { name: 'departure_time', type: 'TEXT', required: true },\n { name: 'stop_id', type: 'TEXT', required: true },\n { name: 'stop_sequence', type: 'INTEGER', required: true },\n { name: 'stop_headsign', type: 'TEXT', required: false },\n { name: 'pickup_type', type: 'INTEGER', required: false },\n { name: 'drop_off_type', type: 'INTEGER', required: false },\n { name: 'continuous_pickup', type: 'INTEGER', required: false },\n { name: 'continuous_drop_off', type: 'INTEGER', required: false },\n { name: 'shape_dist_traveled', type: 'REAL', required: false },\n { name: 'timepoint', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_stop_times_trip_id', columns: ['trip_id'] },\n { name: 'idx_stop_times_stop_id', columns: ['stop_id'] },\n { name: 'idx_stop_times_trip_sequence', columns: ['trip_id', 'stop_sequence'] },\n ],\n },\n {\n name: 'calendar',\n columns: [\n { name: 'service_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'monday', type: 'INTEGER', required: true },\n { name: 'tuesday', type: 'INTEGER', required: true },\n { name: 'wednesday', type: 'INTEGER', required: true },\n { name: 'thursday', type: 'INTEGER', required: true },\n { name: 'friday', type: 'INTEGER', required: true },\n { name: 'saturday', type: 'INTEGER', required: true },\n { name: 'sunday', type: 'INTEGER', required: true },\n { name: 'start_date', type: 'TEXT', required: true },\n { name: 'end_date', type: 'TEXT', required: true },\n ],\n },\n {\n name: 'calendar_dates',\n columns: [\n { name: 'service_id', type: 'TEXT', required: true },\n { name: 'date', type: 'TEXT', required: true },\n { name: 'exception_type', type: 'INTEGER', required: true },\n ],\n indexes: [\n { name: 'idx_calendar_dates_service_id', columns: ['service_id'] },\n { name: 'idx_calendar_dates_date', columns: ['date'] },\n { name: 'idx_calendar_dates_service_date', columns: ['service_id', 'date'] },\n ],\n },\n {\n name: 'fare_attributes',\n columns: [\n { name: 'fare_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'price', type: 'REAL', required: true },\n { name: 'currency_type', type: 'TEXT', required: true },\n { name: 'payment_method', type: 'INTEGER', required: true },\n { name: 'transfers', type: 'INTEGER', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'transfer_duration', type: 'INTEGER', required: false },\n ],\n },\n {\n name: 'fare_rules',\n columns: [\n { name: 'fare_id', type: 'TEXT', required: true },\n { name: 'route_id', type: 'TEXT', required: false },\n { name: 'origin_id', type: 'TEXT', required: false },\n { name: 'destination_id', type: 'TEXT', required: false },\n { name: 'contains_id', type: 'TEXT', required: false },\n ],\n indexes: [\n { name: 'idx_fare_rules_fare_id', columns: ['fare_id'] },\n { name: 'idx_fare_rules_route_id', columns: ['route_id'] },\n ],\n },\n {\n name: 'shapes',\n columns: [\n { name: 'shape_id', type: 'TEXT', required: true },\n { name: 'shape_pt_lat', type: 'REAL', required: true },\n { name: 'shape_pt_lon', type: 'REAL', required: true },\n { name: 'shape_pt_sequence', type: 'INTEGER', required: true },\n { name: 'shape_dist_traveled', type: 'REAL', required: false },\n ],\n indexes: [\n { name: 'idx_shapes_shape_id', columns: ['shape_id'] },\n { name: 'idx_shapes_shape_sequence', columns: ['shape_id', 'shape_pt_sequence'] },\n ],\n },\n {\n name: 'frequencies',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true },\n { name: 'start_time', type: 'TEXT', required: true },\n { name: 'end_time', type: 'TEXT', required: true },\n { name: 'headway_secs', type: 'INTEGER', required: true },\n { name: 'exact_times', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_frequencies_trip_id', columns: ['trip_id'] },\n ],\n },\n {\n name: 'transfers',\n columns: [\n { name: 'from_stop_id', type: 'TEXT', required: true },\n { name: 'to_stop_id', type: 'TEXT', required: true },\n { name: 'transfer_type', type: 'INTEGER', required: true },\n { name: 'min_transfer_time', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_transfers_from_stop_id', columns: ['from_stop_id'] },\n { name: 'idx_transfers_to_stop_id', columns: ['to_stop_id'] },\n ],\n },\n {\n name: 'pathways',\n columns: [\n { name: 'pathway_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'from_stop_id', type: 'TEXT', required: true },\n { name: 'to_stop_id', type: 'TEXT', required: true },\n { name: 'pathway_mode', type: 'INTEGER', required: true },\n { name: 'is_bidirectional', type: 'INTEGER', required: true },\n { name: 'length', type: 'REAL', required: false },\n { name: 'traversal_time', type: 'INTEGER', required: false },\n { name: 'stair_count', type: 'INTEGER', required: false },\n { name: 'max_slope', type: 'REAL', required: false },\n { name: 'min_width', type: 'REAL', required: false },\n { name: 'signposted_as', type: 'TEXT', required: false },\n { name: 'reversed_signposted_as', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'levels',\n columns: [\n { name: 'level_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'level_index', type: 'REAL', required: true },\n { name: 'level_name', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'feed_info',\n columns: [\n { name: 'feed_publisher_name', type: 'TEXT', required: true },\n { name: 'feed_publisher_url', type: 'TEXT', required: true },\n { name: 'feed_lang', type: 'TEXT', required: true },\n { name: 'default_lang', type: 'TEXT', required: false },\n { name: 'feed_start_date', type: 'TEXT', required: false },\n { name: 'feed_end_date', type: 'TEXT', required: false },\n { name: 'feed_version', type: 'TEXT', required: false },\n { name: 'feed_contact_email', type: 'TEXT', required: false },\n { name: 'feed_contact_url', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'attributions',\n columns: [\n { name: 'attribution_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'organization_name', type: 'TEXT', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'route_id', type: 'TEXT', required: false },\n { name: 'trip_id', type: 'TEXT', required: false },\n { name: 'is_producer', type: 'INTEGER', required: false },\n { name: 'is_operator', type: 'INTEGER', required: false },\n { name: 'is_authority', type: 'INTEGER', required: false },\n { name: 'attribution_url', type: 'TEXT', required: false },\n { name: 'attribution_email', type: 'TEXT', required: false },\n { name: 'attribution_phone', type: 'TEXT', required: false },\n ],\n },\n];\n\n/**\n * Generate CREATE TABLE SQL statement from schema definition\n */\nexport function generateCreateTableSQL(schema: TableSchema): string {\n const columns = schema.columns.map((col) => {\n const parts = [col.name, col.type];\n if (col.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n if (col.required && !col.primaryKey) {\n parts.push('NOT NULL');\n }\n return parts.join(' ');\n });\n\n return `CREATE TABLE IF NOT EXISTS ${schema.name} (${columns.join(', ')})`;\n}\n\n/**\n * Generate CREATE INDEX SQL statements from schema definition\n */\nexport function generateCreateIndexSQL(schema: TableSchema): string[] {\n if (!schema.indexes) {\n return [];\n }\n\n return schema.indexes.map((idx) => {\n const unique = idx.unique ? 'UNIQUE ' : '';\n const columns = idx.columns.join(', ');\n return `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name} (${columns})`;\n });\n}\n\n/**\n * Initialize all GTFS tables in the database\n */\nexport function getAllCreateStatements(): string[] {\n const statements: string[] = [];\n\n for (const schema of GTFS_SCHEMA) {\n statements.push(generateCreateTableSQL(schema));\n statements.push(...generateCreateIndexSQL(schema));\n }\n\n return statements;\n}\n","/**\n * ZIP Loader for GTFS files\n */\n\nimport JSZip from 'jszip';\n\nexport interface GTFSFiles {\n [filename: string]: string;\n}\n\n/**\n * Fetch and extract GTFS ZIP file\n * Works in both browser and Node.js environments\n */\nexport async function loadGTFSZip(source: string | ArrayBuffer | Uint8Array): Promise {\n let zipData: ArrayBuffer | Uint8Array;\n\n // If source is a string, treat it as URL or file path\n if (typeof source === 'string') {\n zipData = await fetchZip(source);\n } else {\n zipData = source;\n }\n\n // Load ZIP file\n const zip = await JSZip.loadAsync(zipData);\n\n // Extract all .txt files (GTFS files)\n const files: GTFSFiles = {};\n const filePromises: Promise[] = [];\n\n zip.forEach((relativePath, file) => {\n // Only process .txt files\n if (!file.dir && relativePath.endsWith('.txt')) {\n const fileName = relativePath.split('/').pop() || relativePath;\n filePromises.push(\n file.async('string').then((content) => {\n files[fileName] = content;\n })\n );\n }\n });\n\n await Promise.all(filePromises);\n\n return files;\n}\n\n/**\n * Fetch ZIP file from URL or file path\n */\nasync function fetchZip(source: string): Promise {\n // Check if source is a URL\n const isUrl = source.startsWith('http://') || source.startsWith('https://');\n\n // For URLs, always use fetch\n if (isUrl) {\n if (typeof fetch !== 'undefined') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS ZIP: ${response.status} ${response.statusText}`);\n }\n return await response.arrayBuffer();\n }\n throw new Error('fetch is not available to load URL');\n }\n\n // For non-URLs, try Node.js fs first, fall back to fetch for browser\n // Check if we're in Node.js environment\n const isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n if (isNode) {\n // In Node.js, treat as file path\n try {\n const fs = await import('fs');\n const buffer = await fs.promises.readFile(source);\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n } catch (error) {\n throw new Error(`Failed to read GTFS ZIP file: ${error}`);\n }\n }\n\n // In browser, treat as relative URL and use fetch\n if (typeof fetch !== 'undefined') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS ZIP: ${response.status} ${response.statusText}`);\n }\n return await response.arrayBuffer();\n }\n\n throw new Error('No method available to load ZIP file');\n}\n\n/**\n * Mapping of GTFS file names to database table names\n */\nexport const GTFS_FILE_MAPPING: Record = {\n 'agency.txt': 'agency',\n 'stops.txt': 'stops',\n 'routes.txt': 'routes',\n 'trips.txt': 'trips',\n 'stop_times.txt': 'stop_times',\n 'calendar.txt': 'calendar',\n 'calendar_dates.txt': 'calendar_dates',\n 'fare_attributes.txt': 'fare_attributes',\n 'fare_rules.txt': 'fare_rules',\n 'shapes.txt': 'shapes',\n 'frequencies.txt': 'frequencies',\n 'transfers.txt': 'transfers',\n 'pathways.txt': 'pathways',\n 'levels.txt': 'levels',\n 'feed_info.txt': 'feed_info',\n 'attributions.txt': 'attributions',\n};\n","/**\n * CSV Parser for GTFS files using papaparse\n */\n\nimport Papa from 'papaparse';\n\nexport interface ParsedCSV {\n headers: string[];\n rows: Record[];\n}\n\n/**\n * Parse CSV text into structured data using papaparse\n */\nexport function parseCSV(text: string): ParsedCSV {\n const result = Papa.parse(text, {\n header: true,\n skipEmptyLines: true,\n transformHeader: (header) => header.trim(),\n transform: (value) => value.trim(),\n });\n\n if (result.errors.length > 0) {\n console.warn('CSV parsing warnings:', result.errors);\n }\n\n const headers = result.meta.fields || [];\n const rows = result.data as Record[];\n\n return { headers, rows };\n}\n\n/**\n * Convert parsed row to typed object with proper type conversions\n */\nexport function convertRowTypes(\n row: Record,\n columnTypes: Record\n): Record {\n const result: Record = {};\n\n // Iterate over all expected columns, not just the ones present in the row\n for (const [key, type] of Object.entries(columnTypes)) {\n const value = row[key];\n\n // Handle missing or empty values\n if (value === undefined || value === null || value === '') {\n result[key] = null;\n continue;\n }\n\n if (type === 'INTEGER') {\n const parsed = parseInt(value, 10);\n result[key] = isNaN(parsed) ? null : parsed;\n } else if (type === 'REAL') {\n const parsed = parseFloat(value);\n result[key] = isNaN(parsed) ? null : parsed;\n } else {\n result[key] = value;\n }\n }\n\n return result;\n}\n","/**\n * Data Loader - Loads GTFS data into SQLite database\n */\n\nimport type { Database } from 'sql.js';\nimport { parseCSV, convertRowTypes } from './csv-parser';\nimport { GTFS_SCHEMA, type TableSchema } from '../schema/schema';\nimport type { GTFSFiles } from './zip-loader';\n\n/**\n * Load GTFS files into SQLite database\n * @param skipFiles - Optional array of filenames to skip importing (tables will be created but remain empty)\n */\nexport async function loadGTFSData(db: Database, files: GTFSFiles, skipFiles?: string[]): Promise {\n // Map of file names to table schemas\n const fileToSchema: Map = new Map();\n for (const schema of GTFS_SCHEMA) {\n // Match file names like agency.txt to table name 'agency'\n fileToSchema.set(`${schema.name}.txt`, schema);\n }\n\n // Normalize skipFiles to a Set for faster lookup\n const skipSet = new Set(skipFiles?.map(f => f.toLowerCase()) || []);\n\n // Process each file\n for (const [fileName, content] of Object.entries(files)) {\n const schema = fileToSchema.get(fileName);\n if (!schema) {\n // Skip unknown files\n continue;\n }\n\n // Skip if in skip list\n if (skipSet.has(fileName.toLowerCase())) {\n console.log(`Skipping import of ${fileName} (table ${schema.name} created but empty)`);\n continue;\n }\n\n await loadTableData(db, schema, content);\n }\n}\n\n/**\n * Load data for a single table\n */\nasync function loadTableData(db: Database, schema: TableSchema, csvContent: string): Promise {\n const { headers, rows } = parseCSV(csvContent);\n\n if (rows.length === 0) {\n return;\n }\n\n // Build column type map\n const columnTypes: Record = {};\n for (const col of schema.columns) {\n columnTypes[col.name] = col.type;\n }\n\n // Prepare INSERT statement\n const columns = headers.filter((h) => schema.columns.some((c) => c.name === h));\n const placeholders = columns.map(() => '?').join(', ');\n const insertSQL = `INSERT INTO ${schema.name} (${columns.join(', ')}) VALUES (${placeholders})`;\n\n const stmt = db.prepare(insertSQL);\n\n // Insert each row\n for (const row of rows) {\n const typedRow = convertRowTypes(row, columnTypes);\n const values = columns.map((col) => {\n const value = typedRow[col];\n // Handle undefined, null, or empty values\n return value === null || value === undefined ? null : value;\n });\n\n try {\n stmt.run(values);\n } catch (error) {\n console.error(`Error inserting row into ${schema.name}:`, error);\n console.error('Row data:', row);\n throw error;\n }\n }\n\n stmt.free();\n}\n","import type { Database } from 'sql.js';\n\n/**\n * Create GTFS Realtime tables in the database\n */\nexport function createRealtimeTables(db: Database): void {\n // Alerts table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_alerts (\n id TEXT PRIMARY KEY,\n active_period TEXT, -- JSON array of TimeRange objects\n informed_entity TEXT, -- JSON array of EntitySelector objects\n cause INTEGER,\n effect INTEGER,\n url TEXT, -- JSON TranslatedString\n header_text TEXT, -- JSON TranslatedString\n description_text TEXT, -- JSON TranslatedString\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n // Create index on rt_last_updated for staleness filtering\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_alerts_updated ON rt_alerts(rt_last_updated)');\n\n // Vehicle Positions table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_vehicle_positions (\n trip_id TEXT PRIMARY KEY,\n route_id TEXT,\n vehicle_id TEXT,\n vehicle_label TEXT,\n vehicle_license_plate TEXT,\n latitude REAL,\n longitude REAL,\n bearing REAL,\n odometer REAL,\n speed REAL,\n current_stop_sequence INTEGER,\n stop_id TEXT,\n current_status INTEGER,\n timestamp INTEGER,\n congestion_level INTEGER,\n occupancy_status INTEGER,\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_vehicle_positions_updated ON rt_vehicle_positions(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_vehicle_positions_route ON rt_vehicle_positions(route_id)');\n\n // Trip Updates table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_trip_updates (\n trip_id TEXT PRIMARY KEY,\n route_id TEXT,\n vehicle_id TEXT,\n vehicle_label TEXT,\n vehicle_license_plate TEXT,\n timestamp INTEGER,\n delay INTEGER,\n schedule_relationship INTEGER,\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_trip_updates_updated ON rt_trip_updates(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_trip_updates_route ON rt_trip_updates(route_id)');\n\n // Stop Time Updates table (child of trip updates)\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_stop_time_updates (\n trip_id TEXT NOT NULL,\n stop_sequence INTEGER,\n stop_id TEXT,\n arrival_delay INTEGER,\n arrival_time INTEGER,\n arrival_uncertainty INTEGER,\n departure_delay INTEGER,\n departure_time INTEGER,\n departure_uncertainty INTEGER,\n schedule_relationship INTEGER,\n rt_last_updated INTEGER NOT NULL,\n PRIMARY KEY (trip_id, stop_sequence),\n FOREIGN KEY (trip_id) REFERENCES rt_trip_updates(trip_id) ON DELETE CASCADE\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_stop_time_updates_updated ON rt_stop_time_updates(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_stop_time_updates_stop ON rt_stop_time_updates(stop_id)');\n}\n\n/**\n * Clear all realtime data from the database\n */\nexport function clearRealtimeData(db: Database): void {\n db.run('DELETE FROM rt_alerts');\n db.run('DELETE FROM rt_vehicle_positions');\n db.run('DELETE FROM rt_trip_updates');\n db.run('DELETE FROM rt_stop_time_updates');\n}\n","import type { Database } from 'sql.js';\nimport protobuf from 'protobufjs';\n\n// Types for protobuf decoded objects\ninterface ProtobufTranslation {\n text: string;\n language?: string;\n}\n\ninterface ProtobufTranslatedString {\n translation: ProtobufTranslation[];\n}\n\ninterface ProtobufAlert {\n id: string;\n activePeriod?: unknown[];\n active_period?: unknown[];\n informedEntity?: unknown[];\n informed_entity?: unknown[];\n cause?: number;\n effect?: number;\n url?: ProtobufTranslatedString;\n headerText?: ProtobufTranslatedString;\n header_text?: ProtobufTranslatedString;\n descriptionText?: ProtobufTranslatedString;\n description_text?: ProtobufTranslatedString;\n}\n\ninterface ProtobufTripDescriptor {\n tripId?: string;\n trip_id?: string;\n routeId?: string;\n route_id?: string;\n scheduleRelationship?: number;\n schedule_relationship?: number;\n}\n\ninterface ProtobufVehicleDescriptor {\n id?: string;\n label?: string;\n licensePlate?: string;\n license_plate?: string;\n}\n\ninterface ProtobufPosition {\n latitude?: number;\n longitude?: number;\n bearing?: number;\n odometer?: number;\n speed?: number;\n}\n\ninterface ProtobufVehiclePosition {\n trip?: ProtobufTripDescriptor;\n vehicle?: ProtobufVehicleDescriptor;\n position?: ProtobufPosition;\n currentStopSequence?: number;\n current_stop_sequence?: number;\n stopId?: string;\n stop_id?: string;\n currentStatus?: number;\n current_status?: number;\n timestamp?: number;\n congestionLevel?: number;\n congestion_level?: number;\n occupancyStatus?: number;\n occupancy_status?: number;\n}\n\ninterface ProtobufStopTimeEvent {\n delay?: number;\n time?: number;\n uncertainty?: number;\n}\n\ninterface ProtobufStopTimeUpdate {\n stopSequence?: number;\n stop_sequence?: number;\n stopId?: string;\n stop_id?: string;\n scheduleRelationship?: number;\n schedule_relationship?: number;\n arrival?: ProtobufStopTimeEvent;\n departure?: ProtobufStopTimeEvent;\n}\n\ninterface ProtobufTripUpdate {\n trip?: ProtobufTripDescriptor;\n vehicle?: ProtobufVehicleDescriptor;\n timestamp?: number;\n delay?: number;\n stopTimeUpdate?: ProtobufStopTimeUpdate[];\n stop_time_update?: ProtobufStopTimeUpdate[];\n}\n\n// GTFS Realtime protobuf definition (v2.0)\n// Source: https://github.com/google/transit/blob/master/gtfs-realtime/proto/gtfs-realtime.proto\nconst GTFS_RT_PROTO = `\nsyntax = \"proto2\";\noption java_package = \"com.google.transit.realtime\";\npackage transit_realtime;\n\nmessage FeedMessage {\n required FeedHeader header = 1;\n repeated FeedEntity entity = 2;\n}\n\nmessage FeedHeader {\n required string gtfs_realtime_version = 1;\n enum Incrementality {\n FULL_DATASET = 0;\n DIFFERENTIAL = 1;\n }\n optional Incrementality incrementality = 2 [default = FULL_DATASET];\n optional uint64 timestamp = 3;\n}\n\nmessage FeedEntity {\n required string id = 1;\n optional bool is_deleted = 2 [default = false];\n optional TripUpdate trip_update = 3;\n optional VehiclePosition vehicle = 4;\n optional Alert alert = 5;\n}\n\nmessage TripUpdate {\n required TripDescriptor trip = 1;\n optional VehicleDescriptor vehicle = 3;\n repeated StopTimeUpdate stop_time_update = 2;\n optional uint64 timestamp = 4;\n optional int32 delay = 5;\n\n message StopTimeEvent {\n optional int32 delay = 1;\n optional int64 time = 2;\n optional int32 uncertainty = 3;\n }\n\n message StopTimeUpdate {\n optional uint32 stop_sequence = 1;\n optional string stop_id = 4;\n optional StopTimeEvent arrival = 2;\n optional StopTimeEvent departure = 3;\n enum ScheduleRelationship {\n SCHEDULED = 0;\n SKIPPED = 1;\n NO_DATA = 2;\n }\n optional ScheduleRelationship schedule_relationship = 5 [default = SCHEDULED];\n }\n}\n\nmessage VehiclePosition {\n optional TripDescriptor trip = 1;\n optional VehicleDescriptor vehicle = 8;\n optional Position position = 2;\n optional uint32 current_stop_sequence = 3;\n optional string stop_id = 7;\n enum VehicleStopStatus {\n INCOMING_AT = 0;\n STOPPED_AT = 1;\n IN_TRANSIT_TO = 2;\n }\n optional VehicleStopStatus current_status = 4 [default = IN_TRANSIT_TO];\n optional uint64 timestamp = 5;\n enum CongestionLevel {\n UNKNOWN_CONGESTION_LEVEL = 0;\n RUNNING_SMOOTHLY = 1;\n STOP_AND_GO = 2;\n CONGESTION = 3;\n SEVERE_CONGESTION = 4;\n }\n optional CongestionLevel congestion_level = 6;\n enum OccupancyStatus {\n EMPTY = 0;\n MANY_SEATS_AVAILABLE = 1;\n FEW_SEATS_AVAILABLE = 2;\n STANDING_ROOM_ONLY = 3;\n CRUSHED_STANDING_ROOM_ONLY = 4;\n FULL = 5;\n NOT_ACCEPTING_PASSENGERS = 6;\n }\n optional OccupancyStatus occupancy_status = 9;\n}\n\nmessage Alert {\n repeated TimeRange active_period = 1;\n repeated EntitySelector informed_entity = 5;\n\n enum Cause {\n UNKNOWN_CAUSE = 1;\n OTHER_CAUSE = 2;\n TECHNICAL_PROBLEM = 3;\n STRIKE = 4;\n DEMONSTRATION = 5;\n ACCIDENT = 6;\n HOLIDAY = 7;\n WEATHER = 8;\n MAINTENANCE = 9;\n CONSTRUCTION = 10;\n POLICE_ACTIVITY = 11;\n MEDICAL_EMERGENCY = 12;\n }\n optional Cause cause = 6 [default = UNKNOWN_CAUSE];\n\n enum Effect {\n NO_SERVICE = 1;\n REDUCED_SERVICE = 2;\n SIGNIFICANT_DELAYS = 3;\n DETOUR = 4;\n ADDITIONAL_SERVICE = 5;\n MODIFIED_SERVICE = 6;\n OTHER_EFFECT = 7;\n UNKNOWN_EFFECT = 8;\n STOP_MOVED = 9;\n NO_EFFECT = 10;\n ACCESSIBILITY_ISSUE = 11;\n }\n optional Effect effect = 7 [default = UNKNOWN_EFFECT];\n optional TranslatedString url = 8;\n optional TranslatedString header_text = 10;\n optional TranslatedString description_text = 11;\n}\n\nmessage TimeRange {\n optional uint64 start = 1;\n optional uint64 end = 2;\n}\n\nmessage Position {\n required float latitude = 1;\n required float longitude = 2;\n optional float bearing = 3;\n optional double odometer = 4;\n optional float speed = 5;\n}\n\nmessage TripDescriptor {\n optional string trip_id = 1;\n optional string route_id = 5;\n optional uint32 direction_id = 6;\n optional string start_time = 2;\n optional string start_date = 3;\n enum ScheduleRelationship {\n SCHEDULED = 0;\n ADDED = 1;\n UNSCHEDULED = 2;\n CANCELED = 3;\n }\n optional ScheduleRelationship schedule_relationship = 4;\n}\n\nmessage VehicleDescriptor {\n optional string id = 1;\n optional string label = 2;\n optional string license_plate = 3;\n}\n\nmessage EntitySelector {\n optional string agency_id = 1;\n optional string route_id = 2;\n optional int32 route_type = 3;\n optional TripDescriptor trip = 4;\n optional string stop_id = 5;\n}\n\nmessage TranslatedString {\n message Translation {\n required string text = 1;\n optional string language = 2;\n }\n repeated Translation translation = 1;\n}\n`;\n\n// Fetch protobuf data from URL or local file\n// Uses fetch API (available in modern browsers and Node.js 18+)\nasync function fetchProtobuf(source: string): Promise {\n const isUrl = source.startsWith('http://') || source.startsWith('https://');\n\n if (isUrl) {\n // Fetch from URL\n const response = await fetch(source, {\n headers: {\n 'Accept': 'application/x-protobuf, application/octet-stream'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS-RT feed from ${source}: ${response.status} ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n }\n\n // Read from local file (Node.js only)\n const isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n if (isNode) {\n try {\n const fs = await import('fs');\n const buffer = await fs.promises.readFile(source);\n return new Uint8Array(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));\n } catch (error) {\n throw new Error(`Failed to read GTFS-RT file from ${source}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // In browser, try fetch for relative paths\n const response = await fetch(source, {\n headers: {\n 'Accept': 'application/x-protobuf, application/octet-stream'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS-RT feed from ${source}: ${response.status} ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n}\n\n// Load and parse GTFS-RT protobuf schema\nlet gtfsRtRoot: protobuf.Root | null = null;\n\nfunction loadGtfsRtProto(): protobuf.Root {\n if (!gtfsRtRoot) {\n gtfsRtRoot = protobuf.parse(GTFS_RT_PROTO).root;\n }\n return gtfsRtRoot;\n}\n\n// Convert camelCase object keys to snake_case\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);\n}\n\nfunction convertKeysToSnakeCase(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToSnakeCase);\n }\n if (typeof obj === 'object') {\n const result: Record = {};\n for (const key in obj as Record) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const snakeKey = camelToSnake(key);\n result[snakeKey] = convertKeysToSnakeCase((obj as Record)[key]);\n }\n }\n return result;\n }\n return obj;\n}\n\n// Parse TranslatedString to JSON\nfunction parseTranslatedString(ts: ProtobufTranslatedString | undefined): string | null {\n if (!ts || !ts.translation || ts.translation.length === 0) {\n return null;\n }\n return JSON.stringify({\n translation: ts.translation.map((t) => ({\n text: t.text,\n language: t.language || undefined\n }))\n });\n}\n\n// Insert alerts into database\nfunction insertAlerts(db: Database, alerts: ProtobufAlert[], timestamp: number): void {\n const stmt = db.prepare(`\n INSERT OR REPLACE INTO rt_alerts (\n id, active_period, informed_entity, cause, effect,\n url, header_text, description_text, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const alert of alerts) {\n // Map camelCase protobuf fields to snake_case\n const activePeriod = alert.activePeriod || alert.active_period || [];\n const informedEntity = alert.informedEntity || alert.informed_entity || [];\n const headerText = alert.headerText || alert.header_text;\n const descriptionText = alert.descriptionText || alert.description_text;\n\n // Convert nested objects to snake_case\n const activePeriodSnake = convertKeysToSnakeCase(activePeriod);\n const informedEntitySnake = convertKeysToSnakeCase(informedEntity);\n\n stmt.run([\n alert.id,\n JSON.stringify(activePeriodSnake),\n JSON.stringify(informedEntitySnake),\n alert.cause || null,\n alert.effect || null,\n parseTranslatedString(alert.url),\n parseTranslatedString(headerText),\n parseTranslatedString(descriptionText),\n timestamp\n ]);\n }\n\n stmt.free();\n}\n\n// Insert vehicle positions into database\nfunction insertVehiclePositions(db: Database, positions: ProtobufVehiclePosition[], timestamp: number): void {\n const stmt = db.prepare(`\n INSERT OR REPLACE INTO rt_vehicle_positions (\n trip_id, route_id, vehicle_id, vehicle_label, vehicle_license_plate,\n latitude, longitude, bearing, odometer, speed,\n current_stop_sequence, stop_id, current_status, timestamp,\n congestion_level, occupancy_status, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const vp of positions) {\n const trip = vp.trip;\n if (!trip || !(trip.tripId || trip.trip_id)) continue;\n\n // Map camelCase protobuf fields to snake_case\n const tripId = (trip.tripId || trip.trip_id)!; // Safe: checked above\n const routeId = trip.routeId || trip.route_id;\n const vehicleId = vp.vehicle?.id;\n const vehicleLabel = vp.vehicle?.label;\n const vehicleLicensePlate = vp.vehicle?.licensePlate || vp.vehicle?.license_plate;\n const currentStopSequence = vp.currentStopSequence || vp.current_stop_sequence;\n const stopId = vp.stopId || vp.stop_id;\n const currentStatus = vp.currentStatus || vp.current_status;\n const congestionLevel = vp.congestionLevel || vp.congestion_level;\n const occupancyStatus = vp.occupancyStatus || vp.occupancy_status;\n\n stmt.run([\n tripId,\n routeId || null,\n vehicleId || null,\n vehicleLabel || null,\n vehicleLicensePlate || null,\n vp.position?.latitude || null,\n vp.position?.longitude || null,\n vp.position?.bearing || null,\n vp.position?.odometer || null,\n vp.position?.speed || null,\n currentStopSequence || null,\n stopId || null,\n currentStatus || null,\n vp.timestamp || null,\n congestionLevel || null,\n occupancyStatus || null,\n timestamp\n ]);\n }\n\n stmt.free();\n}\n\n// Insert trip updates into database\nfunction insertTripUpdates(db: Database, updates: ProtobufTripUpdate[], timestamp: number): void {\n const tripStmt = db.prepare(`\n INSERT OR REPLACE INTO rt_trip_updates (\n trip_id, route_id, vehicle_id, vehicle_label, vehicle_license_plate,\n timestamp, delay, schedule_relationship, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const stopTimeStmt = db.prepare(`\n INSERT OR REPLACE INTO rt_stop_time_updates (\n trip_id, stop_sequence, stop_id,\n arrival_delay, arrival_time, arrival_uncertainty,\n departure_delay, departure_time, departure_uncertainty,\n schedule_relationship, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const tu of updates) {\n const trip = tu.trip;\n if (!trip || !(trip.tripId || trip.trip_id)) continue;\n\n // Map camelCase protobuf fields to snake_case\n const tripId = (trip.tripId || trip.trip_id)!; // Safe: checked above\n const routeId = trip.routeId || trip.route_id;\n const vehicleId = tu.vehicle?.id;\n const vehicleLabel = tu.vehicle?.label;\n const vehicleLicensePlate = tu.vehicle?.licensePlate || tu.vehicle?.license_plate;\n const scheduleRelationship = trip.scheduleRelationship || trip.schedule_relationship;\n const stopTimeUpdate = tu.stopTimeUpdate || tu.stop_time_update;\n\n // Insert trip update\n tripStmt.run([\n tripId,\n routeId || null,\n vehicleId || null,\n vehicleLabel || null,\n vehicleLicensePlate || null,\n tu.timestamp || null,\n tu.delay || null,\n scheduleRelationship || null,\n timestamp\n ]);\n\n // Insert stop time updates\n if (stopTimeUpdate) {\n for (const stu of stopTimeUpdate) {\n const stopSequence = stu.stopSequence || stu.stop_sequence;\n const stopId = stu.stopId || stu.stop_id;\n const scheduleRel = stu.scheduleRelationship || stu.schedule_relationship;\n\n stopTimeStmt.run([\n tripId,\n stopSequence || null,\n stopId || null,\n stu.arrival?.delay || null,\n stu.arrival?.time || null,\n stu.arrival?.uncertainty || null,\n stu.departure?.delay || null,\n stu.departure?.time || null,\n stu.departure?.uncertainty || null,\n scheduleRel || null,\n timestamp\n ]);\n }\n }\n }\n\n tripStmt.free();\n stopTimeStmt.free();\n}\n\n/**\n * Fetch and load GTFS Realtime data from multiple feed URLs\n * All feeds are fetched in parallel. If any feed fails, an error is thrown.\n */\nexport async function loadRealtimeData(db: Database, feedUrls: string[]): Promise {\n const root = loadGtfsRtProto();\n const FeedMessage = root.lookupType('transit_realtime.FeedMessage');\n\n // Fetch all feeds in parallel\n const fetchPromises = feedUrls.map(async (url) => {\n try {\n const data = await fetchProtobuf(url);\n const message = FeedMessage.decode(data);\n return FeedMessage.toObject(message, {\n longs: Number,\n enums: Number,\n bytes: String,\n defaults: false,\n arrays: true,\n objects: true,\n oneofs: true\n });\n } catch (error) {\n throw new Error(`Failed to fetch or parse GTFS-RT feed from ${url}: ${error instanceof Error ? error.message : String(error)}`);\n }\n });\n\n // Wait for all feeds - if any fails, this will throw\n const feeds = await Promise.all(fetchPromises);\n\n // Current timestamp for staleness tracking\n const now = Math.floor(Date.now() / 1000);\n\n // Collect all entities by type\n const allAlerts: ProtobufAlert[] = [];\n const allVehiclePositions: ProtobufVehiclePosition[] = [];\n const allTripUpdates: ProtobufTripUpdate[] = [];\n\n for (const feed of feeds) {\n if (!feed.entity) continue;\n\n for (const entity of feed.entity) {\n if (entity.alert) {\n allAlerts.push({ id: entity.id, ...entity.alert });\n }\n if (entity.vehicle) {\n allVehiclePositions.push(entity.vehicle);\n }\n if (entity.tripUpdate) {\n allTripUpdates.push(entity.tripUpdate);\n }\n }\n }\n\n // Insert into database\n if (allAlerts.length > 0) {\n insertAlerts(db, allAlerts, now);\n }\n if (allVehiclePositions.length > 0) {\n insertVehiclePositions(db, allVehiclePositions, now);\n }\n if (allTripUpdates.length > 0) {\n insertTripUpdates(db, allTripUpdates, now);\n }\n}\n","/**\n * Agency Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Agency } from '../types/gtfs';\n\nexport interface AgencyFilters {\n agencyId?: string | string[];\n limit?: number;\n}\n\n/**\n * Get agencies with optional filters\n * - Filters support both single values and arrays\n */\nexport function getAgencies(db: Database, filters: AgencyFilters = {}): Agency[] {\n const { agencyId, limit } = filters;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (agencyId) {\n const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId];\n if (agencyIds.length > 0) {\n const placeholders = agencyIds.map(() => '?').join(', ');\n conditions.push(`agency_id IN (${placeholders})`);\n params.push(...agencyIds);\n }\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM agency';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY agency_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const agencies: Agency[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n agencies.push(rowToAgency(row));\n }\n\n stmt.free();\n return agencies;\n}\n\n/**\n * Convert database row to Agency object\n */\nfunction rowToAgency(row: Record): Agency {\n return {\n agency_id: String(row.agency_id),\n agency_name: String(row.agency_name),\n agency_url: String(row.agency_url),\n agency_timezone: String(row.agency_timezone),\n agency_lang: row.agency_lang ? String(row.agency_lang) : undefined,\n agency_phone: row.agency_phone ? String(row.agency_phone) : undefined,\n agency_fare_url: row.agency_fare_url ? String(row.agency_fare_url) : undefined,\n agency_email: row.agency_email ? String(row.agency_email) : undefined,\n };\n}\n","/**\n * Stop Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Stop } from '../types/gtfs';\n\nexport interface StopFilters {\n stopId?: string | string[];\n stopCode?: string | string[];\n name?: string;\n tripId?: string | string[];\n limit?: number;\n}\n\n/**\n * Get stops with optional filters\n * - Filters support both single values and arrays\n * - Use name filter for partial name matching\n * - Use tripId filter to get stops for a specific trip (ordered by stop_sequence)\n */\nexport function getStops(db: Database, filters: StopFilters = {}): Stop[] {\n const { stopId, stopCode, name, tripId, limit } = filters;\n\n // Handle special case: get stops by trip (requires JOIN)\n if (tripId) {\n const tripIds = Array.isArray(tripId) ? tripId : [tripId];\n if (tripIds.length === 0) return [];\n\n const placeholders = tripIds.map(() => '?').join(', ');\n const stmt = db.prepare(`\n SELECT s.* FROM stops s\n INNER JOIN stop_times st ON s.stop_id = st.stop_id\n WHERE st.trip_id IN (${placeholders})\n ORDER BY st.stop_sequence\n `);\n stmt.bind(tripIds);\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n }\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (stopId) {\n const stopIds = Array.isArray(stopId) ? stopId : [stopId];\n if (stopIds.length > 0) {\n const placeholders = stopIds.map(() => '?').join(', ');\n conditions.push(`stop_id IN (${placeholders})`);\n params.push(...stopIds);\n }\n }\n\n if (stopCode) {\n const stopCodes = Array.isArray(stopCode) ? stopCode : [stopCode];\n if (stopCodes.length > 0) {\n const placeholders = stopCodes.map(() => '?').join(', ');\n conditions.push(`stop_code IN (${placeholders})`);\n params.push(...stopCodes);\n }\n }\n\n if (name) {\n conditions.push('stop_name LIKE ?');\n params.push(`%${name}%`);\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM stops';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY stop_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Search stops by name (case-insensitive, partial match)\n * This is a convenience method for name-based searches\n */\nexport function searchStopsByName(db: Database, name: string, limit = 50): Stop[] {\n return getStops(db, { name, limit });\n}\n\n/**\n * Convert database row to Stop object\n */\nfunction rowToStop(row: Record): Stop {\n return {\n stop_id: String(row.stop_id),\n stop_name: String(row.stop_name),\n stop_lat: Number(row.stop_lat),\n stop_lon: Number(row.stop_lon),\n stop_code: row.stop_code ? String(row.stop_code) : undefined,\n stop_desc: row.stop_desc ? String(row.stop_desc) : undefined,\n zone_id: row.zone_id ? String(row.zone_id) : undefined,\n stop_url: row.stop_url ? String(row.stop_url) : undefined,\n location_type: row.location_type !== null ? Number(row.location_type) : undefined,\n parent_station: row.parent_station ? String(row.parent_station) : undefined,\n stop_timezone: row.stop_timezone ? String(row.stop_timezone) : undefined,\n wheelchair_boarding: row.wheelchair_boarding !== null ? Number(row.wheelchair_boarding) : undefined,\n level_id: row.level_id ? String(row.level_id) : undefined,\n platform_code: row.platform_code ? String(row.platform_code) : undefined,\n };\n}\n","/**\n * Route Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Route } from '../types/gtfs';\n\nexport interface RouteFilters {\n routeId?: string | string[];\n agencyId?: string | string[];\n limit?: number;\n}\n\n/**\n * Get routes with optional filters\n * - Filters support both single values and arrays\n */\nexport function getRoutes(db: Database, filters: RouteFilters = {}): Route[] {\n const { routeId, agencyId, limit } = filters;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (routeId) {\n const routeIds = Array.isArray(routeId) ? routeId : [routeId];\n if (routeIds.length > 0) {\n const placeholders = routeIds.map(() => '?').join(', ');\n conditions.push(`route_id IN (${placeholders})`);\n params.push(...routeIds);\n }\n }\n\n if (agencyId) {\n const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId];\n if (agencyIds.length > 0) {\n const placeholders = agencyIds.map(() => '?').join(', ');\n conditions.push(`agency_id IN (${placeholders})`);\n params.push(...agencyIds);\n }\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM routes';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY route_short_name, route_long_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const routes: Route[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n routes.push(rowToRoute(row));\n }\n\n stmt.free();\n return routes;\n}\n\n/**\n * Convert database row to Route object\n */\nfunction rowToRoute(row: Record): Route {\n return {\n route_id: String(row.route_id),\n route_short_name: String(row.route_short_name),\n route_long_name: String(row.route_long_name),\n route_type: Number(row.route_type),\n agency_id: row.agency_id ? String(row.agency_id) : undefined,\n route_desc: row.route_desc ? String(row.route_desc) : undefined,\n route_url: row.route_url ? String(row.route_url) : undefined,\n route_color: row.route_color ? String(row.route_color) : undefined,\n route_text_color: row.route_text_color ? String(row.route_text_color) : undefined,\n route_sort_order: row.route_sort_order !== null ? Number(row.route_sort_order) : undefined,\n continuous_pickup: row.continuous_pickup !== null ? Number(row.continuous_pickup) : undefined,\n continuous_drop_off: row.continuous_drop_off !== null ? Number(row.continuous_drop_off) : undefined,\n };\n}\n","/**\n * Calendar Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Calendar, CalendarDate } from '../types/gtfs';\n\n/**\n * Get active service IDs for a given date\n */\nexport function getActiveServiceIds(db: Database, date: string): string[] {\n const serviceIds = new Set();\n\n // Parse date (format: YYYYMMDD)\n const year = parseInt(date.substring(0, 4));\n const month = parseInt(date.substring(4, 6));\n const day = parseInt(date.substring(6, 8));\n const dateObj = new Date(year, month - 1, day);\n const dayOfWeek = dateObj.getDay(); // 0 = Sunday, 1 = Monday, etc.\n\n // Map day of week to GTFS calendar field\n const dayFields = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];\n const dayField = dayFields[dayOfWeek];\n\n // Check calendar.txt for regular service\n const calendarStmt = db.prepare(\n `SELECT service_id FROM calendar\n WHERE ${dayField} = 1\n AND start_date <= ?\n AND end_date >= ?`\n );\n calendarStmt.bind([date, date]);\n\n while (calendarStmt.step()) {\n const row = calendarStmt.getAsObject() as { service_id: string };\n serviceIds.add(row.service_id);\n }\n calendarStmt.free();\n\n // Check calendar_dates.txt for exceptions\n const exceptionsStmt = db.prepare('SELECT service_id, exception_type FROM calendar_dates WHERE date = ?');\n exceptionsStmt.bind([date]);\n\n while (exceptionsStmt.step()) {\n const row = exceptionsStmt.getAsObject() as { service_id: string; exception_type: number };\n if (row.exception_type === 1) {\n // Service added\n serviceIds.add(row.service_id);\n } else if (row.exception_type === 2) {\n // Service removed\n serviceIds.delete(row.service_id);\n }\n }\n exceptionsStmt.free();\n\n return Array.from(serviceIds);\n}\n\n/**\n * Get calendar entry by service_id\n */\nexport function getCalendarByServiceId(db: Database, serviceId: string): Calendar | null {\n const stmt = db.prepare('SELECT * FROM calendar WHERE service_id = ?');\n stmt.bind([serviceId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToCalendar(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get calendar date exceptions for a service\n */\nexport function getCalendarDates(db: Database, serviceId: string): CalendarDate[] {\n const stmt = db.prepare('SELECT * FROM calendar_dates WHERE service_id = ? ORDER BY date');\n stmt.bind([serviceId]);\n\n const dates: CalendarDate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n dates.push(rowToCalendarDate(row));\n }\n\n stmt.free();\n return dates;\n}\n\n/**\n * Get calendar date exceptions for a specific date\n */\nexport function getCalendarDatesForDate(db: Database, date: string): CalendarDate[] {\n const stmt = db.prepare('SELECT * FROM calendar_dates WHERE date = ?');\n stmt.bind([date]);\n\n const dates: CalendarDate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n dates.push(rowToCalendarDate(row));\n }\n\n stmt.free();\n return dates;\n}\n\n/**\n * Convert database row to Calendar object\n */\nfunction rowToCalendar(row: Record): Calendar {\n return {\n service_id: String(row.service_id),\n monday: Number(row.monday),\n tuesday: Number(row.tuesday),\n wednesday: Number(row.wednesday),\n thursday: Number(row.thursday),\n friday: Number(row.friday),\n saturday: Number(row.saturday),\n sunday: Number(row.sunday),\n start_date: String(row.start_date),\n end_date: String(row.end_date),\n };\n}\n\n/**\n * Convert database row to CalendarDate object\n */\nfunction rowToCalendarDate(row: Record): CalendarDate {\n return {\n service_id: String(row.service_id),\n date: String(row.date),\n exception_type: Number(row.exception_type),\n };\n}\n","import type { Database} from 'sql.js';\nimport type { VehiclePosition } from '../types/gtfs-rt';\nimport type { VehiclePositionFilters } from '../types/gtfs-rt';\n\nexport type { VehiclePositionFilters };\n\n/**\n * Parse vehicle position from database row\n */\nexport function parseVehiclePosition(row: Record): VehiclePosition {\n const vp: VehiclePosition = {\n trip_id: String(row.trip_id),\n route_id: row.route_id ? String(row.route_id) : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n\n // Vehicle descriptor\n if (row.vehicle_id || row.vehicle_label || row.vehicle_license_plate) {\n vp.vehicle = {\n id: row.vehicle_id ? String(row.vehicle_id) : undefined,\n label: row.vehicle_label ? String(row.vehicle_label) : undefined,\n license_plate: row.vehicle_license_plate ? String(row.vehicle_license_plate) : undefined\n };\n }\n\n // Position\n if (row.latitude !== null && row.longitude !== null) {\n vp.position = {\n latitude: Number(row.latitude),\n longitude: Number(row.longitude),\n bearing: row.bearing !== null ? Number(row.bearing) : undefined,\n odometer: row.odometer !== null ? Number(row.odometer) : undefined,\n speed: row.speed !== null ? Number(row.speed) : undefined\n };\n }\n\n // Other fields\n if (row.current_stop_sequence !== null) {\n vp.current_stop_sequence = Number(row.current_stop_sequence);\n }\n if (row.stop_id) {\n vp.stop_id = String(row.stop_id);\n }\n if (row.current_status !== null) {\n vp.current_status = Number(row.current_status);\n }\n if (row.timestamp !== null) {\n vp.timestamp = Number(row.timestamp);\n }\n if (row.congestion_level !== null) {\n vp.congestion_level = Number(row.congestion_level);\n }\n if (row.occupancy_status !== null) {\n vp.occupancy_status = Number(row.occupancy_status);\n }\n\n return vp;\n}\n\n/**\n * Get vehicle positions with optional filters\n */\nexport function getVehiclePositions(\n db: Database,\n filters: VehiclePositionFilters = {},\n stalenessThreshold: number = 120\n): VehiclePosition[] {\n const { tripId, routeId, vehicleId, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by route_id\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n // Filter by vehicle_id\n if (vehicleId) {\n conditions.push('vehicle_id = ?');\n params.push(vehicleId);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_vehicle_positions';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const positions: VehiclePosition[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n positions.push(parseVehiclePosition(row));\n }\n\n stmt.free();\n return positions;\n}\n\n/**\n * Get vehicle position by trip ID\n */\nexport function getVehiclePositionByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): VehiclePosition | null {\n const positions = getVehiclePositions(db, { tripId, limit: 1 }, stalenessThreshold);\n return positions.length > 0 ? positions[0] : null;\n}\n\n/**\n * Get all vehicle positions without staleness filtering (for debugging)\n */\nexport function getAllVehiclePositions(db: Database): VehiclePosition[] {\n const sql = 'SELECT * FROM rt_vehicle_positions ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const positions: VehiclePosition[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n positions.push(parseVehiclePosition(row));\n }\n\n stmt.free();\n return positions;\n}\n","/**\n * Trip Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Trip } from '../types/gtfs';\nimport type { TripRealtime, VehiclePosition } from '../types/gtfs-rt';\nimport { parseVehiclePosition } from './rt-vehicle-positions';\n\nexport interface TripFilters {\n tripId?: string | string[];\n routeId?: string | string[];\n serviceIds?: string | string[];\n directionId?: number | number[];\n agencyId?: string | string[];\n includeRealtime?: boolean;\n limit?: number;\n}\n\nexport interface TripWithRealtime extends Trip {\n realtime?: TripRealtime;\n}\n\n/**\n * Merge realtime data with trips\n */\nfunction mergeRealtimeData(\n trips: Trip[],\n db: Database,\n stalenessThreshold: number\n): TripWithRealtime[] {\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n\n const tripIds = trips.map(t => t.trip_id);\n if (tripIds.length === 0) return trips;\n\n const placeholders = tripIds.map(() => '?').join(', ');\n\n // Get vehicle positions\n const vpStmt = db.prepare(`\n SELECT * FROM rt_vehicle_positions\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n vpStmt.bind([...tripIds, staleThreshold]);\n\n const vpMap = new Map();\n while (vpStmt.step()) {\n const row = vpStmt.getAsObject() as Record;\n const vp = parseVehiclePosition(row);\n vpMap.set(vp.trip_id, vp);\n }\n vpStmt.free();\n\n // Get trip updates\n const tuStmt = db.prepare(`\n SELECT * FROM rt_trip_updates\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n tuStmt.bind([...tripIds, staleThreshold]);\n\n const tuMap = new Map();\n while (tuStmt.step()) {\n const row = tuStmt.getAsObject() as Record;\n const tripId = String(row.trip_id);\n tuMap.set(tripId, {\n delay: row.delay !== null ? Number(row.delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n });\n }\n tuStmt.free();\n\n // Merge realtime data\n return trips.map((trip): TripWithRealtime => {\n const vp = vpMap.get(trip.trip_id);\n const tu = tuMap.get(trip.trip_id);\n\n if (!vp && !tu) {\n return { ...trip, realtime: { vehicle_position: null, trip_update: null } };\n }\n\n return {\n ...trip,\n realtime: {\n vehicle_position: vp || null,\n trip_update: tu || null\n }\n };\n });\n}\n\n/**\n * Get trips with optional filters\n * - Filters support both single values and arrays\n */\nexport function getTrips(\n db: Database,\n filters: TripFilters = {},\n stalenessThreshold: number = 120\n): Trip[] | TripWithRealtime[] {\n const { tripId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit } = filters;\n\n // Determine if we need to join with routes table\n const needsRoutesJoin = agencyId !== undefined;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (tripId) {\n const tripIds = Array.isArray(tripId) ? tripId : [tripId];\n if (tripIds.length > 0) {\n const placeholders = tripIds.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`);\n params.push(...tripIds);\n }\n }\n\n if (routeId) {\n const routeIds = Array.isArray(routeId) ? routeId : [routeId];\n if (routeIds.length > 0) {\n const placeholders = routeIds.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.route_id IN (${placeholders})` : `route_id IN (${placeholders})`);\n params.push(...routeIds);\n }\n }\n\n if (serviceIds) {\n const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds];\n if (serviceIdArray.length > 0) {\n const placeholders = serviceIdArray.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`);\n params.push(...serviceIdArray);\n }\n }\n\n if (directionId !== undefined) {\n const directionIds = Array.isArray(directionId) ? directionId : [directionId];\n if (directionIds.length > 0) {\n const placeholders = directionIds.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.direction_id IN (${placeholders})` : `direction_id IN (${placeholders})`);\n params.push(...directionIds);\n }\n }\n\n if (agencyId) {\n const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId];\n if (agencyIds.length > 0) {\n const placeholders = agencyIds.map(() => '?').join(', ');\n conditions.push(`r.agency_id IN (${placeholders})`);\n params.push(...agencyIds);\n }\n }\n\n // Build SQL query\n let sql = needsRoutesJoin\n ? 'SELECT t.* FROM trips t INNER JOIN routes r ON t.route_id = r.route_id'\n : 'SELECT * FROM trips';\n\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n\n // Merge realtime data if requested\n if (includeRealtime) {\n return mergeRealtimeData(trips, db, stalenessThreshold);\n }\n\n return trips;\n}\n\n/**\n * Convert database row to Trip object\n */\nfunction rowToTrip(row: Record): Trip {\n return {\n trip_id: String(row.trip_id),\n route_id: String(row.route_id),\n service_id: String(row.service_id),\n trip_headsign: row.trip_headsign ? String(row.trip_headsign) : undefined,\n trip_short_name: row.trip_short_name ? String(row.trip_short_name) : undefined,\n direction_id: row.direction_id !== null ? Number(row.direction_id) : undefined,\n block_id: row.block_id ? String(row.block_id) : undefined,\n shape_id: row.shape_id ? String(row.shape_id) : undefined,\n wheelchair_accessible: row.wheelchair_accessible !== null ? Number(row.wheelchair_accessible) : undefined,\n bikes_allowed: row.bikes_allowed !== null ? Number(row.bikes_allowed) : undefined,\n };\n}\n","/**\n * Stop Time Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { StopTime } from '../types/gtfs';\nimport type { StopTimeRealtime } from '../types/gtfs-rt';\n\nexport interface StopTimeFilters {\n tripId?: string | string[];\n stopId?: string | string[];\n routeId?: string | string[];\n serviceIds?: string | string[];\n directionId?: number | number[];\n agencyId?: string | string[];\n includeRealtime?: boolean;\n limit?: number;\n}\n\nexport interface StopTimeWithRealtime extends StopTime {\n realtime?: StopTimeRealtime;\n}\n\n/**\n * Merge realtime data with stop times\n */\nfunction mergeRealtimeData(\n stopTimes: StopTime[],\n db: Database,\n stalenessThreshold: number\n): StopTimeWithRealtime[] {\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n\n // Build map of trip_id -> stop times for this query\n const tripIds = Array.from(new Set(stopTimes.map(st => st.trip_id)));\n if (tripIds.length === 0) return stopTimes;\n\n const placeholders = tripIds.map(() => '?').join(', ');\n const stmt = db.prepare(`\n SELECT trip_id, stop_sequence, stop_id,\n arrival_delay, arrival_time, departure_delay, departure_time, schedule_relationship\n FROM rt_stop_time_updates\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n stmt.bind([...tripIds, staleThreshold]);\n\n // Build map of trip_id+stop_sequence -> RT data\n const rtMap = new Map();\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n const key = `${row.trip_id}_${row.stop_sequence}`;\n rtMap.set(key, {\n arrival_delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,\n arrival_time: row.arrival_time !== null ? Number(row.arrival_time) : undefined,\n departure_delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,\n departure_time: row.departure_time !== null ? Number(row.departure_time) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n });\n }\n stmt.free();\n\n // Merge RT data with stop times\n return stopTimes.map((st): StopTimeWithRealtime => {\n const key = `${st.trip_id}_${st.stop_sequence}`;\n const rtData = rtMap.get(key);\n\n if (rtData) {\n return { ...st, realtime: rtData };\n }\n return st;\n });\n}\n\n/**\n * Get stop times with optional filters\n * - Filters support both single values and arrays\n */\nexport function getStopTimes(\n db: Database,\n filters: StopTimeFilters = {},\n stalenessThreshold: number = 120\n): StopTime[] | StopTimeWithRealtime[] {\n const { tripId, stopId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit } = filters;\n\n // Determine if we need to join with trips and/or routes table\n const needsTripsJoin = routeId || serviceIds || directionId !== undefined || agencyId !== undefined;\n const needsRoutesJoin = agencyId !== undefined;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (tripId) {\n const tripIds = Array.isArray(tripId) ? tripId : [tripId];\n if (tripIds.length > 0) {\n const placeholders = tripIds.map(() => '?').join(', ');\n conditions.push(needsTripsJoin ? `st.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`);\n params.push(...tripIds);\n }\n }\n\n if (stopId) {\n const stopIds = Array.isArray(stopId) ? stopId : [stopId];\n if (stopIds.length > 0) {\n const placeholders = stopIds.map(() => '?').join(', ');\n conditions.push(needsTripsJoin ? `st.stop_id IN (${placeholders})` : `stop_id IN (${placeholders})`);\n params.push(...stopIds);\n }\n }\n\n if (routeId) {\n const routeIds = Array.isArray(routeId) ? routeId : [routeId];\n if (routeIds.length > 0) {\n const placeholders = routeIds.map(() => '?').join(', ');\n conditions.push(`t.route_id IN (${placeholders})`);\n params.push(...routeIds);\n }\n }\n\n if (serviceIds) {\n const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds];\n if (serviceIdArray.length > 0) {\n const placeholders = serviceIdArray.map(() => '?').join(', ');\n conditions.push(`t.service_id IN (${placeholders})`);\n params.push(...serviceIdArray);\n }\n }\n\n if (directionId !== undefined) {\n const directionIds = Array.isArray(directionId) ? directionId : [directionId];\n if (directionIds.length > 0) {\n const placeholders = directionIds.map(() => '?').join(', ');\n conditions.push(`t.direction_id IN (${placeholders})`);\n params.push(...directionIds);\n }\n }\n\n if (agencyId) {\n const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId];\n if (agencyIds.length > 0) {\n const placeholders = agencyIds.map(() => '?').join(', ');\n conditions.push(`r.agency_id IN (${placeholders})`);\n params.push(...agencyIds);\n }\n }\n\n // Build SQL query\n let sql: string;\n if (needsRoutesJoin) {\n sql = 'SELECT st.* FROM stop_times st INNER JOIN trips t ON st.trip_id = t.trip_id INNER JOIN routes r ON t.route_id = r.route_id';\n } else if (needsTripsJoin) {\n sql = 'SELECT st.* FROM stop_times st INNER JOIN trips t ON st.trip_id = t.trip_id';\n } else {\n sql = 'SELECT * FROM stop_times';\n }\n\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n\n // Order by stop_sequence if filtering by trip, otherwise by arrival_time\n sql += tripId ? ' ORDER BY stop_sequence' : ' ORDER BY arrival_time';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n\n // Merge realtime data if requested\n if (includeRealtime) {\n return mergeRealtimeData(stopTimes, db, stalenessThreshold);\n }\n\n return stopTimes;\n}\n\n/**\n * Convert database row to StopTime object\n */\nfunction rowToStopTime(row: Record): StopTime {\n return {\n trip_id: String(row.trip_id),\n arrival_time: String(row.arrival_time),\n departure_time: String(row.departure_time),\n stop_id: String(row.stop_id),\n stop_sequence: Number(row.stop_sequence),\n stop_headsign: row.stop_headsign ? String(row.stop_headsign) : undefined,\n pickup_type: row.pickup_type !== null ? Number(row.pickup_type) : undefined,\n drop_off_type: row.drop_off_type !== null ? Number(row.drop_off_type) : undefined,\n continuous_pickup: row.continuous_pickup !== null ? Number(row.continuous_pickup) : undefined,\n continuous_drop_off: row.continuous_drop_off !== null ? Number(row.continuous_drop_off) : undefined,\n shape_dist_traveled: row.shape_dist_traveled !== null ? Number(row.shape_dist_traveled) : undefined,\n timepoint: row.timepoint !== null ? Number(row.timepoint) : undefined,\n };\n}\n","import type { Database } from 'sql.js';\nimport type { Alert, EntitySelector, TimeRange, TranslatedString } from '../types/gtfs-rt';\nimport type { AlertFilters } from '../types/gtfs-rt';\n\nexport type { AlertFilters };\n\n/**\n * Parse JSON fields from database\n */\nfunction parseAlert(row: Record): Alert {\n return {\n id: String(row.id),\n active_period: row.active_period ? JSON.parse(String(row.active_period)) as TimeRange[] : [],\n informed_entity: row.informed_entity ? JSON.parse(String(row.informed_entity)) as EntitySelector[] : [],\n cause: row.cause ? Number(row.cause) : undefined,\n effect: row.effect ? Number(row.effect) : undefined,\n url: row.url ? JSON.parse(String(row.url)) as TranslatedString : undefined,\n header_text: row.header_text ? JSON.parse(String(row.header_text)) as TranslatedString : undefined,\n description_text: row.description_text ? JSON.parse(String(row.description_text)) as TranslatedString : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n}\n\n/**\n * Check if an alert is currently active based on its active_period\n */\nfunction isAlertActive(alert: Alert, now: number): boolean {\n // If no active periods defined, assume always active\n if (!alert.active_period || alert.active_period.length === 0) {\n return true;\n }\n\n // Check if current time falls within any active period\n for (const period of alert.active_period) {\n const start = period.start || 0;\n const end = period.end || Number.MAX_SAFE_INTEGER;\n\n if (now >= start && now <= end) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if an alert affects the specified entity\n */\nfunction alertAffectsEntity(alert: Alert, filters: AlertFilters): boolean {\n if (!alert.informed_entity || alert.informed_entity.length === 0) {\n return true; // No specific entities, assume it affects all\n }\n\n for (const entity of alert.informed_entity) {\n // Check route_id\n if (filters.routeId && entity.route_id === filters.routeId) {\n return true;\n }\n\n // Check stop_id\n if (filters.stopId && entity.stop_id === filters.stopId) {\n return true;\n }\n\n // Check trip_id\n if (filters.tripId && entity.trip?.trip_id === filters.tripId) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Get alerts with optional filters\n */\nexport function getAlerts(db: Database, filters: AlertFilters = {}, stalenessThreshold: number = 120): Alert[] {\n const {\n alertId,\n activeOnly,\n routeId,\n stopId,\n tripId,\n cause,\n effect,\n limit\n } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by alert ID\n if (alertId) {\n conditions.push('id = ?');\n params.push(alertId);\n }\n\n // Filter by cause\n if (cause !== undefined) {\n conditions.push('cause = ?');\n params.push(cause);\n }\n\n // Filter by effect\n if (effect !== undefined) {\n conditions.push('effect = ?');\n params.push(effect);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_alerts';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const alerts: Alert[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n const alert = parseAlert(row);\n\n // Apply activeOnly filter in application code\n if (activeOnly && !isAlertActive(alert, now)) {\n continue;\n }\n\n // Apply entity filters in application code (since informed_entity is JSON)\n if (routeId || stopId || tripId) {\n if (!alertAffectsEntity(alert, filters)) {\n continue;\n }\n }\n\n alerts.push(alert);\n }\n\n stmt.free();\n return alerts;\n}\n\n/**\n * Get alert by ID\n */\nexport function getAlertById(db: Database, alertId: string, stalenessThreshold: number = 120): Alert | null {\n const alerts = getAlerts(db, { alertId, limit: 1 }, stalenessThreshold);\n return alerts.length > 0 ? alerts[0] : null;\n}\n\n/**\n * Get all alerts without staleness filtering (for debugging)\n */\nexport function getAllAlerts(db: Database): Alert[] {\n const sql = 'SELECT * FROM rt_alerts ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const alerts: Alert[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n alerts.push(parseAlert(row));\n }\n\n stmt.free();\n return alerts;\n}\n","import type { Database } from 'sql.js';\nimport type { TripUpdate } from '../types/gtfs-rt';\n\nexport interface TripUpdateFilters {\n tripId?: string;\n routeId?: string;\n vehicleId?: string;\n limit?: number;\n}\n\n/**\n * Parse trip update from database row\n */\nexport function parseTripUpdate(row: Record): TripUpdate {\n const tu: TripUpdate = {\n trip_id: String(row.trip_id),\n route_id: row.route_id ? String(row.route_id) : undefined,\n stop_time_update: [], // Will be populated separately\n timestamp: row.timestamp !== null ? Number(row.timestamp) : undefined,\n delay: row.delay !== null ? Number(row.delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n\n // Vehicle descriptor\n if (row.vehicle_id || row.vehicle_label || row.vehicle_license_plate) {\n tu.vehicle = {\n id: row.vehicle_id ? String(row.vehicle_id) : undefined,\n label: row.vehicle_label ? String(row.vehicle_label) : undefined,\n license_plate: row.vehicle_license_plate ? String(row.vehicle_license_plate) : undefined\n };\n }\n\n return tu;\n}\n\n/**\n * Get trip updates with optional filters\n */\nexport function getTripUpdates(\n db: Database,\n filters: TripUpdateFilters = {},\n stalenessThreshold: number = 120\n): TripUpdate[] {\n const { tripId, routeId, vehicleId, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by route_id\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n // Filter by vehicle_id\n if (vehicleId) {\n conditions.push('vehicle_id = ?');\n params.push(vehicleId);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_trip_updates';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const tripUpdates: TripUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n tripUpdates.push(parseTripUpdate(row));\n }\n\n stmt.free();\n return tripUpdates;\n}\n\n/**\n * Get trip update by trip ID\n */\nexport function getTripUpdateByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): TripUpdate | null {\n const updates = getTripUpdates(db, { tripId, limit: 1 }, stalenessThreshold);\n return updates.length > 0 ? updates[0] : null;\n}\n\n/**\n * Get all trip updates without staleness filtering (for debugging)\n */\nexport function getAllTripUpdates(db: Database): TripUpdate[] {\n const sql = 'SELECT * FROM rt_trip_updates ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const tripUpdates: TripUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n tripUpdates.push(parseTripUpdate(row));\n }\n\n stmt.free();\n return tripUpdates;\n}\n","import type { Database } from 'sql.js';\nimport type { StopTimeUpdate } from '../types/gtfs-rt';\n\nexport interface StopTimeUpdateFilters {\n tripId?: string | string[];\n stopId?: string | string[];\n stopSequence?: number | number[];\n limit?: number;\n}\n\n/**\n * Parse stop time update from database row\n */\nfunction parseStopTimeUpdate(row: Record): StopTimeUpdate {\n const stu: StopTimeUpdate = {\n stop_sequence: row.stop_sequence !== null ? Number(row.stop_sequence) : undefined,\n stop_id: row.stop_id ? String(row.stop_id) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n };\n\n // Arrival event\n if (row.arrival_delay !== null || row.arrival_time !== null || row.arrival_uncertainty !== null) {\n stu.arrival = {\n delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,\n time: row.arrival_time !== null ? Number(row.arrival_time) : undefined,\n uncertainty: row.arrival_uncertainty !== null ? Number(row.arrival_uncertainty) : undefined\n };\n }\n\n // Departure event\n if (row.departure_delay !== null || row.departure_time !== null || row.departure_uncertainty !== null) {\n stu.departure = {\n delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,\n time: row.departure_time !== null ? Number(row.departure_time) : undefined,\n uncertainty: row.departure_uncertainty !== null ? Number(row.departure_uncertainty) : undefined\n };\n }\n\n return stu;\n}\n\n/**\n * Extended StopTimeUpdate with trip_id and rt_last_updated for debugging\n */\nexport interface StopTimeUpdateWithMetadata extends StopTimeUpdate {\n trip_id: string;\n rt_last_updated: number;\n}\n\n/**\n * Parse stop time update with metadata from database row\n */\nfunction parseStopTimeUpdateWithMetadata(row: Record): StopTimeUpdateWithMetadata {\n const stu = parseStopTimeUpdate(row) as StopTimeUpdateWithMetadata;\n stu.trip_id = String(row.trip_id);\n stu.rt_last_updated = Number(row.rt_last_updated);\n return stu;\n}\n\n/**\n * Get stop time updates with optional filters\n * - Filters support both single values and arrays\n */\nexport function getStopTimeUpdates(\n db: Database,\n filters: StopTimeUpdateFilters = {},\n stalenessThreshold: number = 120\n): StopTimeUpdate[] {\n const { tripId, stopId, stopSequence, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n const tripIds = Array.isArray(tripId) ? tripId : [tripId];\n if (tripIds.length > 0) {\n const placeholders = tripIds.map(() => '?').join(', ');\n conditions.push(`trip_id IN (${placeholders})`);\n params.push(...tripIds);\n }\n }\n\n // Filter by stop_id\n if (stopId) {\n const stopIds = Array.isArray(stopId) ? stopId : [stopId];\n if (stopIds.length > 0) {\n const placeholders = stopIds.map(() => '?').join(', ');\n conditions.push(`stop_id IN (${placeholders})`);\n params.push(...stopIds);\n }\n }\n\n // Filter by stop_sequence\n if (stopSequence !== undefined) {\n const stopSequences = Array.isArray(stopSequence) ? stopSequence : [stopSequence];\n if (stopSequences.length > 0) {\n const placeholders = stopSequences.map(() => '?').join(', ');\n conditions.push(`stop_sequence IN (${placeholders})`);\n params.push(...stopSequences);\n }\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_stop_time_updates';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY trip_id, stop_sequence';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stopTimeUpdates: StopTimeUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimeUpdates.push(parseStopTimeUpdate(row));\n }\n\n stmt.free();\n return stopTimeUpdates;\n}\n\n/**\n * Get all stop time updates without staleness filtering (for debugging)\n * Returns extended type with trip_id and rt_last_updated for debugging purposes\n */\nexport function getAllStopTimeUpdates(db: Database): StopTimeUpdateWithMetadata[] {\n const sql = 'SELECT * FROM rt_stop_time_updates ORDER BY trip_id, stop_sequence';\n const stmt = db.prepare(sql);\n\n const stopTimeUpdates: StopTimeUpdateWithMetadata[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimeUpdates.push(parseStopTimeUpdateWithMetadata(row));\n }\n\n stmt.free();\n return stopTimeUpdates;\n}\n","/**\n * GTFS Realtime TypeScript types\n * Based on GTFS Realtime v2.0 specification\n */\n\n// Enum types from GTFS-RT specification\n\nexport enum ScheduleRelationship {\n SCHEDULED = 0,\n ADDED = 1,\n UNSCHEDULED = 2,\n CANCELED = 3,\n SKIPPED = 4,\n NO_DATA = 5\n}\n\nexport enum VehicleStopStatus {\n INCOMING_AT = 0,\n STOPPED_AT = 1,\n IN_TRANSIT_TO = 2\n}\n\nexport enum CongestionLevel {\n UNKNOWN_CONGESTION_LEVEL = 0,\n RUNNING_SMOOTHLY = 1,\n STOP_AND_GO = 2,\n CONGESTION = 3,\n SEVERE_CONGESTION = 4\n}\n\nexport enum OccupancyStatus {\n EMPTY = 0,\n MANY_SEATS_AVAILABLE = 1,\n FEW_SEATS_AVAILABLE = 2,\n STANDING_ROOM_ONLY = 3,\n CRUSHED_STANDING_ROOM_ONLY = 4,\n FULL = 5,\n NOT_ACCEPTING_PASSENGERS = 6\n}\n\nexport enum AlertCause {\n UNKNOWN_CAUSE = 1,\n OTHER_CAUSE = 2,\n TECHNICAL_PROBLEM = 3,\n STRIKE = 4,\n DEMONSTRATION = 5,\n ACCIDENT = 6,\n HOLIDAY = 7,\n WEATHER = 8,\n MAINTENANCE = 9,\n CONSTRUCTION = 10,\n POLICE_ACTIVITY = 11,\n MEDICAL_EMERGENCY = 12\n}\n\nexport enum AlertEffect {\n NO_SERVICE = 1,\n REDUCED_SERVICE = 2,\n SIGNIFICANT_DELAYS = 3,\n DETOUR = 4,\n ADDITIONAL_SERVICE = 5,\n MODIFIED_SERVICE = 6,\n OTHER_EFFECT = 7,\n UNKNOWN_EFFECT = 8,\n STOP_MOVED = 9,\n NO_EFFECT = 10,\n ACCESSIBILITY_ISSUE = 11\n}\n\n// Main GTFS-RT types\n\nexport interface TranslatedString {\n translation: Array<{\n text: string;\n language?: string;\n }>;\n}\n\nexport interface EntitySelector {\n agency_id?: string;\n route_id?: string;\n route_type?: number;\n trip?: {\n trip_id?: string;\n route_id?: string;\n direction_id?: number;\n start_time?: string;\n start_date?: string;\n schedule_relationship?: ScheduleRelationship;\n };\n stop_id?: string;\n}\n\nexport interface TimeRange {\n start?: number; // UNIX timestamp\n end?: number; // UNIX timestamp\n}\n\nexport interface Alert {\n id: string;\n active_period: TimeRange[];\n informed_entity: EntitySelector[];\n cause?: AlertCause;\n effect?: AlertEffect;\n url?: TranslatedString;\n header_text?: TranslatedString;\n description_text?: TranslatedString;\n rt_last_updated: number; // UNIX timestamp\n}\n\nexport interface Position {\n latitude: number;\n longitude: number;\n bearing?: number;\n odometer?: number;\n speed?: number;\n}\n\nexport interface VehicleDescriptor {\n id?: string;\n label?: string;\n license_plate?: string;\n}\n\nexport interface VehiclePosition {\n trip_id: string;\n route_id?: string;\n vehicle?: VehicleDescriptor;\n position?: Position;\n current_stop_sequence?: number;\n stop_id?: string;\n current_status?: VehicleStopStatus;\n timestamp?: number;\n congestion_level?: CongestionLevel;\n occupancy_status?: OccupancyStatus;\n rt_last_updated: number; // UNIX timestamp\n}\n\nexport interface StopTimeEvent {\n delay?: number; // seconds\n time?: number; // UNIX timestamp (absolute time)\n uncertainty?: number;\n}\n\nexport interface StopTimeUpdate {\n stop_sequence?: number;\n stop_id?: string;\n arrival?: StopTimeEvent;\n departure?: StopTimeEvent;\n schedule_relationship?: ScheduleRelationship;\n}\n\nexport interface TripUpdate {\n trip_id: string;\n route_id?: string;\n vehicle?: VehicleDescriptor;\n stop_time_update: StopTimeUpdate[];\n timestamp?: number;\n delay?: number;\n schedule_relationship?: ScheduleRelationship;\n rt_last_updated: number; // UNIX timestamp\n}\n\n// Enhanced types for queries with realtime data\n\nexport interface StopTimeRealtime {\n arrival_delay?: number; // seconds\n arrival_time?: number; // UNIX timestamp (absolute time)\n departure_delay?: number; // seconds\n departure_time?: number; // UNIX timestamp (absolute time)\n schedule_relationship?: ScheduleRelationship;\n}\n\nexport interface TripRealtime {\n vehicle_position?: VehiclePosition | null;\n trip_update?: {\n delay?: number;\n schedule_relationship?: ScheduleRelationship;\n } | null;\n}\n\n// Filter types for realtime queries\n\nexport interface AlertFilters {\n alertId?: string;\n activeOnly?: boolean;\n routeId?: string;\n stopId?: string;\n tripId?: string;\n cause?: AlertCause;\n effect?: AlertEffect;\n limit?: number;\n}\n\nexport interface VehiclePositionFilters {\n tripId?: string;\n routeId?: string;\n vehicleId?: string;\n limit?: number;\n}\n\n// Configuration\n\nexport interface RealtimeConfig {\n feedUrls?: string[];\n stalenessThreshold?: number; // seconds, default 120\n}\n"],"mappings":"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","names":["JSZip","Papa","response","arrayBuffer","protobuf","stmt","stops","mergeRealtimeData","initSqlJs","ScheduleRelationship","VehicleStopStatus","CongestionLevel","OccupancyStatus","AlertCause","AlertEffect"]} \ No newline at end of file diff --git a/dist/index.mjs b/dist/index.mjs index ba75994..e0af996 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -950,8 +950,12 @@ function getAgencies(db, filters = {}) { const conditions = []; const params = []; if (agencyId) { - conditions.push("agency_id = ?"); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => "?").join(", "); + conditions.push(`agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } let sql = "SELECT * FROM agency"; if (conditions.length > 0) { @@ -974,17 +978,6 @@ function getAgencies(db, filters = {}) { stmt.free(); return agencies; } -function getAgencyById(db, agencyId) { - const stmt = db.prepare("SELECT * FROM agency WHERE agency_id = ?"); - stmt.bind([agencyId]); - if (stmt.step()) { - const row = stmt.getAsObject(); - stmt.free(); - return rowToAgency(row); - } - stmt.free(); - return null; -} function rowToAgency(row) { return { agency_id: String(row.agency_id), @@ -1002,17 +995,41 @@ function rowToAgency(row) { function getStops(db, filters = {}) { const { stopId, stopCode, name, tripId, limit } = filters; if (tripId) { - return getStopsByTrip(db, tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length === 0) return []; + const placeholders = tripIds.map(() => "?").join(", "); + const stmt2 = db.prepare(` + SELECT s.* FROM stops s + INNER JOIN stop_times st ON s.stop_id = st.stop_id + WHERE st.trip_id IN (${placeholders}) + ORDER BY st.stop_sequence + `); + stmt2.bind(tripIds); + const stops2 = []; + while (stmt2.step()) { + const row = stmt2.getAsObject(); + stops2.push(rowToStop(row)); + } + stmt2.free(); + return stops2; } const conditions = []; const params = []; if (stopId) { - conditions.push("stop_id = ?"); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => "?").join(", "); + conditions.push(`stop_id IN (${placeholders})`); + params.push(...stopIds); + } } if (stopCode) { - conditions.push("stop_code = ?"); - params.push(stopCode); + const stopCodes = Array.isArray(stopCode) ? stopCode : [stopCode]; + if (stopCodes.length > 0) { + const placeholders = stopCodes.map(() => "?").join(", "); + conditions.push(`stop_code IN (${placeholders})`); + params.push(...stopCodes); + } } if (name) { conditions.push("stop_name LIKE ?"); @@ -1039,33 +1056,6 @@ function getStops(db, filters = {}) { stmt.free(); return stops; } -function getStopById(db, stopId) { - const stmt = db.prepare("SELECT * FROM stops WHERE stop_id = ?"); - stmt.bind([stopId]); - if (stmt.step()) { - const row = stmt.getAsObject(); - stmt.free(); - return rowToStop(row); - } - stmt.free(); - return null; -} -function getStopsByTrip(db, tripId) { - const stmt = db.prepare(` - SELECT s.* FROM stops s - INNER JOIN stop_times st ON s.stop_id = st.stop_id - WHERE st.trip_id = ? - ORDER BY st.stop_sequence - `); - stmt.bind([tripId]); - const stops = []; - while (stmt.step()) { - const row = stmt.getAsObject(); - stops.push(rowToStop(row)); - } - stmt.free(); - return stops; -} function rowToStop(row) { return { stop_id: String(row.stop_id), @@ -1091,12 +1081,20 @@ function getRoutes(db, filters = {}) { const conditions = []; const params = []; if (routeId) { - conditions.push("route_id = ?"); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => "?").join(", "); + conditions.push(`route_id IN (${placeholders})`); + params.push(...routeIds); + } } if (agencyId) { - conditions.push("agency_id = ?"); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => "?").join(", "); + conditions.push(`agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } let sql = "SELECT * FROM routes"; if (conditions.length > 0) { @@ -1119,17 +1117,6 @@ function getRoutes(db, filters = {}) { stmt.free(); return routes; } -function getRouteById(db, routeId) { - const stmt = db.prepare("SELECT * FROM routes WHERE route_id = ?"); - stmt.bind([routeId]); - if (stmt.step()) { - const row = stmt.getAsObject(); - stmt.free(); - return rowToRoute(row); - } - stmt.free(); - return null; -} function rowToRoute(row) { return { route_id: String(row.route_id), @@ -1321,10 +1308,6 @@ function getVehiclePositions(db, filters = {}, stalenessThreshold = 120) { stmt.free(); return positions; } -function getVehiclePositionByTripId(db, tripId, stalenessThreshold = 120) { - const positions = getVehiclePositions(db, { tripId, limit: 1 }, stalenessThreshold); - return positions.length > 0 ? positions[0] : null; -} function getAllVehiclePositions(db) { const sql = "SELECT * FROM rt_vehicle_positions ORDER BY rt_last_updated DESC"; const stmt = db.prepare(sql); @@ -1394,25 +1377,44 @@ function getTrips(db, filters = {}, stalenessThreshold = 120) { const conditions = []; const params = []; if (tripId) { - conditions.push(needsRoutesJoin ? "t.trip_id = ?" : "trip_id = ?"); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => "?").join(", "); + conditions.push(needsRoutesJoin ? `t.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`); + params.push(...tripIds); + } } if (routeId) { - conditions.push(needsRoutesJoin ? "t.route_id = ?" : "route_id = ?"); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => "?").join(", "); + conditions.push(needsRoutesJoin ? `t.route_id IN (${placeholders})` : `route_id IN (${placeholders})`); + params.push(...routeIds); + } } - if (serviceIds && serviceIds.length > 0) { - const placeholders = serviceIds.map(() => "?").join(", "); - conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`); - params.push(...serviceIds); + if (serviceIds) { + const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds]; + if (serviceIdArray.length > 0) { + const placeholders = serviceIdArray.map(() => "?").join(", "); + conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`); + params.push(...serviceIdArray); + } } if (directionId !== void 0) { - conditions.push(needsRoutesJoin ? "t.direction_id = ?" : "direction_id = ?"); - params.push(directionId); + const directionIds = Array.isArray(directionId) ? directionId : [directionId]; + if (directionIds.length > 0) { + const placeholders = directionIds.map(() => "?").join(", "); + conditions.push(needsRoutesJoin ? `t.direction_id IN (${placeholders})` : `direction_id IN (${placeholders})`); + params.push(...directionIds); + } } if (agencyId) { - conditions.push("r.agency_id = ?"); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => "?").join(", "); + conditions.push(`r.agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } let sql = needsRoutesJoin ? "SELECT t.* FROM trips t INNER JOIN routes r ON t.route_id = r.route_id" : "SELECT * FROM trips"; if (conditions.length > 0) { @@ -1437,17 +1439,6 @@ function getTrips(db, filters = {}, stalenessThreshold = 120) { } return trips; } -function getTripById(db, tripId) { - const stmt = db.prepare("SELECT * FROM trips WHERE trip_id = ?"); - stmt.bind([tripId]); - if (stmt.step()) { - const row = stmt.getAsObject(); - stmt.free(); - return rowToTrip(row); - } - stmt.free(); - return null; -} function rowToTrip(row) { return { trip_id: String(row.trip_id), @@ -1472,7 +1463,7 @@ function mergeRealtimeData2(stopTimes, db, stalenessThreshold) { const placeholders = tripIds.map(() => "?").join(", "); const stmt = db.prepare(` SELECT trip_id, stop_sequence, stop_id, - arrival_delay, departure_delay, schedule_relationship + arrival_delay, arrival_time, departure_delay, departure_time, schedule_relationship FROM rt_stop_time_updates WHERE trip_id IN (${placeholders}) AND rt_last_updated >= ? @@ -1484,7 +1475,9 @@ function mergeRealtimeData2(stopTimes, db, stalenessThreshold) { const key = `${row.trip_id}_${row.stop_sequence}`; rtMap.set(key, { arrival_delay: row.arrival_delay !== null ? Number(row.arrival_delay) : void 0, + arrival_time: row.arrival_time !== null ? Number(row.arrival_time) : void 0, departure_delay: row.departure_delay !== null ? Number(row.departure_delay) : void 0, + departure_time: row.departure_time !== null ? Number(row.departure_time) : void 0, schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : void 0 }); } @@ -1505,29 +1498,52 @@ function getStopTimes(db, filters = {}, stalenessThreshold = 120) { const conditions = []; const params = []; if (tripId) { - conditions.push(needsTripsJoin ? "st.trip_id = ?" : "trip_id = ?"); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => "?").join(", "); + conditions.push(needsTripsJoin ? `st.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`); + params.push(...tripIds); + } } if (stopId) { - conditions.push(needsTripsJoin ? "st.stop_id = ?" : "stop_id = ?"); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => "?").join(", "); + conditions.push(needsTripsJoin ? `st.stop_id IN (${placeholders})` : `stop_id IN (${placeholders})`); + params.push(...stopIds); + } } if (routeId) { - conditions.push("t.route_id = ?"); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => "?").join(", "); + conditions.push(`t.route_id IN (${placeholders})`); + params.push(...routeIds); + } } - if (serviceIds && serviceIds.length > 0) { - const placeholders = serviceIds.map(() => "?").join(", "); - conditions.push(`t.service_id IN (${placeholders})`); - params.push(...serviceIds); + if (serviceIds) { + const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds]; + if (serviceIdArray.length > 0) { + const placeholders = serviceIdArray.map(() => "?").join(", "); + conditions.push(`t.service_id IN (${placeholders})`); + params.push(...serviceIdArray); + } } if (directionId !== void 0) { - conditions.push("t.direction_id = ?"); - params.push(directionId); + const directionIds = Array.isArray(directionId) ? directionId : [directionId]; + if (directionIds.length > 0) { + const placeholders = directionIds.map(() => "?").join(", "); + conditions.push(`t.direction_id IN (${placeholders})`); + params.push(...directionIds); + } } if (agencyId) { - conditions.push("r.agency_id = ?"); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => "?").join(", "); + conditions.push(`r.agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } let sql; if (needsRoutesJoin) { @@ -1560,17 +1576,6 @@ function getStopTimes(db, filters = {}, stalenessThreshold = 120) { } return stopTimes; } -function getStopTimesByTrip(db, tripId) { - const stmt = db.prepare("SELECT * FROM stop_times WHERE trip_id = ? ORDER BY stop_sequence"); - stmt.bind([tripId]); - const stopTimes = []; - while (stmt.step()) { - const row = stmt.getAsObject(); - stopTimes.push(rowToStopTime(row)); - } - stmt.free(); - return stopTimes; -} function rowToStopTime(row) { return { trip_id: String(row.trip_id), @@ -1691,10 +1696,6 @@ function getAlerts(db, filters = {}, stalenessThreshold = 120) { stmt.free(); return alerts; } -function getAlertById(db, alertId, stalenessThreshold = 120) { - const alerts = getAlerts(db, { alertId, limit: 1 }, stalenessThreshold); - return alerts.length > 0 ? alerts[0] : null; -} function getAllAlerts(db) { const sql = "SELECT * FROM rt_alerts ORDER BY rt_last_updated DESC"; const stmt = db.prepare(sql); @@ -1769,10 +1770,6 @@ function getTripUpdates(db, filters = {}, stalenessThreshold = 120) { stmt.free(); return tripUpdates; } -function getTripUpdateByTripId(db, tripId, stalenessThreshold = 120) { - const updates = getTripUpdates(db, { tripId, limit: 1 }, stalenessThreshold); - return updates.length > 0 ? updates[0] : null; -} function getAllTripUpdates(db) { const sql = "SELECT * FROM rt_trip_updates ORDER BY rt_last_updated DESC"; const stmt = db.prepare(sql); @@ -1819,16 +1816,28 @@ function getStopTimeUpdates(db, filters = {}, stalenessThreshold = 120) { const conditions = []; const params = []; if (tripId) { - conditions.push("trip_id = ?"); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => "?").join(", "); + conditions.push(`trip_id IN (${placeholders})`); + params.push(...tripIds); + } } if (stopId) { - conditions.push("stop_id = ?"); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => "?").join(", "); + conditions.push(`stop_id IN (${placeholders})`); + params.push(...stopIds); + } } if (stopSequence !== void 0) { - conditions.push("stop_sequence = ?"); - params.push(stopSequence); + const stopSequences = Array.isArray(stopSequence) ? stopSequence : [stopSequence]; + if (stopSequences.length > 0) { + const placeholders = stopSequences.map(() => "?").join(", "); + conditions.push(`stop_sequence IN (${placeholders})`); + params.push(...stopSequences); + } } const now = Math.floor(Date.now() / 1e3); const staleThreshold = now - stalenessThreshold; @@ -1855,9 +1864,6 @@ function getStopTimeUpdates(db, filters = {}, stalenessThreshold = 120) { stmt.free(); return stopTimeUpdates; } -function getStopTimeUpdatesByTripId(db, tripId, stalenessThreshold = 120) { - return getStopTimeUpdates(db, { tripId }, stalenessThreshold); -} function getAllStopTimeUpdates(db) { const sql = "SELECT * FROM rt_stop_time_updates ORDER BY trip_id, stop_sequence"; const stmt = db.prepare(sql); @@ -1962,45 +1968,27 @@ var GtfsSqlJs = class _GtfsSqlJs { return this.db; } // ==================== Agency Methods ==================== - /** - * Get an agency by its agency_id - */ - getAgencyById(agencyId) { - if (!this.db) throw new Error("Database not initialized"); - return getAgencyById(this.db, agencyId); - } /** * Get agencies with optional filters + * Pass agencyId filter to get a specific agency */ getAgencies(filters) { if (!this.db) throw new Error("Database not initialized"); return getAgencies(this.db, filters); } // ==================== Stop Methods ==================== - /** - * Get a stop by its stop_id - */ - getStopById(stopId) { - if (!this.db) throw new Error("Database not initialized"); - return getStopById(this.db, stopId); - } /** * Get stops with optional filters + * Pass stopId filter to get a specific stop */ getStops(filters) { if (!this.db) throw new Error("Database not initialized"); return getStops(this.db, filters); } // ==================== Route Methods ==================== - /** - * Get a route by its route_id - */ - getRouteById(routeId) { - if (!this.db) throw new Error("Database not initialized"); - return getRouteById(this.db, routeId); - } /** * Get routes with optional filters + * Pass routeId filter to get a specific route */ getRoutes(filters) { if (!this.db) throw new Error("Database not initialized"); @@ -2036,22 +2024,16 @@ var GtfsSqlJs = class _GtfsSqlJs { return getCalendarDatesForDate(this.db, date); } // ==================== Trip Methods ==================== - /** - * Get a trip by its trip_id - */ - getTripById(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getTripById(this.db, tripId); - } /** * Get trips with optional filters + * Pass tripId filter to get a specific trip * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) * @param filters.limit - Limit number of results * * @example @@ -2061,6 +2043,10 @@ var GtfsSqlJs = class _GtfsSqlJs { * @example * // Get all trips for a route going in one direction * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 }); + * + * @example + * // Get a specific trip + * const trips = gtfs.getTrips({ tripId: 'TRIP_123' }); */ getTrips(filters) { if (!this.db) throw new Error("Database not initialized"); @@ -2073,23 +2059,17 @@ var GtfsSqlJs = class _GtfsSqlJs { return getTrips(this.db, finalFilters, this.stalenessThreshold); } // ==================== Stop Time Methods ==================== - /** - * Get stop times for a trip (ordered by stop_sequence) - */ - getStopTimesByTrip(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getStopTimesByTrip(this.db, tripId); - } /** * Get stop times with optional filters * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.stopId - Filter by stop ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.stopId - Filter by stop ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) + * @param filters.includeRealtime - Include realtime data (delay and time fields) * @param filters.limit - Limit number of results * * @example @@ -2103,6 +2083,13 @@ var GtfsSqlJs = class _GtfsSqlJs { * routeId: 'ROUTE_1', * date: '20240115' * }); + * + * @example + * // Get stop times with realtime data + * const stopTimes = gtfs.getStopTimes({ + * tripId: 'TRIP_123', + * includeRealtime: true + * }); */ getStopTimes(filters) { if (!this.db) throw new Error("Database not initialized"); @@ -2160,60 +2147,36 @@ var GtfsSqlJs = class _GtfsSqlJs { } /** * Get alerts with optional filters + * Pass alertId filter to get a specific alert */ getAlerts(filters) { if (!this.db) throw new Error("Database not initialized"); return getAlerts(this.db, filters, this.stalenessThreshold); } - /** - * Get alert by ID - */ - getAlertById(alertId) { - if (!this.db) throw new Error("Database not initialized"); - return getAlertById(this.db, alertId, this.stalenessThreshold); - } /** * Get vehicle positions with optional filters + * Pass tripId filter to get vehicle position for a specific trip */ getVehiclePositions(filters) { if (!this.db) throw new Error("Database not initialized"); return getVehiclePositions(this.db, filters, this.stalenessThreshold); } - /** - * Get vehicle position by trip ID - */ - getVehiclePositionByTripId(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getVehiclePositionByTripId(this.db, tripId, this.stalenessThreshold); - } /** * Get trip updates with optional filters + * Pass tripId filter to get trip update for a specific trip */ getTripUpdates(filters) { if (!this.db) throw new Error("Database not initialized"); return getTripUpdates(this.db, filters, this.stalenessThreshold); } - /** - * Get trip update by trip ID - */ - getTripUpdateByTripId(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getTripUpdateByTripId(this.db, tripId, this.stalenessThreshold); - } /** * Get stop time updates with optional filters + * Pass tripId filter to get stop time updates for a specific trip */ getStopTimeUpdates(filters) { if (!this.db) throw new Error("Database not initialized"); return getStopTimeUpdates(this.db, filters, this.stalenessThreshold); } - /** - * Get stop time updates for a specific trip - */ - getStopTimeUpdatesByTripId(tripId) { - if (!this.db) throw new Error("Database not initialized"); - return getStopTimeUpdatesByTripId(this.db, tripId, this.stalenessThreshold); - } // ==================== Debug Export Methods ==================== // These methods export all realtime data without staleness filtering // for debugging purposes diff --git a/dist/index.mjs.map b/dist/index.mjs.map index 495a9f5..2f5c0dd 100644 --- a/dist/index.mjs.map +++ b/dist/index.mjs.map @@ -1 +1 @@ -{"version":3,"sources":["../src/gtfs-sqljs.ts","../src/schema/schema.ts","../src/loaders/zip-loader.ts","../src/loaders/csv-parser.ts","../src/loaders/data-loader.ts","../src/schema/gtfs-rt-schema.ts","../src/loaders/gtfs-rt-loader.ts","../src/queries/agencies.ts","../src/queries/stops.ts","../src/queries/routes.ts","../src/queries/calendar.ts","../src/queries/rt-vehicle-positions.ts","../src/queries/trips.ts","../src/queries/stop-times.ts","../src/queries/rt-alerts.ts","../src/queries/rt-trip-updates.ts","../src/queries/rt-stop-time-updates.ts","../src/types/gtfs-rt.ts"],"sourcesContent":["/**\n * Main GtfsSqlJs Class\n */\n\nimport initSqlJs, { type Database, type SqlJsStatic } from 'sql.js';\nimport { getAllCreateStatements } from './schema/schema';\nimport { loadGTFSZip } from './loaders/zip-loader';\nimport { loadGTFSData } from './loaders/data-loader';\nimport { createRealtimeTables, clearRealtimeData as clearRTData } from './schema/gtfs-rt-schema';\nimport { loadRealtimeData } from './loaders/gtfs-rt-loader';\n\n// Query methods\nimport { getAgencyById, getAgencies, type AgencyFilters } from './queries/agencies';\nimport { getStopById, getStops, type StopFilters } from './queries/stops';\nimport { getRouteById, getRoutes, type RouteFilters } from './queries/routes';\nimport {\n getActiveServiceIds,\n getCalendarByServiceId,\n getCalendarDates,\n getCalendarDatesForDate,\n} from './queries/calendar';\nimport { getTripById, getTrips, type TripFilters, type TripWithRealtime } from './queries/trips';\nimport { getStopTimesByTrip, getStopTimes, type StopTimeFilters, type StopTimeWithRealtime } from './queries/stop-times';\nimport { getAlerts as getAlertsQuery, getAlertById, getAllAlerts, type AlertFilters } from './queries/rt-alerts';\nimport { getVehiclePositions as getVehiclePositionsQuery, getVehiclePositionByTripId, getAllVehiclePositions, type VehiclePositionFilters } from './queries/rt-vehicle-positions';\nimport { getTripUpdates, getTripUpdateByTripId, getAllTripUpdates, type TripUpdateFilters } from './queries/rt-trip-updates';\nimport { getStopTimeUpdates, getStopTimeUpdatesByTripId, getAllStopTimeUpdates, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata } from './queries/rt-stop-time-updates';\n\n// Types\nimport type { Agency, Stop, Route, Trip, StopTime, Calendar, CalendarDate } from './types/gtfs';\nimport type { Alert, VehiclePosition, TripUpdate } from './types/gtfs-rt';\n\n// Export filter types for users\nexport type { AgencyFilters, StopFilters, RouteFilters, TripFilters, StopTimeFilters, AlertFilters, VehiclePositionFilters, TripUpdateFilters, StopTimeUpdateFilters };\n// Export RT types\nexport type { Alert, VehiclePosition, TripUpdate, StopTimeUpdateWithMetadata, TripWithRealtime, StopTimeWithRealtime };\n\nexport interface GtfsSqlJsOptions {\n /**\n * Path or URL to GTFS ZIP file\n */\n zipPath?: string;\n\n /**\n * Pre-loaded SQLite database as ArrayBuffer\n */\n database?: ArrayBuffer;\n\n /**\n * Optional: Custom SQL.js instance\n */\n SQL?: SqlJsStatic;\n\n /**\n * Optional: Path to SQL.js WASM file (for custom loading)\n */\n locateFile?: (filename: string) => string;\n\n /**\n * Optional: Array of GTFS filenames to skip importing (e.g., ['shapes.txt'])\n * Tables will be created but no data will be imported for these files\n */\n skipFiles?: string[];\n\n /**\n * Optional: Array of GTFS-RT feed URLs for realtime data\n */\n realtimeFeedUrls?: string[];\n\n /**\n * Optional: Staleness threshold in seconds (default: 120)\n * Realtime data older than this will be excluded from queries\n */\n stalenessThreshold?: number;\n}\n\nexport class GtfsSqlJs {\n private db: Database | null = null;\n private SQL: SqlJsStatic | null = null;\n private realtimeFeedUrls: string[] = [];\n private stalenessThreshold: number = 120;\n\n /**\n * Private constructor - use static factory methods instead\n */\n private constructor() {}\n\n /**\n * Create GtfsSqlJs instance from GTFS ZIP file\n */\n static async fromZip(\n zipPath: string,\n options: Omit = {}\n ): Promise {\n const instance = new GtfsSqlJs();\n await instance.initFromZip(zipPath, options);\n return instance;\n }\n\n /**\n * Create GtfsSqlJs instance from existing SQLite database\n */\n static async fromDatabase(\n database: ArrayBuffer,\n options: Omit = {}\n ): Promise {\n const instance = new GtfsSqlJs();\n await instance.initFromDatabase(database, options);\n return instance;\n }\n\n /**\n * Initialize from ZIP file\n */\n private async initFromZip(zipPath: string, options: Omit): Promise {\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Create new database\n this.db = new this.SQL.Database();\n\n // Create GTFS schema\n const createStatements = getAllCreateStatements();\n for (const statement of createStatements) {\n this.db.run(statement);\n }\n\n // Create GTFS-RT tables\n createRealtimeTables(this.db);\n\n // Load GTFS data\n const files = await loadGTFSZip(zipPath);\n await loadGTFSData(this.db, files, options.skipFiles);\n\n // Set RT configuration\n if (options.realtimeFeedUrls) {\n this.realtimeFeedUrls = options.realtimeFeedUrls;\n }\n if (options.stalenessThreshold !== undefined) {\n this.stalenessThreshold = options.stalenessThreshold;\n }\n }\n\n /**\n * Initialize from existing database\n */\n private async initFromDatabase(\n database: ArrayBuffer,\n options: Omit\n ): Promise {\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Load existing database\n this.db = new this.SQL.Database(new Uint8Array(database));\n\n // Ensure RT tables exist (in case loading old database)\n createRealtimeTables(this.db);\n\n // Set RT configuration\n if (options.realtimeFeedUrls) {\n this.realtimeFeedUrls = options.realtimeFeedUrls;\n }\n if (options.stalenessThreshold !== undefined) {\n this.stalenessThreshold = options.stalenessThreshold;\n }\n }\n\n /**\n * Export database to ArrayBuffer\n */\n export(): ArrayBuffer {\n if (!this.db) {\n throw new Error('Database not initialized');\n }\n\n const data = this.db.export();\n // Create a new ArrayBuffer and copy the data to ensure proper type\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n return buffer;\n }\n\n /**\n * Close the database connection\n */\n close(): void {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n\n /**\n * Get direct access to the database (for advanced queries)\n */\n getDatabase(): Database {\n if (!this.db) {\n throw new Error('Database not initialized');\n }\n return this.db;\n }\n\n // ==================== Agency Methods ====================\n\n /**\n * Get an agency by its agency_id\n */\n getAgencyById(agencyId: string): Agency | null {\n if (!this.db) throw new Error('Database not initialized');\n return getAgencyById(this.db, agencyId);\n }\n\n /**\n * Get agencies with optional filters\n */\n getAgencies(filters?: AgencyFilters): Agency[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAgencies(this.db, filters);\n }\n\n // ==================== Stop Methods ====================\n\n /**\n * Get a stop by its stop_id\n */\n getStopById(stopId: string): Stop | null {\n if (!this.db) throw new Error('Database not initialized');\n return getStopById(this.db, stopId);\n }\n\n /**\n * Get stops with optional filters\n */\n getStops(filters?: StopFilters): Stop[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStops(this.db, filters);\n }\n\n // ==================== Route Methods ====================\n\n /**\n * Get a route by its route_id\n */\n getRouteById(routeId: string): Route | null {\n if (!this.db) throw new Error('Database not initialized');\n return getRouteById(this.db, routeId);\n }\n\n /**\n * Get routes with optional filters\n */\n getRoutes(filters?: RouteFilters): Route[] {\n if (!this.db) throw new Error('Database not initialized');\n return getRoutes(this.db, filters);\n }\n\n // ==================== Calendar Methods ====================\n\n /**\n * Get active service IDs for a given date (YYYYMMDD format)\n */\n getActiveServiceIds(date: string): string[] {\n if (!this.db) throw new Error('Database not initialized');\n return getActiveServiceIds(this.db, date);\n }\n\n /**\n * Get calendar entry by service_id\n */\n getCalendarByServiceId(serviceId: string): Calendar | null {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarByServiceId(this.db, serviceId);\n }\n\n /**\n * Get calendar date exceptions for a service\n */\n getCalendarDates(serviceId: string): CalendarDate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarDates(this.db, serviceId);\n }\n\n /**\n * Get calendar date exceptions for a specific date\n */\n getCalendarDatesForDate(date: string): CalendarDate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarDatesForDate(this.db, date);\n }\n\n // ==================== Trip Methods ====================\n\n /**\n * Get a trip by its trip_id\n */\n getTripById(tripId: string): Trip | null {\n if (!this.db) throw new Error('Database not initialized');\n return getTripById(this.db, tripId);\n }\n\n /**\n * Get trips with optional filters\n *\n * @param filters - Optional filters\n * @param filters.tripId - Filter by trip ID\n * @param filters.routeId - Filter by route ID\n * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date\n * @param filters.directionId - Filter by direction ID\n * @param filters.agencyId - Filter by agency ID\n * @param filters.limit - Limit number of results\n *\n * @example\n * // Get all trips for a route on a specific date\n * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', date: '20240115' });\n *\n * @example\n * // Get all trips for a route going in one direction\n * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 });\n */\n getTrips(filters?: TripFilters & { date?: string }): Trip[] {\n if (!this.db) throw new Error('Database not initialized');\n\n // Handle date parameter by converting it to serviceIds\n const { date, ...restFilters } = filters || {};\n const finalFilters = { ...restFilters };\n\n if (date) {\n const serviceIds = getActiveServiceIds(this.db, date);\n finalFilters.serviceIds = serviceIds;\n }\n\n return getTrips(this.db, finalFilters, this.stalenessThreshold);\n }\n\n // ==================== Stop Time Methods ====================\n\n /**\n * Get stop times for a trip (ordered by stop_sequence)\n */\n getStopTimesByTrip(tripId: string): StopTime[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStopTimesByTrip(this.db, tripId);\n }\n\n /**\n * Get stop times with optional filters\n *\n * @param filters - Optional filters\n * @param filters.tripId - Filter by trip ID\n * @param filters.stopId - Filter by stop ID\n * @param filters.routeId - Filter by route ID\n * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date\n * @param filters.directionId - Filter by direction ID\n * @param filters.agencyId - Filter by agency ID\n * @param filters.limit - Limit number of results\n *\n * @example\n * // Get stop times for a specific trip\n * const stopTimes = gtfs.getStopTimes({ tripId: 'TRIP_123' });\n *\n * @example\n * // Get stop times at a stop for a specific route on a date\n * const stopTimes = gtfs.getStopTimes({\n * stopId: 'STOP_123',\n * routeId: 'ROUTE_1',\n * date: '20240115'\n * });\n */\n getStopTimes(filters?: StopTimeFilters & { date?: string }): StopTime[] {\n if (!this.db) throw new Error('Database not initialized');\n\n // Handle date parameter by converting it to serviceIds\n const { date, ...restFilters } = filters || {};\n const finalFilters = { ...restFilters };\n\n if (date) {\n const serviceIds = getActiveServiceIds(this.db, date);\n finalFilters.serviceIds = serviceIds;\n }\n\n return getStopTimes(this.db, finalFilters, this.stalenessThreshold);\n }\n\n // ==================== Realtime Methods ====================\n\n /**\n * Set GTFS-RT feed URLs\n */\n setRealtimeFeedUrls(urls: string[]): void {\n this.realtimeFeedUrls = urls;\n }\n\n /**\n * Get currently configured GTFS-RT feed URLs\n */\n getRealtimeFeedUrls(): string[] {\n return [...this.realtimeFeedUrls];\n }\n\n /**\n * Set staleness threshold in seconds\n */\n setStalenessThreshold(seconds: number): void {\n this.stalenessThreshold = seconds;\n }\n\n /**\n * Get current staleness threshold\n */\n getStalenessThreshold(): number {\n return this.stalenessThreshold;\n }\n\n /**\n * Fetch and load GTFS Realtime data from configured feed URLs or provided URLs\n * @param urls - Optional array of feed URLs. If not provided, uses configured feed URLs\n */\n async fetchRealtimeData(urls?: string[]): Promise {\n if (!this.db) throw new Error('Database not initialized');\n\n const feedUrls = urls || this.realtimeFeedUrls;\n if (feedUrls.length === 0) {\n throw new Error('No realtime feed URLs configured. Use setRealtimeFeedUrls() or pass urls parameter.');\n }\n\n await loadRealtimeData(this.db, feedUrls);\n }\n\n /**\n * Clear all realtime data from the database\n */\n clearRealtimeData(): void {\n if (!this.db) throw new Error('Database not initialized');\n clearRTData(this.db);\n }\n\n /**\n * Get alerts with optional filters\n */\n getAlerts(filters?: AlertFilters): Alert[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAlertsQuery(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get alert by ID\n */\n getAlertById(alertId: string): Alert | null {\n if (!this.db) throw new Error('Database not initialized');\n return getAlertById(this.db, alertId, this.stalenessThreshold);\n }\n\n /**\n * Get vehicle positions with optional filters\n */\n getVehiclePositions(filters?: VehiclePositionFilters): VehiclePosition[] {\n if (!this.db) throw new Error('Database not initialized');\n return getVehiclePositionsQuery(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get vehicle position by trip ID\n */\n getVehiclePositionByTripId(tripId: string): VehiclePosition | null {\n if (!this.db) throw new Error('Database not initialized');\n return getVehiclePositionByTripId(this.db, tripId, this.stalenessThreshold);\n }\n\n /**\n * Get trip updates with optional filters\n */\n getTripUpdates(filters?: TripUpdateFilters): TripUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getTripUpdates(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get trip update by trip ID\n */\n getTripUpdateByTripId(tripId: string): TripUpdate | null {\n if (!this.db) throw new Error('Database not initialized');\n return getTripUpdateByTripId(this.db, tripId, this.stalenessThreshold);\n }\n\n /**\n * Get stop time updates with optional filters\n */\n getStopTimeUpdates(filters?: StopTimeUpdateFilters): import('./types/gtfs-rt').StopTimeUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStopTimeUpdates(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get stop time updates for a specific trip\n */\n getStopTimeUpdatesByTripId(tripId: string): import('./types/gtfs-rt').StopTimeUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStopTimeUpdatesByTripId(this.db, tripId, this.stalenessThreshold);\n }\n\n // ==================== Debug Export Methods ====================\n // These methods export all realtime data without staleness filtering\n // for debugging purposes\n\n /**\n * Export all alerts without staleness filtering (for debugging)\n */\n debugExportAllAlerts(): Alert[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllAlerts(this.db);\n }\n\n /**\n * Export all vehicle positions without staleness filtering (for debugging)\n */\n debugExportAllVehiclePositions(): VehiclePosition[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllVehiclePositions(this.db);\n }\n\n /**\n * Export all trip updates without staleness filtering (for debugging)\n */\n debugExportAllTripUpdates(): TripUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllTripUpdates(this.db);\n }\n\n /**\n * Export all stop time updates without staleness filtering (for debugging)\n * Returns extended type with trip_id and rt_last_updated for debugging purposes\n */\n debugExportAllStopTimeUpdates(): StopTimeUpdateWithMetadata[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllStopTimeUpdates(this.db);\n }\n}\n","/**\n * SQLite Schema Definitions for GTFS Data\n * Matches required/optional fields from GTFS specification\n */\n\nexport interface TableSchema {\n name: string;\n columns: ColumnDefinition[];\n indexes?: IndexDefinition[];\n}\n\nexport interface ColumnDefinition {\n name: string;\n type: 'TEXT' | 'INTEGER' | 'REAL';\n required: boolean;\n primaryKey?: boolean;\n}\n\nexport interface IndexDefinition {\n name: string;\n columns: string[];\n unique?: boolean;\n}\n\nexport const GTFS_SCHEMA: TableSchema[] = [\n {\n name: 'agency',\n columns: [\n { name: 'agency_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'agency_name', type: 'TEXT', required: true },\n { name: 'agency_url', type: 'TEXT', required: true },\n { name: 'agency_timezone', type: 'TEXT', required: true },\n { name: 'agency_lang', type: 'TEXT', required: false },\n { name: 'agency_phone', type: 'TEXT', required: false },\n { name: 'agency_fare_url', type: 'TEXT', required: false },\n { name: 'agency_email', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'stops',\n columns: [\n { name: 'stop_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'stop_name', type: 'TEXT', required: true },\n { name: 'stop_lat', type: 'REAL', required: true },\n { name: 'stop_lon', type: 'REAL', required: true },\n { name: 'stop_code', type: 'TEXT', required: false },\n { name: 'stop_desc', type: 'TEXT', required: false },\n { name: 'zone_id', type: 'TEXT', required: false },\n { name: 'stop_url', type: 'TEXT', required: false },\n { name: 'location_type', type: 'INTEGER', required: false },\n { name: 'parent_station', type: 'TEXT', required: false },\n { name: 'stop_timezone', type: 'TEXT', required: false },\n { name: 'wheelchair_boarding', type: 'INTEGER', required: false },\n { name: 'level_id', type: 'TEXT', required: false },\n { name: 'platform_code', type: 'TEXT', required: false },\n ],\n indexes: [\n { name: 'idx_stops_stop_code', columns: ['stop_code'] },\n { name: 'idx_stops_stop_name', columns: ['stop_name'] },\n { name: 'idx_stops_parent_station', columns: ['parent_station'] },\n ],\n },\n {\n name: 'routes',\n columns: [\n { name: 'route_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'route_short_name', type: 'TEXT', required: true },\n { name: 'route_long_name', type: 'TEXT', required: true },\n { name: 'route_type', type: 'INTEGER', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'route_desc', type: 'TEXT', required: false },\n { name: 'route_url', type: 'TEXT', required: false },\n { name: 'route_color', type: 'TEXT', required: false },\n { name: 'route_text_color', type: 'TEXT', required: false },\n { name: 'route_sort_order', type: 'INTEGER', required: false },\n { name: 'continuous_pickup', type: 'INTEGER', required: false },\n { name: 'continuous_drop_off', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_routes_agency_id', columns: ['agency_id'] },\n ],\n },\n {\n name: 'trips',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'route_id', type: 'TEXT', required: true },\n { name: 'service_id', type: 'TEXT', required: true },\n { name: 'trip_headsign', type: 'TEXT', required: false },\n { name: 'trip_short_name', type: 'TEXT', required: false },\n { name: 'direction_id', type: 'INTEGER', required: false },\n { name: 'block_id', type: 'TEXT', required: false },\n { name: 'shape_id', type: 'TEXT', required: false },\n { name: 'wheelchair_accessible', type: 'INTEGER', required: false },\n { name: 'bikes_allowed', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_trips_route_id', columns: ['route_id'] },\n { name: 'idx_trips_service_id', columns: ['service_id'] },\n { name: 'idx_trips_route_service', columns: ['route_id', 'service_id'] },\n ],\n },\n {\n name: 'stop_times',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true },\n { name: 'arrival_time', type: 'TEXT', required: true },\n { name: 'departure_time', type: 'TEXT', required: true },\n { name: 'stop_id', type: 'TEXT', required: true },\n { name: 'stop_sequence', type: 'INTEGER', required: true },\n { name: 'stop_headsign', type: 'TEXT', required: false },\n { name: 'pickup_type', type: 'INTEGER', required: false },\n { name: 'drop_off_type', type: 'INTEGER', required: false },\n { name: 'continuous_pickup', type: 'INTEGER', required: false },\n { name: 'continuous_drop_off', type: 'INTEGER', required: false },\n { name: 'shape_dist_traveled', type: 'REAL', required: false },\n { name: 'timepoint', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_stop_times_trip_id', columns: ['trip_id'] },\n { name: 'idx_stop_times_stop_id', columns: ['stop_id'] },\n { name: 'idx_stop_times_trip_sequence', columns: ['trip_id', 'stop_sequence'] },\n ],\n },\n {\n name: 'calendar',\n columns: [\n { name: 'service_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'monday', type: 'INTEGER', required: true },\n { name: 'tuesday', type: 'INTEGER', required: true },\n { name: 'wednesday', type: 'INTEGER', required: true },\n { name: 'thursday', type: 'INTEGER', required: true },\n { name: 'friday', type: 'INTEGER', required: true },\n { name: 'saturday', type: 'INTEGER', required: true },\n { name: 'sunday', type: 'INTEGER', required: true },\n { name: 'start_date', type: 'TEXT', required: true },\n { name: 'end_date', type: 'TEXT', required: true },\n ],\n },\n {\n name: 'calendar_dates',\n columns: [\n { name: 'service_id', type: 'TEXT', required: true },\n { name: 'date', type: 'TEXT', required: true },\n { name: 'exception_type', type: 'INTEGER', required: true },\n ],\n indexes: [\n { name: 'idx_calendar_dates_service_id', columns: ['service_id'] },\n { name: 'idx_calendar_dates_date', columns: ['date'] },\n { name: 'idx_calendar_dates_service_date', columns: ['service_id', 'date'] },\n ],\n },\n {\n name: 'fare_attributes',\n columns: [\n { name: 'fare_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'price', type: 'REAL', required: true },\n { name: 'currency_type', type: 'TEXT', required: true },\n { name: 'payment_method', type: 'INTEGER', required: true },\n { name: 'transfers', type: 'INTEGER', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'transfer_duration', type: 'INTEGER', required: false },\n ],\n },\n {\n name: 'fare_rules',\n columns: [\n { name: 'fare_id', type: 'TEXT', required: true },\n { name: 'route_id', type: 'TEXT', required: false },\n { name: 'origin_id', type: 'TEXT', required: false },\n { name: 'destination_id', type: 'TEXT', required: false },\n { name: 'contains_id', type: 'TEXT', required: false },\n ],\n indexes: [\n { name: 'idx_fare_rules_fare_id', columns: ['fare_id'] },\n { name: 'idx_fare_rules_route_id', columns: ['route_id'] },\n ],\n },\n {\n name: 'shapes',\n columns: [\n { name: 'shape_id', type: 'TEXT', required: true },\n { name: 'shape_pt_lat', type: 'REAL', required: true },\n { name: 'shape_pt_lon', type: 'REAL', required: true },\n { name: 'shape_pt_sequence', type: 'INTEGER', required: true },\n { name: 'shape_dist_traveled', type: 'REAL', required: false },\n ],\n indexes: [\n { name: 'idx_shapes_shape_id', columns: ['shape_id'] },\n { name: 'idx_shapes_shape_sequence', columns: ['shape_id', 'shape_pt_sequence'] },\n ],\n },\n {\n name: 'frequencies',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true },\n { name: 'start_time', type: 'TEXT', required: true },\n { name: 'end_time', type: 'TEXT', required: true },\n { name: 'headway_secs', type: 'INTEGER', required: true },\n { name: 'exact_times', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_frequencies_trip_id', columns: ['trip_id'] },\n ],\n },\n {\n name: 'transfers',\n columns: [\n { name: 'from_stop_id', type: 'TEXT', required: true },\n { name: 'to_stop_id', type: 'TEXT', required: true },\n { name: 'transfer_type', type: 'INTEGER', required: true },\n { name: 'min_transfer_time', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_transfers_from_stop_id', columns: ['from_stop_id'] },\n { name: 'idx_transfers_to_stop_id', columns: ['to_stop_id'] },\n ],\n },\n {\n name: 'pathways',\n columns: [\n { name: 'pathway_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'from_stop_id', type: 'TEXT', required: true },\n { name: 'to_stop_id', type: 'TEXT', required: true },\n { name: 'pathway_mode', type: 'INTEGER', required: true },\n { name: 'is_bidirectional', type: 'INTEGER', required: true },\n { name: 'length', type: 'REAL', required: false },\n { name: 'traversal_time', type: 'INTEGER', required: false },\n { name: 'stair_count', type: 'INTEGER', required: false },\n { name: 'max_slope', type: 'REAL', required: false },\n { name: 'min_width', type: 'REAL', required: false },\n { name: 'signposted_as', type: 'TEXT', required: false },\n { name: 'reversed_signposted_as', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'levels',\n columns: [\n { name: 'level_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'level_index', type: 'REAL', required: true },\n { name: 'level_name', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'feed_info',\n columns: [\n { name: 'feed_publisher_name', type: 'TEXT', required: true },\n { name: 'feed_publisher_url', type: 'TEXT', required: true },\n { name: 'feed_lang', type: 'TEXT', required: true },\n { name: 'default_lang', type: 'TEXT', required: false },\n { name: 'feed_start_date', type: 'TEXT', required: false },\n { name: 'feed_end_date', type: 'TEXT', required: false },\n { name: 'feed_version', type: 'TEXT', required: false },\n { name: 'feed_contact_email', type: 'TEXT', required: false },\n { name: 'feed_contact_url', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'attributions',\n columns: [\n { name: 'attribution_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'organization_name', type: 'TEXT', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'route_id', type: 'TEXT', required: false },\n { name: 'trip_id', type: 'TEXT', required: false },\n { name: 'is_producer', type: 'INTEGER', required: false },\n { name: 'is_operator', type: 'INTEGER', required: false },\n { name: 'is_authority', type: 'INTEGER', required: false },\n { name: 'attribution_url', type: 'TEXT', required: false },\n { name: 'attribution_email', type: 'TEXT', required: false },\n { name: 'attribution_phone', type: 'TEXT', required: false },\n ],\n },\n];\n\n/**\n * Generate CREATE TABLE SQL statement from schema definition\n */\nexport function generateCreateTableSQL(schema: TableSchema): string {\n const columns = schema.columns.map((col) => {\n const parts = [col.name, col.type];\n if (col.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n if (col.required && !col.primaryKey) {\n parts.push('NOT NULL');\n }\n return parts.join(' ');\n });\n\n return `CREATE TABLE IF NOT EXISTS ${schema.name} (${columns.join(', ')})`;\n}\n\n/**\n * Generate CREATE INDEX SQL statements from schema definition\n */\nexport function generateCreateIndexSQL(schema: TableSchema): string[] {\n if (!schema.indexes) {\n return [];\n }\n\n return schema.indexes.map((idx) => {\n const unique = idx.unique ? 'UNIQUE ' : '';\n const columns = idx.columns.join(', ');\n return `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name} (${columns})`;\n });\n}\n\n/**\n * Initialize all GTFS tables in the database\n */\nexport function getAllCreateStatements(): string[] {\n const statements: string[] = [];\n\n for (const schema of GTFS_SCHEMA) {\n statements.push(generateCreateTableSQL(schema));\n statements.push(...generateCreateIndexSQL(schema));\n }\n\n return statements;\n}\n","/**\n * ZIP Loader for GTFS files\n */\n\nimport JSZip from 'jszip';\n\nexport interface GTFSFiles {\n [filename: string]: string;\n}\n\n/**\n * Fetch and extract GTFS ZIP file\n * Works in both browser and Node.js environments\n */\nexport async function loadGTFSZip(source: string | ArrayBuffer | Uint8Array): Promise {\n let zipData: ArrayBuffer | Uint8Array;\n\n // If source is a string, treat it as URL or file path\n if (typeof source === 'string') {\n zipData = await fetchZip(source);\n } else {\n zipData = source;\n }\n\n // Load ZIP file\n const zip = await JSZip.loadAsync(zipData);\n\n // Extract all .txt files (GTFS files)\n const files: GTFSFiles = {};\n const filePromises: Promise[] = [];\n\n zip.forEach((relativePath, file) => {\n // Only process .txt files\n if (!file.dir && relativePath.endsWith('.txt')) {\n const fileName = relativePath.split('/').pop() || relativePath;\n filePromises.push(\n file.async('string').then((content) => {\n files[fileName] = content;\n })\n );\n }\n });\n\n await Promise.all(filePromises);\n\n return files;\n}\n\n/**\n * Fetch ZIP file from URL or file path\n */\nasync function fetchZip(source: string): Promise {\n // Check if source is a URL\n const isUrl = source.startsWith('http://') || source.startsWith('https://');\n\n // For URLs, always use fetch\n if (isUrl) {\n if (typeof fetch !== 'undefined') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS ZIP: ${response.status} ${response.statusText}`);\n }\n return await response.arrayBuffer();\n }\n throw new Error('fetch is not available to load URL');\n }\n\n // For non-URLs, try Node.js fs first, fall back to fetch for browser\n // Check if we're in Node.js environment\n const isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n if (isNode) {\n // In Node.js, treat as file path\n try {\n const fs = await import('fs');\n const buffer = await fs.promises.readFile(source);\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n } catch (error) {\n throw new Error(`Failed to read GTFS ZIP file: ${error}`);\n }\n }\n\n // In browser, treat as relative URL and use fetch\n if (typeof fetch !== 'undefined') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS ZIP: ${response.status} ${response.statusText}`);\n }\n return await response.arrayBuffer();\n }\n\n throw new Error('No method available to load ZIP file');\n}\n\n/**\n * Mapping of GTFS file names to database table names\n */\nexport const GTFS_FILE_MAPPING: Record = {\n 'agency.txt': 'agency',\n 'stops.txt': 'stops',\n 'routes.txt': 'routes',\n 'trips.txt': 'trips',\n 'stop_times.txt': 'stop_times',\n 'calendar.txt': 'calendar',\n 'calendar_dates.txt': 'calendar_dates',\n 'fare_attributes.txt': 'fare_attributes',\n 'fare_rules.txt': 'fare_rules',\n 'shapes.txt': 'shapes',\n 'frequencies.txt': 'frequencies',\n 'transfers.txt': 'transfers',\n 'pathways.txt': 'pathways',\n 'levels.txt': 'levels',\n 'feed_info.txt': 'feed_info',\n 'attributions.txt': 'attributions',\n};\n","/**\n * CSV Parser for GTFS files using papaparse\n */\n\nimport Papa from 'papaparse';\n\nexport interface ParsedCSV {\n headers: string[];\n rows: Record[];\n}\n\n/**\n * Parse CSV text into structured data using papaparse\n */\nexport function parseCSV(text: string): ParsedCSV {\n const result = Papa.parse(text, {\n header: true,\n skipEmptyLines: true,\n transformHeader: (header) => header.trim(),\n transform: (value) => value.trim(),\n });\n\n if (result.errors.length > 0) {\n console.warn('CSV parsing warnings:', result.errors);\n }\n\n const headers = result.meta.fields || [];\n const rows = result.data as Record[];\n\n return { headers, rows };\n}\n\n/**\n * Convert parsed row to typed object with proper type conversions\n */\nexport function convertRowTypes(\n row: Record,\n columnTypes: Record\n): Record {\n const result: Record = {};\n\n // Iterate over all expected columns, not just the ones present in the row\n for (const [key, type] of Object.entries(columnTypes)) {\n const value = row[key];\n\n // Handle missing or empty values\n if (value === undefined || value === null || value === '') {\n result[key] = null;\n continue;\n }\n\n if (type === 'INTEGER') {\n const parsed = parseInt(value, 10);\n result[key] = isNaN(parsed) ? null : parsed;\n } else if (type === 'REAL') {\n const parsed = parseFloat(value);\n result[key] = isNaN(parsed) ? null : parsed;\n } else {\n result[key] = value;\n }\n }\n\n return result;\n}\n","/**\n * Data Loader - Loads GTFS data into SQLite database\n */\n\nimport type { Database } from 'sql.js';\nimport { parseCSV, convertRowTypes } from './csv-parser';\nimport { GTFS_SCHEMA, type TableSchema } from '../schema/schema';\nimport type { GTFSFiles } from './zip-loader';\n\n/**\n * Load GTFS files into SQLite database\n * @param skipFiles - Optional array of filenames to skip importing (tables will be created but remain empty)\n */\nexport async function loadGTFSData(db: Database, files: GTFSFiles, skipFiles?: string[]): Promise {\n // Map of file names to table schemas\n const fileToSchema: Map = new Map();\n for (const schema of GTFS_SCHEMA) {\n // Match file names like agency.txt to table name 'agency'\n fileToSchema.set(`${schema.name}.txt`, schema);\n }\n\n // Normalize skipFiles to a Set for faster lookup\n const skipSet = new Set(skipFiles?.map(f => f.toLowerCase()) || []);\n\n // Process each file\n for (const [fileName, content] of Object.entries(files)) {\n const schema = fileToSchema.get(fileName);\n if (!schema) {\n // Skip unknown files\n continue;\n }\n\n // Skip if in skip list\n if (skipSet.has(fileName.toLowerCase())) {\n console.log(`Skipping import of ${fileName} (table ${schema.name} created but empty)`);\n continue;\n }\n\n await loadTableData(db, schema, content);\n }\n}\n\n/**\n * Load data for a single table\n */\nasync function loadTableData(db: Database, schema: TableSchema, csvContent: string): Promise {\n const { headers, rows } = parseCSV(csvContent);\n\n if (rows.length === 0) {\n return;\n }\n\n // Build column type map\n const columnTypes: Record = {};\n for (const col of schema.columns) {\n columnTypes[col.name] = col.type;\n }\n\n // Prepare INSERT statement\n const columns = headers.filter((h) => schema.columns.some((c) => c.name === h));\n const placeholders = columns.map(() => '?').join(', ');\n const insertSQL = `INSERT INTO ${schema.name} (${columns.join(', ')}) VALUES (${placeholders})`;\n\n const stmt = db.prepare(insertSQL);\n\n // Insert each row\n for (const row of rows) {\n const typedRow = convertRowTypes(row, columnTypes);\n const values = columns.map((col) => {\n const value = typedRow[col];\n // Handle undefined, null, or empty values\n return value === null || value === undefined ? null : value;\n });\n\n try {\n stmt.run(values);\n } catch (error) {\n console.error(`Error inserting row into ${schema.name}:`, error);\n console.error('Row data:', row);\n throw error;\n }\n }\n\n stmt.free();\n}\n","import type { Database } from 'sql.js';\n\n/**\n * Create GTFS Realtime tables in the database\n */\nexport function createRealtimeTables(db: Database): void {\n // Alerts table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_alerts (\n id TEXT PRIMARY KEY,\n active_period TEXT, -- JSON array of TimeRange objects\n informed_entity TEXT, -- JSON array of EntitySelector objects\n cause INTEGER,\n effect INTEGER,\n url TEXT, -- JSON TranslatedString\n header_text TEXT, -- JSON TranslatedString\n description_text TEXT, -- JSON TranslatedString\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n // Create index on rt_last_updated for staleness filtering\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_alerts_updated ON rt_alerts(rt_last_updated)');\n\n // Vehicle Positions table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_vehicle_positions (\n trip_id TEXT PRIMARY KEY,\n route_id TEXT,\n vehicle_id TEXT,\n vehicle_label TEXT,\n vehicle_license_plate TEXT,\n latitude REAL,\n longitude REAL,\n bearing REAL,\n odometer REAL,\n speed REAL,\n current_stop_sequence INTEGER,\n stop_id TEXT,\n current_status INTEGER,\n timestamp INTEGER,\n congestion_level INTEGER,\n occupancy_status INTEGER,\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_vehicle_positions_updated ON rt_vehicle_positions(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_vehicle_positions_route ON rt_vehicle_positions(route_id)');\n\n // Trip Updates table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_trip_updates (\n trip_id TEXT PRIMARY KEY,\n route_id TEXT,\n vehicle_id TEXT,\n vehicle_label TEXT,\n vehicle_license_plate TEXT,\n timestamp INTEGER,\n delay INTEGER,\n schedule_relationship INTEGER,\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_trip_updates_updated ON rt_trip_updates(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_trip_updates_route ON rt_trip_updates(route_id)');\n\n // Stop Time Updates table (child of trip updates)\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_stop_time_updates (\n trip_id TEXT NOT NULL,\n stop_sequence INTEGER,\n stop_id TEXT,\n arrival_delay INTEGER,\n arrival_time INTEGER,\n arrival_uncertainty INTEGER,\n departure_delay INTEGER,\n departure_time INTEGER,\n departure_uncertainty INTEGER,\n schedule_relationship INTEGER,\n rt_last_updated INTEGER NOT NULL,\n PRIMARY KEY (trip_id, stop_sequence),\n FOREIGN KEY (trip_id) REFERENCES rt_trip_updates(trip_id) ON DELETE CASCADE\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_stop_time_updates_updated ON rt_stop_time_updates(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_stop_time_updates_stop ON rt_stop_time_updates(stop_id)');\n}\n\n/**\n * Clear all realtime data from the database\n */\nexport function clearRealtimeData(db: Database): void {\n db.run('DELETE FROM rt_alerts');\n db.run('DELETE FROM rt_vehicle_positions');\n db.run('DELETE FROM rt_trip_updates');\n db.run('DELETE FROM rt_stop_time_updates');\n}\n","import type { Database } from 'sql.js';\nimport protobuf from 'protobufjs';\n\n// Types for protobuf decoded objects\ninterface ProtobufTranslation {\n text: string;\n language?: string;\n}\n\ninterface ProtobufTranslatedString {\n translation: ProtobufTranslation[];\n}\n\ninterface ProtobufAlert {\n id: string;\n activePeriod?: unknown[];\n active_period?: unknown[];\n informedEntity?: unknown[];\n informed_entity?: unknown[];\n cause?: number;\n effect?: number;\n url?: ProtobufTranslatedString;\n headerText?: ProtobufTranslatedString;\n header_text?: ProtobufTranslatedString;\n descriptionText?: ProtobufTranslatedString;\n description_text?: ProtobufTranslatedString;\n}\n\ninterface ProtobufTripDescriptor {\n tripId?: string;\n trip_id?: string;\n routeId?: string;\n route_id?: string;\n scheduleRelationship?: number;\n schedule_relationship?: number;\n}\n\ninterface ProtobufVehicleDescriptor {\n id?: string;\n label?: string;\n licensePlate?: string;\n license_plate?: string;\n}\n\ninterface ProtobufPosition {\n latitude?: number;\n longitude?: number;\n bearing?: number;\n odometer?: number;\n speed?: number;\n}\n\ninterface ProtobufVehiclePosition {\n trip?: ProtobufTripDescriptor;\n vehicle?: ProtobufVehicleDescriptor;\n position?: ProtobufPosition;\n currentStopSequence?: number;\n current_stop_sequence?: number;\n stopId?: string;\n stop_id?: string;\n currentStatus?: number;\n current_status?: number;\n timestamp?: number;\n congestionLevel?: number;\n congestion_level?: number;\n occupancyStatus?: number;\n occupancy_status?: number;\n}\n\ninterface ProtobufStopTimeEvent {\n delay?: number;\n time?: number;\n uncertainty?: number;\n}\n\ninterface ProtobufStopTimeUpdate {\n stopSequence?: number;\n stop_sequence?: number;\n stopId?: string;\n stop_id?: string;\n scheduleRelationship?: number;\n schedule_relationship?: number;\n arrival?: ProtobufStopTimeEvent;\n departure?: ProtobufStopTimeEvent;\n}\n\ninterface ProtobufTripUpdate {\n trip?: ProtobufTripDescriptor;\n vehicle?: ProtobufVehicleDescriptor;\n timestamp?: number;\n delay?: number;\n stopTimeUpdate?: ProtobufStopTimeUpdate[];\n stop_time_update?: ProtobufStopTimeUpdate[];\n}\n\n// GTFS Realtime protobuf definition (v2.0)\n// Source: https://github.com/google/transit/blob/master/gtfs-realtime/proto/gtfs-realtime.proto\nconst GTFS_RT_PROTO = `\nsyntax = \"proto2\";\noption java_package = \"com.google.transit.realtime\";\npackage transit_realtime;\n\nmessage FeedMessage {\n required FeedHeader header = 1;\n repeated FeedEntity entity = 2;\n}\n\nmessage FeedHeader {\n required string gtfs_realtime_version = 1;\n enum Incrementality {\n FULL_DATASET = 0;\n DIFFERENTIAL = 1;\n }\n optional Incrementality incrementality = 2 [default = FULL_DATASET];\n optional uint64 timestamp = 3;\n}\n\nmessage FeedEntity {\n required string id = 1;\n optional bool is_deleted = 2 [default = false];\n optional TripUpdate trip_update = 3;\n optional VehiclePosition vehicle = 4;\n optional Alert alert = 5;\n}\n\nmessage TripUpdate {\n required TripDescriptor trip = 1;\n optional VehicleDescriptor vehicle = 3;\n repeated StopTimeUpdate stop_time_update = 2;\n optional uint64 timestamp = 4;\n optional int32 delay = 5;\n\n message StopTimeEvent {\n optional int32 delay = 1;\n optional int64 time = 2;\n optional int32 uncertainty = 3;\n }\n\n message StopTimeUpdate {\n optional uint32 stop_sequence = 1;\n optional string stop_id = 4;\n optional StopTimeEvent arrival = 2;\n optional StopTimeEvent departure = 3;\n enum ScheduleRelationship {\n SCHEDULED = 0;\n SKIPPED = 1;\n NO_DATA = 2;\n }\n optional ScheduleRelationship schedule_relationship = 5 [default = SCHEDULED];\n }\n}\n\nmessage VehiclePosition {\n optional TripDescriptor trip = 1;\n optional VehicleDescriptor vehicle = 8;\n optional Position position = 2;\n optional uint32 current_stop_sequence = 3;\n optional string stop_id = 7;\n enum VehicleStopStatus {\n INCOMING_AT = 0;\n STOPPED_AT = 1;\n IN_TRANSIT_TO = 2;\n }\n optional VehicleStopStatus current_status = 4 [default = IN_TRANSIT_TO];\n optional uint64 timestamp = 5;\n enum CongestionLevel {\n UNKNOWN_CONGESTION_LEVEL = 0;\n RUNNING_SMOOTHLY = 1;\n STOP_AND_GO = 2;\n CONGESTION = 3;\n SEVERE_CONGESTION = 4;\n }\n optional CongestionLevel congestion_level = 6;\n enum OccupancyStatus {\n EMPTY = 0;\n MANY_SEATS_AVAILABLE = 1;\n FEW_SEATS_AVAILABLE = 2;\n STANDING_ROOM_ONLY = 3;\n CRUSHED_STANDING_ROOM_ONLY = 4;\n FULL = 5;\n NOT_ACCEPTING_PASSENGERS = 6;\n }\n optional OccupancyStatus occupancy_status = 9;\n}\n\nmessage Alert {\n repeated TimeRange active_period = 1;\n repeated EntitySelector informed_entity = 5;\n\n enum Cause {\n UNKNOWN_CAUSE = 1;\n OTHER_CAUSE = 2;\n TECHNICAL_PROBLEM = 3;\n STRIKE = 4;\n DEMONSTRATION = 5;\n ACCIDENT = 6;\n HOLIDAY = 7;\n WEATHER = 8;\n MAINTENANCE = 9;\n CONSTRUCTION = 10;\n POLICE_ACTIVITY = 11;\n MEDICAL_EMERGENCY = 12;\n }\n optional Cause cause = 6 [default = UNKNOWN_CAUSE];\n\n enum Effect {\n NO_SERVICE = 1;\n REDUCED_SERVICE = 2;\n SIGNIFICANT_DELAYS = 3;\n DETOUR = 4;\n ADDITIONAL_SERVICE = 5;\n MODIFIED_SERVICE = 6;\n OTHER_EFFECT = 7;\n UNKNOWN_EFFECT = 8;\n STOP_MOVED = 9;\n NO_EFFECT = 10;\n ACCESSIBILITY_ISSUE = 11;\n }\n optional Effect effect = 7 [default = UNKNOWN_EFFECT];\n optional TranslatedString url = 8;\n optional TranslatedString header_text = 10;\n optional TranslatedString description_text = 11;\n}\n\nmessage TimeRange {\n optional uint64 start = 1;\n optional uint64 end = 2;\n}\n\nmessage Position {\n required float latitude = 1;\n required float longitude = 2;\n optional float bearing = 3;\n optional double odometer = 4;\n optional float speed = 5;\n}\n\nmessage TripDescriptor {\n optional string trip_id = 1;\n optional string route_id = 5;\n optional uint32 direction_id = 6;\n optional string start_time = 2;\n optional string start_date = 3;\n enum ScheduleRelationship {\n SCHEDULED = 0;\n ADDED = 1;\n UNSCHEDULED = 2;\n CANCELED = 3;\n }\n optional ScheduleRelationship schedule_relationship = 4;\n}\n\nmessage VehicleDescriptor {\n optional string id = 1;\n optional string label = 2;\n optional string license_plate = 3;\n}\n\nmessage EntitySelector {\n optional string agency_id = 1;\n optional string route_id = 2;\n optional int32 route_type = 3;\n optional TripDescriptor trip = 4;\n optional string stop_id = 5;\n}\n\nmessage TranslatedString {\n message Translation {\n required string text = 1;\n optional string language = 2;\n }\n repeated Translation translation = 1;\n}\n`;\n\n// Fetch protobuf data from URL or local file\n// Uses fetch API (available in modern browsers and Node.js 18+)\nasync function fetchProtobuf(source: string): Promise {\n const isUrl = source.startsWith('http://') || source.startsWith('https://');\n\n if (isUrl) {\n // Fetch from URL\n const response = await fetch(source, {\n headers: {\n 'Accept': 'application/x-protobuf, application/octet-stream'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS-RT feed from ${source}: ${response.status} ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n }\n\n // Read from local file (Node.js only)\n const isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n if (isNode) {\n try {\n const fs = await import('fs');\n const buffer = await fs.promises.readFile(source);\n return new Uint8Array(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));\n } catch (error) {\n throw new Error(`Failed to read GTFS-RT file from ${source}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // In browser, try fetch for relative paths\n const response = await fetch(source, {\n headers: {\n 'Accept': 'application/x-protobuf, application/octet-stream'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS-RT feed from ${source}: ${response.status} ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n}\n\n// Load and parse GTFS-RT protobuf schema\nlet gtfsRtRoot: protobuf.Root | null = null;\n\nfunction loadGtfsRtProto(): protobuf.Root {\n if (!gtfsRtRoot) {\n gtfsRtRoot = protobuf.parse(GTFS_RT_PROTO).root;\n }\n return gtfsRtRoot;\n}\n\n// Convert camelCase object keys to snake_case\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);\n}\n\nfunction convertKeysToSnakeCase(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToSnakeCase);\n }\n if (typeof obj === 'object') {\n const result: Record = {};\n for (const key in obj as Record) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const snakeKey = camelToSnake(key);\n result[snakeKey] = convertKeysToSnakeCase((obj as Record)[key]);\n }\n }\n return result;\n }\n return obj;\n}\n\n// Parse TranslatedString to JSON\nfunction parseTranslatedString(ts: ProtobufTranslatedString | undefined): string | null {\n if (!ts || !ts.translation || ts.translation.length === 0) {\n return null;\n }\n return JSON.stringify({\n translation: ts.translation.map((t) => ({\n text: t.text,\n language: t.language || undefined\n }))\n });\n}\n\n// Insert alerts into database\nfunction insertAlerts(db: Database, alerts: ProtobufAlert[], timestamp: number): void {\n const stmt = db.prepare(`\n INSERT OR REPLACE INTO rt_alerts (\n id, active_period, informed_entity, cause, effect,\n url, header_text, description_text, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const alert of alerts) {\n // Map camelCase protobuf fields to snake_case\n const activePeriod = alert.activePeriod || alert.active_period || [];\n const informedEntity = alert.informedEntity || alert.informed_entity || [];\n const headerText = alert.headerText || alert.header_text;\n const descriptionText = alert.descriptionText || alert.description_text;\n\n // Convert nested objects to snake_case\n const activePeriodSnake = convertKeysToSnakeCase(activePeriod);\n const informedEntitySnake = convertKeysToSnakeCase(informedEntity);\n\n stmt.run([\n alert.id,\n JSON.stringify(activePeriodSnake),\n JSON.stringify(informedEntitySnake),\n alert.cause || null,\n alert.effect || null,\n parseTranslatedString(alert.url),\n parseTranslatedString(headerText),\n parseTranslatedString(descriptionText),\n timestamp\n ]);\n }\n\n stmt.free();\n}\n\n// Insert vehicle positions into database\nfunction insertVehiclePositions(db: Database, positions: ProtobufVehiclePosition[], timestamp: number): void {\n const stmt = db.prepare(`\n INSERT OR REPLACE INTO rt_vehicle_positions (\n trip_id, route_id, vehicle_id, vehicle_label, vehicle_license_plate,\n latitude, longitude, bearing, odometer, speed,\n current_stop_sequence, stop_id, current_status, timestamp,\n congestion_level, occupancy_status, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const vp of positions) {\n const trip = vp.trip;\n if (!trip || !(trip.tripId || trip.trip_id)) continue;\n\n // Map camelCase protobuf fields to snake_case\n const tripId = (trip.tripId || trip.trip_id)!; // Safe: checked above\n const routeId = trip.routeId || trip.route_id;\n const vehicleId = vp.vehicle?.id;\n const vehicleLabel = vp.vehicle?.label;\n const vehicleLicensePlate = vp.vehicle?.licensePlate || vp.vehicle?.license_plate;\n const currentStopSequence = vp.currentStopSequence || vp.current_stop_sequence;\n const stopId = vp.stopId || vp.stop_id;\n const currentStatus = vp.currentStatus || vp.current_status;\n const congestionLevel = vp.congestionLevel || vp.congestion_level;\n const occupancyStatus = vp.occupancyStatus || vp.occupancy_status;\n\n stmt.run([\n tripId,\n routeId || null,\n vehicleId || null,\n vehicleLabel || null,\n vehicleLicensePlate || null,\n vp.position?.latitude || null,\n vp.position?.longitude || null,\n vp.position?.bearing || null,\n vp.position?.odometer || null,\n vp.position?.speed || null,\n currentStopSequence || null,\n stopId || null,\n currentStatus || null,\n vp.timestamp || null,\n congestionLevel || null,\n occupancyStatus || null,\n timestamp\n ]);\n }\n\n stmt.free();\n}\n\n// Insert trip updates into database\nfunction insertTripUpdates(db: Database, updates: ProtobufTripUpdate[], timestamp: number): void {\n const tripStmt = db.prepare(`\n INSERT OR REPLACE INTO rt_trip_updates (\n trip_id, route_id, vehicle_id, vehicle_label, vehicle_license_plate,\n timestamp, delay, schedule_relationship, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const stopTimeStmt = db.prepare(`\n INSERT OR REPLACE INTO rt_stop_time_updates (\n trip_id, stop_sequence, stop_id,\n arrival_delay, arrival_time, arrival_uncertainty,\n departure_delay, departure_time, departure_uncertainty,\n schedule_relationship, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const tu of updates) {\n const trip = tu.trip;\n if (!trip || !(trip.tripId || trip.trip_id)) continue;\n\n // Map camelCase protobuf fields to snake_case\n const tripId = (trip.tripId || trip.trip_id)!; // Safe: checked above\n const routeId = trip.routeId || trip.route_id;\n const vehicleId = tu.vehicle?.id;\n const vehicleLabel = tu.vehicle?.label;\n const vehicleLicensePlate = tu.vehicle?.licensePlate || tu.vehicle?.license_plate;\n const scheduleRelationship = trip.scheduleRelationship || trip.schedule_relationship;\n const stopTimeUpdate = tu.stopTimeUpdate || tu.stop_time_update;\n\n // Insert trip update\n tripStmt.run([\n tripId,\n routeId || null,\n vehicleId || null,\n vehicleLabel || null,\n vehicleLicensePlate || null,\n tu.timestamp || null,\n tu.delay || null,\n scheduleRelationship || null,\n timestamp\n ]);\n\n // Insert stop time updates\n if (stopTimeUpdate) {\n for (const stu of stopTimeUpdate) {\n const stopSequence = stu.stopSequence || stu.stop_sequence;\n const stopId = stu.stopId || stu.stop_id;\n const scheduleRel = stu.scheduleRelationship || stu.schedule_relationship;\n\n stopTimeStmt.run([\n tripId,\n stopSequence || null,\n stopId || null,\n stu.arrival?.delay || null,\n stu.arrival?.time || null,\n stu.arrival?.uncertainty || null,\n stu.departure?.delay || null,\n stu.departure?.time || null,\n stu.departure?.uncertainty || null,\n scheduleRel || null,\n timestamp\n ]);\n }\n }\n }\n\n tripStmt.free();\n stopTimeStmt.free();\n}\n\n/**\n * Fetch and load GTFS Realtime data from multiple feed URLs\n * All feeds are fetched in parallel. If any feed fails, an error is thrown.\n */\nexport async function loadRealtimeData(db: Database, feedUrls: string[]): Promise {\n const root = loadGtfsRtProto();\n const FeedMessage = root.lookupType('transit_realtime.FeedMessage');\n\n // Fetch all feeds in parallel\n const fetchPromises = feedUrls.map(async (url) => {\n try {\n const data = await fetchProtobuf(url);\n const message = FeedMessage.decode(data);\n return FeedMessage.toObject(message, {\n longs: Number,\n enums: Number,\n bytes: String,\n defaults: false,\n arrays: true,\n objects: true,\n oneofs: true\n });\n } catch (error) {\n throw new Error(`Failed to fetch or parse GTFS-RT feed from ${url}: ${error instanceof Error ? error.message : String(error)}`);\n }\n });\n\n // Wait for all feeds - if any fails, this will throw\n const feeds = await Promise.all(fetchPromises);\n\n // Current timestamp for staleness tracking\n const now = Math.floor(Date.now() / 1000);\n\n // Collect all entities by type\n const allAlerts: ProtobufAlert[] = [];\n const allVehiclePositions: ProtobufVehiclePosition[] = [];\n const allTripUpdates: ProtobufTripUpdate[] = [];\n\n for (const feed of feeds) {\n if (!feed.entity) continue;\n\n for (const entity of feed.entity) {\n if (entity.alert) {\n allAlerts.push({ id: entity.id, ...entity.alert });\n }\n if (entity.vehicle) {\n allVehiclePositions.push(entity.vehicle);\n }\n if (entity.tripUpdate) {\n allTripUpdates.push(entity.tripUpdate);\n }\n }\n }\n\n // Insert into database\n if (allAlerts.length > 0) {\n insertAlerts(db, allAlerts, now);\n }\n if (allVehiclePositions.length > 0) {\n insertVehiclePositions(db, allVehiclePositions, now);\n }\n if (allTripUpdates.length > 0) {\n insertTripUpdates(db, allTripUpdates, now);\n }\n}\n","/**\n * Agency Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Agency } from '../types/gtfs';\n\nexport interface AgencyFilters {\n agencyId?: string;\n limit?: number;\n}\n\n/**\n * Get agencies with optional filters\n */\nexport function getAgencies(db: Database, filters: AgencyFilters = {}): Agency[] {\n const { agencyId, limit } = filters;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (agencyId) {\n conditions.push('agency_id = ?');\n params.push(agencyId);\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM agency';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY agency_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const agencies: Agency[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n agencies.push(rowToAgency(row));\n }\n\n stmt.free();\n return agencies;\n}\n\n/**\n * Get an agency by its agency_id\n */\nexport function getAgencyById(db: Database, agencyId: string): Agency | null {\n const stmt = db.prepare('SELECT * FROM agency WHERE agency_id = ?');\n stmt.bind([agencyId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToAgency(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get all agencies\n */\nexport function getAllAgencies(db: Database, limit?: number): Agency[] {\n const sql = limit\n ? `SELECT * FROM agency ORDER BY agency_name LIMIT ${limit}`\n : 'SELECT * FROM agency ORDER BY agency_name';\n\n const stmt = db.prepare(sql);\n\n const agencies: Agency[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n agencies.push(rowToAgency(row));\n }\n\n stmt.free();\n return agencies;\n}\n\n/**\n * Convert database row to Agency object\n */\nfunction rowToAgency(row: Record): Agency {\n return {\n agency_id: String(row.agency_id),\n agency_name: String(row.agency_name),\n agency_url: String(row.agency_url),\n agency_timezone: String(row.agency_timezone),\n agency_lang: row.agency_lang ? String(row.agency_lang) : undefined,\n agency_phone: row.agency_phone ? String(row.agency_phone) : undefined,\n agency_fare_url: row.agency_fare_url ? String(row.agency_fare_url) : undefined,\n agency_email: row.agency_email ? String(row.agency_email) : undefined,\n };\n}\n","/**\n * Stop Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Stop } from '../types/gtfs';\n\nexport interface StopFilters {\n stopId?: string;\n stopCode?: string;\n name?: string;\n tripId?: string;\n limit?: number;\n}\n\n/**\n * Get stops with optional filters\n */\nexport function getStops(db: Database, filters: StopFilters = {}): Stop[] {\n const { stopId, stopCode, name, tripId, limit } = filters;\n\n // Handle special case: get stops by trip (requires JOIN)\n if (tripId) {\n return getStopsByTrip(db, tripId);\n }\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (stopId) {\n conditions.push('stop_id = ?');\n params.push(stopId);\n }\n\n if (stopCode) {\n conditions.push('stop_code = ?');\n params.push(stopCode);\n }\n\n if (name) {\n conditions.push('stop_name LIKE ?');\n params.push(`%${name}%`);\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM stops';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY stop_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Get a stop by its stop_id\n */\nexport function getStopById(db: Database, stopId: string): Stop | null {\n const stmt = db.prepare('SELECT * FROM stops WHERE stop_id = ?');\n stmt.bind([stopId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToStop(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get a stop by its stop_code\n */\nexport function getStopByCode(db: Database, stopCode: string): Stop | null {\n const stmt = db.prepare('SELECT * FROM stops WHERE stop_code = ?');\n stmt.bind([stopCode]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToStop(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Search stops by name (case-insensitive, partial match)\n */\nexport function searchStopsByName(db: Database, name: string, limit = 50): Stop[] {\n const stmt = db.prepare(\n 'SELECT * FROM stops WHERE stop_name LIKE ? ORDER BY stop_name LIMIT ?'\n );\n stmt.bind([`%${name}%`, limit]);\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Get all stops\n */\nexport function getAllStops(db: Database, limit?: number): Stop[] {\n const sql = limit\n ? `SELECT * FROM stops ORDER BY stop_name LIMIT ${limit}`\n : 'SELECT * FROM stops ORDER BY stop_name';\n\n const stmt = db.prepare(sql);\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Get stops for a given trip (ordered by stop_sequence)\n */\nexport function getStopsByTrip(db: Database, tripId: string): Stop[] {\n const stmt = db.prepare(`\n SELECT s.* FROM stops s\n INNER JOIN stop_times st ON s.stop_id = st.stop_id\n WHERE st.trip_id = ?\n ORDER BY st.stop_sequence\n `);\n stmt.bind([tripId]);\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Convert database row to Stop object\n */\nfunction rowToStop(row: Record): Stop {\n return {\n stop_id: String(row.stop_id),\n stop_name: String(row.stop_name),\n stop_lat: Number(row.stop_lat),\n stop_lon: Number(row.stop_lon),\n stop_code: row.stop_code ? String(row.stop_code) : undefined,\n stop_desc: row.stop_desc ? String(row.stop_desc) : undefined,\n zone_id: row.zone_id ? String(row.zone_id) : undefined,\n stop_url: row.stop_url ? String(row.stop_url) : undefined,\n location_type: row.location_type !== null ? Number(row.location_type) : undefined,\n parent_station: row.parent_station ? String(row.parent_station) : undefined,\n stop_timezone: row.stop_timezone ? String(row.stop_timezone) : undefined,\n wheelchair_boarding: row.wheelchair_boarding !== null ? Number(row.wheelchair_boarding) : undefined,\n level_id: row.level_id ? String(row.level_id) : undefined,\n platform_code: row.platform_code ? String(row.platform_code) : undefined,\n };\n}\n","/**\n * Route Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Route } from '../types/gtfs';\n\nexport interface RouteFilters {\n routeId?: string;\n agencyId?: string;\n limit?: number;\n}\n\n/**\n * Get routes with optional filters\n */\nexport function getRoutes(db: Database, filters: RouteFilters = {}): Route[] {\n const { routeId, agencyId, limit } = filters;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n if (agencyId) {\n conditions.push('agency_id = ?');\n params.push(agencyId);\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM routes';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY route_short_name, route_long_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const routes: Route[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n routes.push(rowToRoute(row));\n }\n\n stmt.free();\n return routes;\n}\n\n/**\n * Get a route by its route_id\n */\nexport function getRouteById(db: Database, routeId: string): Route | null {\n const stmt = db.prepare('SELECT * FROM routes WHERE route_id = ?');\n stmt.bind([routeId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToRoute(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get all routes\n */\nexport function getAllRoutes(db: Database, limit?: number): Route[] {\n const sql = limit\n ? `SELECT * FROM routes ORDER BY route_short_name, route_long_name LIMIT ${limit}`\n : 'SELECT * FROM routes ORDER BY route_short_name, route_long_name';\n\n const stmt = db.prepare(sql);\n\n const routes: Route[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n routes.push(rowToRoute(row));\n }\n\n stmt.free();\n return routes;\n}\n\n/**\n * Get routes by agency\n */\nexport function getRoutesByAgency(db: Database, agencyId: string): Route[] {\n const stmt = db.prepare(\n 'SELECT * FROM routes WHERE agency_id = ? ORDER BY route_short_name, route_long_name'\n );\n stmt.bind([agencyId]);\n\n const routes: Route[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n routes.push(rowToRoute(row));\n }\n\n stmt.free();\n return routes;\n}\n\n/**\n * Convert database row to Route object\n */\nfunction rowToRoute(row: Record): Route {\n return {\n route_id: String(row.route_id),\n route_short_name: String(row.route_short_name),\n route_long_name: String(row.route_long_name),\n route_type: Number(row.route_type),\n agency_id: row.agency_id ? String(row.agency_id) : undefined,\n route_desc: row.route_desc ? String(row.route_desc) : undefined,\n route_url: row.route_url ? String(row.route_url) : undefined,\n route_color: row.route_color ? String(row.route_color) : undefined,\n route_text_color: row.route_text_color ? String(row.route_text_color) : undefined,\n route_sort_order: row.route_sort_order !== null ? Number(row.route_sort_order) : undefined,\n continuous_pickup: row.continuous_pickup !== null ? Number(row.continuous_pickup) : undefined,\n continuous_drop_off: row.continuous_drop_off !== null ? Number(row.continuous_drop_off) : undefined,\n };\n}\n","/**\n * Calendar Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Calendar, CalendarDate } from '../types/gtfs';\n\n/**\n * Get active service IDs for a given date\n */\nexport function getActiveServiceIds(db: Database, date: string): string[] {\n const serviceIds = new Set();\n\n // Parse date (format: YYYYMMDD)\n const year = parseInt(date.substring(0, 4));\n const month = parseInt(date.substring(4, 6));\n const day = parseInt(date.substring(6, 8));\n const dateObj = new Date(year, month - 1, day);\n const dayOfWeek = dateObj.getDay(); // 0 = Sunday, 1 = Monday, etc.\n\n // Map day of week to GTFS calendar field\n const dayFields = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];\n const dayField = dayFields[dayOfWeek];\n\n // Check calendar.txt for regular service\n const calendarStmt = db.prepare(\n `SELECT service_id FROM calendar\n WHERE ${dayField} = 1\n AND start_date <= ?\n AND end_date >= ?`\n );\n calendarStmt.bind([date, date]);\n\n while (calendarStmt.step()) {\n const row = calendarStmt.getAsObject() as { service_id: string };\n serviceIds.add(row.service_id);\n }\n calendarStmt.free();\n\n // Check calendar_dates.txt for exceptions\n const exceptionsStmt = db.prepare('SELECT service_id, exception_type FROM calendar_dates WHERE date = ?');\n exceptionsStmt.bind([date]);\n\n while (exceptionsStmt.step()) {\n const row = exceptionsStmt.getAsObject() as { service_id: string; exception_type: number };\n if (row.exception_type === 1) {\n // Service added\n serviceIds.add(row.service_id);\n } else if (row.exception_type === 2) {\n // Service removed\n serviceIds.delete(row.service_id);\n }\n }\n exceptionsStmt.free();\n\n return Array.from(serviceIds);\n}\n\n/**\n * Get calendar entry by service_id\n */\nexport function getCalendarByServiceId(db: Database, serviceId: string): Calendar | null {\n const stmt = db.prepare('SELECT * FROM calendar WHERE service_id = ?');\n stmt.bind([serviceId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToCalendar(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get calendar date exceptions for a service\n */\nexport function getCalendarDates(db: Database, serviceId: string): CalendarDate[] {\n const stmt = db.prepare('SELECT * FROM calendar_dates WHERE service_id = ? ORDER BY date');\n stmt.bind([serviceId]);\n\n const dates: CalendarDate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n dates.push(rowToCalendarDate(row));\n }\n\n stmt.free();\n return dates;\n}\n\n/**\n * Get calendar date exceptions for a specific date\n */\nexport function getCalendarDatesForDate(db: Database, date: string): CalendarDate[] {\n const stmt = db.prepare('SELECT * FROM calendar_dates WHERE date = ?');\n stmt.bind([date]);\n\n const dates: CalendarDate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n dates.push(rowToCalendarDate(row));\n }\n\n stmt.free();\n return dates;\n}\n\n/**\n * Convert database row to Calendar object\n */\nfunction rowToCalendar(row: Record): Calendar {\n return {\n service_id: String(row.service_id),\n monday: Number(row.monday),\n tuesday: Number(row.tuesday),\n wednesday: Number(row.wednesday),\n thursday: Number(row.thursday),\n friday: Number(row.friday),\n saturday: Number(row.saturday),\n sunday: Number(row.sunday),\n start_date: String(row.start_date),\n end_date: String(row.end_date),\n };\n}\n\n/**\n * Convert database row to CalendarDate object\n */\nfunction rowToCalendarDate(row: Record): CalendarDate {\n return {\n service_id: String(row.service_id),\n date: String(row.date),\n exception_type: Number(row.exception_type),\n };\n}\n","import type { Database} from 'sql.js';\nimport type { VehiclePosition } from '../types/gtfs-rt';\nimport type { VehiclePositionFilters } from '../types/gtfs-rt';\n\nexport type { VehiclePositionFilters };\n\n/**\n * Parse vehicle position from database row\n */\nexport function parseVehiclePosition(row: Record): VehiclePosition {\n const vp: VehiclePosition = {\n trip_id: String(row.trip_id),\n route_id: row.route_id ? String(row.route_id) : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n\n // Vehicle descriptor\n if (row.vehicle_id || row.vehicle_label || row.vehicle_license_plate) {\n vp.vehicle = {\n id: row.vehicle_id ? String(row.vehicle_id) : undefined,\n label: row.vehicle_label ? String(row.vehicle_label) : undefined,\n license_plate: row.vehicle_license_plate ? String(row.vehicle_license_plate) : undefined\n };\n }\n\n // Position\n if (row.latitude !== null && row.longitude !== null) {\n vp.position = {\n latitude: Number(row.latitude),\n longitude: Number(row.longitude),\n bearing: row.bearing !== null ? Number(row.bearing) : undefined,\n odometer: row.odometer !== null ? Number(row.odometer) : undefined,\n speed: row.speed !== null ? Number(row.speed) : undefined\n };\n }\n\n // Other fields\n if (row.current_stop_sequence !== null) {\n vp.current_stop_sequence = Number(row.current_stop_sequence);\n }\n if (row.stop_id) {\n vp.stop_id = String(row.stop_id);\n }\n if (row.current_status !== null) {\n vp.current_status = Number(row.current_status);\n }\n if (row.timestamp !== null) {\n vp.timestamp = Number(row.timestamp);\n }\n if (row.congestion_level !== null) {\n vp.congestion_level = Number(row.congestion_level);\n }\n if (row.occupancy_status !== null) {\n vp.occupancy_status = Number(row.occupancy_status);\n }\n\n return vp;\n}\n\n/**\n * Get vehicle positions with optional filters\n */\nexport function getVehiclePositions(\n db: Database,\n filters: VehiclePositionFilters = {},\n stalenessThreshold: number = 120\n): VehiclePosition[] {\n const { tripId, routeId, vehicleId, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by route_id\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n // Filter by vehicle_id\n if (vehicleId) {\n conditions.push('vehicle_id = ?');\n params.push(vehicleId);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_vehicle_positions';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const positions: VehiclePosition[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n positions.push(parseVehiclePosition(row));\n }\n\n stmt.free();\n return positions;\n}\n\n/**\n * Get vehicle position by trip ID\n */\nexport function getVehiclePositionByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): VehiclePosition | null {\n const positions = getVehiclePositions(db, { tripId, limit: 1 }, stalenessThreshold);\n return positions.length > 0 ? positions[0] : null;\n}\n\n/**\n * Get all vehicle positions without staleness filtering (for debugging)\n */\nexport function getAllVehiclePositions(db: Database): VehiclePosition[] {\n const sql = 'SELECT * FROM rt_vehicle_positions ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const positions: VehiclePosition[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n positions.push(parseVehiclePosition(row));\n }\n\n stmt.free();\n return positions;\n}\n","/**\n * Trip Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Trip } from '../types/gtfs';\nimport type { TripRealtime, VehiclePosition } from '../types/gtfs-rt';\nimport { parseVehiclePosition } from './rt-vehicle-positions';\n\nexport interface TripFilters {\n tripId?: string;\n routeId?: string;\n serviceIds?: string[];\n directionId?: number;\n agencyId?: string;\n includeRealtime?: boolean;\n limit?: number;\n}\n\nexport interface TripWithRealtime extends Trip {\n realtime?: TripRealtime;\n}\n\n/**\n * Merge realtime data with trips\n */\nfunction mergeRealtimeData(\n trips: Trip[],\n db: Database,\n stalenessThreshold: number\n): TripWithRealtime[] {\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n\n const tripIds = trips.map(t => t.trip_id);\n if (tripIds.length === 0) return trips;\n\n const placeholders = tripIds.map(() => '?').join(', ');\n\n // Get vehicle positions\n const vpStmt = db.prepare(`\n SELECT * FROM rt_vehicle_positions\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n vpStmt.bind([...tripIds, staleThreshold]);\n\n const vpMap = new Map();\n while (vpStmt.step()) {\n const row = vpStmt.getAsObject() as Record;\n const vp = parseVehiclePosition(row);\n vpMap.set(vp.trip_id, vp);\n }\n vpStmt.free();\n\n // Get trip updates\n const tuStmt = db.prepare(`\n SELECT * FROM rt_trip_updates\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n tuStmt.bind([...tripIds, staleThreshold]);\n\n const tuMap = new Map();\n while (tuStmt.step()) {\n const row = tuStmt.getAsObject() as Record;\n const tripId = String(row.trip_id);\n tuMap.set(tripId, {\n delay: row.delay !== null ? Number(row.delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n });\n }\n tuStmt.free();\n\n // Merge realtime data\n return trips.map((trip): TripWithRealtime => {\n const vp = vpMap.get(trip.trip_id);\n const tu = tuMap.get(trip.trip_id);\n\n if (!vp && !tu) {\n return { ...trip, realtime: { vehicle_position: null, trip_update: null } };\n }\n\n return {\n ...trip,\n realtime: {\n vehicle_position: vp || null,\n trip_update: tu || null\n }\n };\n });\n}\n\n/**\n * Get trips with optional filters\n */\nexport function getTrips(\n db: Database,\n filters: TripFilters = {},\n stalenessThreshold: number = 120\n): Trip[] | TripWithRealtime[] {\n const { tripId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit } = filters;\n\n // Determine if we need to join with routes table\n const needsRoutesJoin = agencyId !== undefined;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (tripId) {\n conditions.push(needsRoutesJoin ? 't.trip_id = ?' : 'trip_id = ?');\n params.push(tripId);\n }\n\n if (routeId) {\n conditions.push(needsRoutesJoin ? 't.route_id = ?' : 'route_id = ?');\n params.push(routeId);\n }\n\n if (serviceIds && serviceIds.length > 0) {\n const placeholders = serviceIds.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`);\n params.push(...serviceIds);\n }\n\n if (directionId !== undefined) {\n conditions.push(needsRoutesJoin ? 't.direction_id = ?' : 'direction_id = ?');\n params.push(directionId);\n }\n\n if (agencyId) {\n conditions.push('r.agency_id = ?');\n params.push(agencyId);\n }\n\n // Build SQL query\n let sql = needsRoutesJoin\n ? 'SELECT t.* FROM trips t INNER JOIN routes r ON t.route_id = r.route_id'\n : 'SELECT * FROM trips';\n\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n\n // Merge realtime data if requested\n if (includeRealtime) {\n return mergeRealtimeData(trips, db, stalenessThreshold);\n }\n\n return trips;\n}\n\n/**\n * Get a trip by its trip_id\n */\nexport function getTripById(db: Database, tripId: string): Trip | null {\n const stmt = db.prepare('SELECT * FROM trips WHERE trip_id = ?');\n stmt.bind([tripId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToTrip(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get trips for a route\n */\nexport function getTripsByRoute(db: Database, routeId: string): Trip[] {\n const stmt = db.prepare('SELECT * FROM trips WHERE route_id = ?');\n stmt.bind([routeId]);\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n return trips;\n}\n\n/**\n * Get trips for a route and service (active on a given date)\n */\nexport function getTripsByRouteAndService(\n db: Database,\n routeId: string,\n serviceIds: string[]\n): Trip[] {\n if (serviceIds.length === 0) {\n return [];\n }\n\n const placeholders = serviceIds.map(() => '?').join(', ');\n const stmt = db.prepare(\n `SELECT * FROM trips WHERE route_id = ? AND service_id IN (${placeholders})`\n );\n stmt.bind([routeId, ...serviceIds]);\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n return trips;\n}\n\n/**\n * Get trips for a route, service, and direction\n */\nexport function getTripsByRouteServiceAndDirection(\n db: Database,\n routeId: string,\n serviceIds: string[],\n directionId: number\n): Trip[] {\n if (serviceIds.length === 0) {\n return [];\n }\n\n const placeholders = serviceIds.map(() => '?').join(', ');\n const stmt = db.prepare(\n `SELECT * FROM trips WHERE route_id = ? AND service_id IN (${placeholders}) AND direction_id = ?`\n );\n stmt.bind([routeId, ...serviceIds, directionId]);\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n return trips;\n}\n\n/**\n * Get all trips for a service (active on a given date)\n */\nexport function getTripsByService(db: Database, serviceIds: string[]): Trip[] {\n if (serviceIds.length === 0) {\n return [];\n }\n\n const placeholders = serviceIds.map(() => '?').join(', ');\n const stmt = db.prepare(`SELECT * FROM trips WHERE service_id IN (${placeholders})`);\n stmt.bind(serviceIds);\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n return trips;\n}\n\n/**\n * Convert database row to Trip object\n */\nfunction rowToTrip(row: Record): Trip {\n return {\n trip_id: String(row.trip_id),\n route_id: String(row.route_id),\n service_id: String(row.service_id),\n trip_headsign: row.trip_headsign ? String(row.trip_headsign) : undefined,\n trip_short_name: row.trip_short_name ? String(row.trip_short_name) : undefined,\n direction_id: row.direction_id !== null ? Number(row.direction_id) : undefined,\n block_id: row.block_id ? String(row.block_id) : undefined,\n shape_id: row.shape_id ? String(row.shape_id) : undefined,\n wheelchair_accessible: row.wheelchair_accessible !== null ? Number(row.wheelchair_accessible) : undefined,\n bikes_allowed: row.bikes_allowed !== null ? Number(row.bikes_allowed) : undefined,\n };\n}\n","/**\n * Stop Time Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { StopTime } from '../types/gtfs';\nimport type { StopTimeRealtime } from '../types/gtfs-rt';\n\nexport interface StopTimeFilters {\n tripId?: string;\n stopId?: string;\n routeId?: string;\n serviceIds?: string[];\n directionId?: number;\n agencyId?: string;\n includeRealtime?: boolean;\n limit?: number;\n}\n\nexport interface StopTimeWithRealtime extends StopTime {\n realtime?: StopTimeRealtime;\n}\n\n/**\n * Merge realtime data with stop times\n */\nfunction mergeRealtimeData(\n stopTimes: StopTime[],\n db: Database,\n stalenessThreshold: number\n): StopTimeWithRealtime[] {\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n\n // Build map of trip_id -> stop times for this query\n const tripIds = Array.from(new Set(stopTimes.map(st => st.trip_id)));\n if (tripIds.length === 0) return stopTimes;\n\n const placeholders = tripIds.map(() => '?').join(', ');\n const stmt = db.prepare(`\n SELECT trip_id, stop_sequence, stop_id,\n arrival_delay, departure_delay, schedule_relationship\n FROM rt_stop_time_updates\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n stmt.bind([...tripIds, staleThreshold]);\n\n // Build map of trip_id+stop_sequence -> RT data\n const rtMap = new Map();\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n const key = `${row.trip_id}_${row.stop_sequence}`;\n rtMap.set(key, {\n arrival_delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,\n departure_delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n });\n }\n stmt.free();\n\n // Merge RT data with stop times\n return stopTimes.map((st): StopTimeWithRealtime => {\n const key = `${st.trip_id}_${st.stop_sequence}`;\n const rtData = rtMap.get(key);\n\n if (rtData) {\n return { ...st, realtime: rtData };\n }\n return st;\n });\n}\n\n/**\n * Get stop times with optional filters\n */\nexport function getStopTimes(\n db: Database,\n filters: StopTimeFilters = {},\n stalenessThreshold: number = 120\n): StopTime[] | StopTimeWithRealtime[] {\n const { tripId, stopId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit } = filters;\n\n // Determine if we need to join with trips and/or routes table\n const needsTripsJoin = routeId || serviceIds || directionId !== undefined || agencyId !== undefined;\n const needsRoutesJoin = agencyId !== undefined;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (tripId) {\n conditions.push(needsTripsJoin ? 'st.trip_id = ?' : 'trip_id = ?');\n params.push(tripId);\n }\n\n if (stopId) {\n conditions.push(needsTripsJoin ? 'st.stop_id = ?' : 'stop_id = ?');\n params.push(stopId);\n }\n\n if (routeId) {\n conditions.push('t.route_id = ?');\n params.push(routeId);\n }\n\n if (serviceIds && serviceIds.length > 0) {\n const placeholders = serviceIds.map(() => '?').join(', ');\n conditions.push(`t.service_id IN (${placeholders})`);\n params.push(...serviceIds);\n }\n\n if (directionId !== undefined) {\n conditions.push('t.direction_id = ?');\n params.push(directionId);\n }\n\n if (agencyId) {\n conditions.push('r.agency_id = ?');\n params.push(agencyId);\n }\n\n // Build SQL query\n let sql: string;\n if (needsRoutesJoin) {\n sql = 'SELECT st.* FROM stop_times st INNER JOIN trips t ON st.trip_id = t.trip_id INNER JOIN routes r ON t.route_id = r.route_id';\n } else if (needsTripsJoin) {\n sql = 'SELECT st.* FROM stop_times st INNER JOIN trips t ON st.trip_id = t.trip_id';\n } else {\n sql = 'SELECT * FROM stop_times';\n }\n\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n\n // Order by stop_sequence if filtering by trip, otherwise by arrival_time\n sql += tripId ? ' ORDER BY stop_sequence' : ' ORDER BY arrival_time';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n\n // Merge realtime data if requested\n if (includeRealtime) {\n return mergeRealtimeData(stopTimes, db, stalenessThreshold);\n }\n\n return stopTimes;\n}\n\n/**\n * Get stop times for a trip\n */\nexport function getStopTimesByTrip(db: Database, tripId: string): StopTime[] {\n const stmt = db.prepare('SELECT * FROM stop_times WHERE trip_id = ? ORDER BY stop_sequence');\n stmt.bind([tripId]);\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n return stopTimes;\n}\n\n/**\n * Get stop times for a stop\n */\nexport function getStopTimesByStop(db: Database, stopId: string, limit = 100): StopTime[] {\n const stmt = db.prepare(\n 'SELECT * FROM stop_times WHERE stop_id = ? ORDER BY arrival_time LIMIT ?'\n );\n stmt.bind([stopId, limit]);\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n return stopTimes;\n}\n\n/**\n * Get stop times for a stop and specific trips (filtered by route/date)\n */\nexport function getStopTimesByStopAndTrips(\n db: Database,\n stopId: string,\n tripIds: string[]\n): StopTime[] {\n if (tripIds.length === 0) {\n return [];\n }\n\n const placeholders = tripIds.map(() => '?').join(', ');\n const stmt = db.prepare(\n `SELECT * FROM stop_times\n WHERE stop_id = ? AND trip_id IN (${placeholders})\n ORDER BY arrival_time`\n );\n stmt.bind([stopId, ...tripIds]);\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n return stopTimes;\n}\n\n/**\n * Get stop times for a stop, route, and direction on a specific date\n */\nexport function getStopTimesForStopRouteDirection(\n db: Database,\n stopId: string,\n routeId: string,\n serviceIds: string[],\n directionId?: number\n): StopTime[] {\n if (serviceIds.length === 0) {\n return [];\n }\n\n const placeholders = serviceIds.map(() => '?').join(', ');\n\n let sql = `\n SELECT st.* FROM stop_times st\n INNER JOIN trips t ON st.trip_id = t.trip_id\n WHERE st.stop_id = ?\n AND t.route_id = ?\n AND t.service_id IN (${placeholders})\n `;\n\n const params: (string | number)[] = [stopId, routeId, ...serviceIds];\n\n if (directionId !== undefined) {\n sql += ' AND t.direction_id = ?';\n params.push(directionId);\n }\n\n sql += ' ORDER BY st.arrival_time';\n\n const stmt = db.prepare(sql);\n stmt.bind(params);\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n return stopTimes;\n}\n\n/**\n * Convert database row to StopTime object\n */\nfunction rowToStopTime(row: Record): StopTime {\n return {\n trip_id: String(row.trip_id),\n arrival_time: String(row.arrival_time),\n departure_time: String(row.departure_time),\n stop_id: String(row.stop_id),\n stop_sequence: Number(row.stop_sequence),\n stop_headsign: row.stop_headsign ? String(row.stop_headsign) : undefined,\n pickup_type: row.pickup_type !== null ? Number(row.pickup_type) : undefined,\n drop_off_type: row.drop_off_type !== null ? Number(row.drop_off_type) : undefined,\n continuous_pickup: row.continuous_pickup !== null ? Number(row.continuous_pickup) : undefined,\n continuous_drop_off: row.continuous_drop_off !== null ? Number(row.continuous_drop_off) : undefined,\n shape_dist_traveled: row.shape_dist_traveled !== null ? Number(row.shape_dist_traveled) : undefined,\n timepoint: row.timepoint !== null ? Number(row.timepoint) : undefined,\n };\n}\n","import type { Database } from 'sql.js';\nimport type { Alert, EntitySelector, TimeRange, TranslatedString } from '../types/gtfs-rt';\nimport type { AlertFilters } from '../types/gtfs-rt';\n\nexport type { AlertFilters };\n\n/**\n * Parse JSON fields from database\n */\nfunction parseAlert(row: Record): Alert {\n return {\n id: String(row.id),\n active_period: row.active_period ? JSON.parse(String(row.active_period)) as TimeRange[] : [],\n informed_entity: row.informed_entity ? JSON.parse(String(row.informed_entity)) as EntitySelector[] : [],\n cause: row.cause ? Number(row.cause) : undefined,\n effect: row.effect ? Number(row.effect) : undefined,\n url: row.url ? JSON.parse(String(row.url)) as TranslatedString : undefined,\n header_text: row.header_text ? JSON.parse(String(row.header_text)) as TranslatedString : undefined,\n description_text: row.description_text ? JSON.parse(String(row.description_text)) as TranslatedString : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n}\n\n/**\n * Check if an alert is currently active based on its active_period\n */\nfunction isAlertActive(alert: Alert, now: number): boolean {\n // If no active periods defined, assume always active\n if (!alert.active_period || alert.active_period.length === 0) {\n return true;\n }\n\n // Check if current time falls within any active period\n for (const period of alert.active_period) {\n const start = period.start || 0;\n const end = period.end || Number.MAX_SAFE_INTEGER;\n\n if (now >= start && now <= end) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if an alert affects the specified entity\n */\nfunction alertAffectsEntity(alert: Alert, filters: AlertFilters): boolean {\n if (!alert.informed_entity || alert.informed_entity.length === 0) {\n return true; // No specific entities, assume it affects all\n }\n\n for (const entity of alert.informed_entity) {\n // Check route_id\n if (filters.routeId && entity.route_id === filters.routeId) {\n return true;\n }\n\n // Check stop_id\n if (filters.stopId && entity.stop_id === filters.stopId) {\n return true;\n }\n\n // Check trip_id\n if (filters.tripId && entity.trip?.trip_id === filters.tripId) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Get alerts with optional filters\n */\nexport function getAlerts(db: Database, filters: AlertFilters = {}, stalenessThreshold: number = 120): Alert[] {\n const {\n alertId,\n activeOnly,\n routeId,\n stopId,\n tripId,\n cause,\n effect,\n limit\n } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by alert ID\n if (alertId) {\n conditions.push('id = ?');\n params.push(alertId);\n }\n\n // Filter by cause\n if (cause !== undefined) {\n conditions.push('cause = ?');\n params.push(cause);\n }\n\n // Filter by effect\n if (effect !== undefined) {\n conditions.push('effect = ?');\n params.push(effect);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_alerts';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const alerts: Alert[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n const alert = parseAlert(row);\n\n // Apply activeOnly filter in application code\n if (activeOnly && !isAlertActive(alert, now)) {\n continue;\n }\n\n // Apply entity filters in application code (since informed_entity is JSON)\n if (routeId || stopId || tripId) {\n if (!alertAffectsEntity(alert, filters)) {\n continue;\n }\n }\n\n alerts.push(alert);\n }\n\n stmt.free();\n return alerts;\n}\n\n/**\n * Get alert by ID\n */\nexport function getAlertById(db: Database, alertId: string, stalenessThreshold: number = 120): Alert | null {\n const alerts = getAlerts(db, { alertId, limit: 1 }, stalenessThreshold);\n return alerts.length > 0 ? alerts[0] : null;\n}\n\n/**\n * Get all alerts without staleness filtering (for debugging)\n */\nexport function getAllAlerts(db: Database): Alert[] {\n const sql = 'SELECT * FROM rt_alerts ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const alerts: Alert[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n alerts.push(parseAlert(row));\n }\n\n stmt.free();\n return alerts;\n}\n","import type { Database } from 'sql.js';\nimport type { TripUpdate } from '../types/gtfs-rt';\n\nexport interface TripUpdateFilters {\n tripId?: string;\n routeId?: string;\n vehicleId?: string;\n limit?: number;\n}\n\n/**\n * Parse trip update from database row\n */\nexport function parseTripUpdate(row: Record): TripUpdate {\n const tu: TripUpdate = {\n trip_id: String(row.trip_id),\n route_id: row.route_id ? String(row.route_id) : undefined,\n stop_time_update: [], // Will be populated separately\n timestamp: row.timestamp !== null ? Number(row.timestamp) : undefined,\n delay: row.delay !== null ? Number(row.delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n\n // Vehicle descriptor\n if (row.vehicle_id || row.vehicle_label || row.vehicle_license_plate) {\n tu.vehicle = {\n id: row.vehicle_id ? String(row.vehicle_id) : undefined,\n label: row.vehicle_label ? String(row.vehicle_label) : undefined,\n license_plate: row.vehicle_license_plate ? String(row.vehicle_license_plate) : undefined\n };\n }\n\n return tu;\n}\n\n/**\n * Get trip updates with optional filters\n */\nexport function getTripUpdates(\n db: Database,\n filters: TripUpdateFilters = {},\n stalenessThreshold: number = 120\n): TripUpdate[] {\n const { tripId, routeId, vehicleId, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by route_id\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n // Filter by vehicle_id\n if (vehicleId) {\n conditions.push('vehicle_id = ?');\n params.push(vehicleId);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_trip_updates';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const tripUpdates: TripUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n tripUpdates.push(parseTripUpdate(row));\n }\n\n stmt.free();\n return tripUpdates;\n}\n\n/**\n * Get trip update by trip ID\n */\nexport function getTripUpdateByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): TripUpdate | null {\n const updates = getTripUpdates(db, { tripId, limit: 1 }, stalenessThreshold);\n return updates.length > 0 ? updates[0] : null;\n}\n\n/**\n * Get all trip updates without staleness filtering (for debugging)\n */\nexport function getAllTripUpdates(db: Database): TripUpdate[] {\n const sql = 'SELECT * FROM rt_trip_updates ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const tripUpdates: TripUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n tripUpdates.push(parseTripUpdate(row));\n }\n\n stmt.free();\n return tripUpdates;\n}\n","import type { Database } from 'sql.js';\nimport type { StopTimeUpdate } from '../types/gtfs-rt';\n\nexport interface StopTimeUpdateFilters {\n tripId?: string;\n stopId?: string;\n stopSequence?: number;\n limit?: number;\n}\n\n/**\n * Parse stop time update from database row\n */\nfunction parseStopTimeUpdate(row: Record): StopTimeUpdate {\n const stu: StopTimeUpdate = {\n stop_sequence: row.stop_sequence !== null ? Number(row.stop_sequence) : undefined,\n stop_id: row.stop_id ? String(row.stop_id) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n };\n\n // Arrival event\n if (row.arrival_delay !== null || row.arrival_time !== null || row.arrival_uncertainty !== null) {\n stu.arrival = {\n delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,\n time: row.arrival_time !== null ? Number(row.arrival_time) : undefined,\n uncertainty: row.arrival_uncertainty !== null ? Number(row.arrival_uncertainty) : undefined\n };\n }\n\n // Departure event\n if (row.departure_delay !== null || row.departure_time !== null || row.departure_uncertainty !== null) {\n stu.departure = {\n delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,\n time: row.departure_time !== null ? Number(row.departure_time) : undefined,\n uncertainty: row.departure_uncertainty !== null ? Number(row.departure_uncertainty) : undefined\n };\n }\n\n return stu;\n}\n\n/**\n * Extended StopTimeUpdate with trip_id and rt_last_updated for debugging\n */\nexport interface StopTimeUpdateWithMetadata extends StopTimeUpdate {\n trip_id: string;\n rt_last_updated: number;\n}\n\n/**\n * Parse stop time update with metadata from database row\n */\nfunction parseStopTimeUpdateWithMetadata(row: Record): StopTimeUpdateWithMetadata {\n const stu = parseStopTimeUpdate(row) as StopTimeUpdateWithMetadata;\n stu.trip_id = String(row.trip_id);\n stu.rt_last_updated = Number(row.rt_last_updated);\n return stu;\n}\n\n/**\n * Get stop time updates with optional filters\n */\nexport function getStopTimeUpdates(\n db: Database,\n filters: StopTimeUpdateFilters = {},\n stalenessThreshold: number = 120\n): StopTimeUpdate[] {\n const { tripId, stopId, stopSequence, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by stop_id\n if (stopId) {\n conditions.push('stop_id = ?');\n params.push(stopId);\n }\n\n // Filter by stop_sequence\n if (stopSequence !== undefined) {\n conditions.push('stop_sequence = ?');\n params.push(stopSequence);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_stop_time_updates';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY trip_id, stop_sequence';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stopTimeUpdates: StopTimeUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimeUpdates.push(parseStopTimeUpdate(row));\n }\n\n stmt.free();\n return stopTimeUpdates;\n}\n\n/**\n * Get stop time updates for a specific trip\n */\nexport function getStopTimeUpdatesByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): StopTimeUpdate[] {\n return getStopTimeUpdates(db, { tripId }, stalenessThreshold);\n}\n\n/**\n * Get all stop time updates without staleness filtering (for debugging)\n * Returns extended type with trip_id and rt_last_updated for debugging purposes\n */\nexport function getAllStopTimeUpdates(db: Database): StopTimeUpdateWithMetadata[] {\n const sql = 'SELECT * FROM rt_stop_time_updates ORDER BY trip_id, stop_sequence';\n const stmt = db.prepare(sql);\n\n const stopTimeUpdates: StopTimeUpdateWithMetadata[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimeUpdates.push(parseStopTimeUpdateWithMetadata(row));\n }\n\n stmt.free();\n return stopTimeUpdates;\n}\n","/**\n * GTFS Realtime TypeScript types\n * Based on GTFS Realtime v2.0 specification\n */\n\n// Enum types from GTFS-RT specification\n\nexport enum ScheduleRelationship {\n SCHEDULED = 0,\n ADDED = 1,\n UNSCHEDULED = 2,\n CANCELED = 3,\n SKIPPED = 4,\n NO_DATA = 5\n}\n\nexport enum VehicleStopStatus {\n INCOMING_AT = 0,\n STOPPED_AT = 1,\n IN_TRANSIT_TO = 2\n}\n\nexport enum CongestionLevel {\n UNKNOWN_CONGESTION_LEVEL = 0,\n RUNNING_SMOOTHLY = 1,\n STOP_AND_GO = 2,\n CONGESTION = 3,\n SEVERE_CONGESTION = 4\n}\n\nexport enum OccupancyStatus {\n EMPTY = 0,\n MANY_SEATS_AVAILABLE = 1,\n FEW_SEATS_AVAILABLE = 2,\n STANDING_ROOM_ONLY = 3,\n CRUSHED_STANDING_ROOM_ONLY = 4,\n FULL = 5,\n NOT_ACCEPTING_PASSENGERS = 6\n}\n\nexport enum AlertCause {\n UNKNOWN_CAUSE = 1,\n OTHER_CAUSE = 2,\n TECHNICAL_PROBLEM = 3,\n STRIKE = 4,\n DEMONSTRATION = 5,\n ACCIDENT = 6,\n HOLIDAY = 7,\n WEATHER = 8,\n MAINTENANCE = 9,\n CONSTRUCTION = 10,\n POLICE_ACTIVITY = 11,\n MEDICAL_EMERGENCY = 12\n}\n\nexport enum AlertEffect {\n NO_SERVICE = 1,\n REDUCED_SERVICE = 2,\n SIGNIFICANT_DELAYS = 3,\n DETOUR = 4,\n ADDITIONAL_SERVICE = 5,\n MODIFIED_SERVICE = 6,\n OTHER_EFFECT = 7,\n UNKNOWN_EFFECT = 8,\n STOP_MOVED = 9,\n NO_EFFECT = 10,\n ACCESSIBILITY_ISSUE = 11\n}\n\n// Main GTFS-RT types\n\nexport interface TranslatedString {\n translation: Array<{\n text: string;\n language?: string;\n }>;\n}\n\nexport interface EntitySelector {\n agency_id?: string;\n route_id?: string;\n route_type?: number;\n trip?: {\n trip_id?: string;\n route_id?: string;\n direction_id?: number;\n start_time?: string;\n start_date?: string;\n schedule_relationship?: ScheduleRelationship;\n };\n stop_id?: string;\n}\n\nexport interface TimeRange {\n start?: number; // UNIX timestamp\n end?: number; // UNIX timestamp\n}\n\nexport interface Alert {\n id: string;\n active_period: TimeRange[];\n informed_entity: EntitySelector[];\n cause?: AlertCause;\n effect?: AlertEffect;\n url?: TranslatedString;\n header_text?: TranslatedString;\n description_text?: TranslatedString;\n rt_last_updated: number; // UNIX timestamp\n}\n\nexport interface Position {\n latitude: number;\n longitude: number;\n bearing?: number;\n odometer?: number;\n speed?: number;\n}\n\nexport interface VehicleDescriptor {\n id?: string;\n label?: string;\n license_plate?: string;\n}\n\nexport interface VehiclePosition {\n trip_id: string;\n route_id?: string;\n vehicle?: VehicleDescriptor;\n position?: Position;\n current_stop_sequence?: number;\n stop_id?: string;\n current_status?: VehicleStopStatus;\n timestamp?: number;\n congestion_level?: CongestionLevel;\n occupancy_status?: OccupancyStatus;\n rt_last_updated: number; // UNIX timestamp\n}\n\nexport interface StopTimeEvent {\n delay?: number; // seconds\n time?: number; // UNIX timestamp (absolute time)\n uncertainty?: number;\n}\n\nexport interface StopTimeUpdate {\n stop_sequence?: number;\n stop_id?: string;\n arrival?: StopTimeEvent;\n departure?: StopTimeEvent;\n schedule_relationship?: ScheduleRelationship;\n}\n\nexport interface TripUpdate {\n trip_id: string;\n route_id?: string;\n vehicle?: VehicleDescriptor;\n stop_time_update: StopTimeUpdate[];\n timestamp?: number;\n delay?: number;\n schedule_relationship?: ScheduleRelationship;\n rt_last_updated: number; // UNIX timestamp\n}\n\n// Enhanced types for queries with realtime data\n\nexport interface StopTimeRealtime {\n arrival_delay?: number; // seconds\n departure_delay?: number; // seconds\n schedule_relationship?: ScheduleRelationship;\n}\n\nexport interface TripRealtime {\n vehicle_position?: VehiclePosition | null;\n trip_update?: {\n delay?: number;\n schedule_relationship?: ScheduleRelationship;\n } | null;\n}\n\n// Filter types for realtime queries\n\nexport interface AlertFilters {\n alertId?: string;\n activeOnly?: boolean;\n routeId?: string;\n stopId?: string;\n tripId?: string;\n cause?: AlertCause;\n effect?: AlertEffect;\n limit?: number;\n}\n\nexport interface VehiclePositionFilters {\n tripId?: string;\n routeId?: string;\n vehicleId?: string;\n limit?: number;\n}\n\n// Configuration\n\nexport interface RealtimeConfig {\n feedUrls?: string[];\n stalenessThreshold?: number; // seconds, default 120\n}\n"],"mappings":"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","names":["response","arrayBuffer","mergeRealtimeData","ScheduleRelationship","VehicleStopStatus","CongestionLevel","OccupancyStatus","AlertCause","AlertEffect"]} \ No newline at end of file +{"version":3,"sources":["../src/gtfs-sqljs.ts","../src/schema/schema.ts","../src/loaders/zip-loader.ts","../src/loaders/csv-parser.ts","../src/loaders/data-loader.ts","../src/schema/gtfs-rt-schema.ts","../src/loaders/gtfs-rt-loader.ts","../src/queries/agencies.ts","../src/queries/stops.ts","../src/queries/routes.ts","../src/queries/calendar.ts","../src/queries/rt-vehicle-positions.ts","../src/queries/trips.ts","../src/queries/stop-times.ts","../src/queries/rt-alerts.ts","../src/queries/rt-trip-updates.ts","../src/queries/rt-stop-time-updates.ts","../src/types/gtfs-rt.ts"],"sourcesContent":["/**\n * Main GtfsSqlJs Class\n */\n\nimport initSqlJs, { type Database, type SqlJsStatic } from 'sql.js';\nimport { getAllCreateStatements } from './schema/schema';\nimport { loadGTFSZip } from './loaders/zip-loader';\nimport { loadGTFSData } from './loaders/data-loader';\nimport { createRealtimeTables, clearRealtimeData as clearRTData } from './schema/gtfs-rt-schema';\nimport { loadRealtimeData } from './loaders/gtfs-rt-loader';\n\n// Query methods\nimport { getAgencies, type AgencyFilters } from './queries/agencies';\nimport { getStops, type StopFilters } from './queries/stops';\nimport { getRoutes, type RouteFilters } from './queries/routes';\nimport {\n getActiveServiceIds,\n getCalendarByServiceId,\n getCalendarDates,\n getCalendarDatesForDate,\n} from './queries/calendar';\nimport { getTrips, type TripFilters, type TripWithRealtime } from './queries/trips';\nimport { getStopTimes, type StopTimeFilters, type StopTimeWithRealtime } from './queries/stop-times';\nimport { getAlerts as getAlertsQuery, getAllAlerts, type AlertFilters } from './queries/rt-alerts';\nimport { getVehiclePositions as getVehiclePositionsQuery, getAllVehiclePositions, type VehiclePositionFilters } from './queries/rt-vehicle-positions';\nimport { getTripUpdates, getAllTripUpdates, type TripUpdateFilters } from './queries/rt-trip-updates';\nimport { getStopTimeUpdates, getAllStopTimeUpdates, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata } from './queries/rt-stop-time-updates';\n\n// Types\nimport type { Agency, Stop, Route, Trip, StopTime, Calendar, CalendarDate } from './types/gtfs';\nimport type { Alert, VehiclePosition, TripUpdate } from './types/gtfs-rt';\n\n// Export filter types for users\nexport type { AgencyFilters, StopFilters, RouteFilters, TripFilters, StopTimeFilters, AlertFilters, VehiclePositionFilters, TripUpdateFilters, StopTimeUpdateFilters };\n// Export RT types\nexport type { Alert, VehiclePosition, TripUpdate, StopTimeUpdateWithMetadata, TripWithRealtime, StopTimeWithRealtime };\n\nexport interface GtfsSqlJsOptions {\n /**\n * Path or URL to GTFS ZIP file\n */\n zipPath?: string;\n\n /**\n * Pre-loaded SQLite database as ArrayBuffer\n */\n database?: ArrayBuffer;\n\n /**\n * Optional: Custom SQL.js instance\n */\n SQL?: SqlJsStatic;\n\n /**\n * Optional: Path to SQL.js WASM file (for custom loading)\n */\n locateFile?: (filename: string) => string;\n\n /**\n * Optional: Array of GTFS filenames to skip importing (e.g., ['shapes.txt'])\n * Tables will be created but no data will be imported for these files\n */\n skipFiles?: string[];\n\n /**\n * Optional: Array of GTFS-RT feed URLs for realtime data\n */\n realtimeFeedUrls?: string[];\n\n /**\n * Optional: Staleness threshold in seconds (default: 120)\n * Realtime data older than this will be excluded from queries\n */\n stalenessThreshold?: number;\n}\n\nexport class GtfsSqlJs {\n private db: Database | null = null;\n private SQL: SqlJsStatic | null = null;\n private realtimeFeedUrls: string[] = [];\n private stalenessThreshold: number = 120;\n\n /**\n * Private constructor - use static factory methods instead\n */\n private constructor() {}\n\n /**\n * Create GtfsSqlJs instance from GTFS ZIP file\n */\n static async fromZip(\n zipPath: string,\n options: Omit = {}\n ): Promise {\n const instance = new GtfsSqlJs();\n await instance.initFromZip(zipPath, options);\n return instance;\n }\n\n /**\n * Create GtfsSqlJs instance from existing SQLite database\n */\n static async fromDatabase(\n database: ArrayBuffer,\n options: Omit = {}\n ): Promise {\n const instance = new GtfsSqlJs();\n await instance.initFromDatabase(database, options);\n return instance;\n }\n\n /**\n * Initialize from ZIP file\n */\n private async initFromZip(zipPath: string, options: Omit): Promise {\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Create new database\n this.db = new this.SQL.Database();\n\n // Create GTFS schema\n const createStatements = getAllCreateStatements();\n for (const statement of createStatements) {\n this.db.run(statement);\n }\n\n // Create GTFS-RT tables\n createRealtimeTables(this.db);\n\n // Load GTFS data\n const files = await loadGTFSZip(zipPath);\n await loadGTFSData(this.db, files, options.skipFiles);\n\n // Set RT configuration\n if (options.realtimeFeedUrls) {\n this.realtimeFeedUrls = options.realtimeFeedUrls;\n }\n if (options.stalenessThreshold !== undefined) {\n this.stalenessThreshold = options.stalenessThreshold;\n }\n }\n\n /**\n * Initialize from existing database\n */\n private async initFromDatabase(\n database: ArrayBuffer,\n options: Omit\n ): Promise {\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Load existing database\n this.db = new this.SQL.Database(new Uint8Array(database));\n\n // Ensure RT tables exist (in case loading old database)\n createRealtimeTables(this.db);\n\n // Set RT configuration\n if (options.realtimeFeedUrls) {\n this.realtimeFeedUrls = options.realtimeFeedUrls;\n }\n if (options.stalenessThreshold !== undefined) {\n this.stalenessThreshold = options.stalenessThreshold;\n }\n }\n\n /**\n * Export database to ArrayBuffer\n */\n export(): ArrayBuffer {\n if (!this.db) {\n throw new Error('Database not initialized');\n }\n\n const data = this.db.export();\n // Create a new ArrayBuffer and copy the data to ensure proper type\n const buffer = new ArrayBuffer(data.length);\n new Uint8Array(buffer).set(data);\n return buffer;\n }\n\n /**\n * Close the database connection\n */\n close(): void {\n if (this.db) {\n this.db.close();\n this.db = null;\n }\n }\n\n /**\n * Get direct access to the database (for advanced queries)\n */\n getDatabase(): Database {\n if (!this.db) {\n throw new Error('Database not initialized');\n }\n return this.db;\n }\n\n // ==================== Agency Methods ====================\n\n /**\n * Get agencies with optional filters\n * Pass agencyId filter to get a specific agency\n */\n getAgencies(filters?: AgencyFilters): Agency[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAgencies(this.db, filters);\n }\n\n // ==================== Stop Methods ====================\n\n /**\n * Get stops with optional filters\n * Pass stopId filter to get a specific stop\n */\n getStops(filters?: StopFilters): Stop[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStops(this.db, filters);\n }\n\n // ==================== Route Methods ====================\n\n /**\n * Get routes with optional filters\n * Pass routeId filter to get a specific route\n */\n getRoutes(filters?: RouteFilters): Route[] {\n if (!this.db) throw new Error('Database not initialized');\n return getRoutes(this.db, filters);\n }\n\n // ==================== Calendar Methods ====================\n\n /**\n * Get active service IDs for a given date (YYYYMMDD format)\n */\n getActiveServiceIds(date: string): string[] {\n if (!this.db) throw new Error('Database not initialized');\n return getActiveServiceIds(this.db, date);\n }\n\n /**\n * Get calendar entry by service_id\n */\n getCalendarByServiceId(serviceId: string): Calendar | null {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarByServiceId(this.db, serviceId);\n }\n\n /**\n * Get calendar date exceptions for a service\n */\n getCalendarDates(serviceId: string): CalendarDate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarDates(this.db, serviceId);\n }\n\n /**\n * Get calendar date exceptions for a specific date\n */\n getCalendarDatesForDate(date: string): CalendarDate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getCalendarDatesForDate(this.db, date);\n }\n\n // ==================== Trip Methods ====================\n\n /**\n * Get trips with optional filters\n * Pass tripId filter to get a specific trip\n *\n * @param filters - Optional filters\n * @param filters.tripId - Filter by trip ID (single value or array)\n * @param filters.routeId - Filter by route ID (single value or array)\n * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date\n * @param filters.directionId - Filter by direction ID (single value or array)\n * @param filters.agencyId - Filter by agency ID (single value or array)\n * @param filters.limit - Limit number of results\n *\n * @example\n * // Get all trips for a route on a specific date\n * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', date: '20240115' });\n *\n * @example\n * // Get all trips for a route going in one direction\n * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 });\n *\n * @example\n * // Get a specific trip\n * const trips = gtfs.getTrips({ tripId: 'TRIP_123' });\n */\n getTrips(filters?: TripFilters & { date?: string }): Trip[] {\n if (!this.db) throw new Error('Database not initialized');\n\n // Handle date parameter by converting it to serviceIds\n const { date, ...restFilters } = filters || {};\n const finalFilters = { ...restFilters };\n\n if (date) {\n const serviceIds = getActiveServiceIds(this.db, date);\n finalFilters.serviceIds = serviceIds;\n }\n\n return getTrips(this.db, finalFilters, this.stalenessThreshold);\n }\n\n // ==================== Stop Time Methods ====================\n\n /**\n * Get stop times with optional filters\n *\n * @param filters - Optional filters\n * @param filters.tripId - Filter by trip ID (single value or array)\n * @param filters.stopId - Filter by stop ID (single value or array)\n * @param filters.routeId - Filter by route ID (single value or array)\n * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date\n * @param filters.directionId - Filter by direction ID (single value or array)\n * @param filters.agencyId - Filter by agency ID (single value or array)\n * @param filters.includeRealtime - Include realtime data (delay and time fields)\n * @param filters.limit - Limit number of results\n *\n * @example\n * // Get stop times for a specific trip\n * const stopTimes = gtfs.getStopTimes({ tripId: 'TRIP_123' });\n *\n * @example\n * // Get stop times at a stop for a specific route on a date\n * const stopTimes = gtfs.getStopTimes({\n * stopId: 'STOP_123',\n * routeId: 'ROUTE_1',\n * date: '20240115'\n * });\n *\n * @example\n * // Get stop times with realtime data\n * const stopTimes = gtfs.getStopTimes({\n * tripId: 'TRIP_123',\n * includeRealtime: true\n * });\n */\n getStopTimes(filters?: StopTimeFilters & { date?: string }): StopTime[] {\n if (!this.db) throw new Error('Database not initialized');\n\n // Handle date parameter by converting it to serviceIds\n const { date, ...restFilters } = filters || {};\n const finalFilters = { ...restFilters };\n\n if (date) {\n const serviceIds = getActiveServiceIds(this.db, date);\n finalFilters.serviceIds = serviceIds;\n }\n\n return getStopTimes(this.db, finalFilters, this.stalenessThreshold);\n }\n\n // ==================== Realtime Methods ====================\n\n /**\n * Set GTFS-RT feed URLs\n */\n setRealtimeFeedUrls(urls: string[]): void {\n this.realtimeFeedUrls = urls;\n }\n\n /**\n * Get currently configured GTFS-RT feed URLs\n */\n getRealtimeFeedUrls(): string[] {\n return [...this.realtimeFeedUrls];\n }\n\n /**\n * Set staleness threshold in seconds\n */\n setStalenessThreshold(seconds: number): void {\n this.stalenessThreshold = seconds;\n }\n\n /**\n * Get current staleness threshold\n */\n getStalenessThreshold(): number {\n return this.stalenessThreshold;\n }\n\n /**\n * Fetch and load GTFS Realtime data from configured feed URLs or provided URLs\n * @param urls - Optional array of feed URLs. If not provided, uses configured feed URLs\n */\n async fetchRealtimeData(urls?: string[]): Promise {\n if (!this.db) throw new Error('Database not initialized');\n\n const feedUrls = urls || this.realtimeFeedUrls;\n if (feedUrls.length === 0) {\n throw new Error('No realtime feed URLs configured. Use setRealtimeFeedUrls() or pass urls parameter.');\n }\n\n await loadRealtimeData(this.db, feedUrls);\n }\n\n /**\n * Clear all realtime data from the database\n */\n clearRealtimeData(): void {\n if (!this.db) throw new Error('Database not initialized');\n clearRTData(this.db);\n }\n\n /**\n * Get alerts with optional filters\n * Pass alertId filter to get a specific alert\n */\n getAlerts(filters?: AlertFilters): Alert[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAlertsQuery(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get vehicle positions with optional filters\n * Pass tripId filter to get vehicle position for a specific trip\n */\n getVehiclePositions(filters?: VehiclePositionFilters): VehiclePosition[] {\n if (!this.db) throw new Error('Database not initialized');\n return getVehiclePositionsQuery(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get trip updates with optional filters\n * Pass tripId filter to get trip update for a specific trip\n */\n getTripUpdates(filters?: TripUpdateFilters): TripUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getTripUpdates(this.db, filters, this.stalenessThreshold);\n }\n\n /**\n * Get stop time updates with optional filters\n * Pass tripId filter to get stop time updates for a specific trip\n */\n getStopTimeUpdates(filters?: StopTimeUpdateFilters): import('./types/gtfs-rt').StopTimeUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getStopTimeUpdates(this.db, filters, this.stalenessThreshold);\n }\n\n // ==================== Debug Export Methods ====================\n // These methods export all realtime data without staleness filtering\n // for debugging purposes\n\n /**\n * Export all alerts without staleness filtering (for debugging)\n */\n debugExportAllAlerts(): Alert[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllAlerts(this.db);\n }\n\n /**\n * Export all vehicle positions without staleness filtering (for debugging)\n */\n debugExportAllVehiclePositions(): VehiclePosition[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllVehiclePositions(this.db);\n }\n\n /**\n * Export all trip updates without staleness filtering (for debugging)\n */\n debugExportAllTripUpdates(): TripUpdate[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllTripUpdates(this.db);\n }\n\n /**\n * Export all stop time updates without staleness filtering (for debugging)\n * Returns extended type with trip_id and rt_last_updated for debugging purposes\n */\n debugExportAllStopTimeUpdates(): StopTimeUpdateWithMetadata[] {\n if (!this.db) throw new Error('Database not initialized');\n return getAllStopTimeUpdates(this.db);\n }\n}\n","/**\n * SQLite Schema Definitions for GTFS Data\n * Matches required/optional fields from GTFS specification\n */\n\nexport interface TableSchema {\n name: string;\n columns: ColumnDefinition[];\n indexes?: IndexDefinition[];\n}\n\nexport interface ColumnDefinition {\n name: string;\n type: 'TEXT' | 'INTEGER' | 'REAL';\n required: boolean;\n primaryKey?: boolean;\n}\n\nexport interface IndexDefinition {\n name: string;\n columns: string[];\n unique?: boolean;\n}\n\nexport const GTFS_SCHEMA: TableSchema[] = [\n {\n name: 'agency',\n columns: [\n { name: 'agency_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'agency_name', type: 'TEXT', required: true },\n { name: 'agency_url', type: 'TEXT', required: true },\n { name: 'agency_timezone', type: 'TEXT', required: true },\n { name: 'agency_lang', type: 'TEXT', required: false },\n { name: 'agency_phone', type: 'TEXT', required: false },\n { name: 'agency_fare_url', type: 'TEXT', required: false },\n { name: 'agency_email', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'stops',\n columns: [\n { name: 'stop_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'stop_name', type: 'TEXT', required: true },\n { name: 'stop_lat', type: 'REAL', required: true },\n { name: 'stop_lon', type: 'REAL', required: true },\n { name: 'stop_code', type: 'TEXT', required: false },\n { name: 'stop_desc', type: 'TEXT', required: false },\n { name: 'zone_id', type: 'TEXT', required: false },\n { name: 'stop_url', type: 'TEXT', required: false },\n { name: 'location_type', type: 'INTEGER', required: false },\n { name: 'parent_station', type: 'TEXT', required: false },\n { name: 'stop_timezone', type: 'TEXT', required: false },\n { name: 'wheelchair_boarding', type: 'INTEGER', required: false },\n { name: 'level_id', type: 'TEXT', required: false },\n { name: 'platform_code', type: 'TEXT', required: false },\n ],\n indexes: [\n { name: 'idx_stops_stop_code', columns: ['stop_code'] },\n { name: 'idx_stops_stop_name', columns: ['stop_name'] },\n { name: 'idx_stops_parent_station', columns: ['parent_station'] },\n ],\n },\n {\n name: 'routes',\n columns: [\n { name: 'route_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'route_short_name', type: 'TEXT', required: true },\n { name: 'route_long_name', type: 'TEXT', required: true },\n { name: 'route_type', type: 'INTEGER', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'route_desc', type: 'TEXT', required: false },\n { name: 'route_url', type: 'TEXT', required: false },\n { name: 'route_color', type: 'TEXT', required: false },\n { name: 'route_text_color', type: 'TEXT', required: false },\n { name: 'route_sort_order', type: 'INTEGER', required: false },\n { name: 'continuous_pickup', type: 'INTEGER', required: false },\n { name: 'continuous_drop_off', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_routes_agency_id', columns: ['agency_id'] },\n ],\n },\n {\n name: 'trips',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'route_id', type: 'TEXT', required: true },\n { name: 'service_id', type: 'TEXT', required: true },\n { name: 'trip_headsign', type: 'TEXT', required: false },\n { name: 'trip_short_name', type: 'TEXT', required: false },\n { name: 'direction_id', type: 'INTEGER', required: false },\n { name: 'block_id', type: 'TEXT', required: false },\n { name: 'shape_id', type: 'TEXT', required: false },\n { name: 'wheelchair_accessible', type: 'INTEGER', required: false },\n { name: 'bikes_allowed', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_trips_route_id', columns: ['route_id'] },\n { name: 'idx_trips_service_id', columns: ['service_id'] },\n { name: 'idx_trips_route_service', columns: ['route_id', 'service_id'] },\n ],\n },\n {\n name: 'stop_times',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true },\n { name: 'arrival_time', type: 'TEXT', required: true },\n { name: 'departure_time', type: 'TEXT', required: true },\n { name: 'stop_id', type: 'TEXT', required: true },\n { name: 'stop_sequence', type: 'INTEGER', required: true },\n { name: 'stop_headsign', type: 'TEXT', required: false },\n { name: 'pickup_type', type: 'INTEGER', required: false },\n { name: 'drop_off_type', type: 'INTEGER', required: false },\n { name: 'continuous_pickup', type: 'INTEGER', required: false },\n { name: 'continuous_drop_off', type: 'INTEGER', required: false },\n { name: 'shape_dist_traveled', type: 'REAL', required: false },\n { name: 'timepoint', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_stop_times_trip_id', columns: ['trip_id'] },\n { name: 'idx_stop_times_stop_id', columns: ['stop_id'] },\n { name: 'idx_stop_times_trip_sequence', columns: ['trip_id', 'stop_sequence'] },\n ],\n },\n {\n name: 'calendar',\n columns: [\n { name: 'service_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'monday', type: 'INTEGER', required: true },\n { name: 'tuesday', type: 'INTEGER', required: true },\n { name: 'wednesday', type: 'INTEGER', required: true },\n { name: 'thursday', type: 'INTEGER', required: true },\n { name: 'friday', type: 'INTEGER', required: true },\n { name: 'saturday', type: 'INTEGER', required: true },\n { name: 'sunday', type: 'INTEGER', required: true },\n { name: 'start_date', type: 'TEXT', required: true },\n { name: 'end_date', type: 'TEXT', required: true },\n ],\n },\n {\n name: 'calendar_dates',\n columns: [\n { name: 'service_id', type: 'TEXT', required: true },\n { name: 'date', type: 'TEXT', required: true },\n { name: 'exception_type', type: 'INTEGER', required: true },\n ],\n indexes: [\n { name: 'idx_calendar_dates_service_id', columns: ['service_id'] },\n { name: 'idx_calendar_dates_date', columns: ['date'] },\n { name: 'idx_calendar_dates_service_date', columns: ['service_id', 'date'] },\n ],\n },\n {\n name: 'fare_attributes',\n columns: [\n { name: 'fare_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'price', type: 'REAL', required: true },\n { name: 'currency_type', type: 'TEXT', required: true },\n { name: 'payment_method', type: 'INTEGER', required: true },\n { name: 'transfers', type: 'INTEGER', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'transfer_duration', type: 'INTEGER', required: false },\n ],\n },\n {\n name: 'fare_rules',\n columns: [\n { name: 'fare_id', type: 'TEXT', required: true },\n { name: 'route_id', type: 'TEXT', required: false },\n { name: 'origin_id', type: 'TEXT', required: false },\n { name: 'destination_id', type: 'TEXT', required: false },\n { name: 'contains_id', type: 'TEXT', required: false },\n ],\n indexes: [\n { name: 'idx_fare_rules_fare_id', columns: ['fare_id'] },\n { name: 'idx_fare_rules_route_id', columns: ['route_id'] },\n ],\n },\n {\n name: 'shapes',\n columns: [\n { name: 'shape_id', type: 'TEXT', required: true },\n { name: 'shape_pt_lat', type: 'REAL', required: true },\n { name: 'shape_pt_lon', type: 'REAL', required: true },\n { name: 'shape_pt_sequence', type: 'INTEGER', required: true },\n { name: 'shape_dist_traveled', type: 'REAL', required: false },\n ],\n indexes: [\n { name: 'idx_shapes_shape_id', columns: ['shape_id'] },\n { name: 'idx_shapes_shape_sequence', columns: ['shape_id', 'shape_pt_sequence'] },\n ],\n },\n {\n name: 'frequencies',\n columns: [\n { name: 'trip_id', type: 'TEXT', required: true },\n { name: 'start_time', type: 'TEXT', required: true },\n { name: 'end_time', type: 'TEXT', required: true },\n { name: 'headway_secs', type: 'INTEGER', required: true },\n { name: 'exact_times', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_frequencies_trip_id', columns: ['trip_id'] },\n ],\n },\n {\n name: 'transfers',\n columns: [\n { name: 'from_stop_id', type: 'TEXT', required: true },\n { name: 'to_stop_id', type: 'TEXT', required: true },\n { name: 'transfer_type', type: 'INTEGER', required: true },\n { name: 'min_transfer_time', type: 'INTEGER', required: false },\n ],\n indexes: [\n { name: 'idx_transfers_from_stop_id', columns: ['from_stop_id'] },\n { name: 'idx_transfers_to_stop_id', columns: ['to_stop_id'] },\n ],\n },\n {\n name: 'pathways',\n columns: [\n { name: 'pathway_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'from_stop_id', type: 'TEXT', required: true },\n { name: 'to_stop_id', type: 'TEXT', required: true },\n { name: 'pathway_mode', type: 'INTEGER', required: true },\n { name: 'is_bidirectional', type: 'INTEGER', required: true },\n { name: 'length', type: 'REAL', required: false },\n { name: 'traversal_time', type: 'INTEGER', required: false },\n { name: 'stair_count', type: 'INTEGER', required: false },\n { name: 'max_slope', type: 'REAL', required: false },\n { name: 'min_width', type: 'REAL', required: false },\n { name: 'signposted_as', type: 'TEXT', required: false },\n { name: 'reversed_signposted_as', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'levels',\n columns: [\n { name: 'level_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'level_index', type: 'REAL', required: true },\n { name: 'level_name', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'feed_info',\n columns: [\n { name: 'feed_publisher_name', type: 'TEXT', required: true },\n { name: 'feed_publisher_url', type: 'TEXT', required: true },\n { name: 'feed_lang', type: 'TEXT', required: true },\n { name: 'default_lang', type: 'TEXT', required: false },\n { name: 'feed_start_date', type: 'TEXT', required: false },\n { name: 'feed_end_date', type: 'TEXT', required: false },\n { name: 'feed_version', type: 'TEXT', required: false },\n { name: 'feed_contact_email', type: 'TEXT', required: false },\n { name: 'feed_contact_url', type: 'TEXT', required: false },\n ],\n },\n {\n name: 'attributions',\n columns: [\n { name: 'attribution_id', type: 'TEXT', required: true, primaryKey: true },\n { name: 'organization_name', type: 'TEXT', required: true },\n { name: 'agency_id', type: 'TEXT', required: false },\n { name: 'route_id', type: 'TEXT', required: false },\n { name: 'trip_id', type: 'TEXT', required: false },\n { name: 'is_producer', type: 'INTEGER', required: false },\n { name: 'is_operator', type: 'INTEGER', required: false },\n { name: 'is_authority', type: 'INTEGER', required: false },\n { name: 'attribution_url', type: 'TEXT', required: false },\n { name: 'attribution_email', type: 'TEXT', required: false },\n { name: 'attribution_phone', type: 'TEXT', required: false },\n ],\n },\n];\n\n/**\n * Generate CREATE TABLE SQL statement from schema definition\n */\nexport function generateCreateTableSQL(schema: TableSchema): string {\n const columns = schema.columns.map((col) => {\n const parts = [col.name, col.type];\n if (col.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n if (col.required && !col.primaryKey) {\n parts.push('NOT NULL');\n }\n return parts.join(' ');\n });\n\n return `CREATE TABLE IF NOT EXISTS ${schema.name} (${columns.join(', ')})`;\n}\n\n/**\n * Generate CREATE INDEX SQL statements from schema definition\n */\nexport function generateCreateIndexSQL(schema: TableSchema): string[] {\n if (!schema.indexes) {\n return [];\n }\n\n return schema.indexes.map((idx) => {\n const unique = idx.unique ? 'UNIQUE ' : '';\n const columns = idx.columns.join(', ');\n return `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name} (${columns})`;\n });\n}\n\n/**\n * Initialize all GTFS tables in the database\n */\nexport function getAllCreateStatements(): string[] {\n const statements: string[] = [];\n\n for (const schema of GTFS_SCHEMA) {\n statements.push(generateCreateTableSQL(schema));\n statements.push(...generateCreateIndexSQL(schema));\n }\n\n return statements;\n}\n","/**\n * ZIP Loader for GTFS files\n */\n\nimport JSZip from 'jszip';\n\nexport interface GTFSFiles {\n [filename: string]: string;\n}\n\n/**\n * Fetch and extract GTFS ZIP file\n * Works in both browser and Node.js environments\n */\nexport async function loadGTFSZip(source: string | ArrayBuffer | Uint8Array): Promise {\n let zipData: ArrayBuffer | Uint8Array;\n\n // If source is a string, treat it as URL or file path\n if (typeof source === 'string') {\n zipData = await fetchZip(source);\n } else {\n zipData = source;\n }\n\n // Load ZIP file\n const zip = await JSZip.loadAsync(zipData);\n\n // Extract all .txt files (GTFS files)\n const files: GTFSFiles = {};\n const filePromises: Promise[] = [];\n\n zip.forEach((relativePath, file) => {\n // Only process .txt files\n if (!file.dir && relativePath.endsWith('.txt')) {\n const fileName = relativePath.split('/').pop() || relativePath;\n filePromises.push(\n file.async('string').then((content) => {\n files[fileName] = content;\n })\n );\n }\n });\n\n await Promise.all(filePromises);\n\n return files;\n}\n\n/**\n * Fetch ZIP file from URL or file path\n */\nasync function fetchZip(source: string): Promise {\n // Check if source is a URL\n const isUrl = source.startsWith('http://') || source.startsWith('https://');\n\n // For URLs, always use fetch\n if (isUrl) {\n if (typeof fetch !== 'undefined') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS ZIP: ${response.status} ${response.statusText}`);\n }\n return await response.arrayBuffer();\n }\n throw new Error('fetch is not available to load URL');\n }\n\n // For non-URLs, try Node.js fs first, fall back to fetch for browser\n // Check if we're in Node.js environment\n const isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n if (isNode) {\n // In Node.js, treat as file path\n try {\n const fs = await import('fs');\n const buffer = await fs.promises.readFile(source);\n return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);\n } catch (error) {\n throw new Error(`Failed to read GTFS ZIP file: ${error}`);\n }\n }\n\n // In browser, treat as relative URL and use fetch\n if (typeof fetch !== 'undefined') {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS ZIP: ${response.status} ${response.statusText}`);\n }\n return await response.arrayBuffer();\n }\n\n throw new Error('No method available to load ZIP file');\n}\n\n/**\n * Mapping of GTFS file names to database table names\n */\nexport const GTFS_FILE_MAPPING: Record = {\n 'agency.txt': 'agency',\n 'stops.txt': 'stops',\n 'routes.txt': 'routes',\n 'trips.txt': 'trips',\n 'stop_times.txt': 'stop_times',\n 'calendar.txt': 'calendar',\n 'calendar_dates.txt': 'calendar_dates',\n 'fare_attributes.txt': 'fare_attributes',\n 'fare_rules.txt': 'fare_rules',\n 'shapes.txt': 'shapes',\n 'frequencies.txt': 'frequencies',\n 'transfers.txt': 'transfers',\n 'pathways.txt': 'pathways',\n 'levels.txt': 'levels',\n 'feed_info.txt': 'feed_info',\n 'attributions.txt': 'attributions',\n};\n","/**\n * CSV Parser for GTFS files using papaparse\n */\n\nimport Papa from 'papaparse';\n\nexport interface ParsedCSV {\n headers: string[];\n rows: Record[];\n}\n\n/**\n * Parse CSV text into structured data using papaparse\n */\nexport function parseCSV(text: string): ParsedCSV {\n const result = Papa.parse(text, {\n header: true,\n skipEmptyLines: true,\n transformHeader: (header) => header.trim(),\n transform: (value) => value.trim(),\n });\n\n if (result.errors.length > 0) {\n console.warn('CSV parsing warnings:', result.errors);\n }\n\n const headers = result.meta.fields || [];\n const rows = result.data as Record[];\n\n return { headers, rows };\n}\n\n/**\n * Convert parsed row to typed object with proper type conversions\n */\nexport function convertRowTypes(\n row: Record,\n columnTypes: Record\n): Record {\n const result: Record = {};\n\n // Iterate over all expected columns, not just the ones present in the row\n for (const [key, type] of Object.entries(columnTypes)) {\n const value = row[key];\n\n // Handle missing or empty values\n if (value === undefined || value === null || value === '') {\n result[key] = null;\n continue;\n }\n\n if (type === 'INTEGER') {\n const parsed = parseInt(value, 10);\n result[key] = isNaN(parsed) ? null : parsed;\n } else if (type === 'REAL') {\n const parsed = parseFloat(value);\n result[key] = isNaN(parsed) ? null : parsed;\n } else {\n result[key] = value;\n }\n }\n\n return result;\n}\n","/**\n * Data Loader - Loads GTFS data into SQLite database\n */\n\nimport type { Database } from 'sql.js';\nimport { parseCSV, convertRowTypes } from './csv-parser';\nimport { GTFS_SCHEMA, type TableSchema } from '../schema/schema';\nimport type { GTFSFiles } from './zip-loader';\n\n/**\n * Load GTFS files into SQLite database\n * @param skipFiles - Optional array of filenames to skip importing (tables will be created but remain empty)\n */\nexport async function loadGTFSData(db: Database, files: GTFSFiles, skipFiles?: string[]): Promise {\n // Map of file names to table schemas\n const fileToSchema: Map = new Map();\n for (const schema of GTFS_SCHEMA) {\n // Match file names like agency.txt to table name 'agency'\n fileToSchema.set(`${schema.name}.txt`, schema);\n }\n\n // Normalize skipFiles to a Set for faster lookup\n const skipSet = new Set(skipFiles?.map(f => f.toLowerCase()) || []);\n\n // Process each file\n for (const [fileName, content] of Object.entries(files)) {\n const schema = fileToSchema.get(fileName);\n if (!schema) {\n // Skip unknown files\n continue;\n }\n\n // Skip if in skip list\n if (skipSet.has(fileName.toLowerCase())) {\n console.log(`Skipping import of ${fileName} (table ${schema.name} created but empty)`);\n continue;\n }\n\n await loadTableData(db, schema, content);\n }\n}\n\n/**\n * Load data for a single table\n */\nasync function loadTableData(db: Database, schema: TableSchema, csvContent: string): Promise {\n const { headers, rows } = parseCSV(csvContent);\n\n if (rows.length === 0) {\n return;\n }\n\n // Build column type map\n const columnTypes: Record = {};\n for (const col of schema.columns) {\n columnTypes[col.name] = col.type;\n }\n\n // Prepare INSERT statement\n const columns = headers.filter((h) => schema.columns.some((c) => c.name === h));\n const placeholders = columns.map(() => '?').join(', ');\n const insertSQL = `INSERT INTO ${schema.name} (${columns.join(', ')}) VALUES (${placeholders})`;\n\n const stmt = db.prepare(insertSQL);\n\n // Insert each row\n for (const row of rows) {\n const typedRow = convertRowTypes(row, columnTypes);\n const values = columns.map((col) => {\n const value = typedRow[col];\n // Handle undefined, null, or empty values\n return value === null || value === undefined ? null : value;\n });\n\n try {\n stmt.run(values);\n } catch (error) {\n console.error(`Error inserting row into ${schema.name}:`, error);\n console.error('Row data:', row);\n throw error;\n }\n }\n\n stmt.free();\n}\n","import type { Database } from 'sql.js';\n\n/**\n * Create GTFS Realtime tables in the database\n */\nexport function createRealtimeTables(db: Database): void {\n // Alerts table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_alerts (\n id TEXT PRIMARY KEY,\n active_period TEXT, -- JSON array of TimeRange objects\n informed_entity TEXT, -- JSON array of EntitySelector objects\n cause INTEGER,\n effect INTEGER,\n url TEXT, -- JSON TranslatedString\n header_text TEXT, -- JSON TranslatedString\n description_text TEXT, -- JSON TranslatedString\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n // Create index on rt_last_updated for staleness filtering\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_alerts_updated ON rt_alerts(rt_last_updated)');\n\n // Vehicle Positions table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_vehicle_positions (\n trip_id TEXT PRIMARY KEY,\n route_id TEXT,\n vehicle_id TEXT,\n vehicle_label TEXT,\n vehicle_license_plate TEXT,\n latitude REAL,\n longitude REAL,\n bearing REAL,\n odometer REAL,\n speed REAL,\n current_stop_sequence INTEGER,\n stop_id TEXT,\n current_status INTEGER,\n timestamp INTEGER,\n congestion_level INTEGER,\n occupancy_status INTEGER,\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_vehicle_positions_updated ON rt_vehicle_positions(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_vehicle_positions_route ON rt_vehicle_positions(route_id)');\n\n // Trip Updates table\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_trip_updates (\n trip_id TEXT PRIMARY KEY,\n route_id TEXT,\n vehicle_id TEXT,\n vehicle_label TEXT,\n vehicle_license_plate TEXT,\n timestamp INTEGER,\n delay INTEGER,\n schedule_relationship INTEGER,\n rt_last_updated INTEGER NOT NULL\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_trip_updates_updated ON rt_trip_updates(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_trip_updates_route ON rt_trip_updates(route_id)');\n\n // Stop Time Updates table (child of trip updates)\n db.run(`\n CREATE TABLE IF NOT EXISTS rt_stop_time_updates (\n trip_id TEXT NOT NULL,\n stop_sequence INTEGER,\n stop_id TEXT,\n arrival_delay INTEGER,\n arrival_time INTEGER,\n arrival_uncertainty INTEGER,\n departure_delay INTEGER,\n departure_time INTEGER,\n departure_uncertainty INTEGER,\n schedule_relationship INTEGER,\n rt_last_updated INTEGER NOT NULL,\n PRIMARY KEY (trip_id, stop_sequence),\n FOREIGN KEY (trip_id) REFERENCES rt_trip_updates(trip_id) ON DELETE CASCADE\n )\n `);\n\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_stop_time_updates_updated ON rt_stop_time_updates(rt_last_updated)');\n db.run('CREATE INDEX IF NOT EXISTS idx_rt_stop_time_updates_stop ON rt_stop_time_updates(stop_id)');\n}\n\n/**\n * Clear all realtime data from the database\n */\nexport function clearRealtimeData(db: Database): void {\n db.run('DELETE FROM rt_alerts');\n db.run('DELETE FROM rt_vehicle_positions');\n db.run('DELETE FROM rt_trip_updates');\n db.run('DELETE FROM rt_stop_time_updates');\n}\n","import type { Database } from 'sql.js';\nimport protobuf from 'protobufjs';\n\n// Types for protobuf decoded objects\ninterface ProtobufTranslation {\n text: string;\n language?: string;\n}\n\ninterface ProtobufTranslatedString {\n translation: ProtobufTranslation[];\n}\n\ninterface ProtobufAlert {\n id: string;\n activePeriod?: unknown[];\n active_period?: unknown[];\n informedEntity?: unknown[];\n informed_entity?: unknown[];\n cause?: number;\n effect?: number;\n url?: ProtobufTranslatedString;\n headerText?: ProtobufTranslatedString;\n header_text?: ProtobufTranslatedString;\n descriptionText?: ProtobufTranslatedString;\n description_text?: ProtobufTranslatedString;\n}\n\ninterface ProtobufTripDescriptor {\n tripId?: string;\n trip_id?: string;\n routeId?: string;\n route_id?: string;\n scheduleRelationship?: number;\n schedule_relationship?: number;\n}\n\ninterface ProtobufVehicleDescriptor {\n id?: string;\n label?: string;\n licensePlate?: string;\n license_plate?: string;\n}\n\ninterface ProtobufPosition {\n latitude?: number;\n longitude?: number;\n bearing?: number;\n odometer?: number;\n speed?: number;\n}\n\ninterface ProtobufVehiclePosition {\n trip?: ProtobufTripDescriptor;\n vehicle?: ProtobufVehicleDescriptor;\n position?: ProtobufPosition;\n currentStopSequence?: number;\n current_stop_sequence?: number;\n stopId?: string;\n stop_id?: string;\n currentStatus?: number;\n current_status?: number;\n timestamp?: number;\n congestionLevel?: number;\n congestion_level?: number;\n occupancyStatus?: number;\n occupancy_status?: number;\n}\n\ninterface ProtobufStopTimeEvent {\n delay?: number;\n time?: number;\n uncertainty?: number;\n}\n\ninterface ProtobufStopTimeUpdate {\n stopSequence?: number;\n stop_sequence?: number;\n stopId?: string;\n stop_id?: string;\n scheduleRelationship?: number;\n schedule_relationship?: number;\n arrival?: ProtobufStopTimeEvent;\n departure?: ProtobufStopTimeEvent;\n}\n\ninterface ProtobufTripUpdate {\n trip?: ProtobufTripDescriptor;\n vehicle?: ProtobufVehicleDescriptor;\n timestamp?: number;\n delay?: number;\n stopTimeUpdate?: ProtobufStopTimeUpdate[];\n stop_time_update?: ProtobufStopTimeUpdate[];\n}\n\n// GTFS Realtime protobuf definition (v2.0)\n// Source: https://github.com/google/transit/blob/master/gtfs-realtime/proto/gtfs-realtime.proto\nconst GTFS_RT_PROTO = `\nsyntax = \"proto2\";\noption java_package = \"com.google.transit.realtime\";\npackage transit_realtime;\n\nmessage FeedMessage {\n required FeedHeader header = 1;\n repeated FeedEntity entity = 2;\n}\n\nmessage FeedHeader {\n required string gtfs_realtime_version = 1;\n enum Incrementality {\n FULL_DATASET = 0;\n DIFFERENTIAL = 1;\n }\n optional Incrementality incrementality = 2 [default = FULL_DATASET];\n optional uint64 timestamp = 3;\n}\n\nmessage FeedEntity {\n required string id = 1;\n optional bool is_deleted = 2 [default = false];\n optional TripUpdate trip_update = 3;\n optional VehiclePosition vehicle = 4;\n optional Alert alert = 5;\n}\n\nmessage TripUpdate {\n required TripDescriptor trip = 1;\n optional VehicleDescriptor vehicle = 3;\n repeated StopTimeUpdate stop_time_update = 2;\n optional uint64 timestamp = 4;\n optional int32 delay = 5;\n\n message StopTimeEvent {\n optional int32 delay = 1;\n optional int64 time = 2;\n optional int32 uncertainty = 3;\n }\n\n message StopTimeUpdate {\n optional uint32 stop_sequence = 1;\n optional string stop_id = 4;\n optional StopTimeEvent arrival = 2;\n optional StopTimeEvent departure = 3;\n enum ScheduleRelationship {\n SCHEDULED = 0;\n SKIPPED = 1;\n NO_DATA = 2;\n }\n optional ScheduleRelationship schedule_relationship = 5 [default = SCHEDULED];\n }\n}\n\nmessage VehiclePosition {\n optional TripDescriptor trip = 1;\n optional VehicleDescriptor vehicle = 8;\n optional Position position = 2;\n optional uint32 current_stop_sequence = 3;\n optional string stop_id = 7;\n enum VehicleStopStatus {\n INCOMING_AT = 0;\n STOPPED_AT = 1;\n IN_TRANSIT_TO = 2;\n }\n optional VehicleStopStatus current_status = 4 [default = IN_TRANSIT_TO];\n optional uint64 timestamp = 5;\n enum CongestionLevel {\n UNKNOWN_CONGESTION_LEVEL = 0;\n RUNNING_SMOOTHLY = 1;\n STOP_AND_GO = 2;\n CONGESTION = 3;\n SEVERE_CONGESTION = 4;\n }\n optional CongestionLevel congestion_level = 6;\n enum OccupancyStatus {\n EMPTY = 0;\n MANY_SEATS_AVAILABLE = 1;\n FEW_SEATS_AVAILABLE = 2;\n STANDING_ROOM_ONLY = 3;\n CRUSHED_STANDING_ROOM_ONLY = 4;\n FULL = 5;\n NOT_ACCEPTING_PASSENGERS = 6;\n }\n optional OccupancyStatus occupancy_status = 9;\n}\n\nmessage Alert {\n repeated TimeRange active_period = 1;\n repeated EntitySelector informed_entity = 5;\n\n enum Cause {\n UNKNOWN_CAUSE = 1;\n OTHER_CAUSE = 2;\n TECHNICAL_PROBLEM = 3;\n STRIKE = 4;\n DEMONSTRATION = 5;\n ACCIDENT = 6;\n HOLIDAY = 7;\n WEATHER = 8;\n MAINTENANCE = 9;\n CONSTRUCTION = 10;\n POLICE_ACTIVITY = 11;\n MEDICAL_EMERGENCY = 12;\n }\n optional Cause cause = 6 [default = UNKNOWN_CAUSE];\n\n enum Effect {\n NO_SERVICE = 1;\n REDUCED_SERVICE = 2;\n SIGNIFICANT_DELAYS = 3;\n DETOUR = 4;\n ADDITIONAL_SERVICE = 5;\n MODIFIED_SERVICE = 6;\n OTHER_EFFECT = 7;\n UNKNOWN_EFFECT = 8;\n STOP_MOVED = 9;\n NO_EFFECT = 10;\n ACCESSIBILITY_ISSUE = 11;\n }\n optional Effect effect = 7 [default = UNKNOWN_EFFECT];\n optional TranslatedString url = 8;\n optional TranslatedString header_text = 10;\n optional TranslatedString description_text = 11;\n}\n\nmessage TimeRange {\n optional uint64 start = 1;\n optional uint64 end = 2;\n}\n\nmessage Position {\n required float latitude = 1;\n required float longitude = 2;\n optional float bearing = 3;\n optional double odometer = 4;\n optional float speed = 5;\n}\n\nmessage TripDescriptor {\n optional string trip_id = 1;\n optional string route_id = 5;\n optional uint32 direction_id = 6;\n optional string start_time = 2;\n optional string start_date = 3;\n enum ScheduleRelationship {\n SCHEDULED = 0;\n ADDED = 1;\n UNSCHEDULED = 2;\n CANCELED = 3;\n }\n optional ScheduleRelationship schedule_relationship = 4;\n}\n\nmessage VehicleDescriptor {\n optional string id = 1;\n optional string label = 2;\n optional string license_plate = 3;\n}\n\nmessage EntitySelector {\n optional string agency_id = 1;\n optional string route_id = 2;\n optional int32 route_type = 3;\n optional TripDescriptor trip = 4;\n optional string stop_id = 5;\n}\n\nmessage TranslatedString {\n message Translation {\n required string text = 1;\n optional string language = 2;\n }\n repeated Translation translation = 1;\n}\n`;\n\n// Fetch protobuf data from URL or local file\n// Uses fetch API (available in modern browsers and Node.js 18+)\nasync function fetchProtobuf(source: string): Promise {\n const isUrl = source.startsWith('http://') || source.startsWith('https://');\n\n if (isUrl) {\n // Fetch from URL\n const response = await fetch(source, {\n headers: {\n 'Accept': 'application/x-protobuf, application/octet-stream'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS-RT feed from ${source}: ${response.status} ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n }\n\n // Read from local file (Node.js only)\n const isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n if (isNode) {\n try {\n const fs = await import('fs');\n const buffer = await fs.promises.readFile(source);\n return new Uint8Array(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));\n } catch (error) {\n throw new Error(`Failed to read GTFS-RT file from ${source}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // In browser, try fetch for relative paths\n const response = await fetch(source, {\n headers: {\n 'Accept': 'application/x-protobuf, application/octet-stream'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch GTFS-RT feed from ${source}: ${response.status} ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n}\n\n// Load and parse GTFS-RT protobuf schema\nlet gtfsRtRoot: protobuf.Root | null = null;\n\nfunction loadGtfsRtProto(): protobuf.Root {\n if (!gtfsRtRoot) {\n gtfsRtRoot = protobuf.parse(GTFS_RT_PROTO).root;\n }\n return gtfsRtRoot;\n}\n\n// Convert camelCase object keys to snake_case\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);\n}\n\nfunction convertKeysToSnakeCase(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToSnakeCase);\n }\n if (typeof obj === 'object') {\n const result: Record = {};\n for (const key in obj as Record) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n const snakeKey = camelToSnake(key);\n result[snakeKey] = convertKeysToSnakeCase((obj as Record)[key]);\n }\n }\n return result;\n }\n return obj;\n}\n\n// Parse TranslatedString to JSON\nfunction parseTranslatedString(ts: ProtobufTranslatedString | undefined): string | null {\n if (!ts || !ts.translation || ts.translation.length === 0) {\n return null;\n }\n return JSON.stringify({\n translation: ts.translation.map((t) => ({\n text: t.text,\n language: t.language || undefined\n }))\n });\n}\n\n// Insert alerts into database\nfunction insertAlerts(db: Database, alerts: ProtobufAlert[], timestamp: number): void {\n const stmt = db.prepare(`\n INSERT OR REPLACE INTO rt_alerts (\n id, active_period, informed_entity, cause, effect,\n url, header_text, description_text, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const alert of alerts) {\n // Map camelCase protobuf fields to snake_case\n const activePeriod = alert.activePeriod || alert.active_period || [];\n const informedEntity = alert.informedEntity || alert.informed_entity || [];\n const headerText = alert.headerText || alert.header_text;\n const descriptionText = alert.descriptionText || alert.description_text;\n\n // Convert nested objects to snake_case\n const activePeriodSnake = convertKeysToSnakeCase(activePeriod);\n const informedEntitySnake = convertKeysToSnakeCase(informedEntity);\n\n stmt.run([\n alert.id,\n JSON.stringify(activePeriodSnake),\n JSON.stringify(informedEntitySnake),\n alert.cause || null,\n alert.effect || null,\n parseTranslatedString(alert.url),\n parseTranslatedString(headerText),\n parseTranslatedString(descriptionText),\n timestamp\n ]);\n }\n\n stmt.free();\n}\n\n// Insert vehicle positions into database\nfunction insertVehiclePositions(db: Database, positions: ProtobufVehiclePosition[], timestamp: number): void {\n const stmt = db.prepare(`\n INSERT OR REPLACE INTO rt_vehicle_positions (\n trip_id, route_id, vehicle_id, vehicle_label, vehicle_license_plate,\n latitude, longitude, bearing, odometer, speed,\n current_stop_sequence, stop_id, current_status, timestamp,\n congestion_level, occupancy_status, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const vp of positions) {\n const trip = vp.trip;\n if (!trip || !(trip.tripId || trip.trip_id)) continue;\n\n // Map camelCase protobuf fields to snake_case\n const tripId = (trip.tripId || trip.trip_id)!; // Safe: checked above\n const routeId = trip.routeId || trip.route_id;\n const vehicleId = vp.vehicle?.id;\n const vehicleLabel = vp.vehicle?.label;\n const vehicleLicensePlate = vp.vehicle?.licensePlate || vp.vehicle?.license_plate;\n const currentStopSequence = vp.currentStopSequence || vp.current_stop_sequence;\n const stopId = vp.stopId || vp.stop_id;\n const currentStatus = vp.currentStatus || vp.current_status;\n const congestionLevel = vp.congestionLevel || vp.congestion_level;\n const occupancyStatus = vp.occupancyStatus || vp.occupancy_status;\n\n stmt.run([\n tripId,\n routeId || null,\n vehicleId || null,\n vehicleLabel || null,\n vehicleLicensePlate || null,\n vp.position?.latitude || null,\n vp.position?.longitude || null,\n vp.position?.bearing || null,\n vp.position?.odometer || null,\n vp.position?.speed || null,\n currentStopSequence || null,\n stopId || null,\n currentStatus || null,\n vp.timestamp || null,\n congestionLevel || null,\n occupancyStatus || null,\n timestamp\n ]);\n }\n\n stmt.free();\n}\n\n// Insert trip updates into database\nfunction insertTripUpdates(db: Database, updates: ProtobufTripUpdate[], timestamp: number): void {\n const tripStmt = db.prepare(`\n INSERT OR REPLACE INTO rt_trip_updates (\n trip_id, route_id, vehicle_id, vehicle_label, vehicle_license_plate,\n timestamp, delay, schedule_relationship, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n const stopTimeStmt = db.prepare(`\n INSERT OR REPLACE INTO rt_stop_time_updates (\n trip_id, stop_sequence, stop_id,\n arrival_delay, arrival_time, arrival_uncertainty,\n departure_delay, departure_time, departure_uncertainty,\n schedule_relationship, rt_last_updated\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `);\n\n for (const tu of updates) {\n const trip = tu.trip;\n if (!trip || !(trip.tripId || trip.trip_id)) continue;\n\n // Map camelCase protobuf fields to snake_case\n const tripId = (trip.tripId || trip.trip_id)!; // Safe: checked above\n const routeId = trip.routeId || trip.route_id;\n const vehicleId = tu.vehicle?.id;\n const vehicleLabel = tu.vehicle?.label;\n const vehicleLicensePlate = tu.vehicle?.licensePlate || tu.vehicle?.license_plate;\n const scheduleRelationship = trip.scheduleRelationship || trip.schedule_relationship;\n const stopTimeUpdate = tu.stopTimeUpdate || tu.stop_time_update;\n\n // Insert trip update\n tripStmt.run([\n tripId,\n routeId || null,\n vehicleId || null,\n vehicleLabel || null,\n vehicleLicensePlate || null,\n tu.timestamp || null,\n tu.delay || null,\n scheduleRelationship || null,\n timestamp\n ]);\n\n // Insert stop time updates\n if (stopTimeUpdate) {\n for (const stu of stopTimeUpdate) {\n const stopSequence = stu.stopSequence || stu.stop_sequence;\n const stopId = stu.stopId || stu.stop_id;\n const scheduleRel = stu.scheduleRelationship || stu.schedule_relationship;\n\n stopTimeStmt.run([\n tripId,\n stopSequence || null,\n stopId || null,\n stu.arrival?.delay || null,\n stu.arrival?.time || null,\n stu.arrival?.uncertainty || null,\n stu.departure?.delay || null,\n stu.departure?.time || null,\n stu.departure?.uncertainty || null,\n scheduleRel || null,\n timestamp\n ]);\n }\n }\n }\n\n tripStmt.free();\n stopTimeStmt.free();\n}\n\n/**\n * Fetch and load GTFS Realtime data from multiple feed URLs\n * All feeds are fetched in parallel. If any feed fails, an error is thrown.\n */\nexport async function loadRealtimeData(db: Database, feedUrls: string[]): Promise {\n const root = loadGtfsRtProto();\n const FeedMessage = root.lookupType('transit_realtime.FeedMessage');\n\n // Fetch all feeds in parallel\n const fetchPromises = feedUrls.map(async (url) => {\n try {\n const data = await fetchProtobuf(url);\n const message = FeedMessage.decode(data);\n return FeedMessage.toObject(message, {\n longs: Number,\n enums: Number,\n bytes: String,\n defaults: false,\n arrays: true,\n objects: true,\n oneofs: true\n });\n } catch (error) {\n throw new Error(`Failed to fetch or parse GTFS-RT feed from ${url}: ${error instanceof Error ? error.message : String(error)}`);\n }\n });\n\n // Wait for all feeds - if any fails, this will throw\n const feeds = await Promise.all(fetchPromises);\n\n // Current timestamp for staleness tracking\n const now = Math.floor(Date.now() / 1000);\n\n // Collect all entities by type\n const allAlerts: ProtobufAlert[] = [];\n const allVehiclePositions: ProtobufVehiclePosition[] = [];\n const allTripUpdates: ProtobufTripUpdate[] = [];\n\n for (const feed of feeds) {\n if (!feed.entity) continue;\n\n for (const entity of feed.entity) {\n if (entity.alert) {\n allAlerts.push({ id: entity.id, ...entity.alert });\n }\n if (entity.vehicle) {\n allVehiclePositions.push(entity.vehicle);\n }\n if (entity.tripUpdate) {\n allTripUpdates.push(entity.tripUpdate);\n }\n }\n }\n\n // Insert into database\n if (allAlerts.length > 0) {\n insertAlerts(db, allAlerts, now);\n }\n if (allVehiclePositions.length > 0) {\n insertVehiclePositions(db, allVehiclePositions, now);\n }\n if (allTripUpdates.length > 0) {\n insertTripUpdates(db, allTripUpdates, now);\n }\n}\n","/**\n * Agency Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Agency } from '../types/gtfs';\n\nexport interface AgencyFilters {\n agencyId?: string | string[];\n limit?: number;\n}\n\n/**\n * Get agencies with optional filters\n * - Filters support both single values and arrays\n */\nexport function getAgencies(db: Database, filters: AgencyFilters = {}): Agency[] {\n const { agencyId, limit } = filters;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (agencyId) {\n const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId];\n if (agencyIds.length > 0) {\n const placeholders = agencyIds.map(() => '?').join(', ');\n conditions.push(`agency_id IN (${placeholders})`);\n params.push(...agencyIds);\n }\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM agency';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY agency_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const agencies: Agency[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n agencies.push(rowToAgency(row));\n }\n\n stmt.free();\n return agencies;\n}\n\n/**\n * Convert database row to Agency object\n */\nfunction rowToAgency(row: Record): Agency {\n return {\n agency_id: String(row.agency_id),\n agency_name: String(row.agency_name),\n agency_url: String(row.agency_url),\n agency_timezone: String(row.agency_timezone),\n agency_lang: row.agency_lang ? String(row.agency_lang) : undefined,\n agency_phone: row.agency_phone ? String(row.agency_phone) : undefined,\n agency_fare_url: row.agency_fare_url ? String(row.agency_fare_url) : undefined,\n agency_email: row.agency_email ? String(row.agency_email) : undefined,\n };\n}\n","/**\n * Stop Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Stop } from '../types/gtfs';\n\nexport interface StopFilters {\n stopId?: string | string[];\n stopCode?: string | string[];\n name?: string;\n tripId?: string | string[];\n limit?: number;\n}\n\n/**\n * Get stops with optional filters\n * - Filters support both single values and arrays\n * - Use name filter for partial name matching\n * - Use tripId filter to get stops for a specific trip (ordered by stop_sequence)\n */\nexport function getStops(db: Database, filters: StopFilters = {}): Stop[] {\n const { stopId, stopCode, name, tripId, limit } = filters;\n\n // Handle special case: get stops by trip (requires JOIN)\n if (tripId) {\n const tripIds = Array.isArray(tripId) ? tripId : [tripId];\n if (tripIds.length === 0) return [];\n\n const placeholders = tripIds.map(() => '?').join(', ');\n const stmt = db.prepare(`\n SELECT s.* FROM stops s\n INNER JOIN stop_times st ON s.stop_id = st.stop_id\n WHERE st.trip_id IN (${placeholders})\n ORDER BY st.stop_sequence\n `);\n stmt.bind(tripIds);\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n }\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (stopId) {\n const stopIds = Array.isArray(stopId) ? stopId : [stopId];\n if (stopIds.length > 0) {\n const placeholders = stopIds.map(() => '?').join(', ');\n conditions.push(`stop_id IN (${placeholders})`);\n params.push(...stopIds);\n }\n }\n\n if (stopCode) {\n const stopCodes = Array.isArray(stopCode) ? stopCode : [stopCode];\n if (stopCodes.length > 0) {\n const placeholders = stopCodes.map(() => '?').join(', ');\n conditions.push(`stop_code IN (${placeholders})`);\n params.push(...stopCodes);\n }\n }\n\n if (name) {\n conditions.push('stop_name LIKE ?');\n params.push(`%${name}%`);\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM stops';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY stop_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stops: Stop[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stops.push(rowToStop(row));\n }\n\n stmt.free();\n return stops;\n}\n\n/**\n * Search stops by name (case-insensitive, partial match)\n * This is a convenience method for name-based searches\n */\nexport function searchStopsByName(db: Database, name: string, limit = 50): Stop[] {\n return getStops(db, { name, limit });\n}\n\n/**\n * Convert database row to Stop object\n */\nfunction rowToStop(row: Record): Stop {\n return {\n stop_id: String(row.stop_id),\n stop_name: String(row.stop_name),\n stop_lat: Number(row.stop_lat),\n stop_lon: Number(row.stop_lon),\n stop_code: row.stop_code ? String(row.stop_code) : undefined,\n stop_desc: row.stop_desc ? String(row.stop_desc) : undefined,\n zone_id: row.zone_id ? String(row.zone_id) : undefined,\n stop_url: row.stop_url ? String(row.stop_url) : undefined,\n location_type: row.location_type !== null ? Number(row.location_type) : undefined,\n parent_station: row.parent_station ? String(row.parent_station) : undefined,\n stop_timezone: row.stop_timezone ? String(row.stop_timezone) : undefined,\n wheelchair_boarding: row.wheelchair_boarding !== null ? Number(row.wheelchair_boarding) : undefined,\n level_id: row.level_id ? String(row.level_id) : undefined,\n platform_code: row.platform_code ? String(row.platform_code) : undefined,\n };\n}\n","/**\n * Route Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Route } from '../types/gtfs';\n\nexport interface RouteFilters {\n routeId?: string | string[];\n agencyId?: string | string[];\n limit?: number;\n}\n\n/**\n * Get routes with optional filters\n * - Filters support both single values and arrays\n */\nexport function getRoutes(db: Database, filters: RouteFilters = {}): Route[] {\n const { routeId, agencyId, limit } = filters;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (routeId) {\n const routeIds = Array.isArray(routeId) ? routeId : [routeId];\n if (routeIds.length > 0) {\n const placeholders = routeIds.map(() => '?').join(', ');\n conditions.push(`route_id IN (${placeholders})`);\n params.push(...routeIds);\n }\n }\n\n if (agencyId) {\n const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId];\n if (agencyIds.length > 0) {\n const placeholders = agencyIds.map(() => '?').join(', ');\n conditions.push(`agency_id IN (${placeholders})`);\n params.push(...agencyIds);\n }\n }\n\n // Build SQL query\n let sql = 'SELECT * FROM routes';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY route_short_name, route_long_name';\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const routes: Route[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n routes.push(rowToRoute(row));\n }\n\n stmt.free();\n return routes;\n}\n\n/**\n * Convert database row to Route object\n */\nfunction rowToRoute(row: Record): Route {\n return {\n route_id: String(row.route_id),\n route_short_name: String(row.route_short_name),\n route_long_name: String(row.route_long_name),\n route_type: Number(row.route_type),\n agency_id: row.agency_id ? String(row.agency_id) : undefined,\n route_desc: row.route_desc ? String(row.route_desc) : undefined,\n route_url: row.route_url ? String(row.route_url) : undefined,\n route_color: row.route_color ? String(row.route_color) : undefined,\n route_text_color: row.route_text_color ? String(row.route_text_color) : undefined,\n route_sort_order: row.route_sort_order !== null ? Number(row.route_sort_order) : undefined,\n continuous_pickup: row.continuous_pickup !== null ? Number(row.continuous_pickup) : undefined,\n continuous_drop_off: row.continuous_drop_off !== null ? Number(row.continuous_drop_off) : undefined,\n };\n}\n","/**\n * Calendar Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Calendar, CalendarDate } from '../types/gtfs';\n\n/**\n * Get active service IDs for a given date\n */\nexport function getActiveServiceIds(db: Database, date: string): string[] {\n const serviceIds = new Set();\n\n // Parse date (format: YYYYMMDD)\n const year = parseInt(date.substring(0, 4));\n const month = parseInt(date.substring(4, 6));\n const day = parseInt(date.substring(6, 8));\n const dateObj = new Date(year, month - 1, day);\n const dayOfWeek = dateObj.getDay(); // 0 = Sunday, 1 = Monday, etc.\n\n // Map day of week to GTFS calendar field\n const dayFields = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];\n const dayField = dayFields[dayOfWeek];\n\n // Check calendar.txt for regular service\n const calendarStmt = db.prepare(\n `SELECT service_id FROM calendar\n WHERE ${dayField} = 1\n AND start_date <= ?\n AND end_date >= ?`\n );\n calendarStmt.bind([date, date]);\n\n while (calendarStmt.step()) {\n const row = calendarStmt.getAsObject() as { service_id: string };\n serviceIds.add(row.service_id);\n }\n calendarStmt.free();\n\n // Check calendar_dates.txt for exceptions\n const exceptionsStmt = db.prepare('SELECT service_id, exception_type FROM calendar_dates WHERE date = ?');\n exceptionsStmt.bind([date]);\n\n while (exceptionsStmt.step()) {\n const row = exceptionsStmt.getAsObject() as { service_id: string; exception_type: number };\n if (row.exception_type === 1) {\n // Service added\n serviceIds.add(row.service_id);\n } else if (row.exception_type === 2) {\n // Service removed\n serviceIds.delete(row.service_id);\n }\n }\n exceptionsStmt.free();\n\n return Array.from(serviceIds);\n}\n\n/**\n * Get calendar entry by service_id\n */\nexport function getCalendarByServiceId(db: Database, serviceId: string): Calendar | null {\n const stmt = db.prepare('SELECT * FROM calendar WHERE service_id = ?');\n stmt.bind([serviceId]);\n\n if (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stmt.free();\n return rowToCalendar(row);\n }\n\n stmt.free();\n return null;\n}\n\n/**\n * Get calendar date exceptions for a service\n */\nexport function getCalendarDates(db: Database, serviceId: string): CalendarDate[] {\n const stmt = db.prepare('SELECT * FROM calendar_dates WHERE service_id = ? ORDER BY date');\n stmt.bind([serviceId]);\n\n const dates: CalendarDate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n dates.push(rowToCalendarDate(row));\n }\n\n stmt.free();\n return dates;\n}\n\n/**\n * Get calendar date exceptions for a specific date\n */\nexport function getCalendarDatesForDate(db: Database, date: string): CalendarDate[] {\n const stmt = db.prepare('SELECT * FROM calendar_dates WHERE date = ?');\n stmt.bind([date]);\n\n const dates: CalendarDate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n dates.push(rowToCalendarDate(row));\n }\n\n stmt.free();\n return dates;\n}\n\n/**\n * Convert database row to Calendar object\n */\nfunction rowToCalendar(row: Record): Calendar {\n return {\n service_id: String(row.service_id),\n monday: Number(row.monday),\n tuesday: Number(row.tuesday),\n wednesday: Number(row.wednesday),\n thursday: Number(row.thursday),\n friday: Number(row.friday),\n saturday: Number(row.saturday),\n sunday: Number(row.sunday),\n start_date: String(row.start_date),\n end_date: String(row.end_date),\n };\n}\n\n/**\n * Convert database row to CalendarDate object\n */\nfunction rowToCalendarDate(row: Record): CalendarDate {\n return {\n service_id: String(row.service_id),\n date: String(row.date),\n exception_type: Number(row.exception_type),\n };\n}\n","import type { Database} from 'sql.js';\nimport type { VehiclePosition } from '../types/gtfs-rt';\nimport type { VehiclePositionFilters } from '../types/gtfs-rt';\n\nexport type { VehiclePositionFilters };\n\n/**\n * Parse vehicle position from database row\n */\nexport function parseVehiclePosition(row: Record): VehiclePosition {\n const vp: VehiclePosition = {\n trip_id: String(row.trip_id),\n route_id: row.route_id ? String(row.route_id) : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n\n // Vehicle descriptor\n if (row.vehicle_id || row.vehicle_label || row.vehicle_license_plate) {\n vp.vehicle = {\n id: row.vehicle_id ? String(row.vehicle_id) : undefined,\n label: row.vehicle_label ? String(row.vehicle_label) : undefined,\n license_plate: row.vehicle_license_plate ? String(row.vehicle_license_plate) : undefined\n };\n }\n\n // Position\n if (row.latitude !== null && row.longitude !== null) {\n vp.position = {\n latitude: Number(row.latitude),\n longitude: Number(row.longitude),\n bearing: row.bearing !== null ? Number(row.bearing) : undefined,\n odometer: row.odometer !== null ? Number(row.odometer) : undefined,\n speed: row.speed !== null ? Number(row.speed) : undefined\n };\n }\n\n // Other fields\n if (row.current_stop_sequence !== null) {\n vp.current_stop_sequence = Number(row.current_stop_sequence);\n }\n if (row.stop_id) {\n vp.stop_id = String(row.stop_id);\n }\n if (row.current_status !== null) {\n vp.current_status = Number(row.current_status);\n }\n if (row.timestamp !== null) {\n vp.timestamp = Number(row.timestamp);\n }\n if (row.congestion_level !== null) {\n vp.congestion_level = Number(row.congestion_level);\n }\n if (row.occupancy_status !== null) {\n vp.occupancy_status = Number(row.occupancy_status);\n }\n\n return vp;\n}\n\n/**\n * Get vehicle positions with optional filters\n */\nexport function getVehiclePositions(\n db: Database,\n filters: VehiclePositionFilters = {},\n stalenessThreshold: number = 120\n): VehiclePosition[] {\n const { tripId, routeId, vehicleId, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by route_id\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n // Filter by vehicle_id\n if (vehicleId) {\n conditions.push('vehicle_id = ?');\n params.push(vehicleId);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_vehicle_positions';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const positions: VehiclePosition[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n positions.push(parseVehiclePosition(row));\n }\n\n stmt.free();\n return positions;\n}\n\n/**\n * Get vehicle position by trip ID\n */\nexport function getVehiclePositionByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): VehiclePosition | null {\n const positions = getVehiclePositions(db, { tripId, limit: 1 }, stalenessThreshold);\n return positions.length > 0 ? positions[0] : null;\n}\n\n/**\n * Get all vehicle positions without staleness filtering (for debugging)\n */\nexport function getAllVehiclePositions(db: Database): VehiclePosition[] {\n const sql = 'SELECT * FROM rt_vehicle_positions ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const positions: VehiclePosition[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n positions.push(parseVehiclePosition(row));\n }\n\n stmt.free();\n return positions;\n}\n","/**\n * Trip Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { Trip } from '../types/gtfs';\nimport type { TripRealtime, VehiclePosition } from '../types/gtfs-rt';\nimport { parseVehiclePosition } from './rt-vehicle-positions';\n\nexport interface TripFilters {\n tripId?: string | string[];\n routeId?: string | string[];\n serviceIds?: string | string[];\n directionId?: number | number[];\n agencyId?: string | string[];\n includeRealtime?: boolean;\n limit?: number;\n}\n\nexport interface TripWithRealtime extends Trip {\n realtime?: TripRealtime;\n}\n\n/**\n * Merge realtime data with trips\n */\nfunction mergeRealtimeData(\n trips: Trip[],\n db: Database,\n stalenessThreshold: number\n): TripWithRealtime[] {\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n\n const tripIds = trips.map(t => t.trip_id);\n if (tripIds.length === 0) return trips;\n\n const placeholders = tripIds.map(() => '?').join(', ');\n\n // Get vehicle positions\n const vpStmt = db.prepare(`\n SELECT * FROM rt_vehicle_positions\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n vpStmt.bind([...tripIds, staleThreshold]);\n\n const vpMap = new Map();\n while (vpStmt.step()) {\n const row = vpStmt.getAsObject() as Record;\n const vp = parseVehiclePosition(row);\n vpMap.set(vp.trip_id, vp);\n }\n vpStmt.free();\n\n // Get trip updates\n const tuStmt = db.prepare(`\n SELECT * FROM rt_trip_updates\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n tuStmt.bind([...tripIds, staleThreshold]);\n\n const tuMap = new Map();\n while (tuStmt.step()) {\n const row = tuStmt.getAsObject() as Record;\n const tripId = String(row.trip_id);\n tuMap.set(tripId, {\n delay: row.delay !== null ? Number(row.delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n });\n }\n tuStmt.free();\n\n // Merge realtime data\n return trips.map((trip): TripWithRealtime => {\n const vp = vpMap.get(trip.trip_id);\n const tu = tuMap.get(trip.trip_id);\n\n if (!vp && !tu) {\n return { ...trip, realtime: { vehicle_position: null, trip_update: null } };\n }\n\n return {\n ...trip,\n realtime: {\n vehicle_position: vp || null,\n trip_update: tu || null\n }\n };\n });\n}\n\n/**\n * Get trips with optional filters\n * - Filters support both single values and arrays\n */\nexport function getTrips(\n db: Database,\n filters: TripFilters = {},\n stalenessThreshold: number = 120\n): Trip[] | TripWithRealtime[] {\n const { tripId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit } = filters;\n\n // Determine if we need to join with routes table\n const needsRoutesJoin = agencyId !== undefined;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (tripId) {\n const tripIds = Array.isArray(tripId) ? tripId : [tripId];\n if (tripIds.length > 0) {\n const placeholders = tripIds.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`);\n params.push(...tripIds);\n }\n }\n\n if (routeId) {\n const routeIds = Array.isArray(routeId) ? routeId : [routeId];\n if (routeIds.length > 0) {\n const placeholders = routeIds.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.route_id IN (${placeholders})` : `route_id IN (${placeholders})`);\n params.push(...routeIds);\n }\n }\n\n if (serviceIds) {\n const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds];\n if (serviceIdArray.length > 0) {\n const placeholders = serviceIdArray.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`);\n params.push(...serviceIdArray);\n }\n }\n\n if (directionId !== undefined) {\n const directionIds = Array.isArray(directionId) ? directionId : [directionId];\n if (directionIds.length > 0) {\n const placeholders = directionIds.map(() => '?').join(', ');\n conditions.push(needsRoutesJoin ? `t.direction_id IN (${placeholders})` : `direction_id IN (${placeholders})`);\n params.push(...directionIds);\n }\n }\n\n if (agencyId) {\n const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId];\n if (agencyIds.length > 0) {\n const placeholders = agencyIds.map(() => '?').join(', ');\n conditions.push(`r.agency_id IN (${placeholders})`);\n params.push(...agencyIds);\n }\n }\n\n // Build SQL query\n let sql = needsRoutesJoin\n ? 'SELECT t.* FROM trips t INNER JOIN routes r ON t.route_id = r.route_id'\n : 'SELECT * FROM trips';\n\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const trips: Trip[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n trips.push(rowToTrip(row));\n }\n\n stmt.free();\n\n // Merge realtime data if requested\n if (includeRealtime) {\n return mergeRealtimeData(trips, db, stalenessThreshold);\n }\n\n return trips;\n}\n\n/**\n * Convert database row to Trip object\n */\nfunction rowToTrip(row: Record): Trip {\n return {\n trip_id: String(row.trip_id),\n route_id: String(row.route_id),\n service_id: String(row.service_id),\n trip_headsign: row.trip_headsign ? String(row.trip_headsign) : undefined,\n trip_short_name: row.trip_short_name ? String(row.trip_short_name) : undefined,\n direction_id: row.direction_id !== null ? Number(row.direction_id) : undefined,\n block_id: row.block_id ? String(row.block_id) : undefined,\n shape_id: row.shape_id ? String(row.shape_id) : undefined,\n wheelchair_accessible: row.wheelchair_accessible !== null ? Number(row.wheelchair_accessible) : undefined,\n bikes_allowed: row.bikes_allowed !== null ? Number(row.bikes_allowed) : undefined,\n };\n}\n","/**\n * Stop Time Query Methods\n */\n\nimport type { Database } from 'sql.js';\nimport type { StopTime } from '../types/gtfs';\nimport type { StopTimeRealtime } from '../types/gtfs-rt';\n\nexport interface StopTimeFilters {\n tripId?: string | string[];\n stopId?: string | string[];\n routeId?: string | string[];\n serviceIds?: string | string[];\n directionId?: number | number[];\n agencyId?: string | string[];\n includeRealtime?: boolean;\n limit?: number;\n}\n\nexport interface StopTimeWithRealtime extends StopTime {\n realtime?: StopTimeRealtime;\n}\n\n/**\n * Merge realtime data with stop times\n */\nfunction mergeRealtimeData(\n stopTimes: StopTime[],\n db: Database,\n stalenessThreshold: number\n): StopTimeWithRealtime[] {\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n\n // Build map of trip_id -> stop times for this query\n const tripIds = Array.from(new Set(stopTimes.map(st => st.trip_id)));\n if (tripIds.length === 0) return stopTimes;\n\n const placeholders = tripIds.map(() => '?').join(', ');\n const stmt = db.prepare(`\n SELECT trip_id, stop_sequence, stop_id,\n arrival_delay, arrival_time, departure_delay, departure_time, schedule_relationship\n FROM rt_stop_time_updates\n WHERE trip_id IN (${placeholders})\n AND rt_last_updated >= ?\n `);\n stmt.bind([...tripIds, staleThreshold]);\n\n // Build map of trip_id+stop_sequence -> RT data\n const rtMap = new Map();\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n const key = `${row.trip_id}_${row.stop_sequence}`;\n rtMap.set(key, {\n arrival_delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,\n arrival_time: row.arrival_time !== null ? Number(row.arrival_time) : undefined,\n departure_delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,\n departure_time: row.departure_time !== null ? Number(row.departure_time) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n });\n }\n stmt.free();\n\n // Merge RT data with stop times\n return stopTimes.map((st): StopTimeWithRealtime => {\n const key = `${st.trip_id}_${st.stop_sequence}`;\n const rtData = rtMap.get(key);\n\n if (rtData) {\n return { ...st, realtime: rtData };\n }\n return st;\n });\n}\n\n/**\n * Get stop times with optional filters\n * - Filters support both single values and arrays\n */\nexport function getStopTimes(\n db: Database,\n filters: StopTimeFilters = {},\n stalenessThreshold: number = 120\n): StopTime[] | StopTimeWithRealtime[] {\n const { tripId, stopId, routeId, serviceIds, directionId, agencyId, includeRealtime, limit } = filters;\n\n // Determine if we need to join with trips and/or routes table\n const needsTripsJoin = routeId || serviceIds || directionId !== undefined || agencyId !== undefined;\n const needsRoutesJoin = agencyId !== undefined;\n\n // Build WHERE clause dynamically\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n if (tripId) {\n const tripIds = Array.isArray(tripId) ? tripId : [tripId];\n if (tripIds.length > 0) {\n const placeholders = tripIds.map(() => '?').join(', ');\n conditions.push(needsTripsJoin ? `st.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`);\n params.push(...tripIds);\n }\n }\n\n if (stopId) {\n const stopIds = Array.isArray(stopId) ? stopId : [stopId];\n if (stopIds.length > 0) {\n const placeholders = stopIds.map(() => '?').join(', ');\n conditions.push(needsTripsJoin ? `st.stop_id IN (${placeholders})` : `stop_id IN (${placeholders})`);\n params.push(...stopIds);\n }\n }\n\n if (routeId) {\n const routeIds = Array.isArray(routeId) ? routeId : [routeId];\n if (routeIds.length > 0) {\n const placeholders = routeIds.map(() => '?').join(', ');\n conditions.push(`t.route_id IN (${placeholders})`);\n params.push(...routeIds);\n }\n }\n\n if (serviceIds) {\n const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds];\n if (serviceIdArray.length > 0) {\n const placeholders = serviceIdArray.map(() => '?').join(', ');\n conditions.push(`t.service_id IN (${placeholders})`);\n params.push(...serviceIdArray);\n }\n }\n\n if (directionId !== undefined) {\n const directionIds = Array.isArray(directionId) ? directionId : [directionId];\n if (directionIds.length > 0) {\n const placeholders = directionIds.map(() => '?').join(', ');\n conditions.push(`t.direction_id IN (${placeholders})`);\n params.push(...directionIds);\n }\n }\n\n if (agencyId) {\n const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId];\n if (agencyIds.length > 0) {\n const placeholders = agencyIds.map(() => '?').join(', ');\n conditions.push(`r.agency_id IN (${placeholders})`);\n params.push(...agencyIds);\n }\n }\n\n // Build SQL query\n let sql: string;\n if (needsRoutesJoin) {\n sql = 'SELECT st.* FROM stop_times st INNER JOIN trips t ON st.trip_id = t.trip_id INNER JOIN routes r ON t.route_id = r.route_id';\n } else if (needsTripsJoin) {\n sql = 'SELECT st.* FROM stop_times st INNER JOIN trips t ON st.trip_id = t.trip_id';\n } else {\n sql = 'SELECT * FROM stop_times';\n }\n\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n\n // Order by stop_sequence if filtering by trip, otherwise by arrival_time\n sql += tripId ? ' ORDER BY stop_sequence' : ' ORDER BY arrival_time';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stopTimes: StopTime[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimes.push(rowToStopTime(row));\n }\n\n stmt.free();\n\n // Merge realtime data if requested\n if (includeRealtime) {\n return mergeRealtimeData(stopTimes, db, stalenessThreshold);\n }\n\n return stopTimes;\n}\n\n/**\n * Convert database row to StopTime object\n */\nfunction rowToStopTime(row: Record): StopTime {\n return {\n trip_id: String(row.trip_id),\n arrival_time: String(row.arrival_time),\n departure_time: String(row.departure_time),\n stop_id: String(row.stop_id),\n stop_sequence: Number(row.stop_sequence),\n stop_headsign: row.stop_headsign ? String(row.stop_headsign) : undefined,\n pickup_type: row.pickup_type !== null ? Number(row.pickup_type) : undefined,\n drop_off_type: row.drop_off_type !== null ? Number(row.drop_off_type) : undefined,\n continuous_pickup: row.continuous_pickup !== null ? Number(row.continuous_pickup) : undefined,\n continuous_drop_off: row.continuous_drop_off !== null ? Number(row.continuous_drop_off) : undefined,\n shape_dist_traveled: row.shape_dist_traveled !== null ? Number(row.shape_dist_traveled) : undefined,\n timepoint: row.timepoint !== null ? Number(row.timepoint) : undefined,\n };\n}\n","import type { Database } from 'sql.js';\nimport type { Alert, EntitySelector, TimeRange, TranslatedString } from '../types/gtfs-rt';\nimport type { AlertFilters } from '../types/gtfs-rt';\n\nexport type { AlertFilters };\n\n/**\n * Parse JSON fields from database\n */\nfunction parseAlert(row: Record): Alert {\n return {\n id: String(row.id),\n active_period: row.active_period ? JSON.parse(String(row.active_period)) as TimeRange[] : [],\n informed_entity: row.informed_entity ? JSON.parse(String(row.informed_entity)) as EntitySelector[] : [],\n cause: row.cause ? Number(row.cause) : undefined,\n effect: row.effect ? Number(row.effect) : undefined,\n url: row.url ? JSON.parse(String(row.url)) as TranslatedString : undefined,\n header_text: row.header_text ? JSON.parse(String(row.header_text)) as TranslatedString : undefined,\n description_text: row.description_text ? JSON.parse(String(row.description_text)) as TranslatedString : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n}\n\n/**\n * Check if an alert is currently active based on its active_period\n */\nfunction isAlertActive(alert: Alert, now: number): boolean {\n // If no active periods defined, assume always active\n if (!alert.active_period || alert.active_period.length === 0) {\n return true;\n }\n\n // Check if current time falls within any active period\n for (const period of alert.active_period) {\n const start = period.start || 0;\n const end = period.end || Number.MAX_SAFE_INTEGER;\n\n if (now >= start && now <= end) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if an alert affects the specified entity\n */\nfunction alertAffectsEntity(alert: Alert, filters: AlertFilters): boolean {\n if (!alert.informed_entity || alert.informed_entity.length === 0) {\n return true; // No specific entities, assume it affects all\n }\n\n for (const entity of alert.informed_entity) {\n // Check route_id\n if (filters.routeId && entity.route_id === filters.routeId) {\n return true;\n }\n\n // Check stop_id\n if (filters.stopId && entity.stop_id === filters.stopId) {\n return true;\n }\n\n // Check trip_id\n if (filters.tripId && entity.trip?.trip_id === filters.tripId) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Get alerts with optional filters\n */\nexport function getAlerts(db: Database, filters: AlertFilters = {}, stalenessThreshold: number = 120): Alert[] {\n const {\n alertId,\n activeOnly,\n routeId,\n stopId,\n tripId,\n cause,\n effect,\n limit\n } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by alert ID\n if (alertId) {\n conditions.push('id = ?');\n params.push(alertId);\n }\n\n // Filter by cause\n if (cause !== undefined) {\n conditions.push('cause = ?');\n params.push(cause);\n }\n\n // Filter by effect\n if (effect !== undefined) {\n conditions.push('effect = ?');\n params.push(effect);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_alerts';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const alerts: Alert[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n const alert = parseAlert(row);\n\n // Apply activeOnly filter in application code\n if (activeOnly && !isAlertActive(alert, now)) {\n continue;\n }\n\n // Apply entity filters in application code (since informed_entity is JSON)\n if (routeId || stopId || tripId) {\n if (!alertAffectsEntity(alert, filters)) {\n continue;\n }\n }\n\n alerts.push(alert);\n }\n\n stmt.free();\n return alerts;\n}\n\n/**\n * Get alert by ID\n */\nexport function getAlertById(db: Database, alertId: string, stalenessThreshold: number = 120): Alert | null {\n const alerts = getAlerts(db, { alertId, limit: 1 }, stalenessThreshold);\n return alerts.length > 0 ? alerts[0] : null;\n}\n\n/**\n * Get all alerts without staleness filtering (for debugging)\n */\nexport function getAllAlerts(db: Database): Alert[] {\n const sql = 'SELECT * FROM rt_alerts ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const alerts: Alert[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n alerts.push(parseAlert(row));\n }\n\n stmt.free();\n return alerts;\n}\n","import type { Database } from 'sql.js';\nimport type { TripUpdate } from '../types/gtfs-rt';\n\nexport interface TripUpdateFilters {\n tripId?: string;\n routeId?: string;\n vehicleId?: string;\n limit?: number;\n}\n\n/**\n * Parse trip update from database row\n */\nexport function parseTripUpdate(row: Record): TripUpdate {\n const tu: TripUpdate = {\n trip_id: String(row.trip_id),\n route_id: row.route_id ? String(row.route_id) : undefined,\n stop_time_update: [], // Will be populated separately\n timestamp: row.timestamp !== null ? Number(row.timestamp) : undefined,\n delay: row.delay !== null ? Number(row.delay) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined,\n rt_last_updated: Number(row.rt_last_updated)\n };\n\n // Vehicle descriptor\n if (row.vehicle_id || row.vehicle_label || row.vehicle_license_plate) {\n tu.vehicle = {\n id: row.vehicle_id ? String(row.vehicle_id) : undefined,\n label: row.vehicle_label ? String(row.vehicle_label) : undefined,\n license_plate: row.vehicle_license_plate ? String(row.vehicle_license_plate) : undefined\n };\n }\n\n return tu;\n}\n\n/**\n * Get trip updates with optional filters\n */\nexport function getTripUpdates(\n db: Database,\n filters: TripUpdateFilters = {},\n stalenessThreshold: number = 120\n): TripUpdate[] {\n const { tripId, routeId, vehicleId, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n conditions.push('trip_id = ?');\n params.push(tripId);\n }\n\n // Filter by route_id\n if (routeId) {\n conditions.push('route_id = ?');\n params.push(routeId);\n }\n\n // Filter by vehicle_id\n if (vehicleId) {\n conditions.push('vehicle_id = ?');\n params.push(vehicleId);\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_trip_updates';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY rt_last_updated DESC';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const tripUpdates: TripUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n tripUpdates.push(parseTripUpdate(row));\n }\n\n stmt.free();\n return tripUpdates;\n}\n\n/**\n * Get trip update by trip ID\n */\nexport function getTripUpdateByTripId(\n db: Database,\n tripId: string,\n stalenessThreshold: number = 120\n): TripUpdate | null {\n const updates = getTripUpdates(db, { tripId, limit: 1 }, stalenessThreshold);\n return updates.length > 0 ? updates[0] : null;\n}\n\n/**\n * Get all trip updates without staleness filtering (for debugging)\n */\nexport function getAllTripUpdates(db: Database): TripUpdate[] {\n const sql = 'SELECT * FROM rt_trip_updates ORDER BY rt_last_updated DESC';\n const stmt = db.prepare(sql);\n\n const tripUpdates: TripUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n tripUpdates.push(parseTripUpdate(row));\n }\n\n stmt.free();\n return tripUpdates;\n}\n","import type { Database } from 'sql.js';\nimport type { StopTimeUpdate } from '../types/gtfs-rt';\n\nexport interface StopTimeUpdateFilters {\n tripId?: string | string[];\n stopId?: string | string[];\n stopSequence?: number | number[];\n limit?: number;\n}\n\n/**\n * Parse stop time update from database row\n */\nfunction parseStopTimeUpdate(row: Record): StopTimeUpdate {\n const stu: StopTimeUpdate = {\n stop_sequence: row.stop_sequence !== null ? Number(row.stop_sequence) : undefined,\n stop_id: row.stop_id ? String(row.stop_id) : undefined,\n schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined\n };\n\n // Arrival event\n if (row.arrival_delay !== null || row.arrival_time !== null || row.arrival_uncertainty !== null) {\n stu.arrival = {\n delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,\n time: row.arrival_time !== null ? Number(row.arrival_time) : undefined,\n uncertainty: row.arrival_uncertainty !== null ? Number(row.arrival_uncertainty) : undefined\n };\n }\n\n // Departure event\n if (row.departure_delay !== null || row.departure_time !== null || row.departure_uncertainty !== null) {\n stu.departure = {\n delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,\n time: row.departure_time !== null ? Number(row.departure_time) : undefined,\n uncertainty: row.departure_uncertainty !== null ? Number(row.departure_uncertainty) : undefined\n };\n }\n\n return stu;\n}\n\n/**\n * Extended StopTimeUpdate with trip_id and rt_last_updated for debugging\n */\nexport interface StopTimeUpdateWithMetadata extends StopTimeUpdate {\n trip_id: string;\n rt_last_updated: number;\n}\n\n/**\n * Parse stop time update with metadata from database row\n */\nfunction parseStopTimeUpdateWithMetadata(row: Record): StopTimeUpdateWithMetadata {\n const stu = parseStopTimeUpdate(row) as StopTimeUpdateWithMetadata;\n stu.trip_id = String(row.trip_id);\n stu.rt_last_updated = Number(row.rt_last_updated);\n return stu;\n}\n\n/**\n * Get stop time updates with optional filters\n * - Filters support both single values and arrays\n */\nexport function getStopTimeUpdates(\n db: Database,\n filters: StopTimeUpdateFilters = {},\n stalenessThreshold: number = 120\n): StopTimeUpdate[] {\n const { tripId, stopId, stopSequence, limit } = filters;\n\n const conditions: string[] = [];\n const params: (string | number)[] = [];\n\n // Filter by trip_id\n if (tripId) {\n const tripIds = Array.isArray(tripId) ? tripId : [tripId];\n if (tripIds.length > 0) {\n const placeholders = tripIds.map(() => '?').join(', ');\n conditions.push(`trip_id IN (${placeholders})`);\n params.push(...tripIds);\n }\n }\n\n // Filter by stop_id\n if (stopId) {\n const stopIds = Array.isArray(stopId) ? stopId : [stopId];\n if (stopIds.length > 0) {\n const placeholders = stopIds.map(() => '?').join(', ');\n conditions.push(`stop_id IN (${placeholders})`);\n params.push(...stopIds);\n }\n }\n\n // Filter by stop_sequence\n if (stopSequence !== undefined) {\n const stopSequences = Array.isArray(stopSequence) ? stopSequence : [stopSequence];\n if (stopSequences.length > 0) {\n const placeholders = stopSequences.map(() => '?').join(', ');\n conditions.push(`stop_sequence IN (${placeholders})`);\n params.push(...stopSequences);\n }\n }\n\n // Staleness filter (always applied)\n const now = Math.floor(Date.now() / 1000);\n const staleThreshold = now - stalenessThreshold;\n conditions.push('rt_last_updated >= ?');\n params.push(staleThreshold);\n\n // Build query\n let sql = 'SELECT * FROM rt_stop_time_updates';\n if (conditions.length > 0) {\n sql += ' WHERE ' + conditions.join(' AND ');\n }\n sql += ' ORDER BY trip_id, stop_sequence';\n\n if (limit) {\n sql += ' LIMIT ?';\n params.push(limit);\n }\n\n // Execute query\n const stmt = db.prepare(sql);\n if (params.length > 0) {\n stmt.bind(params);\n }\n\n const stopTimeUpdates: StopTimeUpdate[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimeUpdates.push(parseStopTimeUpdate(row));\n }\n\n stmt.free();\n return stopTimeUpdates;\n}\n\n/**\n * Get all stop time updates without staleness filtering (for debugging)\n * Returns extended type with trip_id and rt_last_updated for debugging purposes\n */\nexport function getAllStopTimeUpdates(db: Database): StopTimeUpdateWithMetadata[] {\n const sql = 'SELECT * FROM rt_stop_time_updates ORDER BY trip_id, stop_sequence';\n const stmt = db.prepare(sql);\n\n const stopTimeUpdates: StopTimeUpdateWithMetadata[] = [];\n while (stmt.step()) {\n const row = stmt.getAsObject() as Record;\n stopTimeUpdates.push(parseStopTimeUpdateWithMetadata(row));\n }\n\n stmt.free();\n return stopTimeUpdates;\n}\n","/**\n * GTFS Realtime TypeScript types\n * Based on GTFS Realtime v2.0 specification\n */\n\n// Enum types from GTFS-RT specification\n\nexport enum ScheduleRelationship {\n SCHEDULED = 0,\n ADDED = 1,\n UNSCHEDULED = 2,\n CANCELED = 3,\n SKIPPED = 4,\n NO_DATA = 5\n}\n\nexport enum VehicleStopStatus {\n INCOMING_AT = 0,\n STOPPED_AT = 1,\n IN_TRANSIT_TO = 2\n}\n\nexport enum CongestionLevel {\n UNKNOWN_CONGESTION_LEVEL = 0,\n RUNNING_SMOOTHLY = 1,\n STOP_AND_GO = 2,\n CONGESTION = 3,\n SEVERE_CONGESTION = 4\n}\n\nexport enum OccupancyStatus {\n EMPTY = 0,\n MANY_SEATS_AVAILABLE = 1,\n FEW_SEATS_AVAILABLE = 2,\n STANDING_ROOM_ONLY = 3,\n CRUSHED_STANDING_ROOM_ONLY = 4,\n FULL = 5,\n NOT_ACCEPTING_PASSENGERS = 6\n}\n\nexport enum AlertCause {\n UNKNOWN_CAUSE = 1,\n OTHER_CAUSE = 2,\n TECHNICAL_PROBLEM = 3,\n STRIKE = 4,\n DEMONSTRATION = 5,\n ACCIDENT = 6,\n HOLIDAY = 7,\n WEATHER = 8,\n MAINTENANCE = 9,\n CONSTRUCTION = 10,\n POLICE_ACTIVITY = 11,\n MEDICAL_EMERGENCY = 12\n}\n\nexport enum AlertEffect {\n NO_SERVICE = 1,\n REDUCED_SERVICE = 2,\n SIGNIFICANT_DELAYS = 3,\n DETOUR = 4,\n ADDITIONAL_SERVICE = 5,\n MODIFIED_SERVICE = 6,\n OTHER_EFFECT = 7,\n UNKNOWN_EFFECT = 8,\n STOP_MOVED = 9,\n NO_EFFECT = 10,\n ACCESSIBILITY_ISSUE = 11\n}\n\n// Main GTFS-RT types\n\nexport interface TranslatedString {\n translation: Array<{\n text: string;\n language?: string;\n }>;\n}\n\nexport interface EntitySelector {\n agency_id?: string;\n route_id?: string;\n route_type?: number;\n trip?: {\n trip_id?: string;\n route_id?: string;\n direction_id?: number;\n start_time?: string;\n start_date?: string;\n schedule_relationship?: ScheduleRelationship;\n };\n stop_id?: string;\n}\n\nexport interface TimeRange {\n start?: number; // UNIX timestamp\n end?: number; // UNIX timestamp\n}\n\nexport interface Alert {\n id: string;\n active_period: TimeRange[];\n informed_entity: EntitySelector[];\n cause?: AlertCause;\n effect?: AlertEffect;\n url?: TranslatedString;\n header_text?: TranslatedString;\n description_text?: TranslatedString;\n rt_last_updated: number; // UNIX timestamp\n}\n\nexport interface Position {\n latitude: number;\n longitude: number;\n bearing?: number;\n odometer?: number;\n speed?: number;\n}\n\nexport interface VehicleDescriptor {\n id?: string;\n label?: string;\n license_plate?: string;\n}\n\nexport interface VehiclePosition {\n trip_id: string;\n route_id?: string;\n vehicle?: VehicleDescriptor;\n position?: Position;\n current_stop_sequence?: number;\n stop_id?: string;\n current_status?: VehicleStopStatus;\n timestamp?: number;\n congestion_level?: CongestionLevel;\n occupancy_status?: OccupancyStatus;\n rt_last_updated: number; // UNIX timestamp\n}\n\nexport interface StopTimeEvent {\n delay?: number; // seconds\n time?: number; // UNIX timestamp (absolute time)\n uncertainty?: number;\n}\n\nexport interface StopTimeUpdate {\n stop_sequence?: number;\n stop_id?: string;\n arrival?: StopTimeEvent;\n departure?: StopTimeEvent;\n schedule_relationship?: ScheduleRelationship;\n}\n\nexport interface TripUpdate {\n trip_id: string;\n route_id?: string;\n vehicle?: VehicleDescriptor;\n stop_time_update: StopTimeUpdate[];\n timestamp?: number;\n delay?: number;\n schedule_relationship?: ScheduleRelationship;\n rt_last_updated: number; // UNIX timestamp\n}\n\n// Enhanced types for queries with realtime data\n\nexport interface StopTimeRealtime {\n arrival_delay?: number; // seconds\n arrival_time?: number; // UNIX timestamp (absolute time)\n departure_delay?: number; // seconds\n departure_time?: number; // UNIX timestamp (absolute time)\n schedule_relationship?: ScheduleRelationship;\n}\n\nexport interface TripRealtime {\n vehicle_position?: VehiclePosition | null;\n trip_update?: {\n delay?: number;\n schedule_relationship?: ScheduleRelationship;\n } | null;\n}\n\n// Filter types for realtime queries\n\nexport interface AlertFilters {\n alertId?: string;\n activeOnly?: boolean;\n routeId?: string;\n stopId?: string;\n tripId?: string;\n cause?: AlertCause;\n effect?: AlertEffect;\n limit?: number;\n}\n\nexport interface VehiclePositionFilters {\n tripId?: string;\n routeId?: string;\n vehicleId?: string;\n limit?: number;\n}\n\n// Configuration\n\nexport interface RealtimeConfig {\n feedUrls?: string[];\n stalenessThreshold?: number; // seconds, default 120\n}\n"],"mappings":"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","names":["response","arrayBuffer","stmt","stops","mergeRealtimeData","ScheduleRelationship","VehicleStopStatus","CongestionLevel","OccupancyStatus","AlertCause","AlertEffect"]} \ No newline at end of file diff --git a/src/gtfs-sqljs.ts b/src/gtfs-sqljs.ts index 4f3225c..10c2bb3 100644 --- a/src/gtfs-sqljs.ts +++ b/src/gtfs-sqljs.ts @@ -10,21 +10,21 @@ import { createRealtimeTables, clearRealtimeData as clearRTData } from './schema import { loadRealtimeData } from './loaders/gtfs-rt-loader'; // Query methods -import { getAgencyById, getAgencies, type AgencyFilters } from './queries/agencies'; -import { getStopById, getStops, type StopFilters } from './queries/stops'; -import { getRouteById, getRoutes, type RouteFilters } from './queries/routes'; +import { getAgencies, type AgencyFilters } from './queries/agencies'; +import { getStops, type StopFilters } from './queries/stops'; +import { getRoutes, type RouteFilters } from './queries/routes'; import { getActiveServiceIds, getCalendarByServiceId, getCalendarDates, getCalendarDatesForDate, } from './queries/calendar'; -import { getTripById, getTrips, type TripFilters, type TripWithRealtime } from './queries/trips'; -import { getStopTimesByTrip, getStopTimes, type StopTimeFilters, type StopTimeWithRealtime } from './queries/stop-times'; -import { getAlerts as getAlertsQuery, getAlertById, getAllAlerts, type AlertFilters } from './queries/rt-alerts'; -import { getVehiclePositions as getVehiclePositionsQuery, getVehiclePositionByTripId, getAllVehiclePositions, type VehiclePositionFilters } from './queries/rt-vehicle-positions'; -import { getTripUpdates, getTripUpdateByTripId, getAllTripUpdates, type TripUpdateFilters } from './queries/rt-trip-updates'; -import { getStopTimeUpdates, getStopTimeUpdatesByTripId, getAllStopTimeUpdates, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata } from './queries/rt-stop-time-updates'; +import { getTrips, type TripFilters, type TripWithRealtime } from './queries/trips'; +import { getStopTimes, type StopTimeFilters, type StopTimeWithRealtime } from './queries/stop-times'; +import { getAlerts as getAlertsQuery, getAllAlerts, type AlertFilters } from './queries/rt-alerts'; +import { getVehiclePositions as getVehiclePositionsQuery, getAllVehiclePositions, type VehiclePositionFilters } from './queries/rt-vehicle-positions'; +import { getTripUpdates, getAllTripUpdates, type TripUpdateFilters } from './queries/rt-trip-updates'; +import { getStopTimeUpdates, getAllStopTimeUpdates, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata } from './queries/rt-stop-time-updates'; // Types import type { Agency, Stop, Route, Trip, StopTime, Calendar, CalendarDate } from './types/gtfs'; @@ -203,16 +203,9 @@ export class GtfsSqlJs { // ==================== Agency Methods ==================== - /** - * Get an agency by its agency_id - */ - getAgencyById(agencyId: string): Agency | null { - if (!this.db) throw new Error('Database not initialized'); - return getAgencyById(this.db, agencyId); - } - /** * Get agencies with optional filters + * Pass agencyId filter to get a specific agency */ getAgencies(filters?: AgencyFilters): Agency[] { if (!this.db) throw new Error('Database not initialized'); @@ -221,16 +214,9 @@ export class GtfsSqlJs { // ==================== Stop Methods ==================== - /** - * Get a stop by its stop_id - */ - getStopById(stopId: string): Stop | null { - if (!this.db) throw new Error('Database not initialized'); - return getStopById(this.db, stopId); - } - /** * Get stops with optional filters + * Pass stopId filter to get a specific stop */ getStops(filters?: StopFilters): Stop[] { if (!this.db) throw new Error('Database not initialized'); @@ -239,16 +225,9 @@ export class GtfsSqlJs { // ==================== Route Methods ==================== - /** - * Get a route by its route_id - */ - getRouteById(routeId: string): Route | null { - if (!this.db) throw new Error('Database not initialized'); - return getRouteById(this.db, routeId); - } - /** * Get routes with optional filters + * Pass routeId filter to get a specific route */ getRoutes(filters?: RouteFilters): Route[] { if (!this.db) throw new Error('Database not initialized'); @@ -291,23 +270,16 @@ export class GtfsSqlJs { // ==================== Trip Methods ==================== - /** - * Get a trip by its trip_id - */ - getTripById(tripId: string): Trip | null { - if (!this.db) throw new Error('Database not initialized'); - return getTripById(this.db, tripId); - } - /** * Get trips with optional filters + * Pass tripId filter to get a specific trip * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) * @param filters.limit - Limit number of results * * @example @@ -317,6 +289,10 @@ export class GtfsSqlJs { * @example * // Get all trips for a route going in one direction * const trips = gtfs.getTrips({ routeId: 'ROUTE_1', directionId: 0 }); + * + * @example + * // Get a specific trip + * const trips = gtfs.getTrips({ tripId: 'TRIP_123' }); */ getTrips(filters?: TripFilters & { date?: string }): Trip[] { if (!this.db) throw new Error('Database not initialized'); @@ -335,24 +311,17 @@ export class GtfsSqlJs { // ==================== Stop Time Methods ==================== - /** - * Get stop times for a trip (ordered by stop_sequence) - */ - getStopTimesByTrip(tripId: string): StopTime[] { - if (!this.db) throw new Error('Database not initialized'); - return getStopTimesByTrip(this.db, tripId); - } - /** * Get stop times with optional filters * * @param filters - Optional filters - * @param filters.tripId - Filter by trip ID - * @param filters.stopId - Filter by stop ID - * @param filters.routeId - Filter by route ID + * @param filters.tripId - Filter by trip ID (single value or array) + * @param filters.stopId - Filter by stop ID (single value or array) + * @param filters.routeId - Filter by route ID (single value or array) * @param filters.date - Filter by date (YYYYMMDD format) - will get active services for that date - * @param filters.directionId - Filter by direction ID - * @param filters.agencyId - Filter by agency ID + * @param filters.directionId - Filter by direction ID (single value or array) + * @param filters.agencyId - Filter by agency ID (single value or array) + * @param filters.includeRealtime - Include realtime data (delay and time fields) * @param filters.limit - Limit number of results * * @example @@ -366,6 +335,13 @@ export class GtfsSqlJs { * routeId: 'ROUTE_1', * date: '20240115' * }); + * + * @example + * // Get stop times with realtime data + * const stopTimes = gtfs.getStopTimes({ + * tripId: 'TRIP_123', + * includeRealtime: true + * }); */ getStopTimes(filters?: StopTimeFilters & { date?: string }): StopTime[] { if (!this.db) throw new Error('Database not initialized'); @@ -437,68 +413,40 @@ export class GtfsSqlJs { /** * Get alerts with optional filters + * Pass alertId filter to get a specific alert */ getAlerts(filters?: AlertFilters): Alert[] { if (!this.db) throw new Error('Database not initialized'); return getAlertsQuery(this.db, filters, this.stalenessThreshold); } - /** - * Get alert by ID - */ - getAlertById(alertId: string): Alert | null { - if (!this.db) throw new Error('Database not initialized'); - return getAlertById(this.db, alertId, this.stalenessThreshold); - } - /** * Get vehicle positions with optional filters + * Pass tripId filter to get vehicle position for a specific trip */ getVehiclePositions(filters?: VehiclePositionFilters): VehiclePosition[] { if (!this.db) throw new Error('Database not initialized'); return getVehiclePositionsQuery(this.db, filters, this.stalenessThreshold); } - /** - * Get vehicle position by trip ID - */ - getVehiclePositionByTripId(tripId: string): VehiclePosition | null { - if (!this.db) throw new Error('Database not initialized'); - return getVehiclePositionByTripId(this.db, tripId, this.stalenessThreshold); - } - /** * Get trip updates with optional filters + * Pass tripId filter to get trip update for a specific trip */ getTripUpdates(filters?: TripUpdateFilters): TripUpdate[] { if (!this.db) throw new Error('Database not initialized'); return getTripUpdates(this.db, filters, this.stalenessThreshold); } - /** - * Get trip update by trip ID - */ - getTripUpdateByTripId(tripId: string): TripUpdate | null { - if (!this.db) throw new Error('Database not initialized'); - return getTripUpdateByTripId(this.db, tripId, this.stalenessThreshold); - } - /** * Get stop time updates with optional filters + * Pass tripId filter to get stop time updates for a specific trip */ getStopTimeUpdates(filters?: StopTimeUpdateFilters): import('./types/gtfs-rt').StopTimeUpdate[] { if (!this.db) throw new Error('Database not initialized'); return getStopTimeUpdates(this.db, filters, this.stalenessThreshold); } - /** - * Get stop time updates for a specific trip - */ - getStopTimeUpdatesByTripId(tripId: string): import('./types/gtfs-rt').StopTimeUpdate[] { - if (!this.db) throw new Error('Database not initialized'); - return getStopTimeUpdatesByTripId(this.db, tripId, this.stalenessThreshold); - } - // ==================== Debug Export Methods ==================== // These methods export all realtime data without staleness filtering // for debugging purposes diff --git a/src/queries/agencies.ts b/src/queries/agencies.ts index 4e9e76b..a7fa402 100644 --- a/src/queries/agencies.ts +++ b/src/queries/agencies.ts @@ -6,12 +6,13 @@ import type { Database } from 'sql.js'; import type { Agency } from '../types/gtfs'; export interface AgencyFilters { - agencyId?: string; + agencyId?: string | string[]; limit?: number; } /** * Get agencies with optional filters + * - Filters support both single values and arrays */ export function getAgencies(db: Database, filters: AgencyFilters = {}): Agency[] { const { agencyId, limit } = filters; @@ -21,8 +22,12 @@ export function getAgencies(db: Database, filters: AgencyFilters = {}): Agency[] const params: (string | number)[] = []; if (agencyId) { - conditions.push('agency_id = ?'); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => '?').join(', '); + conditions.push(`agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } // Build SQL query @@ -51,43 +56,6 @@ export function getAgencies(db: Database, filters: AgencyFilters = {}): Agency[] return agencies; } -/** - * Get an agency by its agency_id - */ -export function getAgencyById(db: Database, agencyId: string): Agency | null { - const stmt = db.prepare('SELECT * FROM agency WHERE agency_id = ?'); - stmt.bind([agencyId]); - - if (stmt.step()) { - const row = stmt.getAsObject() as Record; - stmt.free(); - return rowToAgency(row); - } - - stmt.free(); - return null; -} - -/** - * Get all agencies - */ -export function getAllAgencies(db: Database, limit?: number): Agency[] { - const sql = limit - ? `SELECT * FROM agency ORDER BY agency_name LIMIT ${limit}` - : 'SELECT * FROM agency ORDER BY agency_name'; - - const stmt = db.prepare(sql); - - const agencies: Agency[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - agencies.push(rowToAgency(row)); - } - - stmt.free(); - return agencies; -} - /** * Convert database row to Agency object */ diff --git a/src/queries/routes.ts b/src/queries/routes.ts index e4d5bec..55a7699 100644 --- a/src/queries/routes.ts +++ b/src/queries/routes.ts @@ -6,13 +6,14 @@ import type { Database } from 'sql.js'; import type { Route } from '../types/gtfs'; export interface RouteFilters { - routeId?: string; - agencyId?: string; + routeId?: string | string[]; + agencyId?: string | string[]; limit?: number; } /** * Get routes with optional filters + * - Filters support both single values and arrays */ export function getRoutes(db: Database, filters: RouteFilters = {}): Route[] { const { routeId, agencyId, limit } = filters; @@ -22,13 +23,21 @@ export function getRoutes(db: Database, filters: RouteFilters = {}): Route[] { const params: (string | number)[] = []; if (routeId) { - conditions.push('route_id = ?'); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => '?').join(', '); + conditions.push(`route_id IN (${placeholders})`); + params.push(...routeIds); + } } if (agencyId) { - conditions.push('agency_id = ?'); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => '?').join(', '); + conditions.push(`agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } // Build SQL query @@ -57,62 +66,6 @@ export function getRoutes(db: Database, filters: RouteFilters = {}): Route[] { return routes; } -/** - * Get a route by its route_id - */ -export function getRouteById(db: Database, routeId: string): Route | null { - const stmt = db.prepare('SELECT * FROM routes WHERE route_id = ?'); - stmt.bind([routeId]); - - if (stmt.step()) { - const row = stmt.getAsObject() as Record; - stmt.free(); - return rowToRoute(row); - } - - stmt.free(); - return null; -} - -/** - * Get all routes - */ -export function getAllRoutes(db: Database, limit?: number): Route[] { - const sql = limit - ? `SELECT * FROM routes ORDER BY route_short_name, route_long_name LIMIT ${limit}` - : 'SELECT * FROM routes ORDER BY route_short_name, route_long_name'; - - const stmt = db.prepare(sql); - - const routes: Route[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - routes.push(rowToRoute(row)); - } - - stmt.free(); - return routes; -} - -/** - * Get routes by agency - */ -export function getRoutesByAgency(db: Database, agencyId: string): Route[] { - const stmt = db.prepare( - 'SELECT * FROM routes WHERE agency_id = ? ORDER BY route_short_name, route_long_name' - ); - stmt.bind([agencyId]); - - const routes: Route[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - routes.push(rowToRoute(row)); - } - - stmt.free(); - return routes; -} - /** * Convert database row to Route object */ diff --git a/src/queries/rt-stop-time-updates.ts b/src/queries/rt-stop-time-updates.ts index 1116414..033f597 100644 --- a/src/queries/rt-stop-time-updates.ts +++ b/src/queries/rt-stop-time-updates.ts @@ -2,9 +2,9 @@ import type { Database } from 'sql.js'; import type { StopTimeUpdate } from '../types/gtfs-rt'; export interface StopTimeUpdateFilters { - tripId?: string; - stopId?: string; - stopSequence?: number; + tripId?: string | string[]; + stopId?: string | string[]; + stopSequence?: number | number[]; limit?: number; } @@ -59,6 +59,7 @@ function parseStopTimeUpdateWithMetadata(row: Record): StopTime /** * Get stop time updates with optional filters + * - Filters support both single values and arrays */ export function getStopTimeUpdates( db: Database, @@ -72,20 +73,32 @@ export function getStopTimeUpdates( // Filter by trip_id if (tripId) { - conditions.push('trip_id = ?'); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => '?').join(', '); + conditions.push(`trip_id IN (${placeholders})`); + params.push(...tripIds); + } } // Filter by stop_id if (stopId) { - conditions.push('stop_id = ?'); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => '?').join(', '); + conditions.push(`stop_id IN (${placeholders})`); + params.push(...stopIds); + } } // Filter by stop_sequence if (stopSequence !== undefined) { - conditions.push('stop_sequence = ?'); - params.push(stopSequence); + const stopSequences = Array.isArray(stopSequence) ? stopSequence : [stopSequence]; + if (stopSequences.length > 0) { + const placeholders = stopSequences.map(() => '?').join(', '); + conditions.push(`stop_sequence IN (${placeholders})`); + params.push(...stopSequences); + } } // Staleness filter (always applied) @@ -122,17 +135,6 @@ export function getStopTimeUpdates( return stopTimeUpdates; } -/** - * Get stop time updates for a specific trip - */ -export function getStopTimeUpdatesByTripId( - db: Database, - tripId: string, - stalenessThreshold: number = 120 -): StopTimeUpdate[] { - return getStopTimeUpdates(db, { tripId }, stalenessThreshold); -} - /** * Get all stop time updates without staleness filtering (for debugging) * Returns extended type with trip_id and rt_last_updated for debugging purposes diff --git a/src/queries/stop-times.ts b/src/queries/stop-times.ts index 61dd9a9..196ef1e 100644 --- a/src/queries/stop-times.ts +++ b/src/queries/stop-times.ts @@ -7,12 +7,12 @@ import type { StopTime } from '../types/gtfs'; import type { StopTimeRealtime } from '../types/gtfs-rt'; export interface StopTimeFilters { - tripId?: string; - stopId?: string; - routeId?: string; - serviceIds?: string[]; - directionId?: number; - agencyId?: string; + tripId?: string | string[]; + stopId?: string | string[]; + routeId?: string | string[]; + serviceIds?: string | string[]; + directionId?: number | number[]; + agencyId?: string | string[]; includeRealtime?: boolean; limit?: number; } @@ -39,7 +39,7 @@ function mergeRealtimeData( const placeholders = tripIds.map(() => '?').join(', '); const stmt = db.prepare(` SELECT trip_id, stop_sequence, stop_id, - arrival_delay, departure_delay, schedule_relationship + arrival_delay, arrival_time, departure_delay, departure_time, schedule_relationship FROM rt_stop_time_updates WHERE trip_id IN (${placeholders}) AND rt_last_updated >= ? @@ -53,7 +53,9 @@ function mergeRealtimeData( const key = `${row.trip_id}_${row.stop_sequence}`; rtMap.set(key, { arrival_delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined, + arrival_time: row.arrival_time !== null ? Number(row.arrival_time) : undefined, departure_delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined, + departure_time: row.departure_time !== null ? Number(row.departure_time) : undefined, schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined }); } @@ -73,6 +75,7 @@ function mergeRealtimeData( /** * Get stop times with optional filters + * - Filters support both single values and arrays */ export function getStopTimes( db: Database, @@ -90,34 +93,57 @@ export function getStopTimes( const params: (string | number)[] = []; if (tripId) { - conditions.push(needsTripsJoin ? 'st.trip_id = ?' : 'trip_id = ?'); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => '?').join(', '); + conditions.push(needsTripsJoin ? `st.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`); + params.push(...tripIds); + } } if (stopId) { - conditions.push(needsTripsJoin ? 'st.stop_id = ?' : 'stop_id = ?'); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => '?').join(', '); + conditions.push(needsTripsJoin ? `st.stop_id IN (${placeholders})` : `stop_id IN (${placeholders})`); + params.push(...stopIds); + } } if (routeId) { - conditions.push('t.route_id = ?'); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => '?').join(', '); + conditions.push(`t.route_id IN (${placeholders})`); + params.push(...routeIds); + } } - if (serviceIds && serviceIds.length > 0) { - const placeholders = serviceIds.map(() => '?').join(', '); - conditions.push(`t.service_id IN (${placeholders})`); - params.push(...serviceIds); + if (serviceIds) { + const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds]; + if (serviceIdArray.length > 0) { + const placeholders = serviceIdArray.map(() => '?').join(', '); + conditions.push(`t.service_id IN (${placeholders})`); + params.push(...serviceIdArray); + } } if (directionId !== undefined) { - conditions.push('t.direction_id = ?'); - params.push(directionId); + const directionIds = Array.isArray(directionId) ? directionId : [directionId]; + if (directionIds.length > 0) { + const placeholders = directionIds.map(() => '?').join(', '); + conditions.push(`t.direction_id IN (${placeholders})`); + params.push(...directionIds); + } } if (agencyId) { - conditions.push('r.agency_id = ?'); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => '?').join(', '); + conditions.push(`r.agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } // Build SQL query @@ -163,118 +189,6 @@ export function getStopTimes( return stopTimes; } -/** - * Get stop times for a trip - */ -export function getStopTimesByTrip(db: Database, tripId: string): StopTime[] { - const stmt = db.prepare('SELECT * FROM stop_times WHERE trip_id = ? ORDER BY stop_sequence'); - stmt.bind([tripId]); - - const stopTimes: StopTime[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - stopTimes.push(rowToStopTime(row)); - } - - stmt.free(); - return stopTimes; -} - -/** - * Get stop times for a stop - */ -export function getStopTimesByStop(db: Database, stopId: string, limit = 100): StopTime[] { - const stmt = db.prepare( - 'SELECT * FROM stop_times WHERE stop_id = ? ORDER BY arrival_time LIMIT ?' - ); - stmt.bind([stopId, limit]); - - const stopTimes: StopTime[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - stopTimes.push(rowToStopTime(row)); - } - - stmt.free(); - return stopTimes; -} - -/** - * Get stop times for a stop and specific trips (filtered by route/date) - */ -export function getStopTimesByStopAndTrips( - db: Database, - stopId: string, - tripIds: string[] -): StopTime[] { - if (tripIds.length === 0) { - return []; - } - - const placeholders = tripIds.map(() => '?').join(', '); - const stmt = db.prepare( - `SELECT * FROM stop_times - WHERE stop_id = ? AND trip_id IN (${placeholders}) - ORDER BY arrival_time` - ); - stmt.bind([stopId, ...tripIds]); - - const stopTimes: StopTime[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - stopTimes.push(rowToStopTime(row)); - } - - stmt.free(); - return stopTimes; -} - -/** - * Get stop times for a stop, route, and direction on a specific date - */ -export function getStopTimesForStopRouteDirection( - db: Database, - stopId: string, - routeId: string, - serviceIds: string[], - directionId?: number -): StopTime[] { - if (serviceIds.length === 0) { - return []; - } - - const placeholders = serviceIds.map(() => '?').join(', '); - - let sql = ` - SELECT st.* FROM stop_times st - INNER JOIN trips t ON st.trip_id = t.trip_id - WHERE st.stop_id = ? - AND t.route_id = ? - AND t.service_id IN (${placeholders}) - `; - - const params: (string | number)[] = [stopId, routeId, ...serviceIds]; - - if (directionId !== undefined) { - sql += ' AND t.direction_id = ?'; - params.push(directionId); - } - - sql += ' ORDER BY st.arrival_time'; - - const stmt = db.prepare(sql); - stmt.bind(params); - - const stopTimes: StopTime[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - stopTimes.push(rowToStopTime(row)); - } - - stmt.free(); - return stopTimes; -} - /** * Convert database row to StopTime object */ diff --git a/src/queries/stops.ts b/src/queries/stops.ts index f58da3e..823c039 100644 --- a/src/queries/stops.ts +++ b/src/queries/stops.ts @@ -6,22 +6,44 @@ import type { Database } from 'sql.js'; import type { Stop } from '../types/gtfs'; export interface StopFilters { - stopId?: string; - stopCode?: string; + stopId?: string | string[]; + stopCode?: string | string[]; name?: string; - tripId?: string; + tripId?: string | string[]; limit?: number; } /** * Get stops with optional filters + * - Filters support both single values and arrays + * - Use name filter for partial name matching + * - Use tripId filter to get stops for a specific trip (ordered by stop_sequence) */ export function getStops(db: Database, filters: StopFilters = {}): Stop[] { const { stopId, stopCode, name, tripId, limit } = filters; // Handle special case: get stops by trip (requires JOIN) if (tripId) { - return getStopsByTrip(db, tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length === 0) return []; + + const placeholders = tripIds.map(() => '?').join(', '); + const stmt = db.prepare(` + SELECT s.* FROM stops s + INNER JOIN stop_times st ON s.stop_id = st.stop_id + WHERE st.trip_id IN (${placeholders}) + ORDER BY st.stop_sequence + `); + stmt.bind(tripIds); + + const stops: Stop[] = []; + while (stmt.step()) { + const row = stmt.getAsObject() as Record; + stops.push(rowToStop(row)); + } + + stmt.free(); + return stops; } // Build WHERE clause dynamically @@ -29,13 +51,21 @@ export function getStops(db: Database, filters: StopFilters = {}): Stop[] { const params: (string | number)[] = []; if (stopId) { - conditions.push('stop_id = ?'); - params.push(stopId); + const stopIds = Array.isArray(stopId) ? stopId : [stopId]; + if (stopIds.length > 0) { + const placeholders = stopIds.map(() => '?').join(', '); + conditions.push(`stop_id IN (${placeholders})`); + params.push(...stopIds); + } } if (stopCode) { - conditions.push('stop_code = ?'); - params.push(stopCode); + const stopCodes = Array.isArray(stopCode) ? stopCode : [stopCode]; + if (stopCodes.length > 0) { + const placeholders = stopCodes.map(() => '?').join(', '); + conditions.push(`stop_code IN (${placeholders})`); + params.push(...stopCodes); + } } if (name) { @@ -69,99 +99,12 @@ export function getStops(db: Database, filters: StopFilters = {}): Stop[] { return stops; } -/** - * Get a stop by its stop_id - */ -export function getStopById(db: Database, stopId: string): Stop | null { - const stmt = db.prepare('SELECT * FROM stops WHERE stop_id = ?'); - stmt.bind([stopId]); - - if (stmt.step()) { - const row = stmt.getAsObject() as Record; - stmt.free(); - return rowToStop(row); - } - - stmt.free(); - return null; -} - -/** - * Get a stop by its stop_code - */ -export function getStopByCode(db: Database, stopCode: string): Stop | null { - const stmt = db.prepare('SELECT * FROM stops WHERE stop_code = ?'); - stmt.bind([stopCode]); - - if (stmt.step()) { - const row = stmt.getAsObject() as Record; - stmt.free(); - return rowToStop(row); - } - - stmt.free(); - return null; -} - /** * Search stops by name (case-insensitive, partial match) + * This is a convenience method for name-based searches */ export function searchStopsByName(db: Database, name: string, limit = 50): Stop[] { - const stmt = db.prepare( - 'SELECT * FROM stops WHERE stop_name LIKE ? ORDER BY stop_name LIMIT ?' - ); - stmt.bind([`%${name}%`, limit]); - - const stops: Stop[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - stops.push(rowToStop(row)); - } - - stmt.free(); - return stops; -} - -/** - * Get all stops - */ -export function getAllStops(db: Database, limit?: number): Stop[] { - const sql = limit - ? `SELECT * FROM stops ORDER BY stop_name LIMIT ${limit}` - : 'SELECT * FROM stops ORDER BY stop_name'; - - const stmt = db.prepare(sql); - - const stops: Stop[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - stops.push(rowToStop(row)); - } - - stmt.free(); - return stops; -} - -/** - * Get stops for a given trip (ordered by stop_sequence) - */ -export function getStopsByTrip(db: Database, tripId: string): Stop[] { - const stmt = db.prepare(` - SELECT s.* FROM stops s - INNER JOIN stop_times st ON s.stop_id = st.stop_id - WHERE st.trip_id = ? - ORDER BY st.stop_sequence - `); - stmt.bind([tripId]); - - const stops: Stop[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - stops.push(rowToStop(row)); - } - - stmt.free(); - return stops; + return getStops(db, { name, limit }); } /** diff --git a/src/queries/trips.ts b/src/queries/trips.ts index 3660ac8..f101243 100644 --- a/src/queries/trips.ts +++ b/src/queries/trips.ts @@ -8,11 +8,11 @@ import type { TripRealtime, VehiclePosition } from '../types/gtfs-rt'; import { parseVehiclePosition } from './rt-vehicle-positions'; export interface TripFilters { - tripId?: string; - routeId?: string; - serviceIds?: string[]; - directionId?: number; - agencyId?: string; + tripId?: string | string[]; + routeId?: string | string[]; + serviceIds?: string | string[]; + directionId?: number | number[]; + agencyId?: string | string[]; includeRealtime?: boolean; limit?: number; } @@ -93,6 +93,7 @@ function mergeRealtimeData( /** * Get trips with optional filters + * - Filters support both single values and arrays */ export function getTrips( db: Database, @@ -109,29 +110,48 @@ export function getTrips( const params: (string | number)[] = []; if (tripId) { - conditions.push(needsRoutesJoin ? 't.trip_id = ?' : 'trip_id = ?'); - params.push(tripId); + const tripIds = Array.isArray(tripId) ? tripId : [tripId]; + if (tripIds.length > 0) { + const placeholders = tripIds.map(() => '?').join(', '); + conditions.push(needsRoutesJoin ? `t.trip_id IN (${placeholders})` : `trip_id IN (${placeholders})`); + params.push(...tripIds); + } } if (routeId) { - conditions.push(needsRoutesJoin ? 't.route_id = ?' : 'route_id = ?'); - params.push(routeId); + const routeIds = Array.isArray(routeId) ? routeId : [routeId]; + if (routeIds.length > 0) { + const placeholders = routeIds.map(() => '?').join(', '); + conditions.push(needsRoutesJoin ? `t.route_id IN (${placeholders})` : `route_id IN (${placeholders})`); + params.push(...routeIds); + } } - if (serviceIds && serviceIds.length > 0) { - const placeholders = serviceIds.map(() => '?').join(', '); - conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`); - params.push(...serviceIds); + if (serviceIds) { + const serviceIdArray = Array.isArray(serviceIds) ? serviceIds : [serviceIds]; + if (serviceIdArray.length > 0) { + const placeholders = serviceIdArray.map(() => '?').join(', '); + conditions.push(needsRoutesJoin ? `t.service_id IN (${placeholders})` : `service_id IN (${placeholders})`); + params.push(...serviceIdArray); + } } if (directionId !== undefined) { - conditions.push(needsRoutesJoin ? 't.direction_id = ?' : 'direction_id = ?'); - params.push(directionId); + const directionIds = Array.isArray(directionId) ? directionId : [directionId]; + if (directionIds.length > 0) { + const placeholders = directionIds.map(() => '?').join(', '); + conditions.push(needsRoutesJoin ? `t.direction_id IN (${placeholders})` : `direction_id IN (${placeholders})`); + params.push(...directionIds); + } } if (agencyId) { - conditions.push('r.agency_id = ?'); - params.push(agencyId); + const agencyIds = Array.isArray(agencyId) ? agencyId : [agencyId]; + if (agencyIds.length > 0) { + const placeholders = agencyIds.map(() => '?').join(', '); + conditions.push(`r.agency_id IN (${placeholders})`); + params.push(...agencyIds); + } } // Build SQL query @@ -168,119 +188,6 @@ export function getTrips( return trips; } -/** - * Get a trip by its trip_id - */ -export function getTripById(db: Database, tripId: string): Trip | null { - const stmt = db.prepare('SELECT * FROM trips WHERE trip_id = ?'); - stmt.bind([tripId]); - - if (stmt.step()) { - const row = stmt.getAsObject() as Record; - stmt.free(); - return rowToTrip(row); - } - - stmt.free(); - return null; -} - -/** - * Get trips for a route - */ -export function getTripsByRoute(db: Database, routeId: string): Trip[] { - const stmt = db.prepare('SELECT * FROM trips WHERE route_id = ?'); - stmt.bind([routeId]); - - const trips: Trip[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - trips.push(rowToTrip(row)); - } - - stmt.free(); - return trips; -} - -/** - * Get trips for a route and service (active on a given date) - */ -export function getTripsByRouteAndService( - db: Database, - routeId: string, - serviceIds: string[] -): Trip[] { - if (serviceIds.length === 0) { - return []; - } - - const placeholders = serviceIds.map(() => '?').join(', '); - const stmt = db.prepare( - `SELECT * FROM trips WHERE route_id = ? AND service_id IN (${placeholders})` - ); - stmt.bind([routeId, ...serviceIds]); - - const trips: Trip[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - trips.push(rowToTrip(row)); - } - - stmt.free(); - return trips; -} - -/** - * Get trips for a route, service, and direction - */ -export function getTripsByRouteServiceAndDirection( - db: Database, - routeId: string, - serviceIds: string[], - directionId: number -): Trip[] { - if (serviceIds.length === 0) { - return []; - } - - const placeholders = serviceIds.map(() => '?').join(', '); - const stmt = db.prepare( - `SELECT * FROM trips WHERE route_id = ? AND service_id IN (${placeholders}) AND direction_id = ?` - ); - stmt.bind([routeId, ...serviceIds, directionId]); - - const trips: Trip[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - trips.push(rowToTrip(row)); - } - - stmt.free(); - return trips; -} - -/** - * Get all trips for a service (active on a given date) - */ -export function getTripsByService(db: Database, serviceIds: string[]): Trip[] { - if (serviceIds.length === 0) { - return []; - } - - const placeholders = serviceIds.map(() => '?').join(', '); - const stmt = db.prepare(`SELECT * FROM trips WHERE service_id IN (${placeholders})`); - stmt.bind(serviceIds); - - const trips: Trip[] = []; - while (stmt.step()) { - const row = stmt.getAsObject() as Record; - trips.push(rowToTrip(row)); - } - - stmt.free(); - return trips; -} - /** * Convert database row to Trip object */ diff --git a/src/types/gtfs-rt.ts b/src/types/gtfs-rt.ts index c5a1a01..8dd192e 100644 --- a/src/types/gtfs-rt.ts +++ b/src/types/gtfs-rt.ts @@ -165,7 +165,9 @@ export interface TripUpdate { export interface StopTimeRealtime { arrival_delay?: number; // seconds + arrival_time?: number; // UNIX timestamp (absolute time) departure_delay?: number; // seconds + departure_time?: number; // UNIX timestamp (absolute time) schedule_relationship?: ScheduleRelationship; } diff --git a/tests/gtfs-sqljs.test.ts b/tests/gtfs-sqljs.test.ts index e861bed..d56c3f5 100644 --- a/tests/gtfs-sqljs.test.ts +++ b/tests/gtfs-sqljs.test.ts @@ -27,11 +27,11 @@ describe('GtfsSqlJs', () => { }); describe('Stop methods', () => { - it('should get stop by ID', () => { - const stop = gtfs.getStopById('STOP1'); - expect(stop).toBeDefined(); - expect(stop?.stop_id).toBe('STOP1'); - expect(stop?.stop_name).toBe('First Street'); + it('should get stop by ID using filters', () => { + const stops = gtfs.getStops({ stopId: 'STOP1' }); + expect(stops.length).toBe(1); + expect(stops[0].stop_id).toBe('STOP1'); + expect(stops[0].stop_name).toBe('First Street'); }); it('should get stop by code', () => { @@ -46,23 +46,28 @@ describe('GtfsSqlJs', () => { expect(stops[0].stop_name).toContain('Street'); }); - it('should return null for non-existent stop ID', () => { - const stop = gtfs.getStopById('NONEXISTENT'); - expect(stop).toBeNull(); + it('should return empty array for non-existent stop ID', () => { + const stops = gtfs.getStops({ stopId: 'NONEXISTENT' }); + expect(stops.length).toBe(0); }); it('should get all stops', () => { const stops = gtfs.getStops(); expect(stops.length).toBeGreaterThan(0); }); + + it('should get multiple stops by ID array', () => { + const stops = gtfs.getStops({ stopId: ['STOP1', 'STOP2'] }); + expect(stops.length).toBe(2); + }); }); describe('Route methods', () => { - it('should get route by ID', () => { - const route = gtfs.getRouteById('ROUTE1'); - expect(route).toBeDefined(); - expect(route?.route_id).toBe('ROUTE1'); - expect(route?.route_short_name).toBe('1'); + it('should get route by ID using filters', () => { + const routes = gtfs.getRoutes({ routeId: 'ROUTE1' }); + expect(routes.length).toBe(1); + expect(routes[0].route_id).toBe('ROUTE1'); + expect(routes[0].route_short_name).toBe('1'); }); it('should get all routes', () => { @@ -70,9 +75,14 @@ describe('GtfsSqlJs', () => { expect(routes.length).toBeGreaterThan(0); }); - it('should return null for non-existent route', () => { - const route = gtfs.getRouteById('NONEXISTENT'); - expect(route).toBeNull(); + it('should return empty array for non-existent route', () => { + const routes = gtfs.getRoutes({ routeId: 'NONEXISTENT' }); + expect(routes.length).toBe(0); + }); + + it('should get multiple routes by ID array', () => { + const routes = gtfs.getRoutes({ routeId: ['ROUTE1', 'ROUTE2'] }); + expect(routes.length).toBeGreaterThanOrEqual(1); }); }); @@ -97,11 +107,11 @@ describe('GtfsSqlJs', () => { }); describe('Trip methods', () => { - it('should get trip by ID', () => { - const trip = gtfs.getTripById('TRIP1'); - expect(trip).toBeDefined(); - expect(trip?.trip_id).toBe('TRIP1'); - expect(trip?.route_id).toBe('ROUTE1'); + it('should get trip by ID using filters', () => { + const trips = gtfs.getTrips({ tripId: 'TRIP1' }); + expect(trips.length).toBe(1); + expect(trips[0].trip_id).toBe('TRIP1'); + expect(trips[0].route_id).toBe('ROUTE1'); }); it('should get trips by route', () => { @@ -119,11 +129,16 @@ describe('GtfsSqlJs', () => { expect(trips.length).toBeGreaterThan(0); expect(trips.every(t => t.direction_id === 0)).toBe(true); }); + + it('should get multiple trips by ID array', () => { + const trips = gtfs.getTrips({ tripId: ['TRIP1', 'TRIP2'] }); + expect(trips.length).toBeGreaterThanOrEqual(1); + }); }); describe('Stop time methods', () => { - it('should get stop times by trip', () => { - const stopTimes = gtfs.getStopTimesByTrip('TRIP1'); + it('should get stop times by trip using filters', () => { + const stopTimes = gtfs.getStopTimes({ tripId: 'TRIP1' }); expect(stopTimes.length).toBeGreaterThan(0); expect(stopTimes[0].trip_id).toBe('TRIP1'); }); @@ -142,6 +157,11 @@ describe('GtfsSqlJs', () => { const stopTimes = gtfs.getStopTimes({ stopId: 'STOP1', routeId: 'ROUTE1', date: '20240101', directionId: 0 }); expect(stopTimes.length).toBeGreaterThan(0); }); + + it('should get multiple stop times by trip ID array', () => { + const stopTimes = gtfs.getStopTimes({ tripId: ['TRIP1', 'TRIP2'] }); + expect(stopTimes.length).toBeGreaterThan(0); + }); }); describe('Database export', () => { @@ -155,9 +175,9 @@ describe('GtfsSqlJs', () => { const buffer = gtfs.export(); const newGtfs = await GtfsSqlJs.fromDatabase(buffer, { SQL }); - const stop = newGtfs.getStopById('STOP1'); - expect(stop).toBeDefined(); - expect(stop?.stop_name).toBe('First Street'); + const stops = newGtfs.getStops({ stopId: 'STOP1' }); + expect(stops.length).toBe(1); + expect(stops[0].stop_name).toBe('First Street'); newGtfs.close(); }); @@ -166,7 +186,7 @@ describe('GtfsSqlJs', () => { describe('Error handling', () => { it('should throw error when accessing closed database', () => { const closedGtfs = new (GtfsSqlJs as any)(); - expect(() => closedGtfs.getStopById('STOP1')).toThrow('Database not initialized'); + expect(() => closedGtfs.getStops({ stopId: 'STOP1' })).toThrow('Database not initialized'); }); }); }); diff --git a/tests/sample-feed.test.ts b/tests/sample-feed.test.ts index 6a0fb06..218f95b 100644 --- a/tests/sample-feed.test.ts +++ b/tests/sample-feed.test.ts @@ -48,7 +48,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { }); it('should have BEATTY_AIRPORT with correct details', () => { - const stop = gtfs.getStopById('BEATTY_AIRPORT'); + const stop = gtfs.getStops({ stopId: 'BEATTY_AIRPORT' })[0]; expect(stop).not.toBeNull(); expect(stop!.stop_id).toBe('BEATTY_AIRPORT'); @@ -58,7 +58,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { }); it('should have BULLFROG with correct details', () => { - const stop = gtfs.getStopById('BULLFROG'); + const stop = gtfs.getStops({ stopId: 'BULLFROG' })[0]; expect(stop).not.toBeNull(); expect(stop!.stop_id).toBe('BULLFROG'); @@ -105,7 +105,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { }); it('should have route AB (Airport - Bullfrog)', () => { - const route = gtfs.getRouteById('AB'); + const route = gtfs.getRoutes({ routeId: 'AB' })[0]; expect(route).not.toBeNull(); expect(route!.route_id).toBe('AB'); @@ -116,7 +116,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { }); it('should have route BFC (Bullfrog - Furnace Creek Resort)', () => { - const route = gtfs.getRouteById('BFC'); + const route = gtfs.getRoutes({ routeId: 'BFC' })[0]; expect(route).not.toBeNull(); expect(route!.route_id).toBe('BFC'); @@ -126,7 +126,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { }); it('should have route CITY (City)', () => { - const route = gtfs.getRouteById('CITY'); + const route = gtfs.getRoutes({ routeId: 'CITY' })[0]; expect(route).not.toBeNull(); expect(route!.route_id).toBe('CITY'); @@ -205,7 +205,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { describe('Trips', () => { it('should have trip AB1 (to Bullfrog)', () => { - const trip = gtfs.getTripById('AB1'); + const trip = gtfs.getTrips({ tripId: 'AB1' })[0]; expect(trip).not.toBeNull(); expect(trip!.trip_id).toBe('AB1'); @@ -217,7 +217,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { }); it('should have trip AB2 (to Airport)', () => { - const trip = gtfs.getTripById('AB2'); + const trip = gtfs.getTrips({ tripId: 'AB2' })[0]; expect(trip).not.toBeNull(); expect(trip!.trip_id).toBe('AB2'); @@ -275,7 +275,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { describe('Stop Times', () => { it('should have correct stop times for trip AB1', () => { - const stopTimes = gtfs.getStopTimesByTrip('AB1'); + const stopTimes = gtfs.getStopTimes({ tripId: 'AB1' }); expect(stopTimes.length).toBe(2); @@ -295,7 +295,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { }); it('should have correct stop times for trip CITY1', () => { - const stopTimes = gtfs.getStopTimesByTrip('CITY1'); + const stopTimes = gtfs.getStopTimes({ tripId: 'CITY1' }); expect(stopTimes.length).toBe(5); @@ -354,14 +354,14 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { describe('Complete Journey Scenarios', () => { it('should plan journey from Airport to Bullfrog at 8am', () => { // User wants to go from BEATTY_AIRPORT to BULLFROG - const origin = gtfs.getStopById('BEATTY_AIRPORT'); - const destination = gtfs.getStopById('BULLFROG'); + const origin = gtfs.getStops({ stopId: 'BEATTY_AIRPORT' })[0]; + const destination = gtfs.getStops({ stopId: 'BULLFROG' })[0]; expect(origin).not.toBeNull(); expect(destination).not.toBeNull(); // Find route AB - const route = gtfs.getRouteById('AB'); + const route = gtfs.getRoutes({ routeId: 'AB' })[0]; expect(route).not.toBeNull(); // Get trips on a Monday @@ -373,7 +373,7 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { expect(trip!.trip_id).toBe('AB1'); // Get stop times - const stopTimes = gtfs.getStopTimesByTrip('AB1'); + const stopTimes = gtfs.getStopTimes({ tripId: 'AB1' }); expect(stopTimes[0].stop_id).toBe('BEATTY_AIRPORT'); expect(stopTimes[0].departure_time).toBe('8:00:00'); expect(stopTimes[1].stop_id).toBe('BULLFROG'); @@ -397,9 +397,9 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { // Get routes for these trips const routes = new Set(); for (const tripId of tripIds) { - const trip = gtfs.getTripById(tripId); - if (trip) { - routes.add(trip.route_id); + const trips = gtfs.getTrips({ tripId }); + if (trips.length > 0) { + routes.add(trips[0].route_id); } } @@ -410,12 +410,12 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { }); it('should find city circuit route with all stops', () => { - const route = gtfs.getRouteById('CITY'); + const route = gtfs.getRoutes({ routeId: 'CITY' })[0]; expect(route).not.toBeNull(); // Get both directions - const trip1 = gtfs.getTripById('CITY1'); - const trip2 = gtfs.getTripById('CITY2'); + const trip1 = gtfs.getTrips({ tripId: 'CITY1' })[0]; + const trip2 = gtfs.getTrips({ tripId: 'CITY2' })[0]; const stops1 = gtfs.getStops({ tripId: 'CITY1' }); const stops2 = gtfs.getStops({ tripId: 'CITY2' }); @@ -452,12 +452,12 @@ describe('Sample GTFS Feed Tests - Actual Data', () => { expect(agencyData.agency_name).toBe('Demo Transit Authority'); // Verify route - const route = gtfs2.getRouteById('AB'); + const route = gtfs2.getRoutes({ routeId: 'AB' })[0]; expect(route).not.toBeNull(); expect(route!.route_long_name).toBe('Airport - Bullfrog'); // Verify trip - const trip = gtfs2.getTripById('AB1'); + const trip = gtfs2.getTrips({ tripId: 'AB1' })[0]; expect(trip).not.toBeNull(); expect(trip!.trip_headsign).toBe('to Bullfrog');