Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
404 changes: 227 additions & 177 deletions dist/index.d.mts

Large diffs are not rendered by default.

404 changes: 227 additions & 177 deletions dist/index.d.ts

Large diffs are not rendered by default.

386 changes: 301 additions & 85 deletions dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

386 changes: 301 additions & 85 deletions dist/index.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.mjs.map

Large diffs are not rendered by default.

81 changes: 76 additions & 5 deletions src/gtfs-sqljs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ import {
} from './queries/calendar';
import { getTripById, getTrips, type TripFilters, type TripWithRealtime } from './queries/trips';
import { getStopTimesByTrip, getStopTimes, type StopTimeFilters, type StopTimeWithRealtime } from './queries/stop-times';
import { getAlerts as getAlertsQuery, getAlertById, type AlertFilters } from './queries/rt-alerts';
import { getVehiclePositions as getVehiclePositionsQuery, getVehiclePositionByTripId, type VehiclePositionFilters } from './queries/rt-vehicle-positions';
import { getAlerts as getAlertsQuery, getAlertById, getAllAlerts, type AlertFilters } from './queries/rt-alerts';
import { getVehiclePositions as getVehiclePositionsQuery, getVehiclePositionByTripId, getAllVehiclePositions, type VehiclePositionFilters } from './queries/rt-vehicle-positions';
import { getTripUpdates, getTripUpdateByTripId, getAllTripUpdates, type TripUpdateFilters } from './queries/rt-trip-updates';
import { getStopTimeUpdates, getStopTimeUpdatesByTripId, getAllStopTimeUpdates, type StopTimeUpdateFilters, type StopTimeUpdateWithMetadata } from './queries/rt-stop-time-updates';

// Types
import type { Agency, Stop, Route, Trip, StopTime, Calendar, CalendarDate } from './types/gtfs';
import type { Alert, VehiclePosition } from './types/gtfs-rt';
import type { Alert, VehiclePosition, TripUpdate } from './types/gtfs-rt';

// Export filter types for users
export type { AgencyFilters, StopFilters, RouteFilters, TripFilters, StopTimeFilters, AlertFilters, VehiclePositionFilters };
export type { AgencyFilters, StopFilters, RouteFilters, TripFilters, StopTimeFilters, AlertFilters, VehiclePositionFilters, TripUpdateFilters, StopTimeUpdateFilters };
// Export RT types
export type { Alert, VehiclePosition, TripWithRealtime, StopTimeWithRealtime };
export type { Alert, VehiclePosition, TripUpdate, StopTimeUpdateWithMetadata, TripWithRealtime, StopTimeWithRealtime };

export interface GtfsSqlJsOptions {
/**
Expand Down Expand Up @@ -464,4 +466,73 @@ export class GtfsSqlJs {
if (!this.db) throw new Error('Database not initialized');
return getVehiclePositionByTripId(this.db, tripId, this.stalenessThreshold);
}

/**
* Get trip updates with optional filters
*/
getTripUpdates(filters?: TripUpdateFilters): TripUpdate[] {
if (!this.db) throw new Error('Database not initialized');
return getTripUpdates(this.db, filters, this.stalenessThreshold);
}

/**
* Get trip update by trip ID
*/
getTripUpdateByTripId(tripId: string): TripUpdate | null {
if (!this.db) throw new Error('Database not initialized');
return getTripUpdateByTripId(this.db, tripId, this.stalenessThreshold);
}

/**
* Get stop time updates with optional filters
*/
getStopTimeUpdates(filters?: StopTimeUpdateFilters): import('./types/gtfs-rt').StopTimeUpdate[] {
if (!this.db) throw new Error('Database not initialized');
return getStopTimeUpdates(this.db, filters, this.stalenessThreshold);
}

/**
* Get stop time updates for a specific trip
*/
getStopTimeUpdatesByTripId(tripId: string): import('./types/gtfs-rt').StopTimeUpdate[] {
if (!this.db) throw new Error('Database not initialized');
return getStopTimeUpdatesByTripId(this.db, tripId, this.stalenessThreshold);
}

// ==================== Debug Export Methods ====================
// These methods export all realtime data without staleness filtering
// for debugging purposes

/**
* Export all alerts without staleness filtering (for debugging)
*/
debugExportAllAlerts(): Alert[] {
if (!this.db) throw new Error('Database not initialized');
return getAllAlerts(this.db);
}

/**
* Export all vehicle positions without staleness filtering (for debugging)
*/
debugExportAllVehiclePositions(): VehiclePosition[] {
if (!this.db) throw new Error('Database not initialized');
return getAllVehiclePositions(this.db);
}

/**
* Export all trip updates without staleness filtering (for debugging)
*/
debugExportAllTripUpdates(): TripUpdate[] {
if (!this.db) throw new Error('Database not initialized');
return getAllTripUpdates(this.db);
}

/**
* Export all stop time updates without staleness filtering (for debugging)
* Returns extended type with trip_id and rt_last_updated for debugging purposes
*/
debugExportAllStopTimeUpdates(): StopTimeUpdateWithMetadata[] {
if (!this.db) throw new Error('Database not initialized');
return getAllStopTimeUpdates(this.db);
}
}
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ export {
type StopTimeFilters,
type AlertFilters,
type VehiclePositionFilters,
type TripUpdateFilters,
type StopTimeUpdateFilters,
type Alert,
type VehiclePosition,
type TripUpdate,
type StopTimeUpdateWithMetadata,
type TripWithRealtime,
type StopTimeWithRealtime
} from './gtfs-sqljs';
Expand Down Expand Up @@ -49,7 +53,6 @@ export type {
VehicleDescriptor,
StopTimeEvent,
StopTimeUpdate,
TripUpdate,
StopTimeRealtime,
TripRealtime,
RealtimeConfig
Expand Down
17 changes: 17 additions & 0 deletions src/queries/rt-alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,20 @@ export function getAlertById(db: Database, alertId: string, stalenessThreshold:
const alerts = getAlerts(db, { alertId, limit: 1 }, stalenessThreshold);
return alerts.length > 0 ? alerts[0] : null;
}

/**
* Get all alerts without staleness filtering (for debugging)
*/
export function getAllAlerts(db: Database): Alert[] {
const sql = 'SELECT * FROM rt_alerts ORDER BY rt_last_updated DESC';
const stmt = db.prepare(sql);

const alerts: Alert[] = [];
while (stmt.step()) {
const row = stmt.getAsObject() as Record<string, unknown>;
alerts.push(parseAlert(row));
}

stmt.free();
return alerts;
}
152 changes: 152 additions & 0 deletions src/queries/rt-stop-time-updates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import type { Database } from 'sql.js';
import type { StopTimeUpdate } from '../types/gtfs-rt';

export interface StopTimeUpdateFilters {
tripId?: string;
stopId?: string;
stopSequence?: number;
limit?: number;
}

/**
* Parse stop time update from database row
*/
function parseStopTimeUpdate(row: Record<string, unknown>): StopTimeUpdate {
const stu: StopTimeUpdate = {
stop_sequence: row.stop_sequence !== null ? Number(row.stop_sequence) : undefined,
stop_id: row.stop_id ? String(row.stop_id) : undefined,
schedule_relationship: row.schedule_relationship !== null ? Number(row.schedule_relationship) : undefined
};

// Arrival event
if (row.arrival_delay !== null || row.arrival_time !== null || row.arrival_uncertainty !== null) {
stu.arrival = {
delay: row.arrival_delay !== null ? Number(row.arrival_delay) : undefined,
time: row.arrival_time !== null ? Number(row.arrival_time) : undefined,
uncertainty: row.arrival_uncertainty !== null ? Number(row.arrival_uncertainty) : undefined
};
}

// Departure event
if (row.departure_delay !== null || row.departure_time !== null || row.departure_uncertainty !== null) {
stu.departure = {
delay: row.departure_delay !== null ? Number(row.departure_delay) : undefined,
time: row.departure_time !== null ? Number(row.departure_time) : undefined,
uncertainty: row.departure_uncertainty !== null ? Number(row.departure_uncertainty) : undefined
};
}

return stu;
}

/**
* Extended StopTimeUpdate with trip_id and rt_last_updated for debugging
*/
export interface StopTimeUpdateWithMetadata extends StopTimeUpdate {
trip_id: string;
rt_last_updated: number;
}

/**
* Parse stop time update with metadata from database row
*/
function parseStopTimeUpdateWithMetadata(row: Record<string, unknown>): StopTimeUpdateWithMetadata {
const stu = parseStopTimeUpdate(row) as StopTimeUpdateWithMetadata;
stu.trip_id = String(row.trip_id);
stu.rt_last_updated = Number(row.rt_last_updated);
return stu;
}

/**
* Get stop time updates with optional filters
*/
export function getStopTimeUpdates(
db: Database,
filters: StopTimeUpdateFilters = {},
stalenessThreshold: number = 120
): StopTimeUpdate[] {
const { tripId, stopId, stopSequence, limit } = filters;

const conditions: string[] = [];
const params: (string | number)[] = [];

// Filter by trip_id
if (tripId) {
conditions.push('trip_id = ?');
params.push(tripId);
}

// Filter by stop_id
if (stopId) {
conditions.push('stop_id = ?');
params.push(stopId);
}

// Filter by stop_sequence
if (stopSequence !== undefined) {
conditions.push('stop_sequence = ?');
params.push(stopSequence);
}

// Staleness filter (always applied)
const now = Math.floor(Date.now() / 1000);
const staleThreshold = now - stalenessThreshold;
conditions.push('rt_last_updated >= ?');
params.push(staleThreshold);

// Build query
let sql = 'SELECT * FROM rt_stop_time_updates';
if (conditions.length > 0) {
sql += ' WHERE ' + conditions.join(' AND ');
}
sql += ' ORDER BY trip_id, stop_sequence';

if (limit) {
sql += ' LIMIT ?';
params.push(limit);
}

// Execute query
const stmt = db.prepare(sql);
if (params.length > 0) {
stmt.bind(params);
}

const stopTimeUpdates: StopTimeUpdate[] = [];
while (stmt.step()) {
const row = stmt.getAsObject() as Record<string, unknown>;
stopTimeUpdates.push(parseStopTimeUpdate(row));
}

stmt.free();
return stopTimeUpdates;
}

/**
* Get stop time updates for a specific trip
*/
export function getStopTimeUpdatesByTripId(
db: Database,
tripId: string,
stalenessThreshold: number = 120
): StopTimeUpdate[] {
return getStopTimeUpdates(db, { tripId }, stalenessThreshold);
}

/**
* Get all stop time updates without staleness filtering (for debugging)
* Returns extended type with trip_id and rt_last_updated for debugging purposes
*/
export function getAllStopTimeUpdates(db: Database): StopTimeUpdateWithMetadata[] {
const sql = 'SELECT * FROM rt_stop_time_updates ORDER BY trip_id, stop_sequence';
const stmt = db.prepare(sql);

const stopTimeUpdates: StopTimeUpdateWithMetadata[] = [];
while (stmt.step()) {
const row = stmt.getAsObject() as Record<string, unknown>;
stopTimeUpdates.push(parseStopTimeUpdateWithMetadata(row));
}

stmt.free();
return stopTimeUpdates;
}
Loading