+42
-9
@@ -112,12 +112,13 @@ import { defaultEndpoints, subwayRouteColors } from "./src/defaults"; | ||
| async arrivals(query: SubwayArrivalQuery): Promise<Arrival[]> { | ||
| const normalizedQuery = normalizeSubwayArrivalQuery(query); | ||
| if (this.mta.hostedApiEnabled()) { | ||
| return this.mta.hostedJson<Arrival[]>("/api/v1/subway/arrivals", query); | ||
| return this.mta.hostedJson<Arrival[]>("/api/v1/subway/arrivals", normalizedQuery); | ||
| } | ||
| await this.mta.ready(); | ||
| const routeIds = query.route ? [normalizeRouteId(query.route)] : Object.keys(this.mta.endpoints.subwayFeeds); | ||
| const routeIds = normalizedQuery.route ? [normalizeRouteId(normalizedQuery.route)] : Object.keys(this.mta.endpoints.subwayFeeds); | ||
| const feeds = [...new Set(routeIds.map((route) => this.feedForRoute(route)))]; | ||
| const stopIds = this.mta.static.getStopIdsForQuery(query.stopId); | ||
| if (this.mta.static.hasStaticData("subway") && !this.mta.static.getStopOrParent(query.stopId)) { | ||
| throw new UnknownStopError(query.stopId); | ||
| const stopIds = this.mta.static.getStopIdsForQuery(normalizedQuery.stopId); | ||
| if (this.mta.static.hasStaticData("subway") && !this.mta.static.getStopOrParent(normalizedQuery.stopId)) { | ||
| throw new UnknownStopError(normalizedQuery.stopId); | ||
| } | ||
@@ -128,3 +129,3 @@ const arrivals: Arrival[] = []; | ||
| const feed = await this.mta.realtimeFeed(feedUrl); | ||
| arrivals.push(...this.arrivalsFromFeed(feed, stopIds, query)); | ||
| arrivals.push(...this.arrivalsFromFeed(feed, stopIds, normalizedQuery)); | ||
| } | ||
@@ -175,2 +176,3 @@ | ||
| const stop = this.mta.static.getStopOrParent(stopId) ?? fallbackStop(query.stopId); | ||
| const headsign = staticTrip?.headsign ?? undefined; | ||
| arrivals.push({ | ||
@@ -181,3 +183,5 @@ mode: "subway", | ||
| direction, | ||
| headsign: staticTrip?.headsign ?? undefined, | ||
| destination: headsign, | ||
| displayDirection: displayDirection(headsign, direction), | ||
| headsign, | ||
| arrivalTime: new Date(event.time * 1000).toISOString(), | ||
@@ -230,2 +234,3 @@ departureTime: update.departure?.time ? new Date(update.departure.time * 1000).toISOString() : undefined, | ||
| const stop = this.mta.static.getStop(String(call.StopPointRef ?? query.stopId)) ?? fallbackStop(query.stopId); | ||
| const headsign = stringOrUndefined(mvj.DestinationName); | ||
| return { | ||
@@ -236,3 +241,5 @@ mode: "bus", | ||
| direction: "unknown", | ||
| headsign: stringOrUndefined(mvj.DestinationName), | ||
| destination: headsign, | ||
| displayDirection: displayDirection(headsign, "unknown"), | ||
| headsign, | ||
| arrivalTime: new Date(expected).toISOString(), | ||
@@ -383,9 +390,35 @@ minutes: Math.max(0, Math.round((Date.parse(expected) - now) / 60_000)), | ||
| function normalizeDirection(direction: Direction | "uptown" | "downtown" | undefined): Direction | undefined { | ||
| function normalizeSubwayArrivalQuery(query: SubwayArrivalQuery): SubwayArrivalQuery { | ||
| const route = query.route ?? routeFromLStopId(query.stopId); | ||
| return { | ||
| ...query, | ||
| route, | ||
| direction: normalizeDirection(query.direction, route), | ||
| }; | ||
| } | ||
| function normalizeDirection( | ||
| direction: Direction | "uptown" | "downtown" | undefined, | ||
| route?: string, | ||
| ): Direction | undefined { | ||
| if (!direction) return undefined; | ||
| if (direction === "uptown") return "north"; | ||
| if (direction === "downtown") return "south"; | ||
| if (route && normalizeRouteId(route) === "L") { | ||
| if (direction === "east") return "south"; | ||
| if (direction === "west") return "north"; | ||
| } | ||
| return direction; | ||
| } | ||
| function routeFromLStopId(stopId: string) { | ||
| return /^L\d{2}[NS]?$/.test(stopId.toUpperCase().trim()) ? "L" : undefined; | ||
| } | ||
| function displayDirection(headsign: string | undefined, direction: Direction) { | ||
| if (headsign) return `toward ${headsign}`; | ||
| if (direction === "unknown") return undefined; | ||
| return `${direction}bound`; | ||
| } | ||
| function routeWithFallback(route: Route | undefined, routeId: string): Route { | ||
@@ -392,0 +425,0 @@ return ( |
+1
-1
| { | ||
| "name": "mta-js", | ||
| "version": "2.1.0", | ||
| "version": "2.1.1", | ||
| "description": "A TypeScript client for MTA realtime feeds and the hosted MTA API.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+21
-0
@@ -31,2 +31,23 @@ # mta-js | ||
| Arrival rows include display-oriented fields when destination metadata is | ||
| available: | ||
| ```ts | ||
| for (const arrival of lTrain) { | ||
| const direction = | ||
| arrival.displayDirection ?? | ||
| (arrival.destination | ||
| ? `toward ${arrival.destination}` | ||
| : arrival.headsign | ||
| ? `toward ${arrival.headsign}` | ||
| : arrival.direction); | ||
| console.log(`${arrival.route.shortName} ${direction} from ${arrival.stop.name}`); | ||
| } | ||
| ``` | ||
| NYC Subway realtime feeds use NYCT's `north`/`south` stop directions, even on | ||
| east-west lines. For the L train, `mta-js` accepts rider-facing `east`/`west` | ||
| aliases and maps them to the underlying feed directions. | ||
| When `apiKey` is present, `mta-js` sends requests to the hosted API at | ||
@@ -33,0 +54,0 @@ `https://www.mtaapi.dev` by default. Override `apiBaseUrl` for tests or private |
+2
-0
@@ -153,2 +153,4 @@ import type { | ||
| direction: Direction; | ||
| destination?: string; | ||
| displayDirection?: string; | ||
| headsign?: string; | ||
@@ -155,0 +157,0 @@ arrivalTime: string; |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
139087
1.33%8879
0.36%116
22.11%17
-5.56%