diff --git a/dist/index.d.mts b/dist/index.d.mts index 4c91b69..3882845 100644 --- a/dist/index.d.mts +++ b/dist/index.d.mts @@ -479,6 +479,23 @@ interface StopTimeUpdateWithMetadata extends StopTimeUpdate { rt_last_updated: number; } +/** + * Progress information for GTFS data loading + */ +interface ProgressInfo { + phase: 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'complete'; + currentFile: string | null; + filesCompleted: number; + totalFiles: number; + rowsProcessed: number; + totalRows: number; + percentComplete: number; + message: string; +} +/** + * Progress callback function type + */ +type ProgressCallback = (progress: ProgressInfo) => void; interface GtfsSqlJsOptions { /** * Path or URL to GTFS ZIP file @@ -510,6 +527,11 @@ interface GtfsSqlJsOptions { * Realtime data older than this will be excluded from queries */ stalenessThreshold?: number; + /** + * Optional: Progress callback for tracking load progress + * Useful for displaying progress in UI or web workers + */ + onProgress?: ProgressCallback; } declare class GtfsSqlJs { private db; diff --git a/dist/index.d.ts b/dist/index.d.ts index 4c91b69..3882845 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -479,6 +479,23 @@ interface StopTimeUpdateWithMetadata extends StopTimeUpdate { rt_last_updated: number; } +/** + * Progress information for GTFS data loading + */ +interface ProgressInfo { + phase: 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'complete'; + currentFile: string | null; + filesCompleted: number; + totalFiles: number; + rowsProcessed: number; + totalRows: number; + percentComplete: number; + message: string; +} +/** + * Progress callback function type + */ +type ProgressCallback = (progress: ProgressInfo) => void; interface GtfsSqlJsOptions { /** * Path or URL to GTFS ZIP file @@ -510,6 +527,11 @@ interface GtfsSqlJsOptions { * Realtime data older than this will be excluded from queries */ stalenessThreshold?: number; + /** + * Optional: Progress callback for tracking load progress + * Useful for displaying progress in UI or web workers + */ + onProgress?: ProgressCallback; } declare class GtfsSqlJs { private db; diff --git a/dist/index.js b/dist/index.js index 4cda141..ae52210 100644 --- a/dist/index.js +++ b/dist/index.js @@ -318,10 +318,16 @@ function generateCreateIndexSQL(schema) { return `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name} (${columns})`; }); } -function getAllCreateStatements() { +function getAllCreateTableStatements() { const statements = []; for (const schema of GTFS_SCHEMA) { statements.push(generateCreateTableSQL(schema)); + } + return statements; +} +function getAllCreateIndexStatements() { + const statements = []; + for (const schema of GTFS_SCHEMA) { statements.push(...generateCreateIndexSQL(schema)); } return statements; @@ -400,74 +406,150 @@ function parseCSV(text) { const rows = result.data; return { headers, rows }; } -function convertRowTypes(row, columnTypes) { - const result = {}; - for (const [key, type] of Object.entries(columnTypes)) { - const value = row[key]; - if (value === void 0 || value === null || value === "") { - result[key] = null; - continue; - } - if (type === "INTEGER") { - const parsed = parseInt(value, 10); - result[key] = isNaN(parsed) ? null : parsed; - } else if (type === "REAL") { - const parsed = parseFloat(value); - result[key] = isNaN(parsed) ? null : parsed; - } else { - result[key] = value; - } - } - return result; -} // src/loaders/data-loader.ts -async function loadGTFSData(db, files, skipFiles) { +async function loadGTFSData(db, files, skipFiles, onProgress) { const fileToSchema = /* @__PURE__ */ new Map(); for (const schema of GTFS_SCHEMA) { fileToSchema.set(`${schema.name}.txt`, schema); } const skipSet = new Set(skipFiles?.map((f) => f.toLowerCase()) || []); + const filePriority = [ + "agency.txt", + "feed_info.txt", + "attributions.txt", + "levels.txt", + "routes.txt", + "calendar.txt", + "calendar_dates.txt", + "fare_attributes.txt", + "fare_rules.txt", + "stops.txt", + "pathways.txt", + "transfers.txt", + "trips.txt", + "frequencies.txt", + "shapes.txt", + "stop_times.txt" + // Largest file - process last + ]; + const sortedFiles = []; + for (const priorityFile of filePriority) { + if (files[priorityFile]) { + sortedFiles.push([priorityFile, files[priorityFile]]); + } + } for (const [fileName, content] of Object.entries(files)) { + if (!filePriority.includes(fileName)) { + sortedFiles.push([fileName, content]); + } + } + let totalRows = 0; + const fileRowCounts = /* @__PURE__ */ new Map(); + for (const [fileName, content] of sortedFiles) { + const schema = fileToSchema.get(fileName); + if (schema && !skipSet.has(fileName.toLowerCase())) { + const { rows } = parseCSV(content); + fileRowCounts.set(fileName, rows.length); + totalRows += rows.length; + } + } + let rowsProcessed = 0; + let filesCompleted = 0; + for (const [fileName, content] of sortedFiles) { const schema = fileToSchema.get(fileName); if (!schema) { continue; } if (skipSet.has(fileName.toLowerCase())) { console.log(`Skipping import of ${fileName} (table ${schema.name} created but empty)`); + filesCompleted++; continue; } - await loadTableData(db, schema, content); + const fileRows = fileRowCounts.get(fileName) || 0; + onProgress?.({ + phase: "inserting_data", + currentFile: fileName, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed, + totalRows, + percentComplete: 15 + Math.floor(rowsProcessed / totalRows * 70), + message: `Loading ${fileName} (${fileRows.toLocaleString()} rows)` + }); + await loadTableData(db, schema, content, (processedInFile) => { + const currentProgress = rowsProcessed + processedInFile; + onProgress?.({ + phase: "inserting_data", + currentFile: fileName, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed: currentProgress, + totalRows, + percentComplete: 15 + Math.floor(currentProgress / totalRows * 70), + message: `Loading ${fileName} (${processedInFile.toLocaleString()}/${fileRows.toLocaleString()} rows)` + }); + }); + rowsProcessed += fileRows; + filesCompleted++; + onProgress?.({ + phase: "inserting_data", + currentFile: null, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed, + totalRows, + percentComplete: 15 + Math.floor(rowsProcessed / totalRows * 70), + message: `Completed ${fileName}` + }); } } -async function loadTableData(db, schema, csvContent) { +async function loadTableData(db, schema, csvContent, onProgress) { const { headers, rows } = parseCSV(csvContent); if (rows.length === 0) { return; } - const columnTypes = {}; - for (const col of schema.columns) { - columnTypes[col.name] = col.type; - } const columns = headers.filter((h) => schema.columns.some((c) => c.name === h)); - const placeholders = columns.map(() => "?").join(", "); - const insertSQL = `INSERT INTO ${schema.name} (${columns.join(", ")}) VALUES (${placeholders})`; - const stmt = db.prepare(insertSQL); - for (const row of rows) { - const typedRow = convertRowTypes(row, columnTypes); - const values = columns.map((col) => { - const value = typedRow[col]; - return value === null || value === void 0 ? null : value; - }); + if (columns.length === 0) { + return; + } + const BATCH_SIZE = 1e3; + let rowsProcessed = 0; + db.run("BEGIN TRANSACTION"); + try { + for (let i = 0; i < rows.length; i += BATCH_SIZE) { + const batchRows = rows.slice(i, Math.min(i + BATCH_SIZE, rows.length)); + const placeholders = batchRows.map(() => `(${columns.map(() => "?").join(", ")})`).join(", "); + const insertSQL = `INSERT INTO ${schema.name} (${columns.join(", ")}) VALUES ${placeholders}`; + const allValues = []; + for (const row of batchRows) { + for (const col of columns) { + const value = row[col]; + allValues.push(value === null || value === void 0 || value === "" ? null : value); + } + } + const stmt = db.prepare(insertSQL); + try { + stmt.run(allValues); + } catch (error) { + console.error(`Error inserting batch into ${schema.name}:`, error); + console.error("Batch size:", batchRows.length); + throw error; + } finally { + stmt.free(); + } + rowsProcessed += batchRows.length; + onProgress?.(rowsProcessed); + } + db.run("COMMIT"); + } catch (error) { try { - stmt.run(values); - } catch (error) { - console.error(`Error inserting row into ${schema.name}:`, error); - console.error("Row data:", row); - throw error; + db.run("ROLLBACK"); + } catch (rollbackError) { + console.error("Error rolling back transaction:", rollbackError); } + throw error; } - stmt.free(); } // src/schema/gtfs-rt-schema.ts @@ -813,12 +895,8 @@ function insertAlerts(db, alerts, timestamp) { ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `); for (const alert of alerts) { - const activePeriod = alert.activePeriod || alert.active_period || []; - const informedEntity = alert.informedEntity || alert.informed_entity || []; - const headerText = alert.headerText || alert.header_text; - const descriptionText = alert.descriptionText || alert.description_text; - const activePeriodSnake = convertKeysToSnakeCase(activePeriod); - const informedEntitySnake = convertKeysToSnakeCase(informedEntity); + const activePeriodSnake = convertKeysToSnakeCase(alert.activePeriod || []); + const informedEntitySnake = convertKeysToSnakeCase(alert.informedEntity || []); stmt.run([ alert.id, JSON.stringify(activePeriodSnake), @@ -826,8 +904,8 @@ function insertAlerts(db, alerts, timestamp) { alert.cause || null, alert.effect || null, parseTranslatedString(alert.url), - parseTranslatedString(headerText), - parseTranslatedString(descriptionText), + parseTranslatedString(alert.headerText), + parseTranslatedString(alert.descriptionText), timestamp ]); } @@ -844,34 +922,24 @@ function insertVehiclePositions(db, positions, timestamp) { `); for (const vp of positions) { const trip = vp.trip; - if (!trip || !(trip.tripId || trip.trip_id)) continue; - const tripId = trip.tripId || trip.trip_id; - const routeId = trip.routeId || trip.route_id; - const vehicleId = vp.vehicle?.id; - const vehicleLabel = vp.vehicle?.label; - const vehicleLicensePlate = vp.vehicle?.licensePlate || vp.vehicle?.license_plate; - const currentStopSequence = vp.currentStopSequence || vp.current_stop_sequence; - const stopId = vp.stopId || vp.stop_id; - const currentStatus = vp.currentStatus || vp.current_status; - const congestionLevel = vp.congestionLevel || vp.congestion_level; - const occupancyStatus = vp.occupancyStatus || vp.occupancy_status; + if (!trip || !trip.tripId) continue; stmt.run([ - tripId, - routeId || null, - vehicleId || null, - vehicleLabel || null, - vehicleLicensePlate || null, + trip.tripId, + trip.routeId || null, + vp.vehicle?.id || null, + vp.vehicle?.label || null, + vp.vehicle?.licensePlate || null, vp.position?.latitude || null, vp.position?.longitude || null, vp.position?.bearing || null, vp.position?.odometer || null, vp.position?.speed || null, - currentStopSequence || null, - stopId || null, - currentStatus || null, + vp.currentStopSequence || null, + vp.stopId || null, + vp.currentStatus || null, vp.timestamp || null, - congestionLevel || null, - occupancyStatus || null, + vp.congestionLevel || null, + vp.occupancyStatus || null, timestamp ]); } @@ -894,41 +962,31 @@ function insertTripUpdates(db, updates, timestamp) { `); for (const tu of updates) { const trip = tu.trip; - if (!trip || !(trip.tripId || trip.trip_id)) continue; - const tripId = trip.tripId || trip.trip_id; - const routeId = trip.routeId || trip.route_id; - const vehicleId = tu.vehicle?.id; - const vehicleLabel = tu.vehicle?.label; - const vehicleLicensePlate = tu.vehicle?.licensePlate || tu.vehicle?.license_plate; - const scheduleRelationship = trip.scheduleRelationship || trip.schedule_relationship; - const stopTimeUpdate = tu.stopTimeUpdate || tu.stop_time_update; + if (!trip || !trip.tripId) continue; tripStmt.run([ - tripId, - routeId || null, - vehicleId || null, - vehicleLabel || null, - vehicleLicensePlate || null, + trip.tripId, + trip.routeId || null, + tu.vehicle?.id || null, + tu.vehicle?.label || null, + tu.vehicle?.licensePlate || null, tu.timestamp || null, tu.delay || null, - scheduleRelationship || null, + trip.scheduleRelationship || null, timestamp ]); - if (stopTimeUpdate) { - for (const stu of stopTimeUpdate) { - const stopSequence = stu.stopSequence || stu.stop_sequence; - const stopId = stu.stopId || stu.stop_id; - const scheduleRel = stu.scheduleRelationship || stu.schedule_relationship; + if (tu.stopTimeUpdate) { + for (const stu of tu.stopTimeUpdate) { stopTimeStmt.run([ - tripId, - stopSequence || null, - stopId || null, + trip.tripId, + stu.stopSequence || null, + stu.stopId || null, stu.arrival?.delay || null, stu.arrival?.time || null, stu.arrival?.uncertainty || null, stu.departure?.delay || null, stu.departure?.time || null, stu.departure?.uncertainty || null, - scheduleRel || null, + stu.scheduleRelationship || null, timestamp ]); } @@ -1950,21 +2008,127 @@ var GtfsSqlJs = class _GtfsSqlJs { * Initialize from ZIP file */ async initFromZip(zipPath, options) { + const onProgress = options.onProgress; + onProgress?.({ + phase: "downloading", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 0, + message: "Initializing database engine" + }); this.SQL = options.SQL || await (0, import_sql.default)(options.locateFile ? { locateFile: options.locateFile } : {}); this.db = new this.SQL.Database(); - const createStatements = getAllCreateStatements(); - for (const statement of createStatements) { + onProgress?.({ + phase: "creating_schema", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 2, + message: "Optimizing database for bulk import" + }); + this.db.run("PRAGMA synchronous = OFF"); + this.db.run("PRAGMA journal_mode = MEMORY"); + this.db.run("PRAGMA temp_store = MEMORY"); + this.db.run("PRAGMA cache_size = -64000"); + this.db.run("PRAGMA locking_mode = EXCLUSIVE"); + onProgress?.({ + phase: "creating_schema", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 5, + message: "Creating database tables" + }); + const createTableStatements = getAllCreateTableStatements(); + for (const statement of createTableStatements) { this.db.run(statement); } createRealtimeTables(this.db); + onProgress?.({ + phase: "extracting", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 10, + message: "Extracting GTFS ZIP file" + }); const files = await loadGTFSZip(zipPath); - await loadGTFSData(this.db, files, options.skipFiles); + onProgress?.({ + phase: "inserting_data", + currentFile: null, + filesCompleted: 0, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 15, + message: "Starting data import" + }); + await loadGTFSData(this.db, files, options.skipFiles, onProgress); + onProgress?.({ + phase: "creating_indexes", + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 85, + message: "Creating database indexes" + }); + const createIndexStatements = getAllCreateIndexStatements(); + let indexCount = 0; + for (const statement of createIndexStatements) { + this.db.run(statement); + indexCount++; + const indexProgress = 85 + Math.floor(indexCount / createIndexStatements.length * 10); + onProgress?.({ + phase: "creating_indexes", + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: indexProgress, + message: `Creating indexes (${indexCount}/${createIndexStatements.length})` + }); + } + onProgress?.({ + phase: "analyzing", + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 95, + message: "Optimizing query performance" + }); + this.db.run("ANALYZE"); + this.db.run("PRAGMA synchronous = FULL"); + this.db.run("PRAGMA locking_mode = NORMAL"); if (options.realtimeFeedUrls) { this.realtimeFeedUrls = options.realtimeFeedUrls; } if (options.stalenessThreshold !== void 0) { this.stalenessThreshold = options.stalenessThreshold; } + onProgress?.({ + phase: "complete", + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: "GTFS data loaded successfully" + }); } /** * Initialize from existing database diff --git a/dist/index.js.map b/dist/index.js.map index 39a6c11..18d9b45 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 { 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 +{"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 { getAllCreateTableStatements, getAllCreateIndexStatements } 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\n/**\n * Progress information for GTFS data loading\n */\nexport interface ProgressInfo {\n phase: 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'complete';\n currentFile: string | null;\n filesCompleted: number;\n totalFiles: number;\n rowsProcessed: number;\n totalRows: number;\n percentComplete: number; // 0-100\n message: string;\n}\n\n/**\n * Progress callback function type\n */\nexport type ProgressCallback = (progress: ProgressInfo) => void;\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 /**\n * Optional: Progress callback for tracking load progress\n * Useful for displaying progress in UI or web workers\n */\n onProgress?: ProgressCallback;\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 const onProgress = options.onProgress;\n\n // Initialize SQL.js\n onProgress?.({\n phase: 'downloading',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 0,\n message: 'Initializing database engine',\n });\n\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 // Apply performance PRAGMAs for bulk loading\n onProgress?.({\n phase: 'creating_schema',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 2,\n message: 'Optimizing database for bulk import',\n });\n\n this.db.run('PRAGMA synchronous = OFF'); // Skip fsync for performance\n this.db.run('PRAGMA journal_mode = MEMORY'); // Keep journal in memory\n this.db.run('PRAGMA temp_store = MEMORY'); // Temp tables in memory\n this.db.run('PRAGMA cache_size = -64000'); // 64MB cache\n this.db.run('PRAGMA locking_mode = EXCLUSIVE'); // No locking overhead\n\n // Create GTFS tables (without indexes)\n onProgress?.({\n phase: 'creating_schema',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 5,\n message: 'Creating database tables',\n });\n\n const createTableStatements = getAllCreateTableStatements();\n for (const statement of createTableStatements) {\n this.db.run(statement);\n }\n\n // Create GTFS-RT tables\n createRealtimeTables(this.db);\n\n // Load GTFS data\n onProgress?.({\n phase: 'extracting',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 10,\n message: 'Extracting GTFS ZIP file',\n });\n\n const files = await loadGTFSZip(zipPath);\n\n onProgress?.({\n phase: 'inserting_data',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 15,\n message: 'Starting data import',\n });\n\n await loadGTFSData(this.db, files, options.skipFiles, onProgress);\n\n // Create indexes after data is loaded\n onProgress?.({\n phase: 'creating_indexes',\n currentFile: null,\n filesCompleted: Object.keys(files).length,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 85,\n message: 'Creating database indexes',\n });\n\n const createIndexStatements = getAllCreateIndexStatements();\n let indexCount = 0;\n for (const statement of createIndexStatements) {\n this.db.run(statement);\n indexCount++;\n const indexProgress = 85 + Math.floor((indexCount / createIndexStatements.length) * 10);\n onProgress?.({\n phase: 'creating_indexes',\n currentFile: null,\n filesCompleted: Object.keys(files).length,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: indexProgress,\n message: `Creating indexes (${indexCount}/${createIndexStatements.length})`,\n });\n }\n\n // Run ANALYZE to update query planner statistics\n onProgress?.({\n phase: 'analyzing',\n currentFile: null,\n filesCompleted: Object.keys(files).length,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 95,\n message: 'Optimizing query performance',\n });\n\n this.db.run('ANALYZE');\n\n // Restore normal SQLite settings\n this.db.run('PRAGMA synchronous = FULL');\n this.db.run('PRAGMA locking_mode = NORMAL');\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 onProgress?.({\n phase: 'complete',\n currentFile: null,\n filesCompleted: Object.keys(files).length,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 100,\n message: 'GTFS data loaded successfully',\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 * Get CREATE TABLE statements only (without indexes)\n */\nexport function getAllCreateTableStatements(): string[] {\n const statements: string[] = [];\n\n for (const schema of GTFS_SCHEMA) {\n statements.push(generateCreateTableSQL(schema));\n }\n\n return statements;\n}\n\n/**\n * Get CREATE INDEX statements only\n */\nexport function getAllCreateIndexStatements(): string[] {\n const statements: string[] = [];\n\n for (const schema of GTFS_SCHEMA) {\n statements.push(...generateCreateIndexSQL(schema));\n }\n\n return statements;\n}\n\n/**\n * Initialize all GTFS tables in the database (tables + indexes)\n * @deprecated Use getAllCreateTableStatements() and getAllCreateIndexStatements() separately for better performance\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 } from './csv-parser';\nimport { GTFS_SCHEMA, type TableSchema } from '../schema/schema';\nimport type { GTFSFiles } from './zip-loader';\nimport type { ProgressCallback } from '../gtfs-sqljs';\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 * @param onProgress - Optional progress callback\n */\nexport async function loadGTFSData(\n db: Database,\n files: GTFSFiles,\n skipFiles?: string[],\n onProgress?: ProgressCallback\n): 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 // Define file priority order (small files first, largest last)\n const filePriority = [\n 'agency.txt',\n 'feed_info.txt',\n 'attributions.txt',\n 'levels.txt',\n 'routes.txt',\n 'calendar.txt',\n 'calendar_dates.txt',\n 'fare_attributes.txt',\n 'fare_rules.txt',\n 'stops.txt',\n 'pathways.txt',\n 'transfers.txt',\n 'trips.txt',\n 'frequencies.txt',\n 'shapes.txt',\n 'stop_times.txt', // Largest file - process last\n ];\n\n // Sort files by priority\n const sortedFiles: [string, string][] = [];\n for (const priorityFile of filePriority) {\n if (files[priorityFile]) {\n sortedFiles.push([priorityFile, files[priorityFile]]);\n }\n }\n // Add any files not in priority list\n for (const [fileName, content] of Object.entries(files)) {\n if (!filePriority.includes(fileName)) {\n sortedFiles.push([fileName, content]);\n }\n }\n\n // Calculate total rows for progress tracking\n let totalRows = 0;\n const fileRowCounts = new Map();\n for (const [fileName, content] of sortedFiles) {\n const schema = fileToSchema.get(fileName);\n if (schema && !skipSet.has(fileName.toLowerCase())) {\n const { rows } = parseCSV(content);\n fileRowCounts.set(fileName, rows.length);\n totalRows += rows.length;\n }\n }\n\n let rowsProcessed = 0;\n let filesCompleted = 0;\n\n // Process each file\n for (const [fileName, content] of sortedFiles) {\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 filesCompleted++;\n continue;\n }\n\n const fileRows = fileRowCounts.get(fileName) || 0;\n\n onProgress?.({\n phase: 'inserting_data',\n currentFile: fileName,\n filesCompleted,\n totalFiles: sortedFiles.length,\n rowsProcessed,\n totalRows,\n percentComplete: 15 + Math.floor((rowsProcessed / totalRows) * 70),\n message: `Loading ${fileName} (${fileRows.toLocaleString()} rows)`,\n });\n\n await loadTableData(db, schema, content, (processedInFile) => {\n const currentProgress = rowsProcessed + processedInFile;\n onProgress?.({\n phase: 'inserting_data',\n currentFile: fileName,\n filesCompleted,\n totalFiles: sortedFiles.length,\n rowsProcessed: currentProgress,\n totalRows,\n percentComplete: 15 + Math.floor((currentProgress / totalRows) * 70),\n message: `Loading ${fileName} (${processedInFile.toLocaleString()}/${fileRows.toLocaleString()} rows)`,\n });\n });\n\n rowsProcessed += fileRows;\n filesCompleted++;\n\n onProgress?.({\n phase: 'inserting_data',\n currentFile: null,\n filesCompleted,\n totalFiles: sortedFiles.length,\n rowsProcessed,\n totalRows,\n percentComplete: 15 + Math.floor((rowsProcessed / totalRows) * 70),\n message: `Completed ${fileName}`,\n });\n }\n}\n\n/**\n * Load data for a single table with batch inserts and transaction\n */\nasync function loadTableData(\n db: Database,\n schema: TableSchema,\n csvContent: string,\n onProgress?: (rowsProcessed: number) => void\n): Promise {\n const { headers, rows } = parseCSV(csvContent);\n\n if (rows.length === 0) {\n return;\n }\n\n // Prepare INSERT statement\n const columns = headers.filter((h) => schema.columns.some((c) => c.name === h));\n if (columns.length === 0) {\n return;\n }\n\n const BATCH_SIZE = 1000;\n let rowsProcessed = 0;\n\n // Start transaction for better performance\n db.run('BEGIN TRANSACTION');\n\n try {\n // Process in batches\n for (let i = 0; i < rows.length; i += BATCH_SIZE) {\n const batchRows = rows.slice(i, Math.min(i + BATCH_SIZE, rows.length));\n\n // Build multi-row INSERT statement\n const placeholders = batchRows.map(() => `(${columns.map(() => '?').join(', ')})`).join(', ');\n const insertSQL = `INSERT INTO ${schema.name} (${columns.join(', ')}) VALUES ${placeholders}`;\n\n // Collect all values for the batch\n const allValues: (string | number | null)[] = [];\n for (const row of batchRows) {\n for (const col of columns) {\n const value = row[col];\n // Let SQLite handle type conversion - just pass empty strings as NULL\n allValues.push(value === null || value === undefined || value === '' ? null : value);\n }\n }\n\n // Execute batch insert\n const stmt = db.prepare(insertSQL);\n try {\n stmt.run(allValues);\n } catch (error) {\n console.error(`Error inserting batch into ${schema.name}:`, error);\n console.error('Batch size:', batchRows.length);\n throw error;\n } finally {\n stmt.free();\n }\n\n rowsProcessed += batchRows.length;\n onProgress?.(rowsProcessed);\n }\n\n // Commit transaction\n db.run('COMMIT');\n } catch (error) {\n // Rollback on error\n try {\n db.run('ROLLBACK');\n } catch (rollbackError) {\n console.error('Error rolling back transaction:', rollbackError);\n }\n throw error;\n }\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 informedEntity?: unknown[];\n cause?: number;\n effect?: number;\n url?: ProtobufTranslatedString;\n headerText?: ProtobufTranslatedString;\n descriptionText?: ProtobufTranslatedString;\n}\n\ninterface ProtobufTripDescriptor {\n tripId?: string;\n routeId?: string;\n scheduleRelationship?: number;\n}\n\ninterface ProtobufVehicleDescriptor {\n id?: string;\n label?: string;\n licensePlate?: 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 stopId?: string;\n currentStatus?: number;\n timestamp?: number;\n congestionLevel?: number;\n occupancyStatus?: number;\n}\n\ninterface ProtobufStopTimeEvent {\n delay?: number;\n time?: number;\n uncertainty?: number;\n}\n\ninterface ProtobufStopTimeUpdate {\n stopSequence?: number;\n stopId?: string;\n scheduleRelationship?: 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}\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 // Convert nested objects to snake_case for database storage\n const activePeriodSnake = convertKeysToSnakeCase(alert.activePeriod || []);\n const informedEntitySnake = convertKeysToSnakeCase(alert.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(alert.headerText),\n parseTranslatedString(alert.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) continue;\n\n stmt.run([\n trip.tripId,\n trip.routeId || null,\n vp.vehicle?.id || null,\n vp.vehicle?.label || null,\n vp.vehicle?.licensePlate || 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 vp.currentStopSequence || null,\n vp.stopId || null,\n vp.currentStatus || null,\n vp.timestamp || null,\n vp.congestionLevel || null,\n vp.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) continue;\n\n // Insert trip update\n tripStmt.run([\n trip.tripId,\n trip.routeId || null,\n tu.vehicle?.id || null,\n tu.vehicle?.label || null,\n tu.vehicle?.licensePlate || null,\n tu.timestamp || null,\n tu.delay || null,\n trip.scheduleRelationship || null,\n timestamp\n ]);\n\n // Insert stop time updates\n if (tu.stopTimeUpdate) {\n for (const stu of tu.stopTimeUpdate) {\n stopTimeStmt.run([\n trip.tripId,\n stu.stopSequence || null,\n stu.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 stu.scheduleRelationship || 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 e0af996..7d2ede6 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -275,10 +275,16 @@ function generateCreateIndexSQL(schema) { return `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${schema.name} (${columns})`; }); } -function getAllCreateStatements() { +function getAllCreateTableStatements() { const statements = []; for (const schema of GTFS_SCHEMA) { statements.push(generateCreateTableSQL(schema)); + } + return statements; +} +function getAllCreateIndexStatements() { + const statements = []; + for (const schema of GTFS_SCHEMA) { statements.push(...generateCreateIndexSQL(schema)); } return statements; @@ -357,74 +363,150 @@ function parseCSV(text) { const rows = result.data; return { headers, rows }; } -function convertRowTypes(row, columnTypes) { - const result = {}; - for (const [key, type] of Object.entries(columnTypes)) { - const value = row[key]; - if (value === void 0 || value === null || value === "") { - result[key] = null; - continue; - } - if (type === "INTEGER") { - const parsed = parseInt(value, 10); - result[key] = isNaN(parsed) ? null : parsed; - } else if (type === "REAL") { - const parsed = parseFloat(value); - result[key] = isNaN(parsed) ? null : parsed; - } else { - result[key] = value; - } - } - return result; -} // src/loaders/data-loader.ts -async function loadGTFSData(db, files, skipFiles) { +async function loadGTFSData(db, files, skipFiles, onProgress) { const fileToSchema = /* @__PURE__ */ new Map(); for (const schema of GTFS_SCHEMA) { fileToSchema.set(`${schema.name}.txt`, schema); } const skipSet = new Set(skipFiles?.map((f) => f.toLowerCase()) || []); + const filePriority = [ + "agency.txt", + "feed_info.txt", + "attributions.txt", + "levels.txt", + "routes.txt", + "calendar.txt", + "calendar_dates.txt", + "fare_attributes.txt", + "fare_rules.txt", + "stops.txt", + "pathways.txt", + "transfers.txt", + "trips.txt", + "frequencies.txt", + "shapes.txt", + "stop_times.txt" + // Largest file - process last + ]; + const sortedFiles = []; + for (const priorityFile of filePriority) { + if (files[priorityFile]) { + sortedFiles.push([priorityFile, files[priorityFile]]); + } + } for (const [fileName, content] of Object.entries(files)) { + if (!filePriority.includes(fileName)) { + sortedFiles.push([fileName, content]); + } + } + let totalRows = 0; + const fileRowCounts = /* @__PURE__ */ new Map(); + for (const [fileName, content] of sortedFiles) { + const schema = fileToSchema.get(fileName); + if (schema && !skipSet.has(fileName.toLowerCase())) { + const { rows } = parseCSV(content); + fileRowCounts.set(fileName, rows.length); + totalRows += rows.length; + } + } + let rowsProcessed = 0; + let filesCompleted = 0; + for (const [fileName, content] of sortedFiles) { const schema = fileToSchema.get(fileName); if (!schema) { continue; } if (skipSet.has(fileName.toLowerCase())) { console.log(`Skipping import of ${fileName} (table ${schema.name} created but empty)`); + filesCompleted++; continue; } - await loadTableData(db, schema, content); + const fileRows = fileRowCounts.get(fileName) || 0; + onProgress?.({ + phase: "inserting_data", + currentFile: fileName, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed, + totalRows, + percentComplete: 15 + Math.floor(rowsProcessed / totalRows * 70), + message: `Loading ${fileName} (${fileRows.toLocaleString()} rows)` + }); + await loadTableData(db, schema, content, (processedInFile) => { + const currentProgress = rowsProcessed + processedInFile; + onProgress?.({ + phase: "inserting_data", + currentFile: fileName, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed: currentProgress, + totalRows, + percentComplete: 15 + Math.floor(currentProgress / totalRows * 70), + message: `Loading ${fileName} (${processedInFile.toLocaleString()}/${fileRows.toLocaleString()} rows)` + }); + }); + rowsProcessed += fileRows; + filesCompleted++; + onProgress?.({ + phase: "inserting_data", + currentFile: null, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed, + totalRows, + percentComplete: 15 + Math.floor(rowsProcessed / totalRows * 70), + message: `Completed ${fileName}` + }); } } -async function loadTableData(db, schema, csvContent) { +async function loadTableData(db, schema, csvContent, onProgress) { const { headers, rows } = parseCSV(csvContent); if (rows.length === 0) { return; } - const columnTypes = {}; - for (const col of schema.columns) { - columnTypes[col.name] = col.type; - } const columns = headers.filter((h) => schema.columns.some((c) => c.name === h)); - const placeholders = columns.map(() => "?").join(", "); - const insertSQL = `INSERT INTO ${schema.name} (${columns.join(", ")}) VALUES (${placeholders})`; - const stmt = db.prepare(insertSQL); - for (const row of rows) { - const typedRow = convertRowTypes(row, columnTypes); - const values = columns.map((col) => { - const value = typedRow[col]; - return value === null || value === void 0 ? null : value; - }); + if (columns.length === 0) { + return; + } + const BATCH_SIZE = 1e3; + let rowsProcessed = 0; + db.run("BEGIN TRANSACTION"); + try { + for (let i = 0; i < rows.length; i += BATCH_SIZE) { + const batchRows = rows.slice(i, Math.min(i + BATCH_SIZE, rows.length)); + const placeholders = batchRows.map(() => `(${columns.map(() => "?").join(", ")})`).join(", "); + const insertSQL = `INSERT INTO ${schema.name} (${columns.join(", ")}) VALUES ${placeholders}`; + const allValues = []; + for (const row of batchRows) { + for (const col of columns) { + const value = row[col]; + allValues.push(value === null || value === void 0 || value === "" ? null : value); + } + } + const stmt = db.prepare(insertSQL); + try { + stmt.run(allValues); + } catch (error) { + console.error(`Error inserting batch into ${schema.name}:`, error); + console.error("Batch size:", batchRows.length); + throw error; + } finally { + stmt.free(); + } + rowsProcessed += batchRows.length; + onProgress?.(rowsProcessed); + } + db.run("COMMIT"); + } catch (error) { try { - stmt.run(values); - } catch (error) { - console.error(`Error inserting row into ${schema.name}:`, error); - console.error("Row data:", row); - throw error; + db.run("ROLLBACK"); + } catch (rollbackError) { + console.error("Error rolling back transaction:", rollbackError); } + throw error; } - stmt.free(); } // src/schema/gtfs-rt-schema.ts @@ -770,12 +852,8 @@ function insertAlerts(db, alerts, timestamp) { ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) `); for (const alert of alerts) { - const activePeriod = alert.activePeriod || alert.active_period || []; - const informedEntity = alert.informedEntity || alert.informed_entity || []; - const headerText = alert.headerText || alert.header_text; - const descriptionText = alert.descriptionText || alert.description_text; - const activePeriodSnake = convertKeysToSnakeCase(activePeriod); - const informedEntitySnake = convertKeysToSnakeCase(informedEntity); + const activePeriodSnake = convertKeysToSnakeCase(alert.activePeriod || []); + const informedEntitySnake = convertKeysToSnakeCase(alert.informedEntity || []); stmt.run([ alert.id, JSON.stringify(activePeriodSnake), @@ -783,8 +861,8 @@ function insertAlerts(db, alerts, timestamp) { alert.cause || null, alert.effect || null, parseTranslatedString(alert.url), - parseTranslatedString(headerText), - parseTranslatedString(descriptionText), + parseTranslatedString(alert.headerText), + parseTranslatedString(alert.descriptionText), timestamp ]); } @@ -801,34 +879,24 @@ function insertVehiclePositions(db, positions, timestamp) { `); for (const vp of positions) { const trip = vp.trip; - if (!trip || !(trip.tripId || trip.trip_id)) continue; - const tripId = trip.tripId || trip.trip_id; - const routeId = trip.routeId || trip.route_id; - const vehicleId = vp.vehicle?.id; - const vehicleLabel = vp.vehicle?.label; - const vehicleLicensePlate = vp.vehicle?.licensePlate || vp.vehicle?.license_plate; - const currentStopSequence = vp.currentStopSequence || vp.current_stop_sequence; - const stopId = vp.stopId || vp.stop_id; - const currentStatus = vp.currentStatus || vp.current_status; - const congestionLevel = vp.congestionLevel || vp.congestion_level; - const occupancyStatus = vp.occupancyStatus || vp.occupancy_status; + if (!trip || !trip.tripId) continue; stmt.run([ - tripId, - routeId || null, - vehicleId || null, - vehicleLabel || null, - vehicleLicensePlate || null, + trip.tripId, + trip.routeId || null, + vp.vehicle?.id || null, + vp.vehicle?.label || null, + vp.vehicle?.licensePlate || null, vp.position?.latitude || null, vp.position?.longitude || null, vp.position?.bearing || null, vp.position?.odometer || null, vp.position?.speed || null, - currentStopSequence || null, - stopId || null, - currentStatus || null, + vp.currentStopSequence || null, + vp.stopId || null, + vp.currentStatus || null, vp.timestamp || null, - congestionLevel || null, - occupancyStatus || null, + vp.congestionLevel || null, + vp.occupancyStatus || null, timestamp ]); } @@ -851,41 +919,31 @@ function insertTripUpdates(db, updates, timestamp) { `); for (const tu of updates) { const trip = tu.trip; - if (!trip || !(trip.tripId || trip.trip_id)) continue; - const tripId = trip.tripId || trip.trip_id; - const routeId = trip.routeId || trip.route_id; - const vehicleId = tu.vehicle?.id; - const vehicleLabel = tu.vehicle?.label; - const vehicleLicensePlate = tu.vehicle?.licensePlate || tu.vehicle?.license_plate; - const scheduleRelationship = trip.scheduleRelationship || trip.schedule_relationship; - const stopTimeUpdate = tu.stopTimeUpdate || tu.stop_time_update; + if (!trip || !trip.tripId) continue; tripStmt.run([ - tripId, - routeId || null, - vehicleId || null, - vehicleLabel || null, - vehicleLicensePlate || null, + trip.tripId, + trip.routeId || null, + tu.vehicle?.id || null, + tu.vehicle?.label || null, + tu.vehicle?.licensePlate || null, tu.timestamp || null, tu.delay || null, - scheduleRelationship || null, + trip.scheduleRelationship || null, timestamp ]); - if (stopTimeUpdate) { - for (const stu of stopTimeUpdate) { - const stopSequence = stu.stopSequence || stu.stop_sequence; - const stopId = stu.stopId || stu.stop_id; - const scheduleRel = stu.scheduleRelationship || stu.schedule_relationship; + if (tu.stopTimeUpdate) { + for (const stu of tu.stopTimeUpdate) { stopTimeStmt.run([ - tripId, - stopSequence || null, - stopId || null, + trip.tripId, + stu.stopSequence || null, + stu.stopId || null, stu.arrival?.delay || null, stu.arrival?.time || null, stu.arrival?.uncertainty || null, stu.departure?.delay || null, stu.departure?.time || null, stu.departure?.uncertainty || null, - scheduleRel || null, + stu.scheduleRelationship || null, timestamp ]); } @@ -1907,21 +1965,127 @@ var GtfsSqlJs = class _GtfsSqlJs { * Initialize from ZIP file */ async initFromZip(zipPath, options) { + const onProgress = options.onProgress; + onProgress?.({ + phase: "downloading", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 0, + message: "Initializing database engine" + }); this.SQL = options.SQL || await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}); this.db = new this.SQL.Database(); - const createStatements = getAllCreateStatements(); - for (const statement of createStatements) { + onProgress?.({ + phase: "creating_schema", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 2, + message: "Optimizing database for bulk import" + }); + this.db.run("PRAGMA synchronous = OFF"); + this.db.run("PRAGMA journal_mode = MEMORY"); + this.db.run("PRAGMA temp_store = MEMORY"); + this.db.run("PRAGMA cache_size = -64000"); + this.db.run("PRAGMA locking_mode = EXCLUSIVE"); + onProgress?.({ + phase: "creating_schema", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 5, + message: "Creating database tables" + }); + const createTableStatements = getAllCreateTableStatements(); + for (const statement of createTableStatements) { this.db.run(statement); } createRealtimeTables(this.db); + onProgress?.({ + phase: "extracting", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 10, + message: "Extracting GTFS ZIP file" + }); const files = await loadGTFSZip(zipPath); - await loadGTFSData(this.db, files, options.skipFiles); + onProgress?.({ + phase: "inserting_data", + currentFile: null, + filesCompleted: 0, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 15, + message: "Starting data import" + }); + await loadGTFSData(this.db, files, options.skipFiles, onProgress); + onProgress?.({ + phase: "creating_indexes", + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 85, + message: "Creating database indexes" + }); + const createIndexStatements = getAllCreateIndexStatements(); + let indexCount = 0; + for (const statement of createIndexStatements) { + this.db.run(statement); + indexCount++; + const indexProgress = 85 + Math.floor(indexCount / createIndexStatements.length * 10); + onProgress?.({ + phase: "creating_indexes", + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: indexProgress, + message: `Creating indexes (${indexCount}/${createIndexStatements.length})` + }); + } + onProgress?.({ + phase: "analyzing", + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 95, + message: "Optimizing query performance" + }); + this.db.run("ANALYZE"); + this.db.run("PRAGMA synchronous = FULL"); + this.db.run("PRAGMA locking_mode = NORMAL"); if (options.realtimeFeedUrls) { this.realtimeFeedUrls = options.realtimeFeedUrls; } if (options.stalenessThreshold !== void 0) { this.stalenessThreshold = options.stalenessThreshold; } + onProgress?.({ + phase: "complete", + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: "GTFS data loaded successfully" + }); } /** * Initialize from existing database diff --git a/dist/index.mjs.map b/dist/index.mjs.map index 2f5c0dd..d1383cc 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 { 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":";AAIA,OAAO,eAAoD;;;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,OAAO,WAAW;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,MAAM,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,OAAO,UAAU;AAUV,SAAS,SAAS,MAAyB;AAChD,QAAM,SAAS,KAAK,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,OAAO,cAAc;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,UAAMA,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,SAAS,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;;;ACrkBO,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,UAAM,YAAY,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAChE,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,eAAe,UAAU,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACvD,iBAAW,KAAK,iBAAiB,YAAY,GAAG;AAChD,aAAO,KAAK,GAAG,SAAS;AAAA,IAC1B;AAAA,EACF;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;AAKA,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;;;ACnDO,SAAS,SAAS,IAAc,UAAuB,CAAC,GAAW;AACxE,QAAM,EAAE,QAAQ,UAAU,MAAM,QAAQ,MAAM,IAAI;AAGlD,MAAI,QAAQ;AACV,UAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACxD,QAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAElC,UAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,UAAMC,QAAO,GAAG,QAAQ;AAAA;AAAA;AAAA,6BAGC,YAAY;AAAA;AAAA,KAEpC;AACD,IAAAA,MAAK,KAAK,OAAO;AAEjB,UAAMC,SAAgB,CAAC;AACvB,WAAOD,MAAK,KAAK,GAAG;AAClB,YAAM,MAAMA,MAAK,YAAY;AAC7B,MAAAC,OAAM,KAAK,UAAU,GAAG,CAAC;AAAA,IAC3B;AAEA,IAAAD,MAAK,KAAK;AACV,WAAOC;AAAA,EACT;AAGA,QAAM,aAAuB,CAAC;AAC9B,QAAM,SAA8B,CAAC;AAErC,MAAI,QAAQ;AACV,UAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACxD,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,iBAAW,KAAK,eAAe,YAAY,GAAG;AAC9C,aAAO,KAAK,GAAG,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,UAAU;AACZ,UAAM,YAAY,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAChE,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,eAAe,UAAU,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACvD,iBAAW,KAAK,iBAAiB,YAAY,GAAG;AAChD,aAAO,KAAK,GAAG,SAAS;AAAA,IAC1B;AAAA,EACF;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;AAaA,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;;;AChHO,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,UAAM,WAAW,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;AAC5D,QAAI,SAAS,SAAS,GAAG;AACvB,YAAM,eAAe,SAAS,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACtD,iBAAW,KAAK,gBAAgB,YAAY,GAAG;AAC/C,aAAO,KAAK,GAAG,QAAQ;AAAA,IACzB;AAAA,EACF;AAEA,MAAI,UAAU;AACZ,UAAM,YAAY,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAChE,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,eAAe,UAAU,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACvD,iBAAW,KAAK,iBAAiB,YAAY,GAAG;AAChD,aAAO,KAAK,GAAG,SAAS;AAAA,IAC1B;AAAA,EACF;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;AAKA,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;;;AC5EO,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;AAiBO,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;AAMO,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,UAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACxD,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,iBAAW,KAAK,kBAAkB,iBAAiB,YAAY,MAAM,eAAe,YAAY,GAAG;AACnG,aAAO,KAAK,GAAG,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,SAAS;AACX,UAAM,WAAW,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;AAC5D,QAAI,SAAS,SAAS,GAAG;AACvB,YAAM,eAAe,SAAS,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACtD,iBAAW,KAAK,kBAAkB,kBAAkB,YAAY,MAAM,gBAAgB,YAAY,GAAG;AACrG,aAAO,KAAK,GAAG,QAAQ;AAAA,IACzB;AAAA,EACF;AAEA,MAAI,YAAY;AACd,UAAM,iBAAiB,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC,UAAU;AAC3E,QAAI,eAAe,SAAS,GAAG;AAC7B,YAAM,eAAe,eAAe,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AAC5D,iBAAW,KAAK,kBAAkB,oBAAoB,YAAY,MAAM,kBAAkB,YAAY,GAAG;AACzG,aAAO,KAAK,GAAG,cAAc;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,gBAAgB,QAAW;AAC7B,UAAM,eAAe,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC,WAAW;AAC5E,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,eAAe,aAAa,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AAC1D,iBAAW,KAAK,kBAAkB,sBAAsB,YAAY,MAAM,oBAAoB,YAAY,GAAG;AAC7G,aAAO,KAAK,GAAG,YAAY;AAAA,IAC7B;AAAA,EACF;AAEA,MAAI,UAAU;AACZ,UAAM,YAAY,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAChE,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,eAAe,UAAU,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACvD,iBAAW,KAAK,mBAAmB,YAAY,GAAG;AAClD,aAAO,KAAK,GAAG,SAAS;AAAA,IAC1B;AAAA,EACF;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;AAKA,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;;;ACpLA,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,cAAc,IAAI,iBAAiB,OAAO,OAAO,IAAI,YAAY,IAAI;AAAA,MACrE,iBAAiB,IAAI,oBAAoB,OAAO,OAAO,IAAI,eAAe,IAAI;AAAA,MAC9E,gBAAgB,IAAI,mBAAmB,OAAO,OAAO,IAAI,cAAc,IAAI;AAAA,MAC3E,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;AAMO,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,UAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACxD,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,iBAAW,KAAK,iBAAiB,kBAAkB,YAAY,MAAM,eAAe,YAAY,GAAG;AACnG,aAAO,KAAK,GAAG,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,QAAQ;AACV,UAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACxD,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,iBAAW,KAAK,iBAAiB,kBAAkB,YAAY,MAAM,eAAe,YAAY,GAAG;AACnG,aAAO,KAAK,GAAG,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,SAAS;AACX,UAAM,WAAW,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;AAC5D,QAAI,SAAS,SAAS,GAAG;AACvB,YAAM,eAAe,SAAS,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACtD,iBAAW,KAAK,kBAAkB,YAAY,GAAG;AACjD,aAAO,KAAK,GAAG,QAAQ;AAAA,IACzB;AAAA,EACF;AAEA,MAAI,YAAY;AACd,UAAM,iBAAiB,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC,UAAU;AAC3E,QAAI,eAAe,SAAS,GAAG;AAC7B,YAAM,eAAe,eAAe,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AAC5D,iBAAW,KAAK,oBAAoB,YAAY,GAAG;AACnD,aAAO,KAAK,GAAG,cAAc;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,gBAAgB,QAAW;AAC7B,UAAM,eAAe,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC,WAAW;AAC5E,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,eAAe,aAAa,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AAC1D,iBAAW,KAAK,sBAAsB,YAAY,GAAG;AACrD,aAAO,KAAK,GAAG,YAAY;AAAA,IAC7B;AAAA,EACF;AAEA,MAAI,UAAU;AACZ,UAAM,YAAY,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAChE,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,eAAe,UAAU,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACvD,iBAAW,KAAK,mBAAmB,YAAY,GAAG;AAClD,aAAO,KAAK,GAAG,SAAS;AAAA,IAC1B;AAAA,EACF;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;AAKA,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;;;ACxMA,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;AAaO,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;AAiBO,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;AAMO,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,UAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACxD,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,iBAAW,KAAK,eAAe,YAAY,GAAG;AAC9C,aAAO,KAAK,GAAG,OAAO;AAAA,IACxB;AAAA,EACF;AAGA,MAAI,QAAQ;AACV,UAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACxD,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,eAAe,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AACrD,iBAAW,KAAK,eAAe,YAAY,GAAG;AAC9C,aAAO,KAAK,GAAG,OAAO;AAAA,IACxB;AAAA,EACF;AAGA,MAAI,iBAAiB,QAAW;AAC9B,UAAM,gBAAgB,MAAM,QAAQ,YAAY,IAAI,eAAe,CAAC,YAAY;AAChF,QAAI,cAAc,SAAS,GAAG;AAC5B,YAAM,eAAe,cAAc,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AAC3D,iBAAW,KAAK,qBAAqB,YAAY,GAAG;AACpD,aAAO,KAAK,GAAG,aAAa;AAAA,IAC9B;AAAA,EACF;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;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;;;AhB7EO,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,MAAM,UAAU,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,MAAM,UAAU,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;AAAA,EAQA,YAAY,SAAmC;AAC7C,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACxD,WAAO,YAAY,KAAK,IAAI,OAAO;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,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;AAAA,EAQA,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,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;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,EAoCA,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;AAAA,EAMA,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;AAAA,EAMA,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;AAAA,EAMA,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;AAAA,EAMA,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;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;;;AiB9dO,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":["response","arrayBuffer","stmt","stops","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 { getAllCreateTableStatements, getAllCreateIndexStatements } 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\n/**\n * Progress information for GTFS data loading\n */\nexport interface ProgressInfo {\n phase: 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'complete';\n currentFile: string | null;\n filesCompleted: number;\n totalFiles: number;\n rowsProcessed: number;\n totalRows: number;\n percentComplete: number; // 0-100\n message: string;\n}\n\n/**\n * Progress callback function type\n */\nexport type ProgressCallback = (progress: ProgressInfo) => void;\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 /**\n * Optional: Progress callback for tracking load progress\n * Useful for displaying progress in UI or web workers\n */\n onProgress?: ProgressCallback;\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 const onProgress = options.onProgress;\n\n // Initialize SQL.js\n onProgress?.({\n phase: 'downloading',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 0,\n message: 'Initializing database engine',\n });\n\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 // Apply performance PRAGMAs for bulk loading\n onProgress?.({\n phase: 'creating_schema',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 2,\n message: 'Optimizing database for bulk import',\n });\n\n this.db.run('PRAGMA synchronous = OFF'); // Skip fsync for performance\n this.db.run('PRAGMA journal_mode = MEMORY'); // Keep journal in memory\n this.db.run('PRAGMA temp_store = MEMORY'); // Temp tables in memory\n this.db.run('PRAGMA cache_size = -64000'); // 64MB cache\n this.db.run('PRAGMA locking_mode = EXCLUSIVE'); // No locking overhead\n\n // Create GTFS tables (without indexes)\n onProgress?.({\n phase: 'creating_schema',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 5,\n message: 'Creating database tables',\n });\n\n const createTableStatements = getAllCreateTableStatements();\n for (const statement of createTableStatements) {\n this.db.run(statement);\n }\n\n // Create GTFS-RT tables\n createRealtimeTables(this.db);\n\n // Load GTFS data\n onProgress?.({\n phase: 'extracting',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 10,\n message: 'Extracting GTFS ZIP file',\n });\n\n const files = await loadGTFSZip(zipPath);\n\n onProgress?.({\n phase: 'inserting_data',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 15,\n message: 'Starting data import',\n });\n\n await loadGTFSData(this.db, files, options.skipFiles, onProgress);\n\n // Create indexes after data is loaded\n onProgress?.({\n phase: 'creating_indexes',\n currentFile: null,\n filesCompleted: Object.keys(files).length,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 85,\n message: 'Creating database indexes',\n });\n\n const createIndexStatements = getAllCreateIndexStatements();\n let indexCount = 0;\n for (const statement of createIndexStatements) {\n this.db.run(statement);\n indexCount++;\n const indexProgress = 85 + Math.floor((indexCount / createIndexStatements.length) * 10);\n onProgress?.({\n phase: 'creating_indexes',\n currentFile: null,\n filesCompleted: Object.keys(files).length,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: indexProgress,\n message: `Creating indexes (${indexCount}/${createIndexStatements.length})`,\n });\n }\n\n // Run ANALYZE to update query planner statistics\n onProgress?.({\n phase: 'analyzing',\n currentFile: null,\n filesCompleted: Object.keys(files).length,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 95,\n message: 'Optimizing query performance',\n });\n\n this.db.run('ANALYZE');\n\n // Restore normal SQLite settings\n this.db.run('PRAGMA synchronous = FULL');\n this.db.run('PRAGMA locking_mode = NORMAL');\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 onProgress?.({\n phase: 'complete',\n currentFile: null,\n filesCompleted: Object.keys(files).length,\n totalFiles: Object.keys(files).length,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 100,\n message: 'GTFS data loaded successfully',\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 * Get CREATE TABLE statements only (without indexes)\n */\nexport function getAllCreateTableStatements(): string[] {\n const statements: string[] = [];\n\n for (const schema of GTFS_SCHEMA) {\n statements.push(generateCreateTableSQL(schema));\n }\n\n return statements;\n}\n\n/**\n * Get CREATE INDEX statements only\n */\nexport function getAllCreateIndexStatements(): string[] {\n const statements: string[] = [];\n\n for (const schema of GTFS_SCHEMA) {\n statements.push(...generateCreateIndexSQL(schema));\n }\n\n return statements;\n}\n\n/**\n * Initialize all GTFS tables in the database (tables + indexes)\n * @deprecated Use getAllCreateTableStatements() and getAllCreateIndexStatements() separately for better performance\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 } from './csv-parser';\nimport { GTFS_SCHEMA, type TableSchema } from '../schema/schema';\nimport type { GTFSFiles } from './zip-loader';\nimport type { ProgressCallback } from '../gtfs-sqljs';\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 * @param onProgress - Optional progress callback\n */\nexport async function loadGTFSData(\n db: Database,\n files: GTFSFiles,\n skipFiles?: string[],\n onProgress?: ProgressCallback\n): 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 // Define file priority order (small files first, largest last)\n const filePriority = [\n 'agency.txt',\n 'feed_info.txt',\n 'attributions.txt',\n 'levels.txt',\n 'routes.txt',\n 'calendar.txt',\n 'calendar_dates.txt',\n 'fare_attributes.txt',\n 'fare_rules.txt',\n 'stops.txt',\n 'pathways.txt',\n 'transfers.txt',\n 'trips.txt',\n 'frequencies.txt',\n 'shapes.txt',\n 'stop_times.txt', // Largest file - process last\n ];\n\n // Sort files by priority\n const sortedFiles: [string, string][] = [];\n for (const priorityFile of filePriority) {\n if (files[priorityFile]) {\n sortedFiles.push([priorityFile, files[priorityFile]]);\n }\n }\n // Add any files not in priority list\n for (const [fileName, content] of Object.entries(files)) {\n if (!filePriority.includes(fileName)) {\n sortedFiles.push([fileName, content]);\n }\n }\n\n // Calculate total rows for progress tracking\n let totalRows = 0;\n const fileRowCounts = new Map();\n for (const [fileName, content] of sortedFiles) {\n const schema = fileToSchema.get(fileName);\n if (schema && !skipSet.has(fileName.toLowerCase())) {\n const { rows } = parseCSV(content);\n fileRowCounts.set(fileName, rows.length);\n totalRows += rows.length;\n }\n }\n\n let rowsProcessed = 0;\n let filesCompleted = 0;\n\n // Process each file\n for (const [fileName, content] of sortedFiles) {\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 filesCompleted++;\n continue;\n }\n\n const fileRows = fileRowCounts.get(fileName) || 0;\n\n onProgress?.({\n phase: 'inserting_data',\n currentFile: fileName,\n filesCompleted,\n totalFiles: sortedFiles.length,\n rowsProcessed,\n totalRows,\n percentComplete: 15 + Math.floor((rowsProcessed / totalRows) * 70),\n message: `Loading ${fileName} (${fileRows.toLocaleString()} rows)`,\n });\n\n await loadTableData(db, schema, content, (processedInFile) => {\n const currentProgress = rowsProcessed + processedInFile;\n onProgress?.({\n phase: 'inserting_data',\n currentFile: fileName,\n filesCompleted,\n totalFiles: sortedFiles.length,\n rowsProcessed: currentProgress,\n totalRows,\n percentComplete: 15 + Math.floor((currentProgress / totalRows) * 70),\n message: `Loading ${fileName} (${processedInFile.toLocaleString()}/${fileRows.toLocaleString()} rows)`,\n });\n });\n\n rowsProcessed += fileRows;\n filesCompleted++;\n\n onProgress?.({\n phase: 'inserting_data',\n currentFile: null,\n filesCompleted,\n totalFiles: sortedFiles.length,\n rowsProcessed,\n totalRows,\n percentComplete: 15 + Math.floor((rowsProcessed / totalRows) * 70),\n message: `Completed ${fileName}`,\n });\n }\n}\n\n/**\n * Load data for a single table with batch inserts and transaction\n */\nasync function loadTableData(\n db: Database,\n schema: TableSchema,\n csvContent: string,\n onProgress?: (rowsProcessed: number) => void\n): Promise {\n const { headers, rows } = parseCSV(csvContent);\n\n if (rows.length === 0) {\n return;\n }\n\n // Prepare INSERT statement\n const columns = headers.filter((h) => schema.columns.some((c) => c.name === h));\n if (columns.length === 0) {\n return;\n }\n\n const BATCH_SIZE = 1000;\n let rowsProcessed = 0;\n\n // Start transaction for better performance\n db.run('BEGIN TRANSACTION');\n\n try {\n // Process in batches\n for (let i = 0; i < rows.length; i += BATCH_SIZE) {\n const batchRows = rows.slice(i, Math.min(i + BATCH_SIZE, rows.length));\n\n // Build multi-row INSERT statement\n const placeholders = batchRows.map(() => `(${columns.map(() => '?').join(', ')})`).join(', ');\n const insertSQL = `INSERT INTO ${schema.name} (${columns.join(', ')}) VALUES ${placeholders}`;\n\n // Collect all values for the batch\n const allValues: (string | number | null)[] = [];\n for (const row of batchRows) {\n for (const col of columns) {\n const value = row[col];\n // Let SQLite handle type conversion - just pass empty strings as NULL\n allValues.push(value === null || value === undefined || value === '' ? null : value);\n }\n }\n\n // Execute batch insert\n const stmt = db.prepare(insertSQL);\n try {\n stmt.run(allValues);\n } catch (error) {\n console.error(`Error inserting batch into ${schema.name}:`, error);\n console.error('Batch size:', batchRows.length);\n throw error;\n } finally {\n stmt.free();\n }\n\n rowsProcessed += batchRows.length;\n onProgress?.(rowsProcessed);\n }\n\n // Commit transaction\n db.run('COMMIT');\n } catch (error) {\n // Rollback on error\n try {\n db.run('ROLLBACK');\n } catch (rollbackError) {\n console.error('Error rolling back transaction:', rollbackError);\n }\n throw error;\n }\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 informedEntity?: unknown[];\n cause?: number;\n effect?: number;\n url?: ProtobufTranslatedString;\n headerText?: ProtobufTranslatedString;\n descriptionText?: ProtobufTranslatedString;\n}\n\ninterface ProtobufTripDescriptor {\n tripId?: string;\n routeId?: string;\n scheduleRelationship?: number;\n}\n\ninterface ProtobufVehicleDescriptor {\n id?: string;\n label?: string;\n licensePlate?: 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 stopId?: string;\n currentStatus?: number;\n timestamp?: number;\n congestionLevel?: number;\n occupancyStatus?: number;\n}\n\ninterface ProtobufStopTimeEvent {\n delay?: number;\n time?: number;\n uncertainty?: number;\n}\n\ninterface ProtobufStopTimeUpdate {\n stopSequence?: number;\n stopId?: string;\n scheduleRelationship?: 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}\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 // Convert nested objects to snake_case for database storage\n const activePeriodSnake = convertKeysToSnakeCase(alert.activePeriod || []);\n const informedEntitySnake = convertKeysToSnakeCase(alert.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(alert.headerText),\n parseTranslatedString(alert.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) continue;\n\n stmt.run([\n trip.tripId,\n trip.routeId || null,\n vp.vehicle?.id || null,\n vp.vehicle?.label || null,\n vp.vehicle?.licensePlate || 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 vp.currentStopSequence || null,\n vp.stopId || null,\n vp.currentStatus || null,\n vp.timestamp || null,\n vp.congestionLevel || null,\n vp.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) continue;\n\n // Insert trip update\n tripStmt.run([\n trip.tripId,\n trip.routeId || null,\n tu.vehicle?.id || null,\n tu.vehicle?.label || null,\n tu.vehicle?.licensePlate || null,\n tu.timestamp || null,\n tu.delay || null,\n trip.scheduleRelationship || null,\n timestamp\n ]);\n\n // Insert stop time updates\n if (tu.stopTimeUpdate) {\n for (const stu of tu.stopTimeUpdate) {\n stopTimeStmt.run([\n trip.tripId,\n stu.stopSequence || null,\n stu.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 stu.scheduleRelationship || 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/package-lock.json b/package-lock.json index 9418260..d1e830b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gtfs-sqljs", - "version": "1.0.0", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gtfs-sqljs", - "version": "1.0.0", + "version": "0.1.0", "license": "MIT", "dependencies": { "jszip": "^3.10.1", diff --git a/src/gtfs-sqljs.ts b/src/gtfs-sqljs.ts index 10c2bb3..e1723af 100644 --- a/src/gtfs-sqljs.ts +++ b/src/gtfs-sqljs.ts @@ -3,7 +3,7 @@ */ import initSqlJs, { type Database, type SqlJsStatic } from 'sql.js'; -import { getAllCreateStatements } from './schema/schema'; +import { getAllCreateTableStatements, getAllCreateIndexStatements } from './schema/schema'; import { loadGTFSZip } from './loaders/zip-loader'; import { loadGTFSData } from './loaders/data-loader'; import { createRealtimeTables, clearRealtimeData as clearRTData } from './schema/gtfs-rt-schema'; @@ -35,6 +35,25 @@ export type { AgencyFilters, StopFilters, RouteFilters, TripFilters, StopTimeFil // Export RT types export type { Alert, VehiclePosition, TripUpdate, StopTimeUpdateWithMetadata, TripWithRealtime, StopTimeWithRealtime }; +/** + * Progress information for GTFS data loading + */ +export interface ProgressInfo { + phase: 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'complete'; + currentFile: string | null; + filesCompleted: number; + totalFiles: number; + rowsProcessed: number; + totalRows: number; + percentComplete: number; // 0-100 + message: string; +} + +/** + * Progress callback function type + */ +export type ProgressCallback = (progress: ProgressInfo) => void; + export interface GtfsSqlJsOptions { /** * Path or URL to GTFS ZIP file @@ -72,6 +91,12 @@ export interface GtfsSqlJsOptions { * Realtime data older than this will be excluded from queries */ stalenessThreshold?: number; + + /** + * Optional: Progress callback for tracking load progress + * Useful for displaying progress in UI or web workers + */ + onProgress?: ProgressCallback; } export class GtfsSqlJs { @@ -113,15 +138,57 @@ export class GtfsSqlJs { * Initialize from ZIP file */ private async initFromZip(zipPath: string, options: Omit): Promise { + const onProgress = options.onProgress; + // Initialize SQL.js + onProgress?.({ + phase: 'downloading', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 0, + message: 'Initializing database engine', + }); + this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {})); // Create new database this.db = new this.SQL.Database(); - // Create GTFS schema - const createStatements = getAllCreateStatements(); - for (const statement of createStatements) { + // Apply performance PRAGMAs for bulk loading + onProgress?.({ + phase: 'creating_schema', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 2, + message: 'Optimizing database for bulk import', + }); + + this.db.run('PRAGMA synchronous = OFF'); // Skip fsync for performance + this.db.run('PRAGMA journal_mode = MEMORY'); // Keep journal in memory + this.db.run('PRAGMA temp_store = MEMORY'); // Temp tables in memory + this.db.run('PRAGMA cache_size = -64000'); // 64MB cache + this.db.run('PRAGMA locking_mode = EXCLUSIVE'); // No locking overhead + + // Create GTFS tables (without indexes) + onProgress?.({ + phase: 'creating_schema', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 5, + message: 'Creating database tables', + }); + + const createTableStatements = getAllCreateTableStatements(); + for (const statement of createTableStatements) { this.db.run(statement); } @@ -129,8 +196,79 @@ export class GtfsSqlJs { createRealtimeTables(this.db); // Load GTFS data + onProgress?.({ + phase: 'extracting', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 10, + message: 'Extracting GTFS ZIP file', + }); + const files = await loadGTFSZip(zipPath); - await loadGTFSData(this.db, files, options.skipFiles); + + onProgress?.({ + phase: 'inserting_data', + currentFile: null, + filesCompleted: 0, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 15, + message: 'Starting data import', + }); + + await loadGTFSData(this.db, files, options.skipFiles, onProgress); + + // Create indexes after data is loaded + onProgress?.({ + phase: 'creating_indexes', + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 85, + message: 'Creating database indexes', + }); + + const createIndexStatements = getAllCreateIndexStatements(); + let indexCount = 0; + for (const statement of createIndexStatements) { + this.db.run(statement); + indexCount++; + const indexProgress = 85 + Math.floor((indexCount / createIndexStatements.length) * 10); + onProgress?.({ + phase: 'creating_indexes', + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: indexProgress, + message: `Creating indexes (${indexCount}/${createIndexStatements.length})`, + }); + } + + // Run ANALYZE to update query planner statistics + onProgress?.({ + phase: 'analyzing', + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 95, + message: 'Optimizing query performance', + }); + + this.db.run('ANALYZE'); + + // Restore normal SQLite settings + this.db.run('PRAGMA synchronous = FULL'); + this.db.run('PRAGMA locking_mode = NORMAL'); // Set RT configuration if (options.realtimeFeedUrls) { @@ -139,6 +277,17 @@ export class GtfsSqlJs { if (options.stalenessThreshold !== undefined) { this.stalenessThreshold = options.stalenessThreshold; } + + onProgress?.({ + phase: 'complete', + currentFile: null, + filesCompleted: Object.keys(files).length, + totalFiles: Object.keys(files).length, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: 'GTFS data loaded successfully', + }); } /** diff --git a/src/loaders/data-loader.ts b/src/loaders/data-loader.ts index a855027..f79871d 100644 --- a/src/loaders/data-loader.ts +++ b/src/loaders/data-loader.ts @@ -3,15 +3,22 @@ */ import type { Database } from 'sql.js'; -import { parseCSV, convertRowTypes } from './csv-parser'; +import { parseCSV } from './csv-parser'; import { GTFS_SCHEMA, type TableSchema } from '../schema/schema'; import type { GTFSFiles } from './zip-loader'; +import type { ProgressCallback } from '../gtfs-sqljs'; /** * Load GTFS files into SQLite database * @param skipFiles - Optional array of filenames to skip importing (tables will be created but remain empty) + * @param onProgress - Optional progress callback */ -export async function loadGTFSData(db: Database, files: GTFSFiles, skipFiles?: string[]): Promise { +export async function loadGTFSData( + db: Database, + files: GTFSFiles, + skipFiles?: string[], + onProgress?: ProgressCallback +): Promise { // Map of file names to table schemas const fileToSchema: Map = new Map(); for (const schema of GTFS_SCHEMA) { @@ -22,8 +29,57 @@ export async function loadGTFSData(db: Database, files: GTFSFiles, skipFiles?: s // Normalize skipFiles to a Set for faster lookup const skipSet = new Set(skipFiles?.map(f => f.toLowerCase()) || []); - // Process each file + // Define file priority order (small files first, largest last) + const filePriority = [ + 'agency.txt', + 'feed_info.txt', + 'attributions.txt', + 'levels.txt', + 'routes.txt', + 'calendar.txt', + 'calendar_dates.txt', + 'fare_attributes.txt', + 'fare_rules.txt', + 'stops.txt', + 'pathways.txt', + 'transfers.txt', + 'trips.txt', + 'frequencies.txt', + 'shapes.txt', + 'stop_times.txt', // Largest file - process last + ]; + + // Sort files by priority + const sortedFiles: [string, string][] = []; + for (const priorityFile of filePriority) { + if (files[priorityFile]) { + sortedFiles.push([priorityFile, files[priorityFile]]); + } + } + // Add any files not in priority list for (const [fileName, content] of Object.entries(files)) { + if (!filePriority.includes(fileName)) { + sortedFiles.push([fileName, content]); + } + } + + // Calculate total rows for progress tracking + let totalRows = 0; + const fileRowCounts = new Map(); + for (const [fileName, content] of sortedFiles) { + const schema = fileToSchema.get(fileName); + if (schema && !skipSet.has(fileName.toLowerCase())) { + const { rows } = parseCSV(content); + fileRowCounts.set(fileName, rows.length); + totalRows += rows.length; + } + } + + let rowsProcessed = 0; + let filesCompleted = 0; + + // Process each file + for (const [fileName, content] of sortedFiles) { const schema = fileToSchema.get(fileName); if (!schema) { // Skip unknown files @@ -33,53 +89,124 @@ export async function loadGTFSData(db: Database, files: GTFSFiles, skipFiles?: s // Skip if in skip list if (skipSet.has(fileName.toLowerCase())) { console.log(`Skipping import of ${fileName} (table ${schema.name} created but empty)`); + filesCompleted++; continue; } - await loadTableData(db, schema, content); + const fileRows = fileRowCounts.get(fileName) || 0; + + onProgress?.({ + phase: 'inserting_data', + currentFile: fileName, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed, + totalRows, + percentComplete: 15 + Math.floor((rowsProcessed / totalRows) * 70), + message: `Loading ${fileName} (${fileRows.toLocaleString()} rows)`, + }); + + await loadTableData(db, schema, content, (processedInFile) => { + const currentProgress = rowsProcessed + processedInFile; + onProgress?.({ + phase: 'inserting_data', + currentFile: fileName, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed: currentProgress, + totalRows, + percentComplete: 15 + Math.floor((currentProgress / totalRows) * 70), + message: `Loading ${fileName} (${processedInFile.toLocaleString()}/${fileRows.toLocaleString()} rows)`, + }); + }); + + rowsProcessed += fileRows; + filesCompleted++; + + onProgress?.({ + phase: 'inserting_data', + currentFile: null, + filesCompleted, + totalFiles: sortedFiles.length, + rowsProcessed, + totalRows, + percentComplete: 15 + Math.floor((rowsProcessed / totalRows) * 70), + message: `Completed ${fileName}`, + }); } } /** - * Load data for a single table + * Load data for a single table with batch inserts and transaction */ -async function loadTableData(db: Database, schema: TableSchema, csvContent: string): Promise { +async function loadTableData( + db: Database, + schema: TableSchema, + csvContent: string, + onProgress?: (rowsProcessed: number) => void +): Promise { const { headers, rows } = parseCSV(csvContent); if (rows.length === 0) { return; } - // Build column type map - const columnTypes: Record = {}; - for (const col of schema.columns) { - columnTypes[col.name] = col.type; - } - // Prepare INSERT statement const columns = headers.filter((h) => schema.columns.some((c) => c.name === h)); - const placeholders = columns.map(() => '?').join(', '); - const insertSQL = `INSERT INTO ${schema.name} (${columns.join(', ')}) VALUES (${placeholders})`; - - const stmt = db.prepare(insertSQL); - - // Insert each row - for (const row of rows) { - const typedRow = convertRowTypes(row, columnTypes); - const values = columns.map((col) => { - const value = typedRow[col]; - // Handle undefined, null, or empty values - return value === null || value === undefined ? null : value; - }); + if (columns.length === 0) { + return; + } + + const BATCH_SIZE = 1000; + let rowsProcessed = 0; + // Start transaction for better performance + db.run('BEGIN TRANSACTION'); + + try { + // Process in batches + for (let i = 0; i < rows.length; i += BATCH_SIZE) { + const batchRows = rows.slice(i, Math.min(i + BATCH_SIZE, rows.length)); + + // Build multi-row INSERT statement + const placeholders = batchRows.map(() => `(${columns.map(() => '?').join(', ')})`).join(', '); + const insertSQL = `INSERT INTO ${schema.name} (${columns.join(', ')}) VALUES ${placeholders}`; + + // Collect all values for the batch + const allValues: (string | number | null)[] = []; + for (const row of batchRows) { + for (const col of columns) { + const value = row[col]; + // Let SQLite handle type conversion - just pass empty strings as NULL + allValues.push(value === null || value === undefined || value === '' ? null : value); + } + } + + // Execute batch insert + const stmt = db.prepare(insertSQL); + try { + stmt.run(allValues); + } catch (error) { + console.error(`Error inserting batch into ${schema.name}:`, error); + console.error('Batch size:', batchRows.length); + throw error; + } finally { + stmt.free(); + } + + rowsProcessed += batchRows.length; + onProgress?.(rowsProcessed); + } + + // Commit transaction + db.run('COMMIT'); + } catch (error) { + // Rollback on error try { - stmt.run(values); - } catch (error) { - console.error(`Error inserting row into ${schema.name}:`, error); - console.error('Row data:', row); - throw error; + db.run('ROLLBACK'); + } catch (rollbackError) { + console.error('Error rolling back transaction:', rollbackError); } + throw error; } - - stmt.free(); } diff --git a/src/schema/schema.ts b/src/schema/schema.ts index 355fdc2..218b321 100644 --- a/src/schema/schema.ts +++ b/src/schema/schema.ts @@ -307,7 +307,34 @@ export function generateCreateIndexSQL(schema: TableSchema): string[] { } /** - * Initialize all GTFS tables in the database + * Get CREATE TABLE statements only (without indexes) + */ +export function getAllCreateTableStatements(): string[] { + const statements: string[] = []; + + for (const schema of GTFS_SCHEMA) { + statements.push(generateCreateTableSQL(schema)); + } + + return statements; +} + +/** + * Get CREATE INDEX statements only + */ +export function getAllCreateIndexStatements(): string[] { + const statements: string[] = []; + + for (const schema of GTFS_SCHEMA) { + statements.push(...generateCreateIndexSQL(schema)); + } + + return statements; +} + +/** + * Initialize all GTFS tables in the database (tables + indexes) + * @deprecated Use getAllCreateTableStatements() and getAllCreateIndexStatements() separately for better performance */ export function getAllCreateStatements(): string[] { const statements: string[] = [];