Revert "JSX: clean up dead WKWebView fallback paths"

This reverts commit 5f19e48172.
This commit is contained in:
Trey t
2026-04-11 13:55:12 -05:00
parent 5f19e48172
commit 1e74552184
3 changed files with 451 additions and 66 deletions

View File

@@ -21,13 +21,6 @@ hardware against the live production site in April 2026.
4. Driving the on-page UI to submit the search form is a dead end in
**WKWebView** (but works in Playwright) because of a subtle trusted-event
issue described below.
5. **Environment caveat**: the in-page `fetch()` POST (#3) works on a
**real iOS device** but **fails in the iOS Simulator**. Simulator
WebKit runs on macOS's CFNetwork stack, whose TLS/H2 fingerprint
Akamai's per-endpoint protection tier on `/search/simple` treats as
untrusted. Other `api.jsx.com` endpoints (tokens, bootstrap graph
calls, low-fare estimate) work from both. **Test on device; develop
with the low-fare fallback in the simulator.**
The working pattern is therefore:
@@ -35,7 +28,7 @@ The working pattern is therefore:
Wait for SPA bootstrap (station buttons exist, token call finishes)
Read token from sessionStorage
POST /api/nsk/v4/availability/search/simple ← from inside the page
Parse the response (real device only)
Parse the response
---
@@ -353,12 +346,6 @@ via a direct API call whose request shape matches exactly what
This is what `Flights/Services/JSXWebViewFetcher.swift` step 17 does.
**Important environment caveat:** this workaround works on a **real iOS
device** but **fails in the iOS Simulator**. See the anti-bot surface
table below for the specific failure modes. Develop with a real device
plugged in, or accept the low-fare day-total fallback when running
against the simulator.
---
## Anti-bot surface (Akamai)
@@ -370,9 +357,8 @@ JSX fronts `api.jsx.com` with Akamai Bot Manager. Observed behavior:
| Plain `curl`, `fetch` from Node, any external HTTP client | Blocked. Almost all endpoints return HTML challenge page or Akamai error. |
| Playwright's built-in `chromium.launch()` (both bundled chromium and `channel: "chrome"`) | GET requests succeed, but `POST /availability/search/simple` specifically returns `ERR_HTTP2_PROTOCOL_ERROR`. Playwright injects enough automation bits for Akamai to flag the TLS/H2 fingerprint. |
| Real Chrome spawned as a plain process + Playwright attached via `chromium.connectOverCDP()` | **Works reliably.** Chrome has the expected fingerprint and Playwright is only driving it via CDP, not altering it. |
| **WKWebView on iOS Simulator** | GETs to `api.jsx.com` succeed (`/lowfare/estimate`, `/nsk/v1/token`, `/graph/*`). **`POST /availability/search/simple` fails** with `TypeError: Load failed` (in-page `fetch()`/`XHR`) or `NSURLErrorNetworkConnectionLost` (-1005) (native `URLSession` with cookies forwarded from the WKWebView store). iOS Simulator WebKit runs against macOS's CFNetwork stack, whose TLS/H2 fingerprint Akamai's per-endpoint protection tier treats as untrusted for this specific endpoint. No configuration (custom UA, XHR vs fetch, credentials mode, content type, cookie replay into URLSession) makes it work. |
| **WKWebView on real iOS hardware** | **Works.** Direct `POST /availability/search/simple` from inside a loaded `jsx.com` page via `fetch()` returns `status=200` with the full per-flight availability payload. Real iOS WebKit has the iOS-specific network stack whose TLS/H2 fingerprint Akamai accepts. The fetcher in `Flights/Services/JSXWebViewFetcher.swift` step 17 does exactly this and produces correct per-class seat counts. |
| WKWebView with UI-driven Find Flights click (any platform) | Fails for an unrelated reason: WKWebView's synthetic `MouseEvent` has `isTrusted === false`, and JSX's custom datepicker only commits its day-cell selection to the Angular form model on a trusted user gesture. Result: the input visually shows the selected date but the underlying model stays null, `form.invalid === true`, and Angular's `search()` silently returns without firing a request. The direct-fetch POST bypasses this entirely. |
| WKWebView on macOS / iOS | GET requests succeed. Direct `POST /availability/search/simple` from inside a loaded `jsx.com` page via `fetch()` also succeeds. The browser session's cookies and TLS fingerprint are trusted. |
| WKWebView with UI-driven Find Flights click | Fails — but for an unrelated reason (the trusted-events trap above). Angular never fires the POST in the first place, so Akamai never sees it. |
Observations: