After a long debug, the working approach for fetching per-flight
availability from JSX in WKWebView is a direct fetch() POST to
/api/nsk/v4/availability/search/simple from inside the loaded
jsx.com page context, using the anonymous auth token from
sessionStorage["navitaire.digital.token"]. Confirmed end-to-end on
a real iOS device: returns status 200 with the full 14 KB payload,
parses into per-flight JSXFlight objects with correct per-class
seat counts (e.g. XE286 = 6 seats = 3 Hop-On + 3 All-In).
Architecture:
- JSXWebViewFetcher drives the jsx.com SPA through 18 step-by-step
verified phases: create WKWebView, navigate, install passive
PerformanceObserver, dismiss Osano, select One Way, open origin
station picker and select, open destination picker and select,
open depart datepicker (polling for day cells to render), click
the target day cell by aria-label, click the picker's DONE
button to commit, force Angular form revalidation, then fire
the POST directly from the page context.
- The POST attempt is wrapped in a fallback chain: if the direct
fetch fails, try walking __ngContext__ to find the minified-name
flight-search component ("Me" in the current build) by shape
(beginDate + origin + destination + search method) and call
search() directly, then poll Angular's own state for the parsed
availability response. Final fallback is a direct GET to
/api/nsk/v1/availability/lowfare/estimate which returns a
day-total count when all per-flight paths fail.
- JSXSearchResult.flights contains one JSXFlight per unique
journey in data.results[].trips[].journeysAvailableByMarket,
with per-class breakdowns joined against data.faresAvailable.
- Every step has an action + one or more post-condition
verifications that log independently. Step failures dump
action data fields, page state, error markers, and any
PerformanceObserver resource entries so the next iteration
has ground truth, not guesses.
Known environment limitation:
- iOS Simulator CANNOT reach POST /availability/search/simple.
Simulator WebKit runs against macOS's CFNetwork stack, which
Akamai's per-endpoint protection tier treats as a different
TLS/H2 client from real iOS Safari. Every in-page or native
request (fetch, XHR, URLSession with cookies from the WKWebView
store) fails with TypeError: Load failed / error -1005 on that
specific endpoint. Other api.jsx.com endpoints (token, graph/*,
lowfare/estimate) work fine from the simulator because they're
in a looser Akamai group. On real iOS hardware the POST goes
through with status 200.
AirlineLoadService.fetchJSXLoad now threads departureTime into the
XE-specific path so the caller can disambiguate multiple flights
with the same number. Match order: (1) exact flight number match
if unique, (2) departureTime tie-break if multiple, (3) first
same-number flight as last resort. Each branch logs which match
strategy won so caller ambiguity shows up in the log.
FlightLoadDetailView logs full tap metadata (id, flight number,
extracted number, departureTime, route) and received load
(flight number, total available, total capacity) so the
fetch-to-display data flow is traceable end-to-end per tap.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
15 KiB
15 KiB