Skip to content

Releases: sysdevrun/gtfs-sqljs

v0.9.0

Choose a tag to compare

@chtitux chtitux released this 26 Aug 18:54
  • Breaking: drop Node.js 18 supportengines.node is now >=20.0.0. The await import('node:crypto') fallback for Node 18's missing globalThis.crypto is removed, which also fixes vite emitting Module "crypto" has been externalized for browser compatibility for the core gtfs-sqljs entry when bundling for the browser. Node 18 has been EOL since April 2025.
  • Add getCalendars(filters?) — bulk read of the calendar table (filters: serviceId single value or array, limit). getCalendarByServiceId() remains as a convenience wrapper. (#46)
  • getCalendarDates() no longer requires a service id — it now accepts an optional filters object (serviceId, date, limit) and returns the whole calendar_dates table when called without arguments. The legacy getCalendarDates('SERVICE_ID') string form is still accepted. (#46)
  • Add getFeedInfo() — returns the feed_info rows (an array, since the spec allows multiple rows). (#46)
  • Add getFrequencies(filters?) — read the frequencies table (filters: tripId single value or array, limit). exact_times: 0 is preserved. (#46)
  • Fix: ProgressInfo and ProgressCallback types are now exported from the package entry point. (#45)
  • README: document that vite fs/path/crypto warnings pointing at sql.js/dist/sql-wasm.js come from sql.js's UMD build, are harmless in the browser, and cannot be fixed from gtfs-sqljs.
  • CI: test matrix is now Node 20.x / 22.x / 24.x; GitHub Actions bumped across workflows.

v0.8.0

Choose a tag to compare

@chtitux chtitux released this 14 Jul 19:49
  • Add getTripSchedules(filters) — display-ready trip schedules with everything pre-computed: seconds since the start of the service day (past-midnight times > 86400 supported), unix epochs computed in the agency's timezone (DST-safe "noon minus 12h" rule, zero-dependency via Intl), realtime delays resolved per the GTFS-RT spec (delay ↔ absolute time cross-computed, arrival ↔ departure borrowed, delay propagated to subsequent stops until the next update, trip-level delay fallback, stop_id-only matching), SKIPPED/NO_DATA/canceled flags, display_epoch (departure, or arrival at the terminus; displayMode: 'arrival' flips it), and stop names joined in. Batched internally — one trip or a whole route's day costs the same five indexed queries.
  • Add pure time/realtime helpers (exported, usable without a database): parseGtfsTime, gtfsTimeToEpoch, serviceDayStartEpoch, and resolveRealtime — the resolution engine behind getTripSchedules.
  • Update the website demo's stop-times view to getTripSchedules (timezone-correct times, realtime departures with strikethrough schedule, canceled/skipped indicators, no more per-stop queries). Add examples/trip-schedules.ts.
  • Breaking: split ScheduleRelationship into TripScheduleRelationship and StopTimeScheduleRelationship. The single enum conflated two protobuf enums with different numeric values: at stop level, SKIPPED is 1 and NO_DATA is 2 per the GTFS-RT spec, but the old enum decoded 1 as ADDED and defined SKIPPED = 4 (a value that never occurs in feeds). StopTimeUpdate.schedule_relationship and StopTimeRealtime.schedule_relationship are now typed as StopTimeScheduleRelationship; trip-level fields as TripScheduleRelationship. ScheduleRelationship remains as a deprecated alias of TripScheduleRelationship.
  • Fix: GTFS-RT loader dropped legitimate zero values. delay: 0 (explicitly on time), stop_sequence: 0, uncertainty: 0, bearing: 0, speed: 0, and current_status: INCOMING_AT (0) were coerced to NULL; now preserved.
  • Fix: stop time updates identified by stop_id only (without stop_sequence, allowed by the spec) are now stored reliably: the rt_stop_time_updates table no longer has a (trip_id, stop_sequence) primary key and gained a (trip_id, stop_sequence) index instead.

v0.7.0

Choose a tag to compare

@chtitux chtitux released this 21 Apr 02:17
  • Add buildGraph(tripIds) method and Graph / EdgeTrip / EdgeData types. Builds a directed stop-to-stop graph from the given trips, with each deduplicated edge carrying the list of originating trips (plus route_id and direction_id). Handles non-contiguous stop_sequence values via LEAD(). Helpers edgeCount() and edges() are also exported.

v0.6.0

Choose a tag to compare

@chtitux chtitux released this 20 Apr 18:18

Breaking changes — pluggable database adapter

The library now talks to a small async GtfsDatabase interface. sql.js becomes one adapter among others (better-sqlite3 ships in the box; op-sqlite / expo-sqlite / … pluggable by the user). Three things change at every call site: (1) query methods return Promise<T>, (2) an adapter is required, (3) sql.js is an optional peer dependency — you install it yourself.

See the full migration write-up in README and the Usage Guide.

New features

  • New src/adapters/types.ts public surface: GtfsDatabase, GtfsStatement, GtfsDatabaseAdapter, SqlValue, Row, ExportNotSupportedError.
  • sql.js adapter at subpath gtfs-sqljs/adapters/sql-js (exports createSqlJsAdapter, wrapSqlJsDatabase). The core module no longer imports sql.js.
  • better-sqlite3 adapter at subpath gtfs-sqljs/adapters/better-sqlite3 (exports wrapBetterSqlite3, createBetterSqlite3Adapter). First-class Node / file-backed path; the adapter is the only file in the repo that imports better-sqlite3, so projects that do not reference this subpath never pull in the native module. Exercised by tests/e2e-better-sqlite3.test.ts on every CI run.
  • Cache layer now catches ExportNotSupportedError from adapters that cannot serialize in-memory and logs a warning instead of failing the load; file-backed drivers persist their own DB on disk.

Performance

  • Ingestion is ~35-45% faster on medium-to-large feeds: ASTUCE (Rouen, ~430k stop_times rows) drops from ~2650 ms to ~1670 ms; Car Jaune from ~312 ms to ~188 ms. Wins come from parsing each CSV only once (progress totals now use a fast newline-based row-count estimate), loading rows as positional arrays instead of per-row objects, and reusing a single prepared INSERT per table instead of re-preparing a multi-row statement per 1000-row batch.
  • Dropped the bulk-load PRAGMA block (synchronous, journal_mode, temp_store, cache_size, locking_mode) from ingestion. Benchmarked aggregate effect on sql.js is within noise (≤1%); removing them simplifies the code and unblocks the pluggable adapter.

Behaviour changes

  • ProgressInfo.totalRows is now an estimate based on CSV line count — typically exact, but may differ by a few rows per file in edge cases (e.g. trailing blank lines). For a precise post-ingest row count, query the database directly with COUNT(*).

v0.4.1

Choose a tag to compare

@chtitux chtitux released this 11 Mar 18:43
  • Add Claude Code skill file with API reference, usage examples, WASM setup instructions, and Web Worker guidance for LLM code agents

v0.4.0

Choose a tag to compare

@chtitux chtitux released this 09 Mar 17:01

Breaking changes

  • fromZip() no longer reads local file paths in Node.js. Read the file yourself and use fromZipData() instead
  • fetchRealtimeData() / loadRealtimeData() no longer read local file paths in Node.js. Use the new loadRealtimeDataFromBuffers() method with pre-read data instead

New features

  • Add loadRealtimeDataFromBuffers(buffers) method to GtfsSqlJs for loading GTFS-RT data from pre-loaded protobuf Uint8Array buffers without fetching

Internal improvements

  • Remove all Node.js fs imports and isNodeEnvironment() checks, making the published module fully platform-neutral

v0.3.1

Choose a tag to compare

@chtitux chtitux released this 08 Mar 20:39
  • Make route_short_name and route_long_name optional, matching GTFS spec (conditionally required: at least one must be present)
  • Allow transfers.transfer_type to be empty (defaults to 0 per GTFS spec)
  • Make stop_times.arrival_time and stop_times.departure_time optional for intermediate stops per GTFS spec
  • Make stops.stop_lat and stops.stop_lon optional for generic nodes (location_type=3) and boarding areas (location_type=4) per GTFS spec

v0.3.0

Choose a tag to compare

@chtitux chtitux released this 08 Mar 15:15

Breaking changes

  • fromZip() now only accepts string (path or URL). If you were passing ArrayBuffer or Uint8Array, use the new fromZipData() method instead
  • skipFiles behavior change: files listed in skipFiles are now skipped during ZIP extraction entirely (not just excluded from DB loading), improving performance for large feeds

New features

  • Add GtfsSqlJs.fromZipData(zipData, options?, source?) static method for loading from pre-loaded ZIP data (ArrayBuffer or Uint8Array)
  • Extract only known GTFS files from ZIP, skipping unrecognized files for faster extraction

Internal improvements

  • Simplify checksum to use global crypto.subtle directly (drop dynamic import('crypto') fallback)
  • Extract shared isNodeEnvironment() helper into utils/env.ts
  • Replace unknown[], widening casts, and non-null assertions with proper types across query files and loaders

v0.2.2

Choose a tag to compare

@chtitux chtitux released this 26 Feb 18:12
  • Fix getStopTimes and getTrips returning all results instead of none when called with a date outside the feed's validity range
  • Allow fare_attributes.transfers to be empty (NULL), meaning unlimited transfers per GTFS spec

v0.2.0

Choose a tag to compare

@chtitux chtitux released this 26 Feb 12:13
  • Add pickupType and dropOffType filters to getStopTimes, with COALESCE handling so NULL (empty) is treated as 0 (regular) per GTFS spec
  • Add PickupDropOffType enum for GTFS static pickup/drop-off type values
  • Replace getCalendarByServiceId(serviceId) with getCalendars(filters?) for consistent filter-based API
  • Fix README: replace non-existent getStopById with getStops({ stopId })