diff --git a/README.md b/README.md index e918f22..0db0e03 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,15 @@ Try the live demo to explore GTFS data, view routes with colors, and see trip sc - ✅ Include RT data in database exports - ✅ Full support for both GTFS-RT `time` and `delay` fields in stop time updates +### Smart Caching +- ✅ **Automatic caching** - Enabled by default, no configuration needed +- ✅ **Platform-optimized storage** - IndexedDB (browser) or FileSystem (Node.js) +- ✅ **Smart invalidation** - Based on file checksum, size, version, and library version +- ✅ **Automatic expiration** - Configurable (default: 7 days) +- ✅ **Cache management API** - Get stats, clean expired entries, clear cache +- ✅ **Custom cache stores** - Implement your own (Redis, S3, etc.) +- ✅ **Dramatic speed improvement** - Subsequent loads in <1 second + ## Migration Guide (v1.0.0+) If you're upgrading from an earlier version, the API has been simplified and improved: @@ -260,13 +269,18 @@ const gtfs = await GtfsSqlJs.fromZip('https://example.com/gtfs.zip', { The loading process goes through these phases: -1. **`downloading`** - Initializing database engine (0-2%) -2. **`creating_schema`** - Creating database tables (2-10%) -3. **`extracting`** - Loading and extracting GTFS ZIP file (10-15%) -4. **`inserting_data`** - Importing data from CSV files (15-85%) -5. **`creating_indexes`** - Building database indexes (85-95%) -6. **`analyzing`** - Optimizing query performance (95-99%) -7. **`complete`** - Load complete (100%) +1. **`checking_cache`** - Checking if cached database exists (0-2%) +2. **`loading_from_cache`** - Loading from cache (if found, jumps to 100%) +3. **`downloading`** - Downloading GTFS ZIP file (2-5%) +4. **`creating_schema`** - Creating database tables (5-15%) +5. **`extracting`** - Extracting GTFS ZIP file (15-20%) +6. **`inserting_data`** - Importing data from CSV files (20-85%) +7. **`creating_indexes`** - Building database indexes (85-95%) +8. **`analyzing`** - Optimizing query performance (95-98%) +9. **`saving_cache`** - Saving to cache (98-99%) +10. **`complete`** - Load complete (100%) + +**Note:** When a cached database is found, phases 3-9 are skipped, and loading completes in <1 second. **Note:** If `realtimeFeedUrls` are configured, GTFS-RT data will be fetched automatically after the load completes (but won't affect the progress percentage). @@ -686,6 +700,179 @@ if (alert.cause === AlertCause.ACCIDENT) { } ``` +### Smart Caching + +The library automatically caches processed GTFS databases to dramatically speed up subsequent loads. The first load processes the GTFS zip file (~5-10 seconds), but subsequent loads use the cached database (<1 second). + +#### Automatic Caching (Default Behavior) + +Caching is enabled by default with no configuration needed: + +```typescript +import { GtfsSqlJs } from 'gtfs-sqljs'; + +// First load: processes GTFS zip file and caches the result +const gtfs = await GtfsSqlJs.fromZip('gtfs.zip'); + +// Second load: uses cached database (much faster!) +const gtfs2 = await GtfsSqlJs.fromZip('gtfs.zip'); +``` + +**Platform-specific storage:** +- **Browser**: Uses IndexedDB (supports large databases, async) +- **Node.js**: Uses FileSystem (default: `~/.cache/gtfs-sqljs/`) + +#### Cache Invalidation + +The cache is automatically invalidated when any of these change: +- **File checksum** (SHA-256) - Different GTFS data +- **File size** - Quick check before computing checksum +- **Library version** - Schema or processing logic updated +- **Data version** - User-specified version (see below) +- **Skipped files** - Different `skipFiles` options + +#### Data Versioning + +Use `cacheVersion` to control cache invalidation: + +```typescript +// Load with version 1.0 +const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + cacheVersion: '1.0' +}); + +// Load with version 2.0 - will reprocess and create new cache +const gtfs2 = await GtfsSqlJs.fromZip('gtfs.zip', { + cacheVersion: '2.0' +}); +``` + +**When to increment version:** +- GTFS data is updated but filename stays the same +- You want to force cache refresh +- Testing different processing configurations + +#### Custom Cache Store + +**Browser - Custom IndexedDB:** +```typescript +import { GtfsSqlJs, IndexedDBCacheStore } from 'gtfs-sqljs'; + +const cache = new IndexedDBCacheStore({ + dbName: 'my-app-gtfs-cache' +}); + +const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache }); +``` + +**Node.js - Custom Directory:** +```typescript +import { GtfsSqlJs, FileSystemCacheStore } from 'gtfs-sqljs'; + +const cache = new FileSystemCacheStore({ + dir: './my-cache-dir' +}); + +const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache }); +``` + +#### Cache Management + +**Get cache statistics:** +```typescript +const stats = await GtfsSqlJs.getCacheStats(); + +console.log(`Total entries: ${stats.totalEntries}`); +console.log(`Active entries: ${stats.activeEntries}`); +console.log(`Expired entries: ${stats.expiredEntries}`); +console.log(`Total size: ${stats.totalSizeMB} MB`); +``` + +**List cache entries:** +```typescript +const entries = await GtfsSqlJs.listCache(); + +entries.forEach(entry => { + console.log(`Key: ${entry.key}`); + console.log(`Source: ${entry.metadata.source}`); + console.log(`Size: ${(entry.metadata.size / 1024 / 1024).toFixed(2)} MB`); + console.log(`Age: ${((Date.now() - entry.metadata.timestamp) / 1000 / 60 / 60).toFixed(1)} hours`); +}); +``` + +**Clean expired entries:** +```typescript +// Remove entries older than 7 days (default) +const deletedCount = await GtfsSqlJs.cleanExpiredCache(); +console.log(`Deleted ${deletedCount} expired entries`); + +// Custom expiration time (3 days) +const threeDays = 3 * 24 * 60 * 60 * 1000; +await GtfsSqlJs.cleanExpiredCache(undefined, threeDays); +``` + +**Clear all cache:** +```typescript +await GtfsSqlJs.clearCache(); +``` + +#### Disable Caching + +To disable caching, explicitly pass `null`: + +```typescript +const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + cache: null // No caching +}); +``` + +#### Custom Cache Expiration + +Change the default expiration time (default: 7 days): + +```typescript +const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + cacheExpirationMs: 3 * 24 * 60 * 60 * 1000 // 3 days +}); +``` + +#### Custom Cache Store Implementation + +Implement your own cache store (e.g., Redis, S3): + +```typescript +import type { CacheStore, CacheMetadata } from 'gtfs-sqljs'; + +class RedisCacheStore implements CacheStore { + async get(key: string): Promise { + // Implement Redis get + } + + async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise { + // Implement Redis set + } + + async has(key: string): Promise { + // Implement Redis exists check + } + + async delete(key: string): Promise { + // Implement Redis delete + } + + async clear(): Promise { + // Implement Redis clear + } + + async list(): Promise { + // Optional: Implement list + } +} + +const cache = new RedisCacheStore(); +const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { cache }); +``` + ### Export Database ```typescript diff --git a/dist/index.d.mts b/dist/index.d.mts index 3882845..0ce2430 100644 --- a/dist/index.d.mts +++ b/dist/index.d.mts @@ -177,6 +177,87 @@ interface RealtimeConfig { stalenessThreshold?: number; } +/** + * Metadata stored with cached GTFS databases + */ +interface CacheMetadata { + /** Checksum of the source zip file (SHA-256) */ + checksum: string; + /** Version number - cache is invalidated if this changes */ + version: string; + /** Timestamp when the cache was created */ + timestamp: number; + /** Source zip URL or path (for reference) */ + source?: string; + /** Size of the cached database in bytes */ + size: number; + /** Which files were skipped during import (affects cache validity) */ + skipFiles?: string[]; +} +/** + * A single cache entry with its metadata + */ +interface CacheEntry { + /** Unique cache key */ + key: string; + /** Cache metadata */ + metadata: CacheMetadata; +} +/** + * Interface for implementing custom cache storage backends. + * + * The library provides two implementations: + * - IndexedDBCacheStore (for browsers) + * - FileSystemCacheStore (for Node.js) + * + * You can implement this interface to use custom storage backends + * (e.g., Redis, S3, or any other storage system). + */ +interface CacheStore { + /** + * Retrieve a cached database by key + * @param key - Cache key (typically includes checksum and version) + * @returns The cached database as ArrayBuffer, or null if not found + */ + get(key: string): Promise; + /** + * Store a database in the cache + * @param key - Cache key + * @param data - Database as ArrayBuffer + * @param metadata - Metadata about the cached database + */ + set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise; + /** + * Check if a cache entry exists + * @param key - Cache key + * @returns true if the cache entry exists + */ + has(key: string): Promise; + /** + * Delete a specific cache entry + * @param key - Cache key + */ + delete(key: string): Promise; + /** + * Clear all cache entries + */ + clear(): Promise; + /** + * List all cached entries (optional) + * @returns Array of cache entries with their metadata + */ + list?(): Promise; +} +/** + * Options for cache stores + */ +interface CacheStoreOptions { + /** Cache directory path (for FileSystemCacheStore) */ + dir?: string; + /** IndexedDB database name (for IndexedDBCacheStore) */ + dbName?: string; +} + /** * GTFS Type Definitions * Based on GTFS Reference: https://gtfs.org/schedule/reference/ @@ -483,7 +564,7 @@ interface StopTimeUpdateWithMetadata extends StopTimeUpdate { * Progress information for GTFS data loading */ interface ProgressInfo { - phase: 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'complete'; + phase: 'checking_cache' | 'loading_from_cache' | 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'saving_cache' | 'complete'; currentFile: string | null; filesCompleted: number; totalFiles: number; @@ -532,6 +613,30 @@ interface GtfsSqlJsOptions { * Useful for displaying progress in UI or web workers */ onProgress?: ProgressCallback; + /** + * Optional: Cache store for persisting processed GTFS databases + * Use IndexedDBCacheStore (browser) or FileSystemCacheStore (Node.js) + * or implement your own CacheStore + * + * If not provided, caching is enabled by default with: + * - IndexedDBCacheStore in browsers + * - FileSystemCacheStore in Node.js + * + * Set to `null` to disable caching + */ + cache?: CacheStore | null; + /** + * Optional: Data version string + * When changed, cached databases are invalidated and reprocessed + * Default: '1.0' + */ + cacheVersion?: string; + /** + * Optional: Cache expiration time in milliseconds + * Cached databases older than this will be invalidated + * Default: 7 days (604800000 ms) + */ + cacheExpirationMs?: number; } declare class GtfsSqlJs { private db; @@ -554,6 +659,11 @@ declare class GtfsSqlJs { * Initialize from ZIP file */ private initFromZip; + /** + * Helper method to load GTFS data from zip data (ArrayBuffer) + * Used by both cache-enabled and cache-disabled paths + */ + private loadFromZipData; /** * Initialize from existing database */ @@ -725,6 +835,44 @@ declare class GtfsSqlJs { * Returns extended type with trip_id and rt_last_updated for debugging purposes */ debugExportAllStopTimeUpdates(): StopTimeUpdateWithMetadata[]; + /** + * Get cache statistics + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @returns Cache statistics including size, entry count, and age information + */ + static getCacheStats(cacheStore?: CacheStore): Promise<{ + totalEntries: number; + activeEntries: number; + expiredEntries: number; + totalSize: number; + totalSizeMB: string; + oldestEntry: number | null; + newestEntry: number | null; + }>; + /** + * Clean expired cache entries + * @param cacheStore - Cache store to clean (optional, auto-detects if not provided) + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns Number of entries deleted + */ + static cleanExpiredCache(cacheStore?: CacheStore, expirationMs?: number): Promise; + /** + * Clear all cache entries + * @param cacheStore - Cache store to clear (optional, auto-detects if not provided) + */ + static clearCache(cacheStore?: CacheStore): Promise; + /** + * List all cache entries + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @param includeExpired - Include expired entries (default: false) + * @returns Array of cache entries with metadata + */ + static listCache(cacheStore?: CacheStore, includeExpired?: boolean): Promise; + /** + * Get the default cache store for the current environment + * @returns Default cache store or null if unavailable + */ + private static getDefaultCacheStore; } /** @@ -749,4 +897,176 @@ interface IndexDefinition { } declare const GTFS_SCHEMA: TableSchema[]; -export { type Agency, type AgencyFilters, type Alert, AlertCause, AlertEffect, type AlertFilters, type Attribution, type Calendar, type CalendarDate, type ColumnDefinition, CongestionLevel, type EntitySelector, type FareAttribute, type FareRule, type FeedInfo, type Frequency, GTFS_SCHEMA, GtfsSqlJs, type GtfsSqlJsOptions, type IndexDefinition, type Level, OccupancyStatus, type Pathway, type Position, type RealtimeConfig, type Route, type RouteFilters, ScheduleRelationship, type Shape, type Stop, type StopFilters, type StopTime, type StopTimeEvent, type StopTimeFilters, type StopTimeRealtime, type StopTimeUpdate, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata, type StopTimeWithRealtime, type TableSchema, type TimeRange, type Transfer, type TranslatedString, type Trip, type TripFilters, type TripRealtime, type TripUpdate, type TripUpdateFilters, type TripWithRealtime, type VehicleDescriptor, type VehiclePosition, type VehiclePositionFilters, VehicleStopStatus }; +/** + * IndexedDB-based cache store for browsers + * + * Stores GTFS databases in IndexedDB for fast access on subsequent loads. + * Suitable for large databases (100s of MB to several GB depending on browser limits). + * + * @example + * ```typescript + * import { GtfsSqlJs, IndexedDBCacheStore } from 'gtfs-sqljs'; + * + * const cache = new IndexedDBCacheStore(); + * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + * cache, + * cacheVersion: '1.0' + * }); + * ``` + */ +declare class IndexedDBCacheStore implements CacheStore { + private dbName; + private storeName; + private version; + constructor(options?: CacheStoreOptions); + /** + * Open IndexedDB connection + */ + private openDB; + /** + * Get a cached database + */ + get(key: string): Promise; + /** + * Store a database in cache + */ + set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise; + /** + * Check if a cache entry exists + */ + has(key: string): Promise; + /** + * Delete a specific cache entry + */ + delete(key: string): Promise; + /** + * Clear all cache entries + */ + clear(): Promise; + /** + * List all cached entries + */ + list(): Promise; +} + +/** + * File system-based cache store for Node.js + * + * Stores GTFS databases as files on disk for fast access on subsequent loads. + * Suitable for any size database (limited only by disk space). + * + * @example + * ```typescript + * import { GtfsSqlJs, FileSystemCacheStore } from 'gtfs-sqljs'; + * + * const cache = new FileSystemCacheStore({ dir: './.cache/gtfs' }); + * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + * cache, + * cacheVersion: '1.0' + * }); + * ``` + */ +declare class FileSystemCacheStore implements CacheStore { + private cacheDir; + constructor(options?: CacheStoreOptions); + /** + * Get the cache directory path (lazy initialization) + */ + private getCacheDir; + /** + * Ensure cache directory exists + */ + private ensureCacheDir; + /** + * Get file path for a cache key + */ + private getFilePath; + /** + * Get metadata file path for a cache key + */ + private getMetadataPath; + /** + * Get a cached database + */ + get(key: string): Promise; + /** + * Store a database in cache + */ + set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise; + /** + * Check if a cache entry exists + */ + has(key: string): Promise; + /** + * Delete a specific cache entry + */ + delete(key: string): Promise; + /** + * Clear all cache entries + */ + clear(): Promise; + /** + * List all cached entries + */ + list(): Promise; +} + +/** + * Compute SHA-256 checksum of data + * Uses Web Crypto API (available in both browser and Node.js 18+) + */ +declare function computeChecksum(data: ArrayBuffer | Uint8Array): Promise; +/** + * Compute checksum for a zip file + * @param zipData - The zip file data (ArrayBuffer or Uint8Array) + * @returns SHA-256 checksum as hex string + */ +declare function computeZipChecksum(zipData: ArrayBuffer | Uint8Array): Promise; +/** + * Generate a cache key from checksum, version, filesize, source, and options + * Format: v{libVersion}_{dataVersion}_{filesize}_{checksum}_{source}_{skipFiles} + * + * @param checksum - SHA-256 checksum of zip file + * @param libVersion - Library version from package.json + * @param dataVersion - User-specified data version + * @param filesize - Size of the zip file in bytes + * @param source - Source URL or filename (optional) + * @param skipFiles - Files that were skipped during import + * @returns Cache key string + */ +declare function generateCacheKey(checksum: string, libVersion: string, dataVersion: string, filesize: number, source?: string, skipFiles?: string[]): string; + +/** + * Default cache expiration time in milliseconds (7 days) + */ +declare const DEFAULT_CACHE_EXPIRATION_MS: number; +/** + * Check if a cache entry is expired + * @param metadata - Cache metadata + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns true if the cache entry is expired + */ +declare function isCacheExpired(metadata: CacheMetadata, expirationMs?: number): boolean; +/** + * Filter out expired cache entries + * @param entries - Array of cache entries + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns Filtered array of non-expired entries + */ +declare function filterExpiredEntries(entries: CacheEntry[], expirationMs?: number): CacheEntry[]; +/** + * Get cache statistics + * @param entries - Array of cache entries + * @returns Cache statistics + */ +declare function getCacheStats(entries: CacheEntry[]): { + totalEntries: number; + activeEntries: number; + expiredEntries: number; + totalSize: number; + totalSizeMB: string; + oldestEntry: number | null; + newestEntry: number | null; +}; + +export { type Agency, type AgencyFilters, type Alert, AlertCause, AlertEffect, type AlertFilters, type Attribution, type CacheEntry, type CacheMetadata, type CacheStore, type CacheStoreOptions, type Calendar, type CalendarDate, type ColumnDefinition, CongestionLevel, DEFAULT_CACHE_EXPIRATION_MS, type EntitySelector, type FareAttribute, type FareRule, type FeedInfo, FileSystemCacheStore, type Frequency, GTFS_SCHEMA, GtfsSqlJs, type GtfsSqlJsOptions, type IndexDefinition, IndexedDBCacheStore, type Level, OccupancyStatus, type Pathway, type Position, type RealtimeConfig, type Route, type RouteFilters, ScheduleRelationship, type Shape, type Stop, type StopFilters, type StopTime, type StopTimeEvent, type StopTimeFilters, type StopTimeRealtime, type StopTimeUpdate, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata, type StopTimeWithRealtime, type TableSchema, type TimeRange, type Transfer, type TranslatedString, type Trip, type TripFilters, type TripRealtime, type TripUpdate, type TripUpdateFilters, type TripWithRealtime, type VehicleDescriptor, type VehiclePosition, type VehiclePositionFilters, VehicleStopStatus, computeChecksum, computeZipChecksum, filterExpiredEntries, generateCacheKey, getCacheStats, isCacheExpired }; diff --git a/dist/index.d.ts b/dist/index.d.ts index 3882845..0ce2430 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -177,6 +177,87 @@ interface RealtimeConfig { stalenessThreshold?: number; } +/** + * Metadata stored with cached GTFS databases + */ +interface CacheMetadata { + /** Checksum of the source zip file (SHA-256) */ + checksum: string; + /** Version number - cache is invalidated if this changes */ + version: string; + /** Timestamp when the cache was created */ + timestamp: number; + /** Source zip URL or path (for reference) */ + source?: string; + /** Size of the cached database in bytes */ + size: number; + /** Which files were skipped during import (affects cache validity) */ + skipFiles?: string[]; +} +/** + * A single cache entry with its metadata + */ +interface CacheEntry { + /** Unique cache key */ + key: string; + /** Cache metadata */ + metadata: CacheMetadata; +} +/** + * Interface for implementing custom cache storage backends. + * + * The library provides two implementations: + * - IndexedDBCacheStore (for browsers) + * - FileSystemCacheStore (for Node.js) + * + * You can implement this interface to use custom storage backends + * (e.g., Redis, S3, or any other storage system). + */ +interface CacheStore { + /** + * Retrieve a cached database by key + * @param key - Cache key (typically includes checksum and version) + * @returns The cached database as ArrayBuffer, or null if not found + */ + get(key: string): Promise; + /** + * Store a database in the cache + * @param key - Cache key + * @param data - Database as ArrayBuffer + * @param metadata - Metadata about the cached database + */ + set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise; + /** + * Check if a cache entry exists + * @param key - Cache key + * @returns true if the cache entry exists + */ + has(key: string): Promise; + /** + * Delete a specific cache entry + * @param key - Cache key + */ + delete(key: string): Promise; + /** + * Clear all cache entries + */ + clear(): Promise; + /** + * List all cached entries (optional) + * @returns Array of cache entries with their metadata + */ + list?(): Promise; +} +/** + * Options for cache stores + */ +interface CacheStoreOptions { + /** Cache directory path (for FileSystemCacheStore) */ + dir?: string; + /** IndexedDB database name (for IndexedDBCacheStore) */ + dbName?: string; +} + /** * GTFS Type Definitions * Based on GTFS Reference: https://gtfs.org/schedule/reference/ @@ -483,7 +564,7 @@ interface StopTimeUpdateWithMetadata extends StopTimeUpdate { * Progress information for GTFS data loading */ interface ProgressInfo { - phase: 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'complete'; + phase: 'checking_cache' | 'loading_from_cache' | 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'saving_cache' | 'complete'; currentFile: string | null; filesCompleted: number; totalFiles: number; @@ -532,6 +613,30 @@ interface GtfsSqlJsOptions { * Useful for displaying progress in UI or web workers */ onProgress?: ProgressCallback; + /** + * Optional: Cache store for persisting processed GTFS databases + * Use IndexedDBCacheStore (browser) or FileSystemCacheStore (Node.js) + * or implement your own CacheStore + * + * If not provided, caching is enabled by default with: + * - IndexedDBCacheStore in browsers + * - FileSystemCacheStore in Node.js + * + * Set to `null` to disable caching + */ + cache?: CacheStore | null; + /** + * Optional: Data version string + * When changed, cached databases are invalidated and reprocessed + * Default: '1.0' + */ + cacheVersion?: string; + /** + * Optional: Cache expiration time in milliseconds + * Cached databases older than this will be invalidated + * Default: 7 days (604800000 ms) + */ + cacheExpirationMs?: number; } declare class GtfsSqlJs { private db; @@ -554,6 +659,11 @@ declare class GtfsSqlJs { * Initialize from ZIP file */ private initFromZip; + /** + * Helper method to load GTFS data from zip data (ArrayBuffer) + * Used by both cache-enabled and cache-disabled paths + */ + private loadFromZipData; /** * Initialize from existing database */ @@ -725,6 +835,44 @@ declare class GtfsSqlJs { * Returns extended type with trip_id and rt_last_updated for debugging purposes */ debugExportAllStopTimeUpdates(): StopTimeUpdateWithMetadata[]; + /** + * Get cache statistics + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @returns Cache statistics including size, entry count, and age information + */ + static getCacheStats(cacheStore?: CacheStore): Promise<{ + totalEntries: number; + activeEntries: number; + expiredEntries: number; + totalSize: number; + totalSizeMB: string; + oldestEntry: number | null; + newestEntry: number | null; + }>; + /** + * Clean expired cache entries + * @param cacheStore - Cache store to clean (optional, auto-detects if not provided) + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns Number of entries deleted + */ + static cleanExpiredCache(cacheStore?: CacheStore, expirationMs?: number): Promise; + /** + * Clear all cache entries + * @param cacheStore - Cache store to clear (optional, auto-detects if not provided) + */ + static clearCache(cacheStore?: CacheStore): Promise; + /** + * List all cache entries + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @param includeExpired - Include expired entries (default: false) + * @returns Array of cache entries with metadata + */ + static listCache(cacheStore?: CacheStore, includeExpired?: boolean): Promise; + /** + * Get the default cache store for the current environment + * @returns Default cache store or null if unavailable + */ + private static getDefaultCacheStore; } /** @@ -749,4 +897,176 @@ interface IndexDefinition { } declare const GTFS_SCHEMA: TableSchema[]; -export { type Agency, type AgencyFilters, type Alert, AlertCause, AlertEffect, type AlertFilters, type Attribution, type Calendar, type CalendarDate, type ColumnDefinition, CongestionLevel, type EntitySelector, type FareAttribute, type FareRule, type FeedInfo, type Frequency, GTFS_SCHEMA, GtfsSqlJs, type GtfsSqlJsOptions, type IndexDefinition, type Level, OccupancyStatus, type Pathway, type Position, type RealtimeConfig, type Route, type RouteFilters, ScheduleRelationship, type Shape, type Stop, type StopFilters, type StopTime, type StopTimeEvent, type StopTimeFilters, type StopTimeRealtime, type StopTimeUpdate, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata, type StopTimeWithRealtime, type TableSchema, type TimeRange, type Transfer, type TranslatedString, type Trip, type TripFilters, type TripRealtime, type TripUpdate, type TripUpdateFilters, type TripWithRealtime, type VehicleDescriptor, type VehiclePosition, type VehiclePositionFilters, VehicleStopStatus }; +/** + * IndexedDB-based cache store for browsers + * + * Stores GTFS databases in IndexedDB for fast access on subsequent loads. + * Suitable for large databases (100s of MB to several GB depending on browser limits). + * + * @example + * ```typescript + * import { GtfsSqlJs, IndexedDBCacheStore } from 'gtfs-sqljs'; + * + * const cache = new IndexedDBCacheStore(); + * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + * cache, + * cacheVersion: '1.0' + * }); + * ``` + */ +declare class IndexedDBCacheStore implements CacheStore { + private dbName; + private storeName; + private version; + constructor(options?: CacheStoreOptions); + /** + * Open IndexedDB connection + */ + private openDB; + /** + * Get a cached database + */ + get(key: string): Promise; + /** + * Store a database in cache + */ + set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise; + /** + * Check if a cache entry exists + */ + has(key: string): Promise; + /** + * Delete a specific cache entry + */ + delete(key: string): Promise; + /** + * Clear all cache entries + */ + clear(): Promise; + /** + * List all cached entries + */ + list(): Promise; +} + +/** + * File system-based cache store for Node.js + * + * Stores GTFS databases as files on disk for fast access on subsequent loads. + * Suitable for any size database (limited only by disk space). + * + * @example + * ```typescript + * import { GtfsSqlJs, FileSystemCacheStore } from 'gtfs-sqljs'; + * + * const cache = new FileSystemCacheStore({ dir: './.cache/gtfs' }); + * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + * cache, + * cacheVersion: '1.0' + * }); + * ``` + */ +declare class FileSystemCacheStore implements CacheStore { + private cacheDir; + constructor(options?: CacheStoreOptions); + /** + * Get the cache directory path (lazy initialization) + */ + private getCacheDir; + /** + * Ensure cache directory exists + */ + private ensureCacheDir; + /** + * Get file path for a cache key + */ + private getFilePath; + /** + * Get metadata file path for a cache key + */ + private getMetadataPath; + /** + * Get a cached database + */ + get(key: string): Promise; + /** + * Store a database in cache + */ + set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise; + /** + * Check if a cache entry exists + */ + has(key: string): Promise; + /** + * Delete a specific cache entry + */ + delete(key: string): Promise; + /** + * Clear all cache entries + */ + clear(): Promise; + /** + * List all cached entries + */ + list(): Promise; +} + +/** + * Compute SHA-256 checksum of data + * Uses Web Crypto API (available in both browser and Node.js 18+) + */ +declare function computeChecksum(data: ArrayBuffer | Uint8Array): Promise; +/** + * Compute checksum for a zip file + * @param zipData - The zip file data (ArrayBuffer or Uint8Array) + * @returns SHA-256 checksum as hex string + */ +declare function computeZipChecksum(zipData: ArrayBuffer | Uint8Array): Promise; +/** + * Generate a cache key from checksum, version, filesize, source, and options + * Format: v{libVersion}_{dataVersion}_{filesize}_{checksum}_{source}_{skipFiles} + * + * @param checksum - SHA-256 checksum of zip file + * @param libVersion - Library version from package.json + * @param dataVersion - User-specified data version + * @param filesize - Size of the zip file in bytes + * @param source - Source URL or filename (optional) + * @param skipFiles - Files that were skipped during import + * @returns Cache key string + */ +declare function generateCacheKey(checksum: string, libVersion: string, dataVersion: string, filesize: number, source?: string, skipFiles?: string[]): string; + +/** + * Default cache expiration time in milliseconds (7 days) + */ +declare const DEFAULT_CACHE_EXPIRATION_MS: number; +/** + * Check if a cache entry is expired + * @param metadata - Cache metadata + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns true if the cache entry is expired + */ +declare function isCacheExpired(metadata: CacheMetadata, expirationMs?: number): boolean; +/** + * Filter out expired cache entries + * @param entries - Array of cache entries + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns Filtered array of non-expired entries + */ +declare function filterExpiredEntries(entries: CacheEntry[], expirationMs?: number): CacheEntry[]; +/** + * Get cache statistics + * @param entries - Array of cache entries + * @returns Cache statistics + */ +declare function getCacheStats(entries: CacheEntry[]): { + totalEntries: number; + activeEntries: number; + expiredEntries: number; + totalSize: number; + totalSizeMB: string; + oldestEntry: number | null; + newestEntry: number | null; +}; + +export { type Agency, type AgencyFilters, type Alert, AlertCause, AlertEffect, type AlertFilters, type Attribution, type CacheEntry, type CacheMetadata, type CacheStore, type CacheStoreOptions, type Calendar, type CalendarDate, type ColumnDefinition, CongestionLevel, DEFAULT_CACHE_EXPIRATION_MS, type EntitySelector, type FareAttribute, type FareRule, type FeedInfo, FileSystemCacheStore, type Frequency, GTFS_SCHEMA, GtfsSqlJs, type GtfsSqlJsOptions, type IndexDefinition, IndexedDBCacheStore, type Level, OccupancyStatus, type Pathway, type Position, type RealtimeConfig, type Route, type RouteFilters, ScheduleRelationship, type Shape, type Stop, type StopFilters, type StopTime, type StopTimeEvent, type StopTimeFilters, type StopTimeRealtime, type StopTimeUpdate, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata, type StopTimeWithRealtime, type TableSchema, type TimeRange, type Transfer, type TranslatedString, type Trip, type TripFilters, type TripRealtime, type TripUpdate, type TripUpdateFilters, type TripWithRealtime, type VehicleDescriptor, type VehiclePosition, type VehiclePositionFilters, VehicleStopStatus, computeChecksum, computeZipChecksum, filterExpiredEntries, generateCacheKey, getCacheStats, isCacheExpired }; diff --git a/dist/index.js b/dist/index.js index cd35fa1..4b7d91d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; +var __esm = (fn, res) => function __init() { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; +}; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); @@ -27,17 +30,419 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); +// src/cache/utils.ts +var utils_exports = {}; +__export(utils_exports, { + DEFAULT_CACHE_EXPIRATION_MS: () => DEFAULT_CACHE_EXPIRATION_MS, + filterExpiredEntries: () => filterExpiredEntries, + getCacheStats: () => getCacheStats, + isCacheExpired: () => isCacheExpired +}); +function isCacheExpired(metadata, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) { + const now = Date.now(); + const age = now - metadata.timestamp; + return age > expirationMs; +} +function filterExpiredEntries(entries, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) { + return entries.filter((entry) => !isCacheExpired(entry.metadata, expirationMs)); +} +function getCacheStats(entries) { + const totalSize = entries.reduce((sum, entry) => sum + entry.metadata.size, 0); + const expiredEntries = entries.filter((entry) => isCacheExpired(entry.metadata)); + const activeEntries = entries.filter((entry) => !isCacheExpired(entry.metadata)); + return { + totalEntries: entries.length, + activeEntries: activeEntries.length, + expiredEntries: expiredEntries.length, + totalSize, + totalSizeMB: (totalSize / 1024 / 1024).toFixed(2), + oldestEntry: entries.length > 0 ? Math.min(...entries.map((e) => e.metadata.timestamp)) : null, + newestEntry: entries.length > 0 ? Math.max(...entries.map((e) => e.metadata.timestamp)) : null + }; +} +var DEFAULT_CACHE_EXPIRATION_MS; +var init_utils = __esm({ + "src/cache/utils.ts"() { + "use strict"; + DEFAULT_CACHE_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1e3; + } +}); + +// src/cache/indexeddb-store.ts +var indexeddb_store_exports = {}; +__export(indexeddb_store_exports, { + IndexedDBCacheStore: () => IndexedDBCacheStore +}); +var IndexedDBCacheStore; +var init_indexeddb_store = __esm({ + "src/cache/indexeddb-store.ts"() { + "use strict"; + IndexedDBCacheStore = class { + constructor(options = {}) { + this.storeName = "gtfs-cache"; + this.version = 1; + this.dbName = options.dbName || "gtfs-sqljs-cache"; + } + /** + * Open IndexedDB connection + */ + async openDB() { + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.dbName, this.version); + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result); + request.onupgradeneeded = (event) => { + const db = event.target.result; + if (!db.objectStoreNames.contains(this.storeName)) { + const store = db.createObjectStore(this.storeName, { keyPath: "key" }); + store.createIndex("timestamp", "metadata.timestamp", { unique: false }); + } + }; + }); + } + /** + * Get a cached database + */ + async get(key) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.get(key); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + const result = request.result; + resolve(result ? result.data : null); + }; + }); + } + /** + * Store a database in cache + */ + async set(key, data, metadata) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const record = { + key, + data, + metadata + }; + const request = store.put(record); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + /** + * Check if a cache entry exists + */ + async has(key) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.getKey(key); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + resolve(request.result !== void 0); + }; + }); + } + /** + * Delete a specific cache entry + */ + async delete(key) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.delete(key); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + /** + * Clear all cache entries + */ + async clear() { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.clear(); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + /** + * List all cached entries + */ + async list() { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.getAllKeys(); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = async () => { + const keys = request.result; + const entries = []; + const metadataPromises = keys.map(async (key) => { + const metaRequest = store.get(key); + return new Promise((resolveEntry, rejectEntry) => { + metaRequest.onerror = () => rejectEntry(metaRequest.error); + metaRequest.onsuccess = () => { + const record = metaRequest.result; + resolveEntry({ + key, + metadata: record.metadata + }); + }; + }); + }); + try { + const results = await Promise.all(metadataPromises); + entries.push(...results); + } catch (error) { + db.close(); + reject(error); + return; + } + db.close(); + resolve(entries); + }; + }); + } + }; + } +}); + +// src/cache/fs-store.ts +var fs_store_exports = {}; +__export(fs_store_exports, { + FileSystemCacheStore: () => FileSystemCacheStore +}); +var FileSystemCacheStore; +var init_fs_store = __esm({ + "src/cache/fs-store.ts"() { + "use strict"; + FileSystemCacheStore = class { + constructor(options = {}) { + this.cacheDir = options.dir || ""; + } + /** + * Get the cache directory path (lazy initialization) + */ + async getCacheDir() { + if (this.cacheDir) { + return this.cacheDir; + } + const path = await import("path"); + const os = await import("os"); + this.cacheDir = path.join( + process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache"), + "gtfs-sqljs" + ); + return this.cacheDir; + } + /** + * Ensure cache directory exists + */ + async ensureCacheDir() { + const fs = await import("fs"); + const cacheDir = await this.getCacheDir(); + try { + await fs.promises.mkdir(cacheDir, { recursive: true }); + } catch (error) { + if (error.code !== "EEXIST") { + throw error; + } + } + } + /** + * Get file path for a cache key + */ + async getFilePath(key) { + const path = await import("path"); + const cacheDir = await this.getCacheDir(); + const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_"); + return path.join(cacheDir, `${safeKey}.db`); + } + /** + * Get metadata file path for a cache key + */ + async getMetadataPath(key) { + const path = await import("path"); + const cacheDir = await this.getCacheDir(); + const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_"); + return path.join(cacheDir, `${safeKey}.meta.json`); + } + /** + * Get a cached database + */ + async get(key) { + const fs = await import("fs"); + try { + const filePath = await this.getFilePath(key); + const buffer = await fs.promises.readFile(filePath); + return buffer.buffer.slice( + buffer.byteOffset, + buffer.byteOffset + buffer.byteLength + ); + } catch (error) { + if (error.code === "ENOENT") { + return null; + } + throw error; + } + } + /** + * Store a database in cache + */ + async set(key, data, metadata) { + const fs = await import("fs"); + await this.ensureCacheDir(); + const filePath = await this.getFilePath(key); + const metadataPath = await this.getMetadataPath(key); + await fs.promises.writeFile(filePath, new Uint8Array(data)); + await fs.promises.writeFile(metadataPath, JSON.stringify(metadata, null, 2)); + } + /** + * Check if a cache entry exists + */ + async has(key) { + const fs = await import("fs"); + try { + const filePath = await this.getFilePath(key); + await fs.promises.access(filePath); + return true; + } catch { + return false; + } + } + /** + * Delete a specific cache entry + */ + async delete(key) { + const fs = await import("fs"); + const filePath = await this.getFilePath(key); + const metadataPath = await this.getMetadataPath(key); + try { + await fs.promises.unlink(filePath); + } catch (error) { + if (error.code !== "ENOENT") { + throw error; + } + } + try { + await fs.promises.unlink(metadataPath); + } catch { + } + } + /** + * Clear all cache entries + */ + async clear() { + const fs = await import("fs"); + const path = await import("path"); + const cacheDir = await this.getCacheDir(); + try { + const files = await fs.promises.readdir(cacheDir); + await Promise.all( + files.map( + (file) => fs.promises.unlink(path.join(cacheDir, file)).catch(() => { + }) + ) + ); + } catch (error) { + if (error.code !== "ENOENT") { + throw error; + } + } + } + /** + * List all cached entries + */ + async list() { + const fs = await import("fs"); + const path = await import("path"); + const cacheDir = await this.getCacheDir(); + try { + const files = await fs.promises.readdir(cacheDir); + const metadataFiles = files.filter((f) => f.endsWith(".meta.json")); + const entries = []; + for (const metaFile of metadataFiles) { + try { + const metaPath = path.join(cacheDir, metaFile); + const metaContent = await fs.promises.readFile(metaPath, "utf-8"); + const metadata = JSON.parse(metaContent); + const key = metaFile.replace(".meta.json", ""); + entries.push({ key, metadata }); + } catch { + } + } + return entries; + } catch (error) { + if (error.code === "ENOENT") { + return []; + } + throw error; + } + } + }; + } +}); + // src/index.ts var index_exports = {}; __export(index_exports, { AlertCause: () => AlertCause, AlertEffect: () => AlertEffect, CongestionLevel: () => CongestionLevel, + DEFAULT_CACHE_EXPIRATION_MS: () => DEFAULT_CACHE_EXPIRATION_MS, + FileSystemCacheStore: () => FileSystemCacheStore, GTFS_SCHEMA: () => GTFS_SCHEMA, GtfsSqlJs: () => GtfsSqlJs, + IndexedDBCacheStore: () => IndexedDBCacheStore, OccupancyStatus: () => OccupancyStatus, ScheduleRelationship: () => ScheduleRelationship, - VehicleStopStatus: () => VehicleStopStatus + VehicleStopStatus: () => VehicleStopStatus, + computeChecksum: () => computeChecksum, + computeZipChecksum: () => computeZipChecksum, + filterExpiredEntries: () => filterExpiredEntries, + generateCacheKey: () => generateCacheKey, + getCacheStats: () => getCacheStats, + isCacheExpired: () => isCacheExpired }); module.exports = __toCommonJS(index_exports); @@ -1045,6 +1450,50 @@ async function loadRealtimeData(db, feedUrls) { } } +// src/cache/checksum.ts +async function getCrypto() { + if (typeof crypto !== "undefined" && crypto.subtle) { + return crypto; + } + if (typeof globalThis !== "undefined") { + try { + const { webcrypto } = await import("crypto"); + return webcrypto; + } catch { + throw new Error("Web Crypto API not available"); + } + } + throw new Error("Crypto not available in this environment"); +} +async function computeChecksum(data) { + const cryptoInstance = await getCrypto(); + const bufferSource = data instanceof Uint8Array ? data : new Uint8Array(data); + const hashBuffer = await cryptoInstance.subtle.digest("SHA-256", bufferSource); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); + return hashHex; +} +async function computeZipChecksum(zipData) { + return computeChecksum(zipData); +} +function generateCacheKey(checksum, libVersion, dataVersion, filesize, source, skipFiles) { + let key = `v${libVersion}_d${dataVersion}_${filesize}_${checksum}`; + if (source) { + const filename = source.split("/").pop() || source; + const sanitized = filename.replace(/[^a-zA-Z0-9._-]/g, "_"); + key += `_${sanitized}`; + } + if (skipFiles && skipFiles.length > 0) { + const sortedSkips = [...skipFiles].sort(); + const skipsSuffix = sortedSkips.join(",").replace(/\.txt/g, ""); + key += `_skip-${skipsSuffix}`; + } + return key; +} + +// src/gtfs-sqljs.ts +init_utils(); + // src/queries/agencies.ts function getAgencies(db, filters = {}) { const { agencyId, limit } = filters; @@ -1978,6 +2427,7 @@ function getAllStopTimeUpdates(db) { } // src/gtfs-sqljs.ts +var LIB_VERSION = "0.1.0"; var GtfsSqlJs = class _GtfsSqlJs { /** * Private constructor - use static factory methods instead @@ -2009,6 +2459,141 @@ var GtfsSqlJs = class _GtfsSqlJs { */ async initFromZip(zipPath, options) { const onProgress = options.onProgress; + const { + cache: userCache, + cacheVersion = "1.0", + skipFiles + } = options; + this.SQL = options.SQL || await (0, import_sql.default)(options.locateFile ? { locateFile: options.locateFile } : {}); + let cache = null; + if (userCache === null) { + cache = null; + } else if (userCache) { + cache = userCache; + } else { + try { + if (typeof window !== "undefined" && typeof indexedDB !== "undefined") { + const { IndexedDBCacheStore: IndexedDBCacheStore2 } = await Promise.resolve().then(() => (init_indexeddb_store(), indexeddb_store_exports)); + cache = new IndexedDBCacheStore2(); + } else if (typeof process !== "undefined" && process.versions?.node) { + const { FileSystemCacheStore: FileSystemCacheStore2 } = await Promise.resolve().then(() => (init_fs_store(), fs_store_exports)); + cache = new FileSystemCacheStore2(); + } + } catch (error) { + console.warn("Failed to initialize default cache store:", error); + cache = null; + } + } + if (cache) { + onProgress?.({ + phase: "checking_cache", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 0, + message: "Checking cache..." + }); + let zipData2; + if (typeof zipPath === "string") { + onProgress?.({ + phase: "downloading", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 2, + message: "Downloading GTFS ZIP file" + }); + zipData2 = await fetchZip(zipPath); + } else { + zipData2 = zipPath; + } + const filesize = zipData2.byteLength; + const checksum = await computeZipChecksum(zipData2); + const cacheKey = generateCacheKey( + checksum, + LIB_VERSION, + cacheVersion, + filesize, + typeof zipPath === "string" ? zipPath : void 0, + skipFiles + ); + const cachedDb = await cache.get(cacheKey); + if (cachedDb) { + onProgress?.({ + phase: "loading_from_cache", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 50, + message: "Loading from cache..." + }); + this.db = new this.SQL.Database(new Uint8Array(cachedDb)); + if (options.realtimeFeedUrls) { + this.realtimeFeedUrls = options.realtimeFeedUrls; + } + if (options.stalenessThreshold !== void 0) { + this.stalenessThreshold = options.stalenessThreshold; + } + onProgress?.({ + phase: "complete", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: "GTFS data loaded from cache" + }); + return; + } + onProgress?.({ + phase: "extracting", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 5, + message: "Extracting GTFS ZIP file" + }); + await this.loadFromZipData(zipData2, options, onProgress); + onProgress?.({ + phase: "saving_cache", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 98, + message: "Saving to cache..." + }); + const dbBuffer = this.export(); + await cache.set(cacheKey, dbBuffer, { + checksum, + version: cacheVersion, + timestamp: Date.now(), + source: typeof zipPath === "string" ? zipPath : void 0, + size: dbBuffer.byteLength, + skipFiles + }); + onProgress?.({ + phase: "complete", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: "GTFS data loaded successfully" + }); + return; + } onProgress?.({ phase: "downloading", currentFile: null, @@ -2017,9 +2602,31 @@ var GtfsSqlJs = class _GtfsSqlJs { rowsProcessed: 0, totalRows: 0, percentComplete: 0, - message: "Initializing database engine" + message: "Downloading GTFS ZIP file" }); - this.SQL = options.SQL || await (0, import_sql.default)(options.locateFile ? { locateFile: options.locateFile } : {}); + let zipData; + if (typeof zipPath === "string") { + zipData = await fetchZip(zipPath); + } else { + zipData = zipPath; + } + await this.loadFromZipData(zipData, options, onProgress); + onProgress?.({ + phase: "complete", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: "GTFS data loaded successfully" + }); + } + /** + * Helper method to load GTFS data from zip data (ArrayBuffer) + * Used by both cache-enabled and cache-disabled paths + */ + async loadFromZipData(zipData, options, onProgress) { this.db = new this.SQL.Database(); onProgress?.({ phase: "creating_schema", @@ -2028,7 +2635,7 @@ var GtfsSqlJs = class _GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 2, + percentComplete: 10, message: "Optimizing database for bulk import" }); this.db.run("PRAGMA synchronous = OFF"); @@ -2043,7 +2650,7 @@ var GtfsSqlJs = class _GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 5, + percentComplete: 15, message: "Creating database tables" }); const createTableStatements = getAllCreateTableStatements(); @@ -2058,10 +2665,10 @@ var GtfsSqlJs = class _GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 10, - message: "Loading and extracting GTFS ZIP file" + percentComplete: 20, + message: "Extracting GTFS ZIP file" }); - const files = await loadGTFSZip(zipPath); + const files = await loadGTFSZip(zipData); onProgress?.({ phase: "inserting_data", currentFile: null, @@ -2069,7 +2676,7 @@ var GtfsSqlJs = class _GtfsSqlJs { totalFiles: Object.keys(files).length, rowsProcessed: 0, totalRows: 0, - percentComplete: 15, + percentComplete: 25, message: "Starting data import" }); await loadGTFSData(this.db, files, options.skipFiles, onProgress); @@ -2119,16 +2726,6 @@ var GtfsSqlJs = class _GtfsSqlJs { 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" - }); if (this.realtimeFeedUrls.length > 0) { try { await loadRealtimeData(this.db, this.realtimeFeedUrls); @@ -2423,6 +3020,87 @@ var GtfsSqlJs = class _GtfsSqlJs { if (!this.db) throw new Error("Database not initialized"); return getAllStopTimeUpdates(this.db); } + // ==================== Cache Management Methods ==================== + /** + * Get cache statistics + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @returns Cache statistics including size, entry count, and age information + */ + static async getCacheStats(cacheStore) { + const { getCacheStats: getCacheStats2 } = await Promise.resolve().then(() => (init_utils(), utils_exports)); + const cache = cacheStore || await this.getDefaultCacheStore(); + if (!cache) { + throw new Error("No cache store available"); + } + const entries = await cache.list?.() || []; + return getCacheStats2(entries); + } + /** + * Clean expired cache entries + * @param cacheStore - Cache store to clean (optional, auto-detects if not provided) + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns Number of entries deleted + */ + static async cleanExpiredCache(cacheStore, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) { + const { filterExpiredEntries: filterExpiredEntries2 } = await Promise.resolve().then(() => (init_utils(), utils_exports)); + const cache = cacheStore || await this.getDefaultCacheStore(); + if (!cache || !cache.list) { + throw new Error("No cache store available or cache store does not support listing"); + } + const allEntries = await cache.list(); + const expiredEntries = allEntries.filter( + (entry) => !filterExpiredEntries2([entry], expirationMs).length + ); + await Promise.all(expiredEntries.map((entry) => cache.delete(entry.key))); + return expiredEntries.length; + } + /** + * Clear all cache entries + * @param cacheStore - Cache store to clear (optional, auto-detects if not provided) + */ + static async clearCache(cacheStore) { + const cache = cacheStore || await this.getDefaultCacheStore(); + if (!cache) { + throw new Error("No cache store available"); + } + await cache.clear(); + } + /** + * List all cache entries + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @param includeExpired - Include expired entries (default: false) + * @returns Array of cache entries with metadata + */ + static async listCache(cacheStore, includeExpired = false) { + const { filterExpiredEntries: filterExpiredEntries2 } = await Promise.resolve().then(() => (init_utils(), utils_exports)); + const cache = cacheStore || await this.getDefaultCacheStore(); + if (!cache || !cache.list) { + throw new Error("No cache store available or cache store does not support listing"); + } + const entries = await cache.list(); + if (includeExpired) { + return entries; + } + return filterExpiredEntries2(entries); + } + /** + * Get the default cache store for the current environment + * @returns Default cache store or null if unavailable + */ + static async getDefaultCacheStore() { + try { + if (typeof window !== "undefined" && typeof indexedDB !== "undefined") { + const { IndexedDBCacheStore: IndexedDBCacheStore2 } = await Promise.resolve().then(() => (init_indexeddb_store(), indexeddb_store_exports)); + return new IndexedDBCacheStore2(); + } else if (typeof process !== "undefined" && process.versions?.node) { + const { FileSystemCacheStore: FileSystemCacheStore2 } = await Promise.resolve().then(() => (init_fs_store(), fs_store_exports)); + return new FileSystemCacheStore2(); + } + } catch (error) { + console.warn("Failed to initialize default cache store:", error); + } + return null; + } }; // src/types/gtfs-rt.ts @@ -2488,6 +3166,11 @@ var AlertEffect = /* @__PURE__ */ ((AlertEffect2) => { AlertEffect2[AlertEffect2["ACCESSIBILITY_ISSUE"] = 11] = "ACCESSIBILITY_ISSUE"; return AlertEffect2; })(AlertEffect || {}); + +// src/index.ts +init_indexeddb_store(); +init_fs_store(); +init_utils(); /** * gtfs-sqljs - Load GTFS data into sql.js SQLite database * @author Théophile Helleboid/SysDevRun diff --git a/dist/index.js.map b/dist/index.js.map index d634512..6485fed 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 { 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: 'Loading and 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 // Auto-fetch realtime data if feed URLs are configured\n if (this.realtimeFeedUrls.length > 0) {\n try {\n await loadRealtimeData(this.db, this.realtimeFeedUrls);\n } catch (error) {\n // Don't fail the entire load if RT data fetch fails\n console.warn('Failed to fetch initial realtime data:', error);\n }\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 +{"version":3,"sources":["../src/cache/utils.ts","../src/cache/indexeddb-store.ts","../src/cache/fs-store.ts","../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/cache/checksum.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":["import type { CacheMetadata, CacheEntry } from './types';\n\n/**\n * Default cache expiration time in milliseconds (7 days)\n */\nexport const DEFAULT_CACHE_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * Check if a cache entry is expired\n * @param metadata - Cache metadata\n * @param expirationMs - Expiration time in milliseconds (default: 7 days)\n * @returns true if the cache entry is expired\n */\nexport function isCacheExpired(\n metadata: CacheMetadata,\n expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS\n): boolean {\n const now = Date.now();\n const age = now - metadata.timestamp;\n return age > expirationMs;\n}\n\n/**\n * Filter out expired cache entries\n * @param entries - Array of cache entries\n * @param expirationMs - Expiration time in milliseconds (default: 7 days)\n * @returns Filtered array of non-expired entries\n */\nexport function filterExpiredEntries(\n entries: CacheEntry[],\n expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS\n): CacheEntry[] {\n return entries.filter(entry => !isCacheExpired(entry.metadata, expirationMs));\n}\n\n/**\n * Get cache statistics\n * @param entries - Array of cache entries\n * @returns Cache statistics\n */\nexport function getCacheStats(entries: CacheEntry[]) {\n const totalSize = entries.reduce((sum, entry) => sum + entry.metadata.size, 0);\n const expiredEntries = entries.filter(entry => isCacheExpired(entry.metadata));\n const activeEntries = entries.filter(entry => !isCacheExpired(entry.metadata));\n\n return {\n totalEntries: entries.length,\n activeEntries: activeEntries.length,\n expiredEntries: expiredEntries.length,\n totalSize,\n totalSizeMB: (totalSize / 1024 / 1024).toFixed(2),\n oldestEntry: entries.length > 0\n ? Math.min(...entries.map(e => e.metadata.timestamp))\n : null,\n newestEntry: entries.length > 0\n ? Math.max(...entries.map(e => e.metadata.timestamp))\n : null,\n };\n}\n","import type { CacheStore, CacheMetadata, CacheEntry, CacheStoreOptions } from './types';\n\n/**\n * IndexedDB-based cache store for browsers\n *\n * Stores GTFS databases in IndexedDB for fast access on subsequent loads.\n * Suitable for large databases (100s of MB to several GB depending on browser limits).\n *\n * @example\n * ```typescript\n * import { GtfsSqlJs, IndexedDBCacheStore } from 'gtfs-sqljs';\n *\n * const cache = new IndexedDBCacheStore();\n * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', {\n * cache,\n * cacheVersion: '1.0'\n * });\n * ```\n */\nexport class IndexedDBCacheStore implements CacheStore {\n private dbName: string;\n private storeName = 'gtfs-cache';\n private version = 1;\n\n constructor(options: CacheStoreOptions = {}) {\n this.dbName = options.dbName || 'gtfs-sqljs-cache';\n }\n\n /**\n * Open IndexedDB connection\n */\n private async openDB(): Promise {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, this.version);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n\n // Create object store if it doesn't exist\n if (!db.objectStoreNames.contains(this.storeName)) {\n const store = db.createObjectStore(this.storeName, { keyPath: 'key' });\n store.createIndex('timestamp', 'metadata.timestamp', { unique: false });\n }\n };\n });\n }\n\n /**\n * Get a cached database\n */\n async get(key: string): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readonly');\n const store = transaction.objectStore(this.storeName);\n const request = store.get(key);\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n const result = request.result;\n resolve(result ? result.data : null);\n };\n });\n }\n\n /**\n * Store a database in cache\n */\n async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readwrite');\n const store = transaction.objectStore(this.storeName);\n\n const record = {\n key,\n data,\n metadata\n };\n\n const request = store.put(record);\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n resolve();\n };\n });\n }\n\n /**\n * Check if a cache entry exists\n */\n async has(key: string): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readonly');\n const store = transaction.objectStore(this.storeName);\n const request = store.getKey(key);\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n resolve(request.result !== undefined);\n };\n });\n }\n\n /**\n * Delete a specific cache entry\n */\n async delete(key: string): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readwrite');\n const store = transaction.objectStore(this.storeName);\n const request = store.delete(key);\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n resolve();\n };\n });\n }\n\n /**\n * Clear all cache entries\n */\n async clear(): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readwrite');\n const store = transaction.objectStore(this.storeName);\n const request = store.clear();\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n resolve();\n };\n });\n }\n\n /**\n * List all cached entries\n */\n async list(): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readonly');\n const store = transaction.objectStore(this.storeName);\n const request = store.getAllKeys();\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = async () => {\n const keys = request.result as string[];\n const entries: CacheEntry[] = [];\n\n // Get metadata for each key\n const metadataPromises = keys.map(async (key) => {\n const metaRequest = store.get(key);\n return new Promise((resolveEntry, rejectEntry) => {\n metaRequest.onerror = () => rejectEntry(metaRequest.error);\n metaRequest.onsuccess = () => {\n const record = metaRequest.result;\n resolveEntry({\n key,\n metadata: record.metadata\n });\n };\n });\n });\n\n try {\n const results = await Promise.all(metadataPromises);\n entries.push(...results);\n } catch (error) {\n db.close();\n reject(error);\n return;\n }\n\n db.close();\n resolve(entries);\n };\n });\n }\n}\n","import type { CacheStore, CacheMetadata, CacheEntry, CacheStoreOptions } from './types';\n\n/**\n * File system-based cache store for Node.js\n *\n * Stores GTFS databases as files on disk for fast access on subsequent loads.\n * Suitable for any size database (limited only by disk space).\n *\n * @example\n * ```typescript\n * import { GtfsSqlJs, FileSystemCacheStore } from 'gtfs-sqljs';\n *\n * const cache = new FileSystemCacheStore({ dir: './.cache/gtfs' });\n * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', {\n * cache,\n * cacheVersion: '1.0'\n * });\n * ```\n */\nexport class FileSystemCacheStore implements CacheStore {\n private cacheDir: string;\n\n constructor(options: CacheStoreOptions = {}) {\n // Default cache directory (will be resolved lazily on first use)\n this.cacheDir = options.dir || '';\n }\n\n /**\n * Get the cache directory path (lazy initialization)\n */\n private async getCacheDir(): Promise {\n if (this.cacheDir) {\n return this.cacheDir;\n }\n\n // Dynamically import Node.js modules\n const path = await import('path');\n const os = await import('os');\n\n // Default to XDG cache directory on Linux/Mac, or temp dir as fallback\n this.cacheDir = path.join(\n process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'),\n 'gtfs-sqljs'\n );\n\n return this.cacheDir;\n }\n\n /**\n * Ensure cache directory exists\n */\n private async ensureCacheDir(): Promise {\n const fs = await import('fs');\n const cacheDir = await this.getCacheDir();\n\n try {\n await fs.promises.mkdir(cacheDir, { recursive: true });\n } catch (error) {\n // Ignore error if directory already exists\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n }\n\n /**\n * Get file path for a cache key\n */\n private async getFilePath(key: string): Promise {\n const path = await import('path');\n const cacheDir = await this.getCacheDir();\n\n // Sanitize key to be filesystem-safe\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');\n return path.join(cacheDir, `${safeKey}.db`);\n }\n\n /**\n * Get metadata file path for a cache key\n */\n private async getMetadataPath(key: string): Promise {\n const path = await import('path');\n const cacheDir = await this.getCacheDir();\n\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');\n return path.join(cacheDir, `${safeKey}.meta.json`);\n }\n\n /**\n * Get a cached database\n */\n async get(key: string): Promise {\n const fs = await import('fs');\n\n try {\n const filePath = await this.getFilePath(key);\n const buffer = await fs.promises.readFile(filePath);\n return buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength\n );\n } catch (error) {\n // Return null if file doesn't exist\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Store a database in cache\n */\n async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise {\n const fs = await import('fs');\n await this.ensureCacheDir();\n\n const filePath = await this.getFilePath(key);\n const metadataPath = await this.getMetadataPath(key);\n\n // Write database file\n await fs.promises.writeFile(filePath, new Uint8Array(data));\n\n // Write metadata file\n await fs.promises.writeFile(metadataPath, JSON.stringify(metadata, null, 2));\n }\n\n /**\n * Check if a cache entry exists\n */\n async has(key: string): Promise {\n const fs = await import('fs');\n\n try {\n const filePath = await this.getFilePath(key);\n await fs.promises.access(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Delete a specific cache entry\n */\n async delete(key: string): Promise {\n const fs = await import('fs');\n\n const filePath = await this.getFilePath(key);\n const metadataPath = await this.getMetadataPath(key);\n\n try {\n await fs.promises.unlink(filePath);\n } catch (error) {\n // Ignore error if file doesn't exist\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n\n try {\n await fs.promises.unlink(metadataPath);\n } catch {\n // Ignore metadata file errors\n }\n }\n\n /**\n * Clear all cache entries\n */\n async clear(): Promise {\n const fs = await import('fs');\n const path = await import('path');\n const cacheDir = await this.getCacheDir();\n\n try {\n const files = await fs.promises.readdir(cacheDir);\n\n await Promise.all(\n files.map(file =>\n fs.promises.unlink(path.join(cacheDir, file)).catch(() => {\n // Ignore errors\n })\n )\n );\n } catch (error) {\n // Ignore error if directory doesn't exist\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n /**\n * List all cached entries\n */\n async list(): Promise {\n const fs = await import('fs');\n const path = await import('path');\n const cacheDir = await this.getCacheDir();\n\n try {\n const files = await fs.promises.readdir(cacheDir);\n const metadataFiles = files.filter(f => f.endsWith('.meta.json'));\n\n const entries: CacheEntry[] = [];\n\n for (const metaFile of metadataFiles) {\n try {\n const metaPath = path.join(cacheDir, metaFile);\n const metaContent = await fs.promises.readFile(metaPath, 'utf-8');\n const metadata = JSON.parse(metaContent) as CacheMetadata;\n\n // Extract key from filename (remove .meta.json)\n const key = metaFile.replace('.meta.json', '');\n\n entries.push({ key, metadata });\n } catch {\n // Skip invalid metadata files\n }\n }\n\n return entries;\n } catch (error) {\n // Return empty array if directory doesn't exist\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return [];\n }\n throw error;\n }\n }\n}\n","/**\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// Export cache types and interfaces\nexport type {\n CacheStore,\n CacheMetadata,\n CacheEntry,\n CacheStoreOptions\n} from './cache/types';\n\n// Export cache store implementations\nexport { IndexedDBCacheStore } from './cache/indexeddb-store';\nexport { FileSystemCacheStore } from './cache/fs-store';\n\n// Export cache utilities\nexport { computeChecksum, computeZipChecksum, generateCacheKey } from './cache/checksum';\nexport { isCacheExpired, filterExpiredEntries, getCacheStats, DEFAULT_CACHE_EXPIRATION_MS } from './cache/utils';\n","/**\n * Main GtfsSqlJs Class\n */\n\nimport initSqlJs, { type Database, type SqlJsStatic } from 'sql.js';\nimport { getAllCreateTableStatements, getAllCreateIndexStatements } from './schema/schema';\nimport { loadGTFSZip, fetchZip } 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';\nimport type { CacheStore } from './cache/types';\nimport { computeZipChecksum, generateCacheKey } from './cache/checksum';\nimport { DEFAULT_CACHE_EXPIRATION_MS } from './cache/utils';\n\n// Library version from package.json\nconst LIB_VERSION = '0.1.0';\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: 'checking_cache' | 'loading_from_cache' | 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'saving_cache' | '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 /**\n * Optional: Cache store for persisting processed GTFS databases\n * Use IndexedDBCacheStore (browser) or FileSystemCacheStore (Node.js)\n * or implement your own CacheStore\n *\n * If not provided, caching is enabled by default with:\n * - IndexedDBCacheStore in browsers\n * - FileSystemCacheStore in Node.js\n *\n * Set to `null` to disable caching\n */\n cache?: CacheStore | null;\n\n /**\n * Optional: Data version string\n * When changed, cached databases are invalidated and reprocessed\n * Default: '1.0'\n */\n cacheVersion?: string;\n\n /**\n * Optional: Cache expiration time in milliseconds\n * Cached databases older than this will be invalidated\n * Default: 7 days (604800000 ms)\n */\n cacheExpirationMs?: 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 const onProgress = options.onProgress;\n const {\n cache: userCache,\n cacheVersion = '1.0',\n skipFiles\n } = options;\n\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Determine cache store to use\n let cache: CacheStore | null = null;\n\n if (userCache === null) {\n // User explicitly disabled caching\n cache = null;\n } else if (userCache) {\n // User provided a cache store\n cache = userCache;\n } else {\n // Auto-detect environment and create default cache store\n try {\n if (typeof window !== 'undefined' && typeof indexedDB !== 'undefined') {\n // Browser environment - use IndexedDB\n const { IndexedDBCacheStore } = await import('./cache/indexeddb-store');\n cache = new IndexedDBCacheStore();\n } else if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js environment - use FileSystem\n const { FileSystemCacheStore } = await import('./cache/fs-store');\n cache = new FileSystemCacheStore();\n }\n } catch (error) {\n // Fallback to no caching if import fails\n console.warn('Failed to initialize default cache store:', error);\n cache = null;\n }\n }\n\n // Check cache if enabled\n if (cache) {\n onProgress?.({\n phase: 'checking_cache',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 0,\n message: 'Checking cache...',\n });\n\n // Fetch raw zip data for checksum (only if zipPath is a string)\n let zipData: ArrayBuffer;\n if (typeof zipPath === 'string') {\n onProgress?.({\n phase: 'downloading',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 2,\n message: 'Downloading GTFS ZIP file',\n });\n\n zipData = await fetchZip(zipPath);\n } else {\n // zipPath is already ArrayBuffer or Uint8Array\n zipData = zipPath as ArrayBuffer;\n }\n\n // Calculate filesize\n const filesize = zipData.byteLength;\n\n // Compute checksum\n const checksum = await computeZipChecksum(zipData);\n\n // Generate cache key with all parameters\n const cacheKey = generateCacheKey(\n checksum,\n LIB_VERSION,\n cacheVersion,\n filesize,\n typeof zipPath === 'string' ? zipPath : undefined,\n skipFiles\n );\n\n // Check if cache exists\n const cachedDb = await cache.get(cacheKey);\n\n if (cachedDb) {\n // Note: We retrieve the cache entry but we should check if it's expired\n // For now, we check expiration when listing/managing cache\n // The cache store itself doesn't track metadata on get()\n // TODO: Consider updating CacheStore.get() to return metadata as well\n\n // Load from cache\n onProgress?.({\n phase: 'loading_from_cache',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 50,\n message: 'Loading from cache...',\n });\n\n this.db = new this.SQL.Database(new Uint8Array(cachedDb));\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: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 100,\n message: 'GTFS data loaded from cache',\n });\n\n return;\n }\n\n // Cache miss - continue with normal loading but use already-fetched zip data\n onProgress?.({\n phase: 'extracting',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 5,\n message: 'Extracting GTFS ZIP file',\n });\n\n await this.loadFromZipData(zipData, options, onProgress);\n\n // Save to cache\n onProgress?.({\n phase: 'saving_cache',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 98,\n message: 'Saving to cache...',\n });\n\n const dbBuffer = this.export();\n await cache.set(cacheKey, dbBuffer, {\n checksum,\n version: cacheVersion,\n timestamp: Date.now(),\n source: typeof zipPath === 'string' ? zipPath : undefined,\n size: dbBuffer.byteLength,\n skipFiles,\n });\n\n onProgress?.({\n phase: 'complete',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 100,\n message: 'GTFS data loaded successfully',\n });\n\n return;\n }\n\n // No cache - use normal loading flow\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: 'Downloading GTFS ZIP file',\n });\n\n // Fetch zip data\n let zipData: ArrayBuffer;\n if (typeof zipPath === 'string') {\n zipData = await fetchZip(zipPath);\n } else {\n zipData = zipPath as ArrayBuffer;\n }\n\n // Load from zip data\n await this.loadFromZipData(zipData, options, onProgress);\n\n onProgress?.({\n phase: 'complete',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 100,\n message: 'GTFS data loaded successfully',\n });\n }\n\n /**\n * Helper method to load GTFS data from zip data (ArrayBuffer)\n * Used by both cache-enabled and cache-disabled paths\n */\n private async loadFromZipData(\n zipData: ArrayBuffer,\n options: Omit,\n onProgress?: ProgressCallback\n ): Promise {\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: 10,\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: 15,\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 // Extract files from zip\n onProgress?.({\n phase: 'extracting',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 20,\n message: 'Extracting GTFS ZIP file',\n });\n\n const files = await loadGTFSZip(zipData);\n\n // Load GTFS data\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: 25,\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 // Auto-fetch realtime data if feed URLs are configured\n if (this.realtimeFeedUrls.length > 0) {\n try {\n await loadRealtimeData(this.db, this.realtimeFeedUrls);\n } catch (error) {\n // Don't fail the entire load if RT data fetch fails\n console.warn('Failed to fetch initial realtime data:', error);\n }\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 // ==================== Cache Management Methods ====================\n\n /**\n * Get cache statistics\n * @param cacheStore - Cache store to query (optional, auto-detects if not provided)\n * @returns Cache statistics including size, entry count, and age information\n */\n static async getCacheStats(cacheStore?: CacheStore) {\n const { getCacheStats } = await import('./cache/utils');\n const cache = cacheStore || await this.getDefaultCacheStore();\n\n if (!cache) {\n throw new Error('No cache store available');\n }\n\n const entries = await cache.list?.() || [];\n return getCacheStats(entries);\n }\n\n /**\n * Clean expired cache entries\n * @param cacheStore - Cache store to clean (optional, auto-detects if not provided)\n * @param expirationMs - Expiration time in milliseconds (default: 7 days)\n * @returns Number of entries deleted\n */\n static async cleanExpiredCache(\n cacheStore?: CacheStore,\n expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS\n ): Promise {\n const { filterExpiredEntries } = await import('./cache/utils');\n const cache = cacheStore || await this.getDefaultCacheStore();\n\n if (!cache || !cache.list) {\n throw new Error('No cache store available or cache store does not support listing');\n }\n\n const allEntries = await cache.list();\n const expiredEntries = allEntries.filter(entry =>\n !filterExpiredEntries([entry], expirationMs).length\n );\n\n // Delete expired entries\n await Promise.all(expiredEntries.map(entry => cache.delete(entry.key)));\n\n return expiredEntries.length;\n }\n\n /**\n * Clear all cache entries\n * @param cacheStore - Cache store to clear (optional, auto-detects if not provided)\n */\n static async clearCache(cacheStore?: CacheStore): Promise {\n const cache = cacheStore || await this.getDefaultCacheStore();\n\n if (!cache) {\n throw new Error('No cache store available');\n }\n\n await cache.clear();\n }\n\n /**\n * List all cache entries\n * @param cacheStore - Cache store to query (optional, auto-detects if not provided)\n * @param includeExpired - Include expired entries (default: false)\n * @returns Array of cache entries with metadata\n */\n static async listCache(\n cacheStore?: CacheStore,\n includeExpired: boolean = false\n ) {\n const { filterExpiredEntries } = await import('./cache/utils');\n const cache = cacheStore || await this.getDefaultCacheStore();\n\n if (!cache || !cache.list) {\n throw new Error('No cache store available or cache store does not support listing');\n }\n\n const entries = await cache.list();\n\n if (includeExpired) {\n return entries;\n }\n\n return filterExpiredEntries(entries);\n }\n\n /**\n * Get the default cache store for the current environment\n * @returns Default cache store or null if unavailable\n */\n private static async getDefaultCacheStore(): Promise {\n try {\n if (typeof window !== 'undefined' && typeof indexedDB !== 'undefined') {\n // Browser environment - use IndexedDB\n const { IndexedDBCacheStore } = await import('./cache/indexeddb-store');\n return new IndexedDBCacheStore();\n } else if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js environment - use FileSystem\n const { FileSystemCacheStore } = await import('./cache/fs-store');\n return new FileSystemCacheStore();\n }\n } catch (error) {\n console.warn('Failed to initialize default cache store:', error);\n }\n\n return null;\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 (exported for checksum computation)\n */\nexport async 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 * Get crypto object (browser or Node.js)\n */\nasync function getCrypto(): Promise {\n // Browser environment\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n return crypto;\n }\n\n // Node.js environment - import webcrypto\n if (typeof globalThis !== 'undefined') {\n try {\n const { webcrypto } = await import('crypto');\n return webcrypto as unknown as Crypto;\n } catch {\n // Fallback for older Node.js versions\n throw new Error('Web Crypto API not available');\n }\n }\n\n throw new Error('Crypto not available in this environment');\n}\n\n/**\n * Compute SHA-256 checksum of data\n * Uses Web Crypto API (available in both browser and Node.js 18+)\n */\nexport async function computeChecksum(data: ArrayBuffer | Uint8Array): Promise {\n // Get crypto instance\n const cryptoInstance = await getCrypto();\n\n // Ensure we have a BufferSource for crypto.subtle.digest\n // Use type assertion to handle ArrayBufferLike compatibility\n const bufferSource = (data instanceof Uint8Array ? data : new Uint8Array(data)) as BufferSource;\n const hashBuffer = await cryptoInstance.subtle.digest('SHA-256', bufferSource);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return hashHex;\n}\n\n/**\n * Compute checksum for a zip file\n * @param zipData - The zip file data (ArrayBuffer or Uint8Array)\n * @returns SHA-256 checksum as hex string\n */\nexport async function computeZipChecksum(zipData: ArrayBuffer | Uint8Array): Promise {\n return computeChecksum(zipData);\n}\n\n/**\n * Generate a cache key from checksum, version, filesize, source, and options\n * Format: v{libVersion}_{dataVersion}_{filesize}_{checksum}_{source}_{skipFiles}\n *\n * @param checksum - SHA-256 checksum of zip file\n * @param libVersion - Library version from package.json\n * @param dataVersion - User-specified data version\n * @param filesize - Size of the zip file in bytes\n * @param source - Source URL or filename (optional)\n * @param skipFiles - Files that were skipped during import\n * @returns Cache key string\n */\nexport function generateCacheKey(\n checksum: string,\n libVersion: string,\n dataVersion: string,\n filesize: number,\n source?: string,\n skipFiles?: string[]\n): string {\n // Start with versions and filesize\n let key = `v${libVersion}_d${dataVersion}_${filesize}_${checksum}`;\n\n // Add source (sanitized)\n if (source) {\n // Extract filename from URL/path and sanitize\n const filename = source.split('/').pop() || source;\n const sanitized = filename.replace(/[^a-zA-Z0-9._-]/g, '_');\n key += `_${sanitized}`;\n }\n\n // Add skip files if any\n if (skipFiles && skipFiles.length > 0) {\n // Sort to ensure consistent key regardless of order\n const sortedSkips = [...skipFiles].sort();\n const skipsSuffix = sortedSkips.join(',').replace(/\\.txt/g, '');\n key += `_skip-${skipsSuffix}`;\n }\n\n return key;\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","IndexedDBCacheStore","FileSystemCacheStore","zipData","getCacheStats","filterExpiredEntries","ScheduleRelationship","VehicleStopStatus","CongestionLevel","OccupancyStatus","AlertCause","AlertEffect"]} \ No newline at end of file diff --git a/dist/index.mjs b/dist/index.mjs index 75f403e..8ba02b8 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -1,3 +1,406 @@ +var __defProp = Object.defineProperty; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __esm = (fn, res) => function __init() { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; + +// src/cache/utils.ts +var utils_exports = {}; +__export(utils_exports, { + DEFAULT_CACHE_EXPIRATION_MS: () => DEFAULT_CACHE_EXPIRATION_MS, + filterExpiredEntries: () => filterExpiredEntries, + getCacheStats: () => getCacheStats, + isCacheExpired: () => isCacheExpired +}); +function isCacheExpired(metadata, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) { + const now = Date.now(); + const age = now - metadata.timestamp; + return age > expirationMs; +} +function filterExpiredEntries(entries, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) { + return entries.filter((entry) => !isCacheExpired(entry.metadata, expirationMs)); +} +function getCacheStats(entries) { + const totalSize = entries.reduce((sum, entry) => sum + entry.metadata.size, 0); + const expiredEntries = entries.filter((entry) => isCacheExpired(entry.metadata)); + const activeEntries = entries.filter((entry) => !isCacheExpired(entry.metadata)); + return { + totalEntries: entries.length, + activeEntries: activeEntries.length, + expiredEntries: expiredEntries.length, + totalSize, + totalSizeMB: (totalSize / 1024 / 1024).toFixed(2), + oldestEntry: entries.length > 0 ? Math.min(...entries.map((e) => e.metadata.timestamp)) : null, + newestEntry: entries.length > 0 ? Math.max(...entries.map((e) => e.metadata.timestamp)) : null + }; +} +var DEFAULT_CACHE_EXPIRATION_MS; +var init_utils = __esm({ + "src/cache/utils.ts"() { + "use strict"; + DEFAULT_CACHE_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1e3; + } +}); + +// src/cache/indexeddb-store.ts +var indexeddb_store_exports = {}; +__export(indexeddb_store_exports, { + IndexedDBCacheStore: () => IndexedDBCacheStore +}); +var IndexedDBCacheStore; +var init_indexeddb_store = __esm({ + "src/cache/indexeddb-store.ts"() { + "use strict"; + IndexedDBCacheStore = class { + constructor(options = {}) { + this.storeName = "gtfs-cache"; + this.version = 1; + this.dbName = options.dbName || "gtfs-sqljs-cache"; + } + /** + * Open IndexedDB connection + */ + async openDB() { + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.dbName, this.version); + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result); + request.onupgradeneeded = (event) => { + const db = event.target.result; + if (!db.objectStoreNames.contains(this.storeName)) { + const store = db.createObjectStore(this.storeName, { keyPath: "key" }); + store.createIndex("timestamp", "metadata.timestamp", { unique: false }); + } + }; + }); + } + /** + * Get a cached database + */ + async get(key) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.get(key); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + const result = request.result; + resolve(result ? result.data : null); + }; + }); + } + /** + * Store a database in cache + */ + async set(key, data, metadata) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const record = { + key, + data, + metadata + }; + const request = store.put(record); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + /** + * Check if a cache entry exists + */ + async has(key) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.getKey(key); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + resolve(request.result !== void 0); + }; + }); + } + /** + * Delete a specific cache entry + */ + async delete(key) { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.delete(key); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + /** + * Clear all cache entries + */ + async clear() { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readwrite"); + const store = transaction.objectStore(this.storeName); + const request = store.clear(); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + /** + * List all cached entries + */ + async list() { + const db = await this.openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], "readonly"); + const store = transaction.objectStore(this.storeName); + const request = store.getAllKeys(); + request.onerror = () => { + db.close(); + reject(request.error); + }; + request.onsuccess = async () => { + const keys = request.result; + const entries = []; + const metadataPromises = keys.map(async (key) => { + const metaRequest = store.get(key); + return new Promise((resolveEntry, rejectEntry) => { + metaRequest.onerror = () => rejectEntry(metaRequest.error); + metaRequest.onsuccess = () => { + const record = metaRequest.result; + resolveEntry({ + key, + metadata: record.metadata + }); + }; + }); + }); + try { + const results = await Promise.all(metadataPromises); + entries.push(...results); + } catch (error) { + db.close(); + reject(error); + return; + } + db.close(); + resolve(entries); + }; + }); + } + }; + } +}); + +// src/cache/fs-store.ts +var fs_store_exports = {}; +__export(fs_store_exports, { + FileSystemCacheStore: () => FileSystemCacheStore +}); +var FileSystemCacheStore; +var init_fs_store = __esm({ + "src/cache/fs-store.ts"() { + "use strict"; + FileSystemCacheStore = class { + constructor(options = {}) { + this.cacheDir = options.dir || ""; + } + /** + * Get the cache directory path (lazy initialization) + */ + async getCacheDir() { + if (this.cacheDir) { + return this.cacheDir; + } + const path = await import("path"); + const os = await import("os"); + this.cacheDir = path.join( + process.env.XDG_CACHE_HOME || path.join(os.homedir(), ".cache"), + "gtfs-sqljs" + ); + return this.cacheDir; + } + /** + * Ensure cache directory exists + */ + async ensureCacheDir() { + const fs = await import("fs"); + const cacheDir = await this.getCacheDir(); + try { + await fs.promises.mkdir(cacheDir, { recursive: true }); + } catch (error) { + if (error.code !== "EEXIST") { + throw error; + } + } + } + /** + * Get file path for a cache key + */ + async getFilePath(key) { + const path = await import("path"); + const cacheDir = await this.getCacheDir(); + const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_"); + return path.join(cacheDir, `${safeKey}.db`); + } + /** + * Get metadata file path for a cache key + */ + async getMetadataPath(key) { + const path = await import("path"); + const cacheDir = await this.getCacheDir(); + const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_"); + return path.join(cacheDir, `${safeKey}.meta.json`); + } + /** + * Get a cached database + */ + async get(key) { + const fs = await import("fs"); + try { + const filePath = await this.getFilePath(key); + const buffer = await fs.promises.readFile(filePath); + return buffer.buffer.slice( + buffer.byteOffset, + buffer.byteOffset + buffer.byteLength + ); + } catch (error) { + if (error.code === "ENOENT") { + return null; + } + throw error; + } + } + /** + * Store a database in cache + */ + async set(key, data, metadata) { + const fs = await import("fs"); + await this.ensureCacheDir(); + const filePath = await this.getFilePath(key); + const metadataPath = await this.getMetadataPath(key); + await fs.promises.writeFile(filePath, new Uint8Array(data)); + await fs.promises.writeFile(metadataPath, JSON.stringify(metadata, null, 2)); + } + /** + * Check if a cache entry exists + */ + async has(key) { + const fs = await import("fs"); + try { + const filePath = await this.getFilePath(key); + await fs.promises.access(filePath); + return true; + } catch { + return false; + } + } + /** + * Delete a specific cache entry + */ + async delete(key) { + const fs = await import("fs"); + const filePath = await this.getFilePath(key); + const metadataPath = await this.getMetadataPath(key); + try { + await fs.promises.unlink(filePath); + } catch (error) { + if (error.code !== "ENOENT") { + throw error; + } + } + try { + await fs.promises.unlink(metadataPath); + } catch { + } + } + /** + * Clear all cache entries + */ + async clear() { + const fs = await import("fs"); + const path = await import("path"); + const cacheDir = await this.getCacheDir(); + try { + const files = await fs.promises.readdir(cacheDir); + await Promise.all( + files.map( + (file) => fs.promises.unlink(path.join(cacheDir, file)).catch(() => { + }) + ) + ); + } catch (error) { + if (error.code !== "ENOENT") { + throw error; + } + } + } + /** + * List all cached entries + */ + async list() { + const fs = await import("fs"); + const path = await import("path"); + const cacheDir = await this.getCacheDir(); + try { + const files = await fs.promises.readdir(cacheDir); + const metadataFiles = files.filter((f) => f.endsWith(".meta.json")); + const entries = []; + for (const metaFile of metadataFiles) { + try { + const metaPath = path.join(cacheDir, metaFile); + const metaContent = await fs.promises.readFile(metaPath, "utf-8"); + const metadata = JSON.parse(metaContent); + const key = metaFile.replace(".meta.json", ""); + entries.push({ key, metadata }); + } catch { + } + } + return entries; + } catch (error) { + if (error.code === "ENOENT") { + return []; + } + throw error; + } + } + }; + } +}); + // src/gtfs-sqljs.ts import initSqlJs from "sql.js"; @@ -1002,6 +1405,50 @@ async function loadRealtimeData(db, feedUrls) { } } +// src/cache/checksum.ts +async function getCrypto() { + if (typeof crypto !== "undefined" && crypto.subtle) { + return crypto; + } + if (typeof globalThis !== "undefined") { + try { + const { webcrypto } = await import("crypto"); + return webcrypto; + } catch { + throw new Error("Web Crypto API not available"); + } + } + throw new Error("Crypto not available in this environment"); +} +async function computeChecksum(data) { + const cryptoInstance = await getCrypto(); + const bufferSource = data instanceof Uint8Array ? data : new Uint8Array(data); + const hashBuffer = await cryptoInstance.subtle.digest("SHA-256", bufferSource); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); + return hashHex; +} +async function computeZipChecksum(zipData) { + return computeChecksum(zipData); +} +function generateCacheKey(checksum, libVersion, dataVersion, filesize, source, skipFiles) { + let key = `v${libVersion}_d${dataVersion}_${filesize}_${checksum}`; + if (source) { + const filename = source.split("/").pop() || source; + const sanitized = filename.replace(/[^a-zA-Z0-9._-]/g, "_"); + key += `_${sanitized}`; + } + if (skipFiles && skipFiles.length > 0) { + const sortedSkips = [...skipFiles].sort(); + const skipsSuffix = sortedSkips.join(",").replace(/\.txt/g, ""); + key += `_skip-${skipsSuffix}`; + } + return key; +} + +// src/gtfs-sqljs.ts +init_utils(); + // src/queries/agencies.ts function getAgencies(db, filters = {}) { const { agencyId, limit } = filters; @@ -1935,6 +2382,7 @@ function getAllStopTimeUpdates(db) { } // src/gtfs-sqljs.ts +var LIB_VERSION = "0.1.0"; var GtfsSqlJs = class _GtfsSqlJs { /** * Private constructor - use static factory methods instead @@ -1966,6 +2414,141 @@ var GtfsSqlJs = class _GtfsSqlJs { */ async initFromZip(zipPath, options) { const onProgress = options.onProgress; + const { + cache: userCache, + cacheVersion = "1.0", + skipFiles + } = options; + this.SQL = options.SQL || await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}); + let cache = null; + if (userCache === null) { + cache = null; + } else if (userCache) { + cache = userCache; + } else { + try { + if (typeof window !== "undefined" && typeof indexedDB !== "undefined") { + const { IndexedDBCacheStore: IndexedDBCacheStore2 } = await Promise.resolve().then(() => (init_indexeddb_store(), indexeddb_store_exports)); + cache = new IndexedDBCacheStore2(); + } else if (typeof process !== "undefined" && process.versions?.node) { + const { FileSystemCacheStore: FileSystemCacheStore2 } = await Promise.resolve().then(() => (init_fs_store(), fs_store_exports)); + cache = new FileSystemCacheStore2(); + } + } catch (error) { + console.warn("Failed to initialize default cache store:", error); + cache = null; + } + } + if (cache) { + onProgress?.({ + phase: "checking_cache", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 0, + message: "Checking cache..." + }); + let zipData2; + if (typeof zipPath === "string") { + onProgress?.({ + phase: "downloading", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 2, + message: "Downloading GTFS ZIP file" + }); + zipData2 = await fetchZip(zipPath); + } else { + zipData2 = zipPath; + } + const filesize = zipData2.byteLength; + const checksum = await computeZipChecksum(zipData2); + const cacheKey = generateCacheKey( + checksum, + LIB_VERSION, + cacheVersion, + filesize, + typeof zipPath === "string" ? zipPath : void 0, + skipFiles + ); + const cachedDb = await cache.get(cacheKey); + if (cachedDb) { + onProgress?.({ + phase: "loading_from_cache", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 50, + message: "Loading from cache..." + }); + this.db = new this.SQL.Database(new Uint8Array(cachedDb)); + if (options.realtimeFeedUrls) { + this.realtimeFeedUrls = options.realtimeFeedUrls; + } + if (options.stalenessThreshold !== void 0) { + this.stalenessThreshold = options.stalenessThreshold; + } + onProgress?.({ + phase: "complete", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: "GTFS data loaded from cache" + }); + return; + } + onProgress?.({ + phase: "extracting", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 5, + message: "Extracting GTFS ZIP file" + }); + await this.loadFromZipData(zipData2, options, onProgress); + onProgress?.({ + phase: "saving_cache", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 98, + message: "Saving to cache..." + }); + const dbBuffer = this.export(); + await cache.set(cacheKey, dbBuffer, { + checksum, + version: cacheVersion, + timestamp: Date.now(), + source: typeof zipPath === "string" ? zipPath : void 0, + size: dbBuffer.byteLength, + skipFiles + }); + onProgress?.({ + phase: "complete", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: "GTFS data loaded successfully" + }); + return; + } onProgress?.({ phase: "downloading", currentFile: null, @@ -1974,9 +2557,31 @@ var GtfsSqlJs = class _GtfsSqlJs { rowsProcessed: 0, totalRows: 0, percentComplete: 0, - message: "Initializing database engine" + message: "Downloading GTFS ZIP file" }); - this.SQL = options.SQL || await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}); + let zipData; + if (typeof zipPath === "string") { + zipData = await fetchZip(zipPath); + } else { + zipData = zipPath; + } + await this.loadFromZipData(zipData, options, onProgress); + onProgress?.({ + phase: "complete", + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: "GTFS data loaded successfully" + }); + } + /** + * Helper method to load GTFS data from zip data (ArrayBuffer) + * Used by both cache-enabled and cache-disabled paths + */ + async loadFromZipData(zipData, options, onProgress) { this.db = new this.SQL.Database(); onProgress?.({ phase: "creating_schema", @@ -1985,7 +2590,7 @@ var GtfsSqlJs = class _GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 2, + percentComplete: 10, message: "Optimizing database for bulk import" }); this.db.run("PRAGMA synchronous = OFF"); @@ -2000,7 +2605,7 @@ var GtfsSqlJs = class _GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 5, + percentComplete: 15, message: "Creating database tables" }); const createTableStatements = getAllCreateTableStatements(); @@ -2015,10 +2620,10 @@ var GtfsSqlJs = class _GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 10, - message: "Loading and extracting GTFS ZIP file" + percentComplete: 20, + message: "Extracting GTFS ZIP file" }); - const files = await loadGTFSZip(zipPath); + const files = await loadGTFSZip(zipData); onProgress?.({ phase: "inserting_data", currentFile: null, @@ -2026,7 +2631,7 @@ var GtfsSqlJs = class _GtfsSqlJs { totalFiles: Object.keys(files).length, rowsProcessed: 0, totalRows: 0, - percentComplete: 15, + percentComplete: 25, message: "Starting data import" }); await loadGTFSData(this.db, files, options.skipFiles, onProgress); @@ -2076,16 +2681,6 @@ var GtfsSqlJs = class _GtfsSqlJs { 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" - }); if (this.realtimeFeedUrls.length > 0) { try { await loadRealtimeData(this.db, this.realtimeFeedUrls); @@ -2380,6 +2975,87 @@ var GtfsSqlJs = class _GtfsSqlJs { if (!this.db) throw new Error("Database not initialized"); return getAllStopTimeUpdates(this.db); } + // ==================== Cache Management Methods ==================== + /** + * Get cache statistics + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @returns Cache statistics including size, entry count, and age information + */ + static async getCacheStats(cacheStore) { + const { getCacheStats: getCacheStats2 } = await Promise.resolve().then(() => (init_utils(), utils_exports)); + const cache = cacheStore || await this.getDefaultCacheStore(); + if (!cache) { + throw new Error("No cache store available"); + } + const entries = await cache.list?.() || []; + return getCacheStats2(entries); + } + /** + * Clean expired cache entries + * @param cacheStore - Cache store to clean (optional, auto-detects if not provided) + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns Number of entries deleted + */ + static async cleanExpiredCache(cacheStore, expirationMs = DEFAULT_CACHE_EXPIRATION_MS) { + const { filterExpiredEntries: filterExpiredEntries2 } = await Promise.resolve().then(() => (init_utils(), utils_exports)); + const cache = cacheStore || await this.getDefaultCacheStore(); + if (!cache || !cache.list) { + throw new Error("No cache store available or cache store does not support listing"); + } + const allEntries = await cache.list(); + const expiredEntries = allEntries.filter( + (entry) => !filterExpiredEntries2([entry], expirationMs).length + ); + await Promise.all(expiredEntries.map((entry) => cache.delete(entry.key))); + return expiredEntries.length; + } + /** + * Clear all cache entries + * @param cacheStore - Cache store to clear (optional, auto-detects if not provided) + */ + static async clearCache(cacheStore) { + const cache = cacheStore || await this.getDefaultCacheStore(); + if (!cache) { + throw new Error("No cache store available"); + } + await cache.clear(); + } + /** + * List all cache entries + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @param includeExpired - Include expired entries (default: false) + * @returns Array of cache entries with metadata + */ + static async listCache(cacheStore, includeExpired = false) { + const { filterExpiredEntries: filterExpiredEntries2 } = await Promise.resolve().then(() => (init_utils(), utils_exports)); + const cache = cacheStore || await this.getDefaultCacheStore(); + if (!cache || !cache.list) { + throw new Error("No cache store available or cache store does not support listing"); + } + const entries = await cache.list(); + if (includeExpired) { + return entries; + } + return filterExpiredEntries2(entries); + } + /** + * Get the default cache store for the current environment + * @returns Default cache store or null if unavailable + */ + static async getDefaultCacheStore() { + try { + if (typeof window !== "undefined" && typeof indexedDB !== "undefined") { + const { IndexedDBCacheStore: IndexedDBCacheStore2 } = await Promise.resolve().then(() => (init_indexeddb_store(), indexeddb_store_exports)); + return new IndexedDBCacheStore2(); + } else if (typeof process !== "undefined" && process.versions?.node) { + const { FileSystemCacheStore: FileSystemCacheStore2 } = await Promise.resolve().then(() => (init_fs_store(), fs_store_exports)); + return new FileSystemCacheStore2(); + } + } catch (error) { + console.warn("Failed to initialize default cache store:", error); + } + return null; + } }; // src/types/gtfs-rt.ts @@ -2445,15 +3121,29 @@ var AlertEffect = /* @__PURE__ */ ((AlertEffect2) => { AlertEffect2[AlertEffect2["ACCESSIBILITY_ISSUE"] = 11] = "ACCESSIBILITY_ISSUE"; return AlertEffect2; })(AlertEffect || {}); + +// src/index.ts +init_indexeddb_store(); +init_fs_store(); +init_utils(); export { AlertCause, AlertEffect, CongestionLevel, + DEFAULT_CACHE_EXPIRATION_MS, + FileSystemCacheStore, GTFS_SCHEMA, GtfsSqlJs, + IndexedDBCacheStore, OccupancyStatus, ScheduleRelationship, - VehicleStopStatus + VehicleStopStatus, + computeChecksum, + computeZipChecksum, + filterExpiredEntries, + generateCacheKey, + getCacheStats, + isCacheExpired }; /** * gtfs-sqljs - Load GTFS data into sql.js SQLite database diff --git a/dist/index.mjs.map b/dist/index.mjs.map index 9e71e7c..8ce3a12 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 { 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: 'Loading and 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 // Auto-fetch realtime data if feed URLs are configured\n if (this.realtimeFeedUrls.length > 0) {\n try {\n await loadRealtimeData(this.db, this.realtimeFeedUrls);\n } catch (error) {\n // Don't fail the entire load if RT data fetch fails\n console.warn('Failed to fetch initial realtime data:', error);\n }\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 +{"version":3,"sources":["../src/cache/utils.ts","../src/cache/indexeddb-store.ts","../src/cache/fs-store.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/cache/checksum.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","../src/index.ts"],"sourcesContent":["import type { CacheMetadata, CacheEntry } from './types';\n\n/**\n * Default cache expiration time in milliseconds (7 days)\n */\nexport const DEFAULT_CACHE_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * Check if a cache entry is expired\n * @param metadata - Cache metadata\n * @param expirationMs - Expiration time in milliseconds (default: 7 days)\n * @returns true if the cache entry is expired\n */\nexport function isCacheExpired(\n metadata: CacheMetadata,\n expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS\n): boolean {\n const now = Date.now();\n const age = now - metadata.timestamp;\n return age > expirationMs;\n}\n\n/**\n * Filter out expired cache entries\n * @param entries - Array of cache entries\n * @param expirationMs - Expiration time in milliseconds (default: 7 days)\n * @returns Filtered array of non-expired entries\n */\nexport function filterExpiredEntries(\n entries: CacheEntry[],\n expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS\n): CacheEntry[] {\n return entries.filter(entry => !isCacheExpired(entry.metadata, expirationMs));\n}\n\n/**\n * Get cache statistics\n * @param entries - Array of cache entries\n * @returns Cache statistics\n */\nexport function getCacheStats(entries: CacheEntry[]) {\n const totalSize = entries.reduce((sum, entry) => sum + entry.metadata.size, 0);\n const expiredEntries = entries.filter(entry => isCacheExpired(entry.metadata));\n const activeEntries = entries.filter(entry => !isCacheExpired(entry.metadata));\n\n return {\n totalEntries: entries.length,\n activeEntries: activeEntries.length,\n expiredEntries: expiredEntries.length,\n totalSize,\n totalSizeMB: (totalSize / 1024 / 1024).toFixed(2),\n oldestEntry: entries.length > 0\n ? Math.min(...entries.map(e => e.metadata.timestamp))\n : null,\n newestEntry: entries.length > 0\n ? Math.max(...entries.map(e => e.metadata.timestamp))\n : null,\n };\n}\n","import type { CacheStore, CacheMetadata, CacheEntry, CacheStoreOptions } from './types';\n\n/**\n * IndexedDB-based cache store for browsers\n *\n * Stores GTFS databases in IndexedDB for fast access on subsequent loads.\n * Suitable for large databases (100s of MB to several GB depending on browser limits).\n *\n * @example\n * ```typescript\n * import { GtfsSqlJs, IndexedDBCacheStore } from 'gtfs-sqljs';\n *\n * const cache = new IndexedDBCacheStore();\n * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', {\n * cache,\n * cacheVersion: '1.0'\n * });\n * ```\n */\nexport class IndexedDBCacheStore implements CacheStore {\n private dbName: string;\n private storeName = 'gtfs-cache';\n private version = 1;\n\n constructor(options: CacheStoreOptions = {}) {\n this.dbName = options.dbName || 'gtfs-sqljs-cache';\n }\n\n /**\n * Open IndexedDB connection\n */\n private async openDB(): Promise {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, this.version);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n\n // Create object store if it doesn't exist\n if (!db.objectStoreNames.contains(this.storeName)) {\n const store = db.createObjectStore(this.storeName, { keyPath: 'key' });\n store.createIndex('timestamp', 'metadata.timestamp', { unique: false });\n }\n };\n });\n }\n\n /**\n * Get a cached database\n */\n async get(key: string): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readonly');\n const store = transaction.objectStore(this.storeName);\n const request = store.get(key);\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n const result = request.result;\n resolve(result ? result.data : null);\n };\n });\n }\n\n /**\n * Store a database in cache\n */\n async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readwrite');\n const store = transaction.objectStore(this.storeName);\n\n const record = {\n key,\n data,\n metadata\n };\n\n const request = store.put(record);\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n resolve();\n };\n });\n }\n\n /**\n * Check if a cache entry exists\n */\n async has(key: string): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readonly');\n const store = transaction.objectStore(this.storeName);\n const request = store.getKey(key);\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n resolve(request.result !== undefined);\n };\n });\n }\n\n /**\n * Delete a specific cache entry\n */\n async delete(key: string): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readwrite');\n const store = transaction.objectStore(this.storeName);\n const request = store.delete(key);\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n resolve();\n };\n });\n }\n\n /**\n * Clear all cache entries\n */\n async clear(): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readwrite');\n const store = transaction.objectStore(this.storeName);\n const request = store.clear();\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = () => {\n db.close();\n resolve();\n };\n });\n }\n\n /**\n * List all cached entries\n */\n async list(): Promise {\n const db = await this.openDB();\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([this.storeName], 'readonly');\n const store = transaction.objectStore(this.storeName);\n const request = store.getAllKeys();\n\n request.onerror = () => {\n db.close();\n reject(request.error);\n };\n\n request.onsuccess = async () => {\n const keys = request.result as string[];\n const entries: CacheEntry[] = [];\n\n // Get metadata for each key\n const metadataPromises = keys.map(async (key) => {\n const metaRequest = store.get(key);\n return new Promise((resolveEntry, rejectEntry) => {\n metaRequest.onerror = () => rejectEntry(metaRequest.error);\n metaRequest.onsuccess = () => {\n const record = metaRequest.result;\n resolveEntry({\n key,\n metadata: record.metadata\n });\n };\n });\n });\n\n try {\n const results = await Promise.all(metadataPromises);\n entries.push(...results);\n } catch (error) {\n db.close();\n reject(error);\n return;\n }\n\n db.close();\n resolve(entries);\n };\n });\n }\n}\n","import type { CacheStore, CacheMetadata, CacheEntry, CacheStoreOptions } from './types';\n\n/**\n * File system-based cache store for Node.js\n *\n * Stores GTFS databases as files on disk for fast access on subsequent loads.\n * Suitable for any size database (limited only by disk space).\n *\n * @example\n * ```typescript\n * import { GtfsSqlJs, FileSystemCacheStore } from 'gtfs-sqljs';\n *\n * const cache = new FileSystemCacheStore({ dir: './.cache/gtfs' });\n * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', {\n * cache,\n * cacheVersion: '1.0'\n * });\n * ```\n */\nexport class FileSystemCacheStore implements CacheStore {\n private cacheDir: string;\n\n constructor(options: CacheStoreOptions = {}) {\n // Default cache directory (will be resolved lazily on first use)\n this.cacheDir = options.dir || '';\n }\n\n /**\n * Get the cache directory path (lazy initialization)\n */\n private async getCacheDir(): Promise {\n if (this.cacheDir) {\n return this.cacheDir;\n }\n\n // Dynamically import Node.js modules\n const path = await import('path');\n const os = await import('os');\n\n // Default to XDG cache directory on Linux/Mac, or temp dir as fallback\n this.cacheDir = path.join(\n process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'),\n 'gtfs-sqljs'\n );\n\n return this.cacheDir;\n }\n\n /**\n * Ensure cache directory exists\n */\n private async ensureCacheDir(): Promise {\n const fs = await import('fs');\n const cacheDir = await this.getCacheDir();\n\n try {\n await fs.promises.mkdir(cacheDir, { recursive: true });\n } catch (error) {\n // Ignore error if directory already exists\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n }\n\n /**\n * Get file path for a cache key\n */\n private async getFilePath(key: string): Promise {\n const path = await import('path');\n const cacheDir = await this.getCacheDir();\n\n // Sanitize key to be filesystem-safe\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');\n return path.join(cacheDir, `${safeKey}.db`);\n }\n\n /**\n * Get metadata file path for a cache key\n */\n private async getMetadataPath(key: string): Promise {\n const path = await import('path');\n const cacheDir = await this.getCacheDir();\n\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');\n return path.join(cacheDir, `${safeKey}.meta.json`);\n }\n\n /**\n * Get a cached database\n */\n async get(key: string): Promise {\n const fs = await import('fs');\n\n try {\n const filePath = await this.getFilePath(key);\n const buffer = await fs.promises.readFile(filePath);\n return buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength\n );\n } catch (error) {\n // Return null if file doesn't exist\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n throw error;\n }\n }\n\n /**\n * Store a database in cache\n */\n async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise {\n const fs = await import('fs');\n await this.ensureCacheDir();\n\n const filePath = await this.getFilePath(key);\n const metadataPath = await this.getMetadataPath(key);\n\n // Write database file\n await fs.promises.writeFile(filePath, new Uint8Array(data));\n\n // Write metadata file\n await fs.promises.writeFile(metadataPath, JSON.stringify(metadata, null, 2));\n }\n\n /**\n * Check if a cache entry exists\n */\n async has(key: string): Promise {\n const fs = await import('fs');\n\n try {\n const filePath = await this.getFilePath(key);\n await fs.promises.access(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Delete a specific cache entry\n */\n async delete(key: string): Promise {\n const fs = await import('fs');\n\n const filePath = await this.getFilePath(key);\n const metadataPath = await this.getMetadataPath(key);\n\n try {\n await fs.promises.unlink(filePath);\n } catch (error) {\n // Ignore error if file doesn't exist\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n\n try {\n await fs.promises.unlink(metadataPath);\n } catch {\n // Ignore metadata file errors\n }\n }\n\n /**\n * Clear all cache entries\n */\n async clear(): Promise {\n const fs = await import('fs');\n const path = await import('path');\n const cacheDir = await this.getCacheDir();\n\n try {\n const files = await fs.promises.readdir(cacheDir);\n\n await Promise.all(\n files.map(file =>\n fs.promises.unlink(path.join(cacheDir, file)).catch(() => {\n // Ignore errors\n })\n )\n );\n } catch (error) {\n // Ignore error if directory doesn't exist\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n /**\n * List all cached entries\n */\n async list(): Promise {\n const fs = await import('fs');\n const path = await import('path');\n const cacheDir = await this.getCacheDir();\n\n try {\n const files = await fs.promises.readdir(cacheDir);\n const metadataFiles = files.filter(f => f.endsWith('.meta.json'));\n\n const entries: CacheEntry[] = [];\n\n for (const metaFile of metadataFiles) {\n try {\n const metaPath = path.join(cacheDir, metaFile);\n const metaContent = await fs.promises.readFile(metaPath, 'utf-8');\n const metadata = JSON.parse(metaContent) as CacheMetadata;\n\n // Extract key from filename (remove .meta.json)\n const key = metaFile.replace('.meta.json', '');\n\n entries.push({ key, metadata });\n } catch {\n // Skip invalid metadata files\n }\n }\n\n return entries;\n } catch (error) {\n // Return empty array if directory doesn't exist\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return [];\n }\n throw error;\n }\n }\n}\n","/**\n * Main GtfsSqlJs Class\n */\n\nimport initSqlJs, { type Database, type SqlJsStatic } from 'sql.js';\nimport { getAllCreateTableStatements, getAllCreateIndexStatements } from './schema/schema';\nimport { loadGTFSZip, fetchZip } 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';\nimport type { CacheStore } from './cache/types';\nimport { computeZipChecksum, generateCacheKey } from './cache/checksum';\nimport { DEFAULT_CACHE_EXPIRATION_MS } from './cache/utils';\n\n// Library version from package.json\nconst LIB_VERSION = '0.1.0';\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: 'checking_cache' | 'loading_from_cache' | 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'saving_cache' | '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 /**\n * Optional: Cache store for persisting processed GTFS databases\n * Use IndexedDBCacheStore (browser) or FileSystemCacheStore (Node.js)\n * or implement your own CacheStore\n *\n * If not provided, caching is enabled by default with:\n * - IndexedDBCacheStore in browsers\n * - FileSystemCacheStore in Node.js\n *\n * Set to `null` to disable caching\n */\n cache?: CacheStore | null;\n\n /**\n * Optional: Data version string\n * When changed, cached databases are invalidated and reprocessed\n * Default: '1.0'\n */\n cacheVersion?: string;\n\n /**\n * Optional: Cache expiration time in milliseconds\n * Cached databases older than this will be invalidated\n * Default: 7 days (604800000 ms)\n */\n cacheExpirationMs?: 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 const onProgress = options.onProgress;\n const {\n cache: userCache,\n cacheVersion = '1.0',\n skipFiles\n } = options;\n\n // Initialize SQL.js\n this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {}));\n\n // Determine cache store to use\n let cache: CacheStore | null = null;\n\n if (userCache === null) {\n // User explicitly disabled caching\n cache = null;\n } else if (userCache) {\n // User provided a cache store\n cache = userCache;\n } else {\n // Auto-detect environment and create default cache store\n try {\n if (typeof window !== 'undefined' && typeof indexedDB !== 'undefined') {\n // Browser environment - use IndexedDB\n const { IndexedDBCacheStore } = await import('./cache/indexeddb-store');\n cache = new IndexedDBCacheStore();\n } else if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js environment - use FileSystem\n const { FileSystemCacheStore } = await import('./cache/fs-store');\n cache = new FileSystemCacheStore();\n }\n } catch (error) {\n // Fallback to no caching if import fails\n console.warn('Failed to initialize default cache store:', error);\n cache = null;\n }\n }\n\n // Check cache if enabled\n if (cache) {\n onProgress?.({\n phase: 'checking_cache',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 0,\n message: 'Checking cache...',\n });\n\n // Fetch raw zip data for checksum (only if zipPath is a string)\n let zipData: ArrayBuffer;\n if (typeof zipPath === 'string') {\n onProgress?.({\n phase: 'downloading',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 2,\n message: 'Downloading GTFS ZIP file',\n });\n\n zipData = await fetchZip(zipPath);\n } else {\n // zipPath is already ArrayBuffer or Uint8Array\n zipData = zipPath as ArrayBuffer;\n }\n\n // Calculate filesize\n const filesize = zipData.byteLength;\n\n // Compute checksum\n const checksum = await computeZipChecksum(zipData);\n\n // Generate cache key with all parameters\n const cacheKey = generateCacheKey(\n checksum,\n LIB_VERSION,\n cacheVersion,\n filesize,\n typeof zipPath === 'string' ? zipPath : undefined,\n skipFiles\n );\n\n // Check if cache exists\n const cachedDb = await cache.get(cacheKey);\n\n if (cachedDb) {\n // Note: We retrieve the cache entry but we should check if it's expired\n // For now, we check expiration when listing/managing cache\n // The cache store itself doesn't track metadata on get()\n // TODO: Consider updating CacheStore.get() to return metadata as well\n\n // Load from cache\n onProgress?.({\n phase: 'loading_from_cache',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 50,\n message: 'Loading from cache...',\n });\n\n this.db = new this.SQL.Database(new Uint8Array(cachedDb));\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: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 100,\n message: 'GTFS data loaded from cache',\n });\n\n return;\n }\n\n // Cache miss - continue with normal loading but use already-fetched zip data\n onProgress?.({\n phase: 'extracting',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 5,\n message: 'Extracting GTFS ZIP file',\n });\n\n await this.loadFromZipData(zipData, options, onProgress);\n\n // Save to cache\n onProgress?.({\n phase: 'saving_cache',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 98,\n message: 'Saving to cache...',\n });\n\n const dbBuffer = this.export();\n await cache.set(cacheKey, dbBuffer, {\n checksum,\n version: cacheVersion,\n timestamp: Date.now(),\n source: typeof zipPath === 'string' ? zipPath : undefined,\n size: dbBuffer.byteLength,\n skipFiles,\n });\n\n onProgress?.({\n phase: 'complete',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 100,\n message: 'GTFS data loaded successfully',\n });\n\n return;\n }\n\n // No cache - use normal loading flow\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: 'Downloading GTFS ZIP file',\n });\n\n // Fetch zip data\n let zipData: ArrayBuffer;\n if (typeof zipPath === 'string') {\n zipData = await fetchZip(zipPath);\n } else {\n zipData = zipPath as ArrayBuffer;\n }\n\n // Load from zip data\n await this.loadFromZipData(zipData, options, onProgress);\n\n onProgress?.({\n phase: 'complete',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 100,\n message: 'GTFS data loaded successfully',\n });\n }\n\n /**\n * Helper method to load GTFS data from zip data (ArrayBuffer)\n * Used by both cache-enabled and cache-disabled paths\n */\n private async loadFromZipData(\n zipData: ArrayBuffer,\n options: Omit,\n onProgress?: ProgressCallback\n ): Promise {\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: 10,\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: 15,\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 // Extract files from zip\n onProgress?.({\n phase: 'extracting',\n currentFile: null,\n filesCompleted: 0,\n totalFiles: 0,\n rowsProcessed: 0,\n totalRows: 0,\n percentComplete: 20,\n message: 'Extracting GTFS ZIP file',\n });\n\n const files = await loadGTFSZip(zipData);\n\n // Load GTFS data\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: 25,\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 // Auto-fetch realtime data if feed URLs are configured\n if (this.realtimeFeedUrls.length > 0) {\n try {\n await loadRealtimeData(this.db, this.realtimeFeedUrls);\n } catch (error) {\n // Don't fail the entire load if RT data fetch fails\n console.warn('Failed to fetch initial realtime data:', error);\n }\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 // ==================== Cache Management Methods ====================\n\n /**\n * Get cache statistics\n * @param cacheStore - Cache store to query (optional, auto-detects if not provided)\n * @returns Cache statistics including size, entry count, and age information\n */\n static async getCacheStats(cacheStore?: CacheStore) {\n const { getCacheStats } = await import('./cache/utils');\n const cache = cacheStore || await this.getDefaultCacheStore();\n\n if (!cache) {\n throw new Error('No cache store available');\n }\n\n const entries = await cache.list?.() || [];\n return getCacheStats(entries);\n }\n\n /**\n * Clean expired cache entries\n * @param cacheStore - Cache store to clean (optional, auto-detects if not provided)\n * @param expirationMs - Expiration time in milliseconds (default: 7 days)\n * @returns Number of entries deleted\n */\n static async cleanExpiredCache(\n cacheStore?: CacheStore,\n expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS\n ): Promise {\n const { filterExpiredEntries } = await import('./cache/utils');\n const cache = cacheStore || await this.getDefaultCacheStore();\n\n if (!cache || !cache.list) {\n throw new Error('No cache store available or cache store does not support listing');\n }\n\n const allEntries = await cache.list();\n const expiredEntries = allEntries.filter(entry =>\n !filterExpiredEntries([entry], expirationMs).length\n );\n\n // Delete expired entries\n await Promise.all(expiredEntries.map(entry => cache.delete(entry.key)));\n\n return expiredEntries.length;\n }\n\n /**\n * Clear all cache entries\n * @param cacheStore - Cache store to clear (optional, auto-detects if not provided)\n */\n static async clearCache(cacheStore?: CacheStore): Promise {\n const cache = cacheStore || await this.getDefaultCacheStore();\n\n if (!cache) {\n throw new Error('No cache store available');\n }\n\n await cache.clear();\n }\n\n /**\n * List all cache entries\n * @param cacheStore - Cache store to query (optional, auto-detects if not provided)\n * @param includeExpired - Include expired entries (default: false)\n * @returns Array of cache entries with metadata\n */\n static async listCache(\n cacheStore?: CacheStore,\n includeExpired: boolean = false\n ) {\n const { filterExpiredEntries } = await import('./cache/utils');\n const cache = cacheStore || await this.getDefaultCacheStore();\n\n if (!cache || !cache.list) {\n throw new Error('No cache store available or cache store does not support listing');\n }\n\n const entries = await cache.list();\n\n if (includeExpired) {\n return entries;\n }\n\n return filterExpiredEntries(entries);\n }\n\n /**\n * Get the default cache store for the current environment\n * @returns Default cache store or null if unavailable\n */\n private static async getDefaultCacheStore(): Promise {\n try {\n if (typeof window !== 'undefined' && typeof indexedDB !== 'undefined') {\n // Browser environment - use IndexedDB\n const { IndexedDBCacheStore } = await import('./cache/indexeddb-store');\n return new IndexedDBCacheStore();\n } else if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js environment - use FileSystem\n const { FileSystemCacheStore } = await import('./cache/fs-store');\n return new FileSystemCacheStore();\n }\n } catch (error) {\n console.warn('Failed to initialize default cache store:', error);\n }\n\n return null;\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 (exported for checksum computation)\n */\nexport async 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 * Get crypto object (browser or Node.js)\n */\nasync function getCrypto(): Promise {\n // Browser environment\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n return crypto;\n }\n\n // Node.js environment - import webcrypto\n if (typeof globalThis !== 'undefined') {\n try {\n const { webcrypto } = await import('crypto');\n return webcrypto as unknown as Crypto;\n } catch {\n // Fallback for older Node.js versions\n throw new Error('Web Crypto API not available');\n }\n }\n\n throw new Error('Crypto not available in this environment');\n}\n\n/**\n * Compute SHA-256 checksum of data\n * Uses Web Crypto API (available in both browser and Node.js 18+)\n */\nexport async function computeChecksum(data: ArrayBuffer | Uint8Array): Promise {\n // Get crypto instance\n const cryptoInstance = await getCrypto();\n\n // Ensure we have a BufferSource for crypto.subtle.digest\n // Use type assertion to handle ArrayBufferLike compatibility\n const bufferSource = (data instanceof Uint8Array ? data : new Uint8Array(data)) as BufferSource;\n const hashBuffer = await cryptoInstance.subtle.digest('SHA-256', bufferSource);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return hashHex;\n}\n\n/**\n * Compute checksum for a zip file\n * @param zipData - The zip file data (ArrayBuffer or Uint8Array)\n * @returns SHA-256 checksum as hex string\n */\nexport async function computeZipChecksum(zipData: ArrayBuffer | Uint8Array): Promise {\n return computeChecksum(zipData);\n}\n\n/**\n * Generate a cache key from checksum, version, filesize, source, and options\n * Format: v{libVersion}_{dataVersion}_{filesize}_{checksum}_{source}_{skipFiles}\n *\n * @param checksum - SHA-256 checksum of zip file\n * @param libVersion - Library version from package.json\n * @param dataVersion - User-specified data version\n * @param filesize - Size of the zip file in bytes\n * @param source - Source URL or filename (optional)\n * @param skipFiles - Files that were skipped during import\n * @returns Cache key string\n */\nexport function generateCacheKey(\n checksum: string,\n libVersion: string,\n dataVersion: string,\n filesize: number,\n source?: string,\n skipFiles?: string[]\n): string {\n // Start with versions and filesize\n let key = `v${libVersion}_d${dataVersion}_${filesize}_${checksum}`;\n\n // Add source (sanitized)\n if (source) {\n // Extract filename from URL/path and sanitize\n const filename = source.split('/').pop() || source;\n const sanitized = filename.replace(/[^a-zA-Z0-9._-]/g, '_');\n key += `_${sanitized}`;\n }\n\n // Add skip files if any\n if (skipFiles && skipFiles.length > 0) {\n // Sort to ensure consistent key regardless of order\n const sortedSkips = [...skipFiles].sort();\n const skipsSuffix = sortedSkips.join(',').replace(/\\.txt/g, '');\n key += `_skip-${skipsSuffix}`;\n }\n\n return key;\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","/**\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// Export cache types and interfaces\nexport type {\n CacheStore,\n CacheMetadata,\n CacheEntry,\n CacheStoreOptions\n} from './cache/types';\n\n// Export cache store implementations\nexport { IndexedDBCacheStore } from './cache/indexeddb-store';\nexport { FileSystemCacheStore } from './cache/fs-store';\n\n// Export cache utilities\nexport { computeChecksum, computeZipChecksum, generateCacheKey } from './cache/checksum';\nexport { isCacheExpired, filterExpiredEntries, getCacheStats, DEFAULT_CACHE_EXPIRATION_MS } from './cache/utils';\n"],"mappings":";;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaO,SAAS,eACd,UACA,eAAuB,6BACd;AACT,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,MAAM,MAAM,SAAS;AAC3B,SAAO,MAAM;AACf;AAQO,SAAS,qBACd,SACA,eAAuB,6BACT;AACd,SAAO,QAAQ,OAAO,WAAS,CAAC,eAAe,MAAM,UAAU,YAAY,CAAC;AAC9E;AAOO,SAAS,cAAc,SAAuB;AACnD,QAAM,YAAY,QAAQ,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,SAAS,MAAM,CAAC;AAC7E,QAAM,iBAAiB,QAAQ,OAAO,WAAS,eAAe,MAAM,QAAQ,CAAC;AAC7E,QAAM,gBAAgB,QAAQ,OAAO,WAAS,CAAC,eAAe,MAAM,QAAQ,CAAC;AAE7E,SAAO;AAAA,IACL,cAAc,QAAQ;AAAA,IACtB,eAAe,cAAc;AAAA,IAC7B,gBAAgB,eAAe;AAAA,IAC/B;AAAA,IACA,cAAc,YAAY,OAAO,MAAM,QAAQ,CAAC;AAAA,IAChD,aAAa,QAAQ,SAAS,IAC1B,KAAK,IAAI,GAAG,QAAQ,IAAI,OAAK,EAAE,SAAS,SAAS,CAAC,IAClD;AAAA,IACJ,aAAa,QAAQ,SAAS,IAC1B,KAAK,IAAI,GAAG,QAAQ,IAAI,OAAK,EAAE,SAAS,SAAS,CAAC,IAClD;AAAA,EACN;AACF;AA1DA,IAKa;AALb;AAAA;AAAA;AAKO,IAAM,8BAA8B,IAAI,KAAK,KAAK,KAAK;AAAA;AAAA;;;ACL9D;AAAA;AAAA;AAAA;AAAA,IAmBa;AAnBb;AAAA;AAAA;AAmBO,IAAM,sBAAN,MAAgD;AAAA,MAKrD,YAAY,UAA6B,CAAC,GAAG;AAH7C,aAAQ,YAAY;AACpB,aAAQ,UAAU;AAGhB,aAAK,SAAS,QAAQ,UAAU;AAAA,MAClC;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,SAA+B;AAC3C,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,UAAU,UAAU,KAAK,KAAK,QAAQ,KAAK,OAAO;AAExD,kBAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC5C,kBAAQ,YAAY,MAAM,QAAQ,QAAQ,MAAM;AAEhD,kBAAQ,kBAAkB,CAAC,UAAU;AACnC,kBAAM,KAAM,MAAM,OAA4B;AAG9C,gBAAI,CAAC,GAAG,iBAAiB,SAAS,KAAK,SAAS,GAAG;AACjD,oBAAM,QAAQ,GAAG,kBAAkB,KAAK,WAAW,EAAE,SAAS,MAAM,CAAC;AACrE,oBAAM,YAAY,aAAa,sBAAsB,EAAE,QAAQ,MAAM,CAAC;AAAA,YACxE;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,IAAI,KAA0C;AAClD,cAAM,KAAK,MAAM,KAAK,OAAO;AAE7B,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,cAAc,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,UAAU;AAC/D,gBAAM,QAAQ,YAAY,YAAY,KAAK,SAAS;AACpD,gBAAM,UAAU,MAAM,IAAI,GAAG;AAE7B,kBAAQ,UAAU,MAAM;AACtB,eAAG,MAAM;AACT,mBAAO,QAAQ,KAAK;AAAA,UACtB;AAEA,kBAAQ,YAAY,MAAM;AACxB,eAAG,MAAM;AACT,kBAAM,SAAS,QAAQ;AACvB,oBAAQ,SAAS,OAAO,OAAO,IAAI;AAAA,UACrC;AAAA,QACF,CAAC;AAAA,MACH;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,IAAI,KAAa,MAAmB,UAAwC;AAChF,cAAM,KAAK,MAAM,KAAK,OAAO;AAE7B,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,cAAc,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,WAAW;AAChE,gBAAM,QAAQ,YAAY,YAAY,KAAK,SAAS;AAEpD,gBAAM,SAAS;AAAA,YACb;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAEA,gBAAM,UAAU,MAAM,IAAI,MAAM;AAEhC,kBAAQ,UAAU,MAAM;AACtB,eAAG,MAAM;AACT,mBAAO,QAAQ,KAAK;AAAA,UACtB;AAEA,kBAAQ,YAAY,MAAM;AACxB,eAAG,MAAM;AACT,oBAAQ;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,IAAI,KAA+B;AACvC,cAAM,KAAK,MAAM,KAAK,OAAO;AAE7B,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,cAAc,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,UAAU;AAC/D,gBAAM,QAAQ,YAAY,YAAY,KAAK,SAAS;AACpD,gBAAM,UAAU,MAAM,OAAO,GAAG;AAEhC,kBAAQ,UAAU,MAAM;AACtB,eAAG,MAAM;AACT,mBAAO,QAAQ,KAAK;AAAA,UACtB;AAEA,kBAAQ,YAAY,MAAM;AACxB,eAAG,MAAM;AACT,oBAAQ,QAAQ,WAAW,MAAS;AAAA,UACtC;AAAA,QACF,CAAC;AAAA,MACH;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,OAAO,KAA4B;AACvC,cAAM,KAAK,MAAM,KAAK,OAAO;AAE7B,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,cAAc,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,WAAW;AAChE,gBAAM,QAAQ,YAAY,YAAY,KAAK,SAAS;AACpD,gBAAM,UAAU,MAAM,OAAO,GAAG;AAEhC,kBAAQ,UAAU,MAAM;AACtB,eAAG,MAAM;AACT,mBAAO,QAAQ,KAAK;AAAA,UACtB;AAEA,kBAAQ,YAAY,MAAM;AACxB,eAAG,MAAM;AACT,oBAAQ;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,QAAuB;AAC3B,cAAM,KAAK,MAAM,KAAK,OAAO;AAE7B,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,cAAc,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,WAAW;AAChE,gBAAM,QAAQ,YAAY,YAAY,KAAK,SAAS;AACpD,gBAAM,UAAU,MAAM,MAAM;AAE5B,kBAAQ,UAAU,MAAM;AACtB,eAAG,MAAM;AACT,mBAAO,QAAQ,KAAK;AAAA,UACtB;AAEA,kBAAQ,YAAY,MAAM;AACxB,eAAG,MAAM;AACT,oBAAQ;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,OAA8B;AAClC,cAAM,KAAK,MAAM,KAAK,OAAO;AAE7B,eAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAM,cAAc,GAAG,YAAY,CAAC,KAAK,SAAS,GAAG,UAAU;AAC/D,gBAAM,QAAQ,YAAY,YAAY,KAAK,SAAS;AACpD,gBAAM,UAAU,MAAM,WAAW;AAEjC,kBAAQ,UAAU,MAAM;AACtB,eAAG,MAAM;AACT,mBAAO,QAAQ,KAAK;AAAA,UACtB;AAEA,kBAAQ,YAAY,YAAY;AAC9B,kBAAM,OAAO,QAAQ;AACrB,kBAAM,UAAwB,CAAC;AAG/B,kBAAM,mBAAmB,KAAK,IAAI,OAAO,QAAQ;AAC/C,oBAAM,cAAc,MAAM,IAAI,GAAG;AACjC,qBAAO,IAAI,QAAoB,CAAC,cAAc,gBAAgB;AAC5D,4BAAY,UAAU,MAAM,YAAY,YAAY,KAAK;AACzD,4BAAY,YAAY,MAAM;AAC5B,wBAAM,SAAS,YAAY;AAC3B,+BAAa;AAAA,oBACX;AAAA,oBACA,UAAU,OAAO;AAAA,kBACnB,CAAC;AAAA,gBACH;AAAA,cACF,CAAC;AAAA,YACH,CAAC;AAED,gBAAI;AACF,oBAAM,UAAU,MAAM,QAAQ,IAAI,gBAAgB;AAClD,sBAAQ,KAAK,GAAG,OAAO;AAAA,YACzB,SAAS,OAAO;AACd,iBAAG,MAAM;AACT,qBAAO,KAAK;AACZ;AAAA,YACF;AAEA,eAAG,MAAM;AACT,oBAAQ,OAAO;AAAA,UACjB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA;AAAA;;;AC9NA;AAAA;AAAA;AAAA;AAAA,IAmBa;AAnBb;AAAA;AAAA;AAmBO,IAAM,uBAAN,MAAiD;AAAA,MAGtD,YAAY,UAA6B,CAAC,GAAG;AAE3C,aAAK,WAAW,QAAQ,OAAO;AAAA,MACjC;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,cAA+B;AAC3C,YAAI,KAAK,UAAU;AACjB,iBAAO,KAAK;AAAA,QACd;AAGA,cAAM,OAAO,MAAM,OAAO,MAAM;AAChC,cAAM,KAAK,MAAM,OAAO,IAAI;AAG5B,aAAK,WAAW,KAAK;AAAA,UACnB,QAAQ,IAAI,kBAAkB,KAAK,KAAK,GAAG,QAAQ,GAAG,QAAQ;AAAA,UAC9D;AAAA,QACF;AAEA,eAAO,KAAK;AAAA,MACd;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,iBAAgC;AAC5C,cAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,cAAM,WAAW,MAAM,KAAK,YAAY;AAExC,YAAI;AACF,gBAAM,GAAG,SAAS,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,QACvD,SAAS,OAAO;AAEd,cAAK,MAAgC,SAAS,UAAU;AACtD,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,YAAY,KAA8B;AACtD,cAAM,OAAO,MAAM,OAAO,MAAM;AAChC,cAAM,WAAW,MAAM,KAAK,YAAY;AAGxC,cAAM,UAAU,IAAI,QAAQ,mBAAmB,GAAG;AAClD,eAAO,KAAK,KAAK,UAAU,GAAG,OAAO,KAAK;AAAA,MAC5C;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,gBAAgB,KAA8B;AAC1D,cAAM,OAAO,MAAM,OAAO,MAAM;AAChC,cAAM,WAAW,MAAM,KAAK,YAAY;AAExC,cAAM,UAAU,IAAI,QAAQ,mBAAmB,GAAG;AAClD,eAAO,KAAK,KAAK,UAAU,GAAG,OAAO,YAAY;AAAA,MACnD;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,IAAI,KAA0C;AAClD,cAAM,KAAK,MAAM,OAAO,IAAI;AAE5B,YAAI;AACF,gBAAM,WAAW,MAAM,KAAK,YAAY,GAAG;AAC3C,gBAAM,SAAS,MAAM,GAAG,SAAS,SAAS,QAAQ;AAClD,iBAAO,OAAO,OAAO;AAAA,YACnB,OAAO;AAAA,YACP,OAAO,aAAa,OAAO;AAAA,UAC7B;AAAA,QACF,SAAS,OAAO;AAEd,cAAK,MAAgC,SAAS,UAAU;AACtD,mBAAO;AAAA,UACT;AACA,gBAAM;AAAA,QACR;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,IAAI,KAAa,MAAmB,UAAwC;AAChF,cAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,cAAM,KAAK,eAAe;AAE1B,cAAM,WAAW,MAAM,KAAK,YAAY,GAAG;AAC3C,cAAM,eAAe,MAAM,KAAK,gBAAgB,GAAG;AAGnD,cAAM,GAAG,SAAS,UAAU,UAAU,IAAI,WAAW,IAAI,CAAC;AAG1D,cAAM,GAAG,SAAS,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,MAC7E;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,IAAI,KAA+B;AACvC,cAAM,KAAK,MAAM,OAAO,IAAI;AAE5B,YAAI;AACF,gBAAM,WAAW,MAAM,KAAK,YAAY,GAAG;AAC3C,gBAAM,GAAG,SAAS,OAAO,QAAQ;AACjC,iBAAO;AAAA,QACT,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,OAAO,KAA4B;AACvC,cAAM,KAAK,MAAM,OAAO,IAAI;AAE5B,cAAM,WAAW,MAAM,KAAK,YAAY,GAAG;AAC3C,cAAM,eAAe,MAAM,KAAK,gBAAgB,GAAG;AAEnD,YAAI;AACF,gBAAM,GAAG,SAAS,OAAO,QAAQ;AAAA,QACnC,SAAS,OAAO;AAEd,cAAK,MAAgC,SAAS,UAAU;AACtD,kBAAM;AAAA,UACR;AAAA,QACF;AAEA,YAAI;AACF,gBAAM,GAAG,SAAS,OAAO,YAAY;AAAA,QACvC,QAAQ;AAAA,QAER;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,QAAuB;AAC3B,cAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,cAAM,OAAO,MAAM,OAAO,MAAM;AAChC,cAAM,WAAW,MAAM,KAAK,YAAY;AAExC,YAAI;AACF,gBAAM,QAAQ,MAAM,GAAG,SAAS,QAAQ,QAAQ;AAEhD,gBAAM,QAAQ;AAAA,YACZ,MAAM;AAAA,cAAI,UACR,GAAG,SAAS,OAAO,KAAK,KAAK,UAAU,IAAI,CAAC,EAAE,MAAM,MAAM;AAAA,cAE1D,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AAEd,cAAK,MAAgC,SAAS,UAAU;AACtD,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,OAA8B;AAClC,cAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,cAAM,OAAO,MAAM,OAAO,MAAM;AAChC,cAAM,WAAW,MAAM,KAAK,YAAY;AAExC,YAAI;AACF,gBAAM,QAAQ,MAAM,GAAG,SAAS,QAAQ,QAAQ;AAChD,gBAAM,gBAAgB,MAAM,OAAO,OAAK,EAAE,SAAS,YAAY,CAAC;AAEhE,gBAAM,UAAwB,CAAC;AAE/B,qBAAW,YAAY,eAAe;AACpC,gBAAI;AACF,oBAAM,WAAW,KAAK,KAAK,UAAU,QAAQ;AAC7C,oBAAM,cAAc,MAAM,GAAG,SAAS,SAAS,UAAU,OAAO;AAChE,oBAAM,WAAW,KAAK,MAAM,WAAW;AAGvC,oBAAM,MAAM,SAAS,QAAQ,cAAc,EAAE;AAE7C,sBAAQ,KAAK,EAAE,KAAK,SAAS,CAAC;AAAA,YAChC,QAAQ;AAAA,YAER;AAAA,UACF;AAEA,iBAAO;AAAA,QACT,SAAS,OAAO;AAEd,cAAK,MAAgC,SAAS,UAAU;AACtD,mBAAO,CAAC;AAAA,UACV;AACA,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA;AAAA;;;ACnOA,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,8BAAwC;AACtD,QAAM,aAAuB,CAAC;AAE9B,aAAW,UAAU,aAAa;AAChC,eAAW,KAAK,uBAAuB,MAAM,CAAC;AAAA,EAChD;AAEA,SAAO;AACT;AAKO,SAAS,8BAAwC;AACtD,QAAM,aAAuB,CAAC;AAE9B,aAAW,UAAU,aAAa;AAChC,eAAW,KAAK,GAAG,uBAAuB,MAAM,CAAC;AAAA,EACnD;AAEA,SAAO;AACT;;;ACxUA,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,eAAsB,SAAS,QAAsC;AAEnE,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;;;ACfA,eAAsB,aACpB,IACA,OACA,WACA,YACe;AAEf,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,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,EACF;AAGA,QAAM,cAAkC,CAAC;AACzC,aAAW,gBAAgB,cAAc;AACvC,QAAI,MAAM,YAAY,GAAG;AACvB,kBAAY,KAAK,CAAC,cAAc,MAAM,YAAY,CAAC,CAAC;AAAA,IACtD;AAAA,EACF;AAEA,aAAW,CAAC,UAAU,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AACvD,QAAI,CAAC,aAAa,SAAS,QAAQ,GAAG;AACpC,kBAAY,KAAK,CAAC,UAAU,OAAO,CAAC;AAAA,IACtC;AAAA,EACF;AAGA,MAAI,YAAY;AAChB,QAAM,gBAAgB,oBAAI,IAAoB;AAC9C,aAAW,CAAC,UAAU,OAAO,KAAK,aAAa;AAC7C,UAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,QAAI,UAAU,CAAC,QAAQ,IAAI,SAAS,YAAY,CAAC,GAAG;AAClD,YAAM,EAAE,KAAK,IAAI,SAAS,OAAO;AACjC,oBAAc,IAAI,UAAU,KAAK,MAAM;AACvC,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,MAAI,gBAAgB;AACpB,MAAI,iBAAiB;AAGrB,aAAW,CAAC,UAAU,OAAO,KAAK,aAAa;AAC7C,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;AACA;AAAA,IACF;AAEA,UAAM,WAAW,cAAc,IAAI,QAAQ,KAAK;AAEhD,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb;AAAA,MACA,YAAY,YAAY;AAAA,MACxB;AAAA,MACA;AAAA,MACA,iBAAiB,KAAK,KAAK,MAAO,gBAAgB,YAAa,EAAE;AAAA,MACjE,SAAS,WAAW,QAAQ,KAAK,SAAS,eAAe,CAAC;AAAA,IAC5D,CAAC;AAED,UAAM,cAAc,IAAI,QAAQ,SAAS,CAAC,oBAAoB;AAC5D,YAAM,kBAAkB,gBAAgB;AACxC,mBAAa;AAAA,QACX,OAAO;AAAA,QACP,aAAa;AAAA,QACb;AAAA,QACA,YAAY,YAAY;AAAA,QACxB,eAAe;AAAA,QACf;AAAA,QACA,iBAAiB,KAAK,KAAK,MAAO,kBAAkB,YAAa,EAAE;AAAA,QACnE,SAAS,WAAW,QAAQ,KAAK,gBAAgB,eAAe,CAAC,IAAI,SAAS,eAAe,CAAC;AAAA,MAChG,CAAC;AAAA,IACH,CAAC;AAED,qBAAiB;AACjB;AAEA,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb;AAAA,MACA,YAAY,YAAY;AAAA,MACxB;AAAA,MACA;AAAA,MACA,iBAAiB,KAAK,KAAK,MAAO,gBAAgB,YAAa,EAAE;AAAA,MACjE,SAAS,aAAa,QAAQ;AAAA,IAChC,CAAC;AAAA,EACH;AACF;AAKA,eAAe,cACb,IACA,QACA,YACA,YACe;AACf,QAAM,EAAE,SAAS,KAAK,IAAI,SAAS,UAAU;AAE7C,MAAI,KAAK,WAAW,GAAG;AACrB;AAAA,EACF;AAGA,QAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC9E,MAAI,QAAQ,WAAW,GAAG;AACxB;AAAA,EACF;AAEA,QAAM,aAAa;AACnB,MAAI,gBAAgB;AAGpB,KAAG,IAAI,mBAAmB;AAE1B,MAAI;AAEF,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,YAAY;AAChD,YAAM,YAAY,KAAK,MAAM,GAAG,KAAK,IAAI,IAAI,YAAY,KAAK,MAAM,CAAC;AAGrE,YAAM,eAAe,UAAU,IAAI,MAAM,IAAI,QAAQ,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAC5F,YAAM,YAAY,eAAe,OAAO,IAAI,KAAK,QAAQ,KAAK,IAAI,CAAC,YAAY,YAAY;AAG3F,YAAM,YAAwC,CAAC;AAC/C,iBAAW,OAAO,WAAW;AAC3B,mBAAW,OAAO,SAAS;AACzB,gBAAM,QAAQ,IAAI,GAAG;AAErB,oBAAU,KAAK,UAAU,QAAQ,UAAU,UAAa,UAAU,KAAK,OAAO,KAAK;AAAA,QACrF;AAAA,MACF;AAGA,YAAM,OAAO,GAAG,QAAQ,SAAS;AACjC,UAAI;AACF,aAAK,IAAI,SAAS;AAAA,MACpB,SAAS,OAAO;AACd,gBAAQ,MAAM,8BAA8B,OAAO,IAAI,KAAK,KAAK;AACjE,gBAAQ,MAAM,eAAe,UAAU,MAAM;AAC7C,cAAM;AAAA,MACR,UAAE;AACA,aAAK,KAAK;AAAA,MACZ;AAEA,uBAAiB,UAAU;AAC3B,mBAAa,aAAa;AAAA,IAC5B;AAGA,OAAG,IAAI,QAAQ;AAAA,EACjB,SAAS,OAAO;AAEd,QAAI;AACF,SAAG,IAAI,UAAU;AAAA,IACnB,SAAS,eAAe;AACtB,cAAQ,MAAM,mCAAmC,aAAa;AAAA,IAChE;AACA,UAAM;AAAA,EACR;AACF;;;AC9MO,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;AA+ErB,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,oBAAoB,uBAAuB,MAAM,gBAAgB,CAAC,CAAC;AACzE,UAAM,sBAAsB,uBAAuB,MAAM,kBAAkB,CAAC,CAAC;AAE7E,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,MAAM,UAAU;AAAA,MACtC,sBAAsB,MAAM,eAAe;AAAA,MAC3C;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,CAAC,KAAK,OAAQ;AAE3B,SAAK,IAAI;AAAA,MACP,KAAK;AAAA,MACL,KAAK,WAAW;AAAA,MAChB,GAAG,SAAS,MAAM;AAAA,MAClB,GAAG,SAAS,SAAS;AAAA,MACrB,GAAG,SAAS,gBAAgB;AAAA,MAC5B,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,GAAG,uBAAuB;AAAA,MAC1B,GAAG,UAAU;AAAA,MACb,GAAG,iBAAiB;AAAA,MACpB,GAAG,aAAa;AAAA,MAChB,GAAG,mBAAmB;AAAA,MACtB,GAAG,mBAAmB;AAAA,MACtB;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,CAAC,KAAK,OAAQ;AAG3B,aAAS,IAAI;AAAA,MACX,KAAK;AAAA,MACL,KAAK,WAAW;AAAA,MAChB,GAAG,SAAS,MAAM;AAAA,MAClB,GAAG,SAAS,SAAS;AAAA,MACrB,GAAG,SAAS,gBAAgB;AAAA,MAC5B,GAAG,aAAa;AAAA,MAChB,GAAG,SAAS;AAAA,MACZ,KAAK,wBAAwB;AAAA,MAC7B;AAAA,IACF,CAAC;AAGD,QAAI,GAAG,gBAAgB;AACrB,iBAAW,OAAO,GAAG,gBAAgB;AACnC,qBAAa,IAAI;AAAA,UACf,KAAK;AAAA,UACL,IAAI,gBAAgB;AAAA,UACpB,IAAI,UAAU;AAAA,UACd,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,IAAI,wBAAwB;AAAA,UAC5B;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;;;ACliBA,eAAe,YAA6B;AAE1C,MAAI,OAAO,WAAW,eAAe,OAAO,QAAQ;AAClD,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,eAAe,aAAa;AACrC,QAAI;AACF,YAAM,EAAE,UAAU,IAAI,MAAM,OAAO,QAAQ;AAC3C,aAAO;AAAA,IACT,QAAQ;AAEN,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,0CAA0C;AAC5D;AAMA,eAAsB,gBAAgB,MAAiD;AAErF,QAAM,iBAAiB,MAAM,UAAU;AAIvC,QAAM,eAAgB,gBAAgB,aAAa,OAAO,IAAI,WAAW,IAAI;AAC7E,QAAM,aAAa,MAAM,eAAe,OAAO,OAAO,WAAW,YAAY;AAC7E,QAAM,YAAY,MAAM,KAAK,IAAI,WAAW,UAAU,CAAC;AACvD,QAAM,UAAU,UAAU,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC3E,SAAO;AACT;AAOA,eAAsB,mBAAmB,SAAoD;AAC3F,SAAO,gBAAgB,OAAO;AAChC;AAcO,SAAS,iBACd,UACA,YACA,aACA,UACA,QACA,WACQ;AAER,MAAI,MAAM,IAAI,UAAU,KAAK,WAAW,IAAI,QAAQ,IAAI,QAAQ;AAGhE,MAAI,QAAQ;AAEV,UAAM,WAAW,OAAO,MAAM,GAAG,EAAE,IAAI,KAAK;AAC5C,UAAM,YAAY,SAAS,QAAQ,oBAAoB,GAAG;AAC1D,WAAO,IAAI,SAAS;AAAA,EACtB;AAGA,MAAI,aAAa,UAAU,SAAS,GAAG;AAErC,UAAM,cAAc,CAAC,GAAG,SAAS,EAAE,KAAK;AACxC,UAAM,cAAc,YAAY,KAAK,GAAG,EAAE,QAAQ,UAAU,EAAE;AAC9D,WAAO,SAAS,WAAW;AAAA,EAC7B;AAEA,SAAO;AACT;;;AP7EA;;;AQIO,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;;;AjB1IA,IAAM,cAAc;AAuHb,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;AACjH,UAAM,aAAa,QAAQ;AAC3B,UAAM;AAAA,MACJ,OAAO;AAAA,MACP,eAAe;AAAA,MACf;AAAA,IACF,IAAI;AAGJ,SAAK,MAAM,QAAQ,OAAQ,MAAM,UAAU,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC,CAAC;AAGvG,QAAI,QAA2B;AAE/B,QAAI,cAAc,MAAM;AAEtB,cAAQ;AAAA,IACV,WAAW,WAAW;AAEpB,cAAQ;AAAA,IACV,OAAO;AAEL,UAAI;AACF,YAAI,OAAO,WAAW,eAAe,OAAO,cAAc,aAAa;AAErE,gBAAM,EAAE,qBAAAC,qBAAoB,IAAI,MAAM;AACtC,kBAAQ,IAAIA,qBAAoB;AAAA,QAClC,WAAW,OAAO,YAAY,eAAe,QAAQ,UAAU,MAAM;AAEnE,gBAAM,EAAE,sBAAAC,sBAAqB,IAAI,MAAM;AACvC,kBAAQ,IAAIA,sBAAqB;AAAA,QACnC;AAAA,MACF,SAAS,OAAO;AAEd,gBAAQ,KAAK,6CAA6C,KAAK;AAC/D,gBAAQ;AAAA,MACV;AAAA,IACF;AAGA,QAAI,OAAO;AACT,mBAAa;AAAA,QACX,OAAO;AAAA,QACP,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAGD,UAAIC;AACJ,UAAI,OAAO,YAAY,UAAU;AAC/B,qBAAa;AAAA,UACX,OAAO;AAAA,UACP,aAAa;AAAA,UACb,gBAAgB;AAAA,UAChB,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,WAAW;AAAA,UACX,iBAAiB;AAAA,UACjB,SAAS;AAAA,QACX,CAAC;AAED,QAAAA,WAAU,MAAM,SAAS,OAAO;AAAA,MAClC,OAAO;AAEL,QAAAA,WAAU;AAAA,MACZ;AAGA,YAAM,WAAWA,SAAQ;AAGzB,YAAM,WAAW,MAAM,mBAAmBA,QAAO;AAGjD,YAAM,WAAW;AAAA,QACf;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,YAAY,WAAW,UAAU;AAAA,QACxC;AAAA,MACF;AAGA,YAAM,WAAW,MAAM,MAAM,IAAI,QAAQ;AAEzC,UAAI,UAAU;AAOZ,qBAAa;AAAA,UACX,OAAO;AAAA,UACP,aAAa;AAAA,UACb,gBAAgB;AAAA,UAChB,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,WAAW;AAAA,UACX,iBAAiB;AAAA,UACjB,SAAS;AAAA,QACX,CAAC;AAED,aAAK,KAAK,IAAI,KAAK,IAAI,SAAS,IAAI,WAAW,QAAQ,CAAC;AAGxD,YAAI,QAAQ,kBAAkB;AAC5B,eAAK,mBAAmB,QAAQ;AAAA,QAClC;AACA,YAAI,QAAQ,uBAAuB,QAAW;AAC5C,eAAK,qBAAqB,QAAQ;AAAA,QACpC;AAEA,qBAAa;AAAA,UACX,OAAO;AAAA,UACP,aAAa;AAAA,UACb,gBAAgB;AAAA,UAChB,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,WAAW;AAAA,UACX,iBAAiB;AAAA,UACjB,SAAS;AAAA,QACX,CAAC;AAED;AAAA,MACF;AAGA,mBAAa;AAAA,QACX,OAAO;AAAA,QACP,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAED,YAAM,KAAK,gBAAgBA,UAAS,SAAS,UAAU;AAGvD,mBAAa;AAAA,QACX,OAAO;AAAA,QACP,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAED,YAAM,WAAW,KAAK,OAAO;AAC7B,YAAM,MAAM,IAAI,UAAU,UAAU;AAAA,QAClC;AAAA,QACA,SAAS;AAAA,QACT,WAAW,KAAK,IAAI;AAAA,QACpB,QAAQ,OAAO,YAAY,WAAW,UAAU;AAAA,QAChD,MAAM,SAAS;AAAA,QACf;AAAA,MACF,CAAC;AAED,mBAAa;AAAA,QACX,OAAO;AAAA,QACP,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX,CAAC;AAED;AAAA,IACF;AAGA,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX,CAAC;AAGD,QAAI;AACJ,QAAI,OAAO,YAAY,UAAU;AAC/B,gBAAU,MAAM,SAAS,OAAO;AAAA,IAClC,OAAO;AACL,gBAAU;AAAA,IACZ;AAGA,UAAM,KAAK,gBAAgB,SAAS,SAAS,UAAU;AAEvD,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,gBACZ,SACA,SACA,YACe;AAEf,SAAK,KAAK,IAAI,KAAK,IAAK,SAAS;AAGjC,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX,CAAC;AAED,SAAK,GAAG,IAAI,0BAA0B;AACtC,SAAK,GAAG,IAAI,8BAA8B;AAC1C,SAAK,GAAG,IAAI,4BAA4B;AACxC,SAAK,GAAG,IAAI,4BAA4B;AACxC,SAAK,GAAG,IAAI,iCAAiC;AAG7C,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX,CAAC;AAED,UAAM,wBAAwB,4BAA4B;AAC1D,eAAW,aAAa,uBAAuB;AAC7C,WAAK,GAAG,IAAI,SAAS;AAAA,IACvB;AAGA,yBAAqB,KAAK,EAAE;AAG5B,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX,CAAC;AAED,UAAM,QAAQ,MAAM,YAAY,OAAO;AAGvC,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb,gBAAgB;AAAA,MAChB,YAAY,OAAO,KAAK,KAAK,EAAE;AAAA,MAC/B,eAAe;AAAA,MACf,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX,CAAC;AAED,UAAM,aAAa,KAAK,IAAI,OAAO,QAAQ,WAAW,UAAU;AAGhE,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb,gBAAgB,OAAO,KAAK,KAAK,EAAE;AAAA,MACnC,YAAY,OAAO,KAAK,KAAK,EAAE;AAAA,MAC/B,eAAe;AAAA,MACf,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX,CAAC;AAED,UAAM,wBAAwB,4BAA4B;AAC1D,QAAI,aAAa;AACjB,eAAW,aAAa,uBAAuB;AAC7C,WAAK,GAAG,IAAI,SAAS;AACrB;AACA,YAAM,gBAAgB,KAAK,KAAK,MAAO,aAAa,sBAAsB,SAAU,EAAE;AACtF,mBAAa;AAAA,QACX,OAAO;AAAA,QACP,aAAa;AAAA,QACb,gBAAgB,OAAO,KAAK,KAAK,EAAE;AAAA,QACnC,YAAY,OAAO,KAAK,KAAK,EAAE;AAAA,QAC/B,eAAe;AAAA,QACf,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB,SAAS,qBAAqB,UAAU,IAAI,sBAAsB,MAAM;AAAA,MAC1E,CAAC;AAAA,IACH;AAGA,iBAAa;AAAA,MACX,OAAO;AAAA,MACP,aAAa;AAAA,MACb,gBAAgB,OAAO,KAAK,KAAK,EAAE;AAAA,MACnC,YAAY,OAAO,KAAK,KAAK,EAAE;AAAA,MAC/B,eAAe;AAAA,MACf,WAAW;AAAA,MACX,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX,CAAC;AAED,SAAK,GAAG,IAAI,SAAS;AAGrB,SAAK,GAAG,IAAI,2BAA2B;AACvC,SAAK,GAAG,IAAI,8BAA8B;AAG1C,QAAI,QAAQ,kBAAkB;AAC5B,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AACA,QAAI,QAAQ,uBAAuB,QAAW;AAC5C,WAAK,qBAAqB,QAAQ;AAAA,IACpC;AAGA,QAAI,KAAK,iBAAiB,SAAS,GAAG;AACpC,UAAI;AACF,cAAM,iBAAiB,KAAK,IAAI,KAAK,gBAAgB;AAAA,MACvD,SAAS,OAAO;AAEd,gBAAQ,KAAK,0CAA0C,KAAK;AAAA,MAC9D;AAAA,IACF;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,aAAa,cAAc,YAAyB;AAClD,UAAM,EAAE,eAAAC,eAAc,IAAI,MAAM;AAChC,UAAM,QAAQ,cAAc,MAAM,KAAK,qBAAqB;AAE5D,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,UAAU,MAAM,MAAM,OAAO,KAAK,CAAC;AACzC,WAAOA,eAAc,OAAO;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,kBACX,YACA,eAAuB,6BACN;AACjB,UAAM,EAAE,sBAAAC,sBAAqB,IAAI,MAAM;AACvC,UAAM,QAAQ,cAAc,MAAM,KAAK,qBAAqB;AAE5D,QAAI,CAAC,SAAS,CAAC,MAAM,MAAM;AACzB,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AAEA,UAAM,aAAa,MAAM,MAAM,KAAK;AACpC,UAAM,iBAAiB,WAAW;AAAA,MAAO,WACvC,CAACA,sBAAqB,CAAC,KAAK,GAAG,YAAY,EAAE;AAAA,IAC/C;AAGA,UAAM,QAAQ,IAAI,eAAe,IAAI,WAAS,MAAM,OAAO,MAAM,GAAG,CAAC,CAAC;AAEtE,WAAO,eAAe;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,WAAW,YAAwC;AAC9D,UAAM,QAAQ,cAAc,MAAM,KAAK,qBAAqB;AAE5D,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,MAAM,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,UACX,YACA,iBAA0B,OAC1B;AACA,UAAM,EAAE,sBAAAA,sBAAqB,IAAI,MAAM;AACvC,UAAM,QAAQ,cAAc,MAAM,KAAK,qBAAqB;AAE5D,QAAI,CAAC,SAAS,CAAC,MAAM,MAAM;AACzB,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AAEA,UAAM,UAAU,MAAM,MAAM,KAAK;AAEjC,QAAI,gBAAgB;AAClB,aAAO;AAAA,IACT;AAEA,WAAOA,sBAAqB,OAAO;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAqB,uBAAmD;AACtE,QAAI;AACF,UAAI,OAAO,WAAW,eAAe,OAAO,cAAc,aAAa;AAErE,cAAM,EAAE,qBAAAJ,qBAAoB,IAAI,MAAM;AACtC,eAAO,IAAIA,qBAAoB;AAAA,MACjC,WAAW,OAAO,YAAY,eAAe,QAAQ,UAAU,MAAM;AAEnE,cAAM,EAAE,sBAAAC,sBAAqB,IAAI,MAAM;AACvC,eAAO,IAAIA,sBAAqB;AAAA,MAClC;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,6CAA6C,KAAK;AAAA,IACjE;AAEA,WAAO;AAAA,EACT;AACF;;;AkBn9BO,IAAK,uBAAL,kBAAKI,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;;;AC2BZ;AACA;AAIA;","names":["response","arrayBuffer","stmt","stops","mergeRealtimeData","IndexedDBCacheStore","FileSystemCacheStore","zipData","getCacheStats","filterExpiredEntries","ScheduleRelationship","VehicleStopStatus","CongestionLevel","OccupancyStatus","AlertCause","AlertEffect"]} \ No newline at end of file diff --git a/examples/cache-example-browser.html b/examples/cache-example-browser.html new file mode 100644 index 0000000..9b722ed --- /dev/null +++ b/examples/cache-example-browser.html @@ -0,0 +1,133 @@ + + + + + + GTFS-SQLjs Cache Example (Browser) + + +

GTFS-SQLjs with IndexedDB Cache

+ +
+ + + + +
+ +
+
+ + + + diff --git a/examples/cache-example-node.js b/examples/cache-example-node.js new file mode 100644 index 0000000..f6a6ee1 --- /dev/null +++ b/examples/cache-example-node.js @@ -0,0 +1,216 @@ +/** + * GTFS-SQLjs Cache Example for Node.js + * + * This example demonstrates how to use the FileSystem cache in Node.js + */ + +import { GtfsSqlJs, FileSystemCacheStore } from 'gtfs-sqljs'; + +// Example 1: Automatic caching (default behavior) +async function example1_AutomaticCache() { + console.log('\n=== Example 1: Automatic Caching ==='); + + try { + const startTime = Date.now(); + + // Caching is automatic by default - uses FileSystemCacheStore in Node.js + const gtfs = await GtfsSqlJs.fromZip('./data/gtfs.zip', { + cacheVersion: '1.0', + onProgress: (progress) => { + console.log(`${progress.phase}: ${progress.message} (${progress.percentComplete}%)`); + } + }); + + const loadTime = ((Date.now() - startTime) / 1000).toFixed(2); + console.log(`✓ GTFS data loaded in ${loadTime}s`); + + const routes = gtfs.getRoutes(); + console.log(`Found ${routes.length} routes`); + + // On subsequent runs, loading will be much faster (from cache) + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Example 2: Custom cache directory +async function example2_CustomCacheDir() { + console.log('\n=== Example 2: Custom Cache Directory ==='); + + try { + const cache = new FileSystemCacheStore({ + dir: './my-custom-cache' + }); + + const gtfs = await GtfsSqlJs.fromZip('./data/gtfs.zip', { + cache, + cacheVersion: '1.0' + }); + + console.log('✓ Using custom cache directory: ./my-custom-cache'); + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Example 3: Cache management - Show statistics +async function example3_CacheStats() { + console.log('\n=== Example 3: Cache Statistics ==='); + + try { + const stats = await GtfsSqlJs.getCacheStats(); + + console.log('\nCache Statistics:'); + console.log(`- Total entries: ${stats.totalEntries}`); + console.log(`- Active entries: ${stats.activeEntries}`); + console.log(`- Expired entries: ${stats.expiredEntries}`); + console.log(`- Total size: ${stats.totalSizeMB} MB`); + + if (stats.oldestEntry) { + const oldest = new Date(stats.oldestEntry); + console.log(`- Oldest entry: ${oldest.toLocaleString()}`); + } + + if (stats.newestEntry) { + const newest = new Date(stats.newestEntry); + console.log(`- Newest entry: ${newest.toLocaleString()}`); + } + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Example 4: List cache entries +async function example4_ListCache() { + console.log('\n=== Example 4: List Cache Entries ==='); + + try { + const entries = await GtfsSqlJs.listCache(); + + console.log(`\nFound ${entries.length} active cache entries:`); + + entries.forEach((entry, index) => { + const age = ((Date.now() - entry.metadata.timestamp) / 1000 / 60 / 60).toFixed(1); + const sizeMB = (entry.metadata.size / 1024 / 1024).toFixed(2); + + console.log(`\n${index + 1}. ${entry.key}`); + console.log(` Source: ${entry.metadata.source || 'N/A'}`); + console.log(` Version: ${entry.metadata.version}`); + console.log(` Size: ${sizeMB} MB`); + console.log(` Age: ${age} hours`); + console.log(` Skip files: ${entry.metadata.skipFiles?.join(', ') || 'none'}`); + }); + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Example 5: Clean expired cache entries +async function example5_CleanExpired() { + console.log('\n=== Example 5: Clean Expired Cache ==='); + + try { + console.log('Cleaning expired cache entries (older than 7 days)...'); + const deletedCount = await GtfsSqlJs.cleanExpiredCache(); + + console.log(`✓ Deleted ${deletedCount} expired entries`); + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Example 6: Custom expiration time +async function example6_CustomExpiration() { + console.log('\n=== Example 6: Custom Cache Expiration ==='); + + try { + // Clean entries older than 3 days + const threeDaysInMs = 3 * 24 * 60 * 60 * 1000; + + console.log('Cleaning cache entries older than 3 days...'); + const deletedCount = await GtfsSqlJs.cleanExpiredCache(undefined, threeDaysInMs); + + console.log(`✓ Deleted ${deletedCount} entries`); + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Example 7: Clear all cache +async function example7_ClearCache() { + console.log('\n=== Example 7: Clear All Cache ==='); + + try { + console.log('Clearing all cache entries...'); + await GtfsSqlJs.clearCache(); + + console.log('✓ Cache cleared'); + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Example 8: Disable caching +async function example8_DisableCache() { + console.log('\n=== Example 8: Disable Caching ==='); + + try { + // Explicitly disable caching by passing null + const gtfs = await GtfsSqlJs.fromZip('./data/gtfs.zip', { + cache: null + }); + + console.log('✓ GTFS data loaded without caching'); + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Example 9: Version management +async function example9_VersionManagement() { + console.log('\n=== Example 9: Cache Version Management ==='); + + try { + // Load with version 1.0 + const gtfs1 = await GtfsSqlJs.fromZip('./data/gtfs.zip', { + cacheVersion: '1.0' + }); + + // Load with version 2.0 - will not use cache from v1.0 + const gtfs2 = await GtfsSqlJs.fromZip('./data/gtfs.zip', { + cacheVersion: '2.0' + }); + + console.log('✓ Different versions create separate cache entries'); + console.log(' Tip: Increment version when you update processing logic or schema'); + + } catch (error) { + console.error('✗ Error:', error.message); + } +} + +// Run all examples +async function runAllExamples() { + await example1_AutomaticCache(); + await example3_CacheStats(); + await example4_ListCache(); + await example5_CleanExpired(); + + // Uncomment to run other examples: + // await example2_CustomCacheDir(); + // await example6_CustomExpiration(); + // await example7_ClearCache(); + // await example8_DisableCache(); + // await example9_VersionManagement(); +} + +// Run examples +runAllExamples().catch(console.error); diff --git a/src/cache/checksum.ts b/src/cache/checksum.ts new file mode 100644 index 0000000..25ca86e --- /dev/null +++ b/src/cache/checksum.ts @@ -0,0 +1,90 @@ +/** + * Get crypto object (browser or Node.js) + */ +async function getCrypto(): Promise { + // Browser environment + if (typeof crypto !== 'undefined' && crypto.subtle) { + return crypto; + } + + // Node.js environment - import webcrypto + if (typeof globalThis !== 'undefined') { + try { + const { webcrypto } = await import('crypto'); + return webcrypto as unknown as Crypto; + } catch { + // Fallback for older Node.js versions + throw new Error('Web Crypto API not available'); + } + } + + throw new Error('Crypto not available in this environment'); +} + +/** + * Compute SHA-256 checksum of data + * Uses Web Crypto API (available in both browser and Node.js 18+) + */ +export async function computeChecksum(data: ArrayBuffer | Uint8Array): Promise { + // Get crypto instance + const cryptoInstance = await getCrypto(); + + // Ensure we have a BufferSource for crypto.subtle.digest + // Use type assertion to handle ArrayBufferLike compatibility + const bufferSource = (data instanceof Uint8Array ? data : new Uint8Array(data)) as BufferSource; + const hashBuffer = await cryptoInstance.subtle.digest('SHA-256', bufferSource); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + return hashHex; +} + +/** + * Compute checksum for a zip file + * @param zipData - The zip file data (ArrayBuffer or Uint8Array) + * @returns SHA-256 checksum as hex string + */ +export async function computeZipChecksum(zipData: ArrayBuffer | Uint8Array): Promise { + return computeChecksum(zipData); +} + +/** + * Generate a cache key from checksum, version, filesize, source, and options + * Format: v{libVersion}_{dataVersion}_{filesize}_{checksum}_{source}_{skipFiles} + * + * @param checksum - SHA-256 checksum of zip file + * @param libVersion - Library version from package.json + * @param dataVersion - User-specified data version + * @param filesize - Size of the zip file in bytes + * @param source - Source URL or filename (optional) + * @param skipFiles - Files that were skipped during import + * @returns Cache key string + */ +export function generateCacheKey( + checksum: string, + libVersion: string, + dataVersion: string, + filesize: number, + source?: string, + skipFiles?: string[] +): string { + // Start with versions and filesize + let key = `v${libVersion}_d${dataVersion}_${filesize}_${checksum}`; + + // Add source (sanitized) + if (source) { + // Extract filename from URL/path and sanitize + const filename = source.split('/').pop() || source; + const sanitized = filename.replace(/[^a-zA-Z0-9._-]/g, '_'); + key += `_${sanitized}`; + } + + // Add skip files if any + if (skipFiles && skipFiles.length > 0) { + // Sort to ensure consistent key regardless of order + const sortedSkips = [...skipFiles].sort(); + const skipsSuffix = sortedSkips.join(',').replace(/\.txt/g, ''); + key += `_skip-${skipsSuffix}`; + } + + return key; +} diff --git a/src/cache/fs-store.ts b/src/cache/fs-store.ts new file mode 100644 index 0000000..4a2ace5 --- /dev/null +++ b/src/cache/fs-store.ts @@ -0,0 +1,232 @@ +import type { CacheStore, CacheMetadata, CacheEntry, CacheStoreOptions } from './types'; + +/** + * File system-based cache store for Node.js + * + * Stores GTFS databases as files on disk for fast access on subsequent loads. + * Suitable for any size database (limited only by disk space). + * + * @example + * ```typescript + * import { GtfsSqlJs, FileSystemCacheStore } from 'gtfs-sqljs'; + * + * const cache = new FileSystemCacheStore({ dir: './.cache/gtfs' }); + * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + * cache, + * cacheVersion: '1.0' + * }); + * ``` + */ +export class FileSystemCacheStore implements CacheStore { + private cacheDir: string; + + constructor(options: CacheStoreOptions = {}) { + // Default cache directory (will be resolved lazily on first use) + this.cacheDir = options.dir || ''; + } + + /** + * Get the cache directory path (lazy initialization) + */ + private async getCacheDir(): Promise { + if (this.cacheDir) { + return this.cacheDir; + } + + // Dynamically import Node.js modules + const path = await import('path'); + const os = await import('os'); + + // Default to XDG cache directory on Linux/Mac, or temp dir as fallback + this.cacheDir = path.join( + process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'), + 'gtfs-sqljs' + ); + + return this.cacheDir; + } + + /** + * Ensure cache directory exists + */ + private async ensureCacheDir(): Promise { + const fs = await import('fs'); + const cacheDir = await this.getCacheDir(); + + try { + await fs.promises.mkdir(cacheDir, { recursive: true }); + } catch (error) { + // Ignore error if directory already exists + if ((error as NodeJS.ErrnoException).code !== 'EEXIST') { + throw error; + } + } + } + + /** + * Get file path for a cache key + */ + private async getFilePath(key: string): Promise { + const path = await import('path'); + const cacheDir = await this.getCacheDir(); + + // Sanitize key to be filesystem-safe + const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_'); + return path.join(cacheDir, `${safeKey}.db`); + } + + /** + * Get metadata file path for a cache key + */ + private async getMetadataPath(key: string): Promise { + const path = await import('path'); + const cacheDir = await this.getCacheDir(); + + const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_'); + return path.join(cacheDir, `${safeKey}.meta.json`); + } + + /** + * Get a cached database + */ + async get(key: string): Promise { + const fs = await import('fs'); + + try { + const filePath = await this.getFilePath(key); + const buffer = await fs.promises.readFile(filePath); + return buffer.buffer.slice( + buffer.byteOffset, + buffer.byteOffset + buffer.byteLength + ); + } catch (error) { + // Return null if file doesn't exist + if ((error as NodeJS.ErrnoException).code === 'ENOENT') { + return null; + } + throw error; + } + } + + /** + * Store a database in cache + */ + async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise { + const fs = await import('fs'); + await this.ensureCacheDir(); + + const filePath = await this.getFilePath(key); + const metadataPath = await this.getMetadataPath(key); + + // Write database file + await fs.promises.writeFile(filePath, new Uint8Array(data)); + + // Write metadata file + await fs.promises.writeFile(metadataPath, JSON.stringify(metadata, null, 2)); + } + + /** + * Check if a cache entry exists + */ + async has(key: string): Promise { + const fs = await import('fs'); + + try { + const filePath = await this.getFilePath(key); + await fs.promises.access(filePath); + return true; + } catch { + return false; + } + } + + /** + * Delete a specific cache entry + */ + async delete(key: string): Promise { + const fs = await import('fs'); + + const filePath = await this.getFilePath(key); + const metadataPath = await this.getMetadataPath(key); + + try { + await fs.promises.unlink(filePath); + } catch (error) { + // Ignore error if file doesn't exist + if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { + throw error; + } + } + + try { + await fs.promises.unlink(metadataPath); + } catch { + // Ignore metadata file errors + } + } + + /** + * Clear all cache entries + */ + async clear(): Promise { + const fs = await import('fs'); + const path = await import('path'); + const cacheDir = await this.getCacheDir(); + + try { + const files = await fs.promises.readdir(cacheDir); + + await Promise.all( + files.map(file => + fs.promises.unlink(path.join(cacheDir, file)).catch(() => { + // Ignore errors + }) + ) + ); + } catch (error) { + // Ignore error if directory doesn't exist + if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { + throw error; + } + } + } + + /** + * List all cached entries + */ + async list(): Promise { + const fs = await import('fs'); + const path = await import('path'); + const cacheDir = await this.getCacheDir(); + + try { + const files = await fs.promises.readdir(cacheDir); + const metadataFiles = files.filter(f => f.endsWith('.meta.json')); + + const entries: CacheEntry[] = []; + + for (const metaFile of metadataFiles) { + try { + const metaPath = path.join(cacheDir, metaFile); + const metaContent = await fs.promises.readFile(metaPath, 'utf-8'); + const metadata = JSON.parse(metaContent) as CacheMetadata; + + // Extract key from filename (remove .meta.json) + const key = metaFile.replace('.meta.json', ''); + + entries.push({ key, metadata }); + } catch { + // Skip invalid metadata files + } + } + + return entries; + } catch (error) { + // Return empty array if directory doesn't exist + if ((error as NodeJS.ErrnoException).code === 'ENOENT') { + return []; + } + throw error; + } + } +} diff --git a/src/cache/indexeddb-store.ts b/src/cache/indexeddb-store.ts new file mode 100644 index 0000000..1646a6d --- /dev/null +++ b/src/cache/indexeddb-store.ts @@ -0,0 +1,223 @@ +import type { CacheStore, CacheMetadata, CacheEntry, CacheStoreOptions } from './types'; + +/** + * IndexedDB-based cache store for browsers + * + * Stores GTFS databases in IndexedDB for fast access on subsequent loads. + * Suitable for large databases (100s of MB to several GB depending on browser limits). + * + * @example + * ```typescript + * import { GtfsSqlJs, IndexedDBCacheStore } from 'gtfs-sqljs'; + * + * const cache = new IndexedDBCacheStore(); + * const gtfs = await GtfsSqlJs.fromZip('gtfs.zip', { + * cache, + * cacheVersion: '1.0' + * }); + * ``` + */ +export class IndexedDBCacheStore implements CacheStore { + private dbName: string; + private storeName = 'gtfs-cache'; + private version = 1; + + constructor(options: CacheStoreOptions = {}) { + this.dbName = options.dbName || 'gtfs-sqljs-cache'; + } + + /** + * Open IndexedDB connection + */ + private async openDB(): Promise { + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.dbName, this.version); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result); + + request.onupgradeneeded = (event) => { + const db = (event.target as IDBOpenDBRequest).result; + + // Create object store if it doesn't exist + if (!db.objectStoreNames.contains(this.storeName)) { + const store = db.createObjectStore(this.storeName, { keyPath: 'key' }); + store.createIndex('timestamp', 'metadata.timestamp', { unique: false }); + } + }; + }); + } + + /** + * Get a cached database + */ + async get(key: string): Promise { + const db = await this.openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], 'readonly'); + const store = transaction.objectStore(this.storeName); + const request = store.get(key); + + request.onerror = () => { + db.close(); + reject(request.error); + }; + + request.onsuccess = () => { + db.close(); + const result = request.result; + resolve(result ? result.data : null); + }; + }); + } + + /** + * Store a database in cache + */ + async set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise { + const db = await this.openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], 'readwrite'); + const store = transaction.objectStore(this.storeName); + + const record = { + key, + data, + metadata + }; + + const request = store.put(record); + + request.onerror = () => { + db.close(); + reject(request.error); + }; + + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + + /** + * Check if a cache entry exists + */ + async has(key: string): Promise { + const db = await this.openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], 'readonly'); + const store = transaction.objectStore(this.storeName); + const request = store.getKey(key); + + request.onerror = () => { + db.close(); + reject(request.error); + }; + + request.onsuccess = () => { + db.close(); + resolve(request.result !== undefined); + }; + }); + } + + /** + * Delete a specific cache entry + */ + async delete(key: string): Promise { + const db = await this.openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], 'readwrite'); + const store = transaction.objectStore(this.storeName); + const request = store.delete(key); + + request.onerror = () => { + db.close(); + reject(request.error); + }; + + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + + /** + * Clear all cache entries + */ + async clear(): Promise { + const db = await this.openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], 'readwrite'); + const store = transaction.objectStore(this.storeName); + const request = store.clear(); + + request.onerror = () => { + db.close(); + reject(request.error); + }; + + request.onsuccess = () => { + db.close(); + resolve(); + }; + }); + } + + /** + * List all cached entries + */ + async list(): Promise { + const db = await this.openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction([this.storeName], 'readonly'); + const store = transaction.objectStore(this.storeName); + const request = store.getAllKeys(); + + request.onerror = () => { + db.close(); + reject(request.error); + }; + + request.onsuccess = async () => { + const keys = request.result as string[]; + const entries: CacheEntry[] = []; + + // Get metadata for each key + const metadataPromises = keys.map(async (key) => { + const metaRequest = store.get(key); + return new Promise((resolveEntry, rejectEntry) => { + metaRequest.onerror = () => rejectEntry(metaRequest.error); + metaRequest.onsuccess = () => { + const record = metaRequest.result; + resolveEntry({ + key, + metadata: record.metadata + }); + }; + }); + }); + + try { + const results = await Promise.all(metadataPromises); + entries.push(...results); + } catch (error) { + db.close(); + reject(error); + return; + } + + db.close(); + resolve(entries); + }; + }); + } +} diff --git a/src/cache/types.ts b/src/cache/types.ts new file mode 100644 index 0000000..ecd9ae8 --- /dev/null +++ b/src/cache/types.ts @@ -0,0 +1,88 @@ +/** + * Metadata stored with cached GTFS databases + */ +export interface CacheMetadata { + /** Checksum of the source zip file (SHA-256) */ + checksum: string; + /** Version number - cache is invalidated if this changes */ + version: string; + /** Timestamp when the cache was created */ + timestamp: number; + /** Source zip URL or path (for reference) */ + source?: string; + /** Size of the cached database in bytes */ + size: number; + /** Which files were skipped during import (affects cache validity) */ + skipFiles?: string[]; +} + +/** + * A single cache entry with its metadata + */ +export interface CacheEntry { + /** Unique cache key */ + key: string; + /** Cache metadata */ + metadata: CacheMetadata; +} + +/** + * Interface for implementing custom cache storage backends. + * + * The library provides two implementations: + * - IndexedDBCacheStore (for browsers) + * - FileSystemCacheStore (for Node.js) + * + * You can implement this interface to use custom storage backends + * (e.g., Redis, S3, or any other storage system). + */ +export interface CacheStore { + /** + * Retrieve a cached database by key + * @param key - Cache key (typically includes checksum and version) + * @returns The cached database as ArrayBuffer, or null if not found + */ + get(key: string): Promise; + + /** + * Store a database in the cache + * @param key - Cache key + * @param data - Database as ArrayBuffer + * @param metadata - Metadata about the cached database + */ + set(key: string, data: ArrayBuffer, metadata: CacheMetadata): Promise; + + /** + * Check if a cache entry exists + * @param key - Cache key + * @returns true if the cache entry exists + */ + has(key: string): Promise; + + /** + * Delete a specific cache entry + * @param key - Cache key + */ + delete(key: string): Promise; + + /** + * Clear all cache entries + */ + clear(): Promise; + + /** + * List all cached entries (optional) + * @returns Array of cache entries with their metadata + */ + list?(): Promise; +} + +/** + * Options for cache stores + */ +export interface CacheStoreOptions { + /** Cache directory path (for FileSystemCacheStore) */ + dir?: string; + /** IndexedDB database name (for IndexedDBCacheStore) */ + dbName?: string; +} diff --git a/src/cache/utils.ts b/src/cache/utils.ts new file mode 100644 index 0000000..80610c7 --- /dev/null +++ b/src/cache/utils.ts @@ -0,0 +1,59 @@ +import type { CacheMetadata, CacheEntry } from './types'; + +/** + * Default cache expiration time in milliseconds (7 days) + */ +export const DEFAULT_CACHE_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000; + +/** + * Check if a cache entry is expired + * @param metadata - Cache metadata + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns true if the cache entry is expired + */ +export function isCacheExpired( + metadata: CacheMetadata, + expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS +): boolean { + const now = Date.now(); + const age = now - metadata.timestamp; + return age > expirationMs; +} + +/** + * Filter out expired cache entries + * @param entries - Array of cache entries + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns Filtered array of non-expired entries + */ +export function filterExpiredEntries( + entries: CacheEntry[], + expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS +): CacheEntry[] { + return entries.filter(entry => !isCacheExpired(entry.metadata, expirationMs)); +} + +/** + * Get cache statistics + * @param entries - Array of cache entries + * @returns Cache statistics + */ +export function getCacheStats(entries: CacheEntry[]) { + const totalSize = entries.reduce((sum, entry) => sum + entry.metadata.size, 0); + const expiredEntries = entries.filter(entry => isCacheExpired(entry.metadata)); + const activeEntries = entries.filter(entry => !isCacheExpired(entry.metadata)); + + return { + totalEntries: entries.length, + activeEntries: activeEntries.length, + expiredEntries: expiredEntries.length, + totalSize, + totalSizeMB: (totalSize / 1024 / 1024).toFixed(2), + oldestEntry: entries.length > 0 + ? Math.min(...entries.map(e => e.metadata.timestamp)) + : null, + newestEntry: entries.length > 0 + ? Math.max(...entries.map(e => e.metadata.timestamp)) + : null, + }; +} diff --git a/src/gtfs-sqljs.ts b/src/gtfs-sqljs.ts index 6399c83..fad345c 100644 --- a/src/gtfs-sqljs.ts +++ b/src/gtfs-sqljs.ts @@ -4,10 +4,16 @@ import initSqlJs, { type Database, type SqlJsStatic } from 'sql.js'; import { getAllCreateTableStatements, getAllCreateIndexStatements } from './schema/schema'; -import { loadGTFSZip } from './loaders/zip-loader'; +import { loadGTFSZip, fetchZip } from './loaders/zip-loader'; import { loadGTFSData } from './loaders/data-loader'; import { createRealtimeTables, clearRealtimeData as clearRTData } from './schema/gtfs-rt-schema'; import { loadRealtimeData } from './loaders/gtfs-rt-loader'; +import type { CacheStore } from './cache/types'; +import { computeZipChecksum, generateCacheKey } from './cache/checksum'; +import { DEFAULT_CACHE_EXPIRATION_MS } from './cache/utils'; + +// Library version from package.json +const LIB_VERSION = '0.1.0'; // Query methods import { getAgencies, type AgencyFilters } from './queries/agencies'; @@ -39,7 +45,7 @@ export type { Alert, VehiclePosition, TripUpdate, StopTimeUpdateWithMetadata, Tr * Progress information for GTFS data loading */ export interface ProgressInfo { - phase: 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'complete'; + phase: 'checking_cache' | 'loading_from_cache' | 'downloading' | 'extracting' | 'creating_schema' | 'inserting_data' | 'creating_indexes' | 'analyzing' | 'saving_cache' | 'complete'; currentFile: string | null; filesCompleted: number; totalFiles: number; @@ -97,6 +103,33 @@ export interface GtfsSqlJsOptions { * Useful for displaying progress in UI or web workers */ onProgress?: ProgressCallback; + + /** + * Optional: Cache store for persisting processed GTFS databases + * Use IndexedDBCacheStore (browser) or FileSystemCacheStore (Node.js) + * or implement your own CacheStore + * + * If not provided, caching is enabled by default with: + * - IndexedDBCacheStore in browsers + * - FileSystemCacheStore in Node.js + * + * Set to `null` to disable caching + */ + cache?: CacheStore | null; + + /** + * Optional: Data version string + * When changed, cached databases are invalidated and reprocessed + * Default: '1.0' + */ + cacheVersion?: string; + + /** + * Optional: Cache expiration time in milliseconds + * Cached databases older than this will be invalidated + * Default: 7 days (604800000 ms) + */ + cacheExpirationMs?: number; } export class GtfsSqlJs { @@ -139,8 +172,188 @@ export class GtfsSqlJs { */ private async initFromZip(zipPath: string, options: Omit): Promise { const onProgress = options.onProgress; + const { + cache: userCache, + cacheVersion = '1.0', + skipFiles + } = options; // Initialize SQL.js + this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {})); + + // Determine cache store to use + let cache: CacheStore | null = null; + + if (userCache === null) { + // User explicitly disabled caching + cache = null; + } else if (userCache) { + // User provided a cache store + cache = userCache; + } else { + // Auto-detect environment and create default cache store + try { + if (typeof window !== 'undefined' && typeof indexedDB !== 'undefined') { + // Browser environment - use IndexedDB + const { IndexedDBCacheStore } = await import('./cache/indexeddb-store'); + cache = new IndexedDBCacheStore(); + } else if (typeof process !== 'undefined' && process.versions?.node) { + // Node.js environment - use FileSystem + const { FileSystemCacheStore } = await import('./cache/fs-store'); + cache = new FileSystemCacheStore(); + } + } catch (error) { + // Fallback to no caching if import fails + console.warn('Failed to initialize default cache store:', error); + cache = null; + } + } + + // Check cache if enabled + if (cache) { + onProgress?.({ + phase: 'checking_cache', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 0, + message: 'Checking cache...', + }); + + // Fetch raw zip data for checksum (only if zipPath is a string) + let zipData: ArrayBuffer; + if (typeof zipPath === 'string') { + onProgress?.({ + phase: 'downloading', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 2, + message: 'Downloading GTFS ZIP file', + }); + + zipData = await fetchZip(zipPath); + } else { + // zipPath is already ArrayBuffer or Uint8Array + zipData = zipPath as ArrayBuffer; + } + + // Calculate filesize + const filesize = zipData.byteLength; + + // Compute checksum + const checksum = await computeZipChecksum(zipData); + + // Generate cache key with all parameters + const cacheKey = generateCacheKey( + checksum, + LIB_VERSION, + cacheVersion, + filesize, + typeof zipPath === 'string' ? zipPath : undefined, + skipFiles + ); + + // Check if cache exists + const cachedDb = await cache.get(cacheKey); + + if (cachedDb) { + // Note: We retrieve the cache entry but we should check if it's expired + // For now, we check expiration when listing/managing cache + // The cache store itself doesn't track metadata on get() + // TODO: Consider updating CacheStore.get() to return metadata as well + + // Load from cache + onProgress?.({ + phase: 'loading_from_cache', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 50, + message: 'Loading from cache...', + }); + + this.db = new this.SQL.Database(new Uint8Array(cachedDb)); + + // Set RT configuration + if (options.realtimeFeedUrls) { + this.realtimeFeedUrls = options.realtimeFeedUrls; + } + if (options.stalenessThreshold !== undefined) { + this.stalenessThreshold = options.stalenessThreshold; + } + + onProgress?.({ + phase: 'complete', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: 'GTFS data loaded from cache', + }); + + return; + } + + // Cache miss - continue with normal loading but use already-fetched zip data + onProgress?.({ + phase: 'extracting', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 5, + message: 'Extracting GTFS ZIP file', + }); + + await this.loadFromZipData(zipData, options, onProgress); + + // Save to cache + onProgress?.({ + phase: 'saving_cache', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 98, + message: 'Saving to cache...', + }); + + const dbBuffer = this.export(); + await cache.set(cacheKey, dbBuffer, { + checksum, + version: cacheVersion, + timestamp: Date.now(), + source: typeof zipPath === 'string' ? zipPath : undefined, + size: dbBuffer.byteLength, + skipFiles, + }); + + onProgress?.({ + phase: 'complete', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: 'GTFS data loaded successfully', + }); + + return; + } + + // No cache - use normal loading flow onProgress?.({ phase: 'downloading', currentFile: null, @@ -149,13 +362,43 @@ export class GtfsSqlJs { rowsProcessed: 0, totalRows: 0, percentComplete: 0, - message: 'Initializing database engine', + message: 'Downloading GTFS ZIP file', }); - this.SQL = options.SQL || (await initSqlJs(options.locateFile ? { locateFile: options.locateFile } : {})); + // Fetch zip data + let zipData: ArrayBuffer; + if (typeof zipPath === 'string') { + zipData = await fetchZip(zipPath); + } else { + zipData = zipPath as ArrayBuffer; + } + + // Load from zip data + await this.loadFromZipData(zipData, options, onProgress); + + onProgress?.({ + phase: 'complete', + currentFile: null, + filesCompleted: 0, + totalFiles: 0, + rowsProcessed: 0, + totalRows: 0, + percentComplete: 100, + message: 'GTFS data loaded successfully', + }); + } + /** + * Helper method to load GTFS data from zip data (ArrayBuffer) + * Used by both cache-enabled and cache-disabled paths + */ + private async loadFromZipData( + zipData: ArrayBuffer, + options: Omit, + onProgress?: ProgressCallback + ): Promise { // Create new database - this.db = new this.SQL.Database(); + this.db = new this.SQL!.Database(); // Apply performance PRAGMAs for bulk loading onProgress?.({ @@ -165,7 +408,7 @@ export class GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 2, + percentComplete: 10, message: 'Optimizing database for bulk import', }); @@ -183,7 +426,7 @@ export class GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 5, + percentComplete: 15, message: 'Creating database tables', }); @@ -195,7 +438,7 @@ export class GtfsSqlJs { // Create GTFS-RT tables createRealtimeTables(this.db); - // Load GTFS data + // Extract files from zip onProgress?.({ phase: 'extracting', currentFile: null, @@ -203,12 +446,13 @@ export class GtfsSqlJs { totalFiles: 0, rowsProcessed: 0, totalRows: 0, - percentComplete: 10, - message: 'Loading and extracting GTFS ZIP file', + percentComplete: 20, + message: 'Extracting GTFS ZIP file', }); - const files = await loadGTFSZip(zipPath); + const files = await loadGTFSZip(zipData); + // Load GTFS data onProgress?.({ phase: 'inserting_data', currentFile: null, @@ -216,7 +460,7 @@ export class GtfsSqlJs { totalFiles: Object.keys(files).length, rowsProcessed: 0, totalRows: 0, - percentComplete: 15, + percentComplete: 25, message: 'Starting data import', }); @@ -278,17 +522,6 @@ export class GtfsSqlJs { 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', - }); - // Auto-fetch realtime data if feed URLs are configured if (this.realtimeFeedUrls.length > 0) { try { @@ -642,4 +875,113 @@ export class GtfsSqlJs { if (!this.db) throw new Error('Database not initialized'); return getAllStopTimeUpdates(this.db); } + + // ==================== Cache Management Methods ==================== + + /** + * Get cache statistics + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @returns Cache statistics including size, entry count, and age information + */ + static async getCacheStats(cacheStore?: CacheStore) { + const { getCacheStats } = await import('./cache/utils'); + const cache = cacheStore || await this.getDefaultCacheStore(); + + if (!cache) { + throw new Error('No cache store available'); + } + + const entries = await cache.list?.() || []; + return getCacheStats(entries); + } + + /** + * Clean expired cache entries + * @param cacheStore - Cache store to clean (optional, auto-detects if not provided) + * @param expirationMs - Expiration time in milliseconds (default: 7 days) + * @returns Number of entries deleted + */ + static async cleanExpiredCache( + cacheStore?: CacheStore, + expirationMs: number = DEFAULT_CACHE_EXPIRATION_MS + ): Promise { + const { filterExpiredEntries } = await import('./cache/utils'); + const cache = cacheStore || await this.getDefaultCacheStore(); + + if (!cache || !cache.list) { + throw new Error('No cache store available or cache store does not support listing'); + } + + const allEntries = await cache.list(); + const expiredEntries = allEntries.filter(entry => + !filterExpiredEntries([entry], expirationMs).length + ); + + // Delete expired entries + await Promise.all(expiredEntries.map(entry => cache.delete(entry.key))); + + return expiredEntries.length; + } + + /** + * Clear all cache entries + * @param cacheStore - Cache store to clear (optional, auto-detects if not provided) + */ + static async clearCache(cacheStore?: CacheStore): Promise { + const cache = cacheStore || await this.getDefaultCacheStore(); + + if (!cache) { + throw new Error('No cache store available'); + } + + await cache.clear(); + } + + /** + * List all cache entries + * @param cacheStore - Cache store to query (optional, auto-detects if not provided) + * @param includeExpired - Include expired entries (default: false) + * @returns Array of cache entries with metadata + */ + static async listCache( + cacheStore?: CacheStore, + includeExpired: boolean = false + ) { + const { filterExpiredEntries } = await import('./cache/utils'); + const cache = cacheStore || await this.getDefaultCacheStore(); + + if (!cache || !cache.list) { + throw new Error('No cache store available or cache store does not support listing'); + } + + const entries = await cache.list(); + + if (includeExpired) { + return entries; + } + + return filterExpiredEntries(entries); + } + + /** + * Get the default cache store for the current environment + * @returns Default cache store or null if unavailable + */ + private static async getDefaultCacheStore(): Promise { + try { + if (typeof window !== 'undefined' && typeof indexedDB !== 'undefined') { + // Browser environment - use IndexedDB + const { IndexedDBCacheStore } = await import('./cache/indexeddb-store'); + return new IndexedDBCacheStore(); + } else if (typeof process !== 'undefined' && process.versions?.node) { + // Node.js environment - use FileSystem + const { FileSystemCacheStore } = await import('./cache/fs-store'); + return new FileSystemCacheStore(); + } + } catch (error) { + console.warn('Failed to initialize default cache store:', error); + } + + return null; + } } diff --git a/src/index.ts b/src/index.ts index e11551a..4d567d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -70,3 +70,19 @@ export { // Export schema definitions for advanced use export { GTFS_SCHEMA, type TableSchema, type ColumnDefinition, type IndexDefinition } from './schema/schema'; + +// Export cache types and interfaces +export type { + CacheStore, + CacheMetadata, + CacheEntry, + CacheStoreOptions +} from './cache/types'; + +// Export cache store implementations +export { IndexedDBCacheStore } from './cache/indexeddb-store'; +export { FileSystemCacheStore } from './cache/fs-store'; + +// Export cache utilities +export { computeChecksum, computeZipChecksum, generateCacheKey } from './cache/checksum'; +export { isCacheExpired, filterExpiredEntries, getCacheStats, DEFAULT_CACHE_EXPIRATION_MS } from './cache/utils'; diff --git a/src/loaders/zip-loader.ts b/src/loaders/zip-loader.ts index 7eb0674..84102cb 100644 --- a/src/loaders/zip-loader.ts +++ b/src/loaders/zip-loader.ts @@ -47,9 +47,9 @@ export async function loadGTFSZip(source: string | ArrayBuffer | Uint8Array): Pr } /** - * Fetch ZIP file from URL or file path + * Fetch ZIP file from URL or file path (exported for checksum computation) */ -async function fetchZip(source: string): Promise { +export async function fetchZip(source: string): Promise { // Check if source is a URL const isUrl = source.startsWith('http://') || source.startsWith('https://'); diff --git a/tsup.config.ts b/tsup.config.ts index 0d72d1d..e47f32d 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -9,4 +9,8 @@ export default defineConfig({ clean: true, target: 'es2020', platform: 'neutral', + // Mark Node.js built-in modules as external to avoid bundling errors + external: ['fs', 'path', 'os'], + // Don't bundle node built-ins + noExternal: [], });