@@ -270,11 +270,76 @@ export async function measureLatency(gw: GatewayInfo): Promise<number | null> {
270270 return rttMs ;
271271}
272272
273+ /**
274+ * TEMP iOS discovery diagnostic. Logs every discovery request's network outcome
275+ * (without reading the body, so it can't disturb the real read) so we can tell,
276+ * from the device console, whether the failure is the fetch itself (ATS /
277+ * reachability) or something after it. Tagged [CVPN-DISCOVERY]. Remove once iOS
278+ * discovery is confirmed working.
279+ */
280+ const diagFetch : typeof fetch = async ( input , init ) => {
281+ const url =
282+ typeof input === 'string' ? input : input instanceof URL ? input . href : ( input as Request ) . url ;
283+ try {
284+ const res = await fetch ( input as Parameters < typeof fetch > [ 0 ] , init ) ;
285+ // eslint-disable-next-line no-console
286+ console . warn ( `[CVPN-DISCOVERY] ${ res . status } ${ url } ` ) ;
287+ return res ;
288+ } catch ( e ) {
289+ // eslint-disable-next-line no-console
290+ console . warn ( `[CVPN-DISCOVERY] FETCH_ERR ${ url } :: ${ String ( ( e as Error ) ?. message ?? e ) } ` ) ;
291+ throw e ;
292+ }
293+ } ;
294+
295+ /**
296+ * TEMP: independently fetch one gateway's /v1/info and log status + how the body
297+ * reads (arrayBuffer vs text) — `fetchSigned` uses res.arrayBuffer(), which is a
298+ * known-fragile path on RN iOS. Runs off the first Flux-API candidate so it never
299+ * touches the real discovery reads. Non-blocking; failures only log.
300+ */
301+ async function diagArrayBufferSelfTest ( ) : Promise < void > {
302+ try {
303+ const specs = bundledSpecs ( ) ;
304+ if ( specs . length === 0 ) return ;
305+ const locRes = await fetch ( `https://api.runonflux.io/apps/location/${ specs [ 0 ] } ` ) ;
306+ const loc = ( await locRes . json ( ) ) as { data ?: { ip ?: string } [ ] } ;
307+ const ip = loc . data ?. [ 0 ] ?. ip ?. split ( ':' ) [ 0 ] ;
308+ if ( ! ip ) {
309+ // eslint-disable-next-line no-console
310+ console . warn ( '[CVPN-DISCOVERY] selftest: no candidate IP from Flux API' ) ;
311+ return ;
312+ }
313+ const url = `http://${ ip } :51821/v1/info` ;
314+ const res = await fetch ( url ) ;
315+ let ab = 'n/a' ;
316+ let txt = 'n/a' ;
317+ try {
318+ ab = String ( ( await res . clone ( ) . arrayBuffer ( ) ) . byteLength ) ;
319+ } catch ( e ) {
320+ ab = `ERR ${ String ( ( e as Error ) ?. message ?? e ) } ` ;
321+ }
322+ try {
323+ txt = String ( ( await res . text ( ) ) . length ) ;
324+ } catch ( e ) {
325+ txt = `ERR ${ String ( ( e as Error ) ?. message ?? e ) } ` ;
326+ }
327+ // eslint-disable-next-line no-console
328+ console . warn (
329+ `[CVPN-DISCOVERY] selftest ${ url } status=${ res . status } arrayBuffer=${ ab } text=${ txt } ` ,
330+ ) ;
331+ } catch ( e ) {
332+ // eslint-disable-next-line no-console
333+ console . warn ( `[CVPN-DISCOVERY] selftest FAILED :: ${ String ( ( e as Error ) ?. message ?? e ) } ` ) ;
334+ }
335+ }
336+
273337/**
274338 * Resolve the live gateway fleet from the Flux network, using the bundled
275339 * signed snapshot for spec names + seed nodes. Networking is delegated to core.
276340 * POC: disk-cache tier of the discovery order is not implemented here.
277341 */
278342export async function discoverFleet ( ) : Promise < GatewayInfo [ ] > {
279- return discoverGateways ( bundledSpecs ( ) , { nodes : [ ...seedNodeIps ( ) ] } ) ;
343+ void diagArrayBufferSelfTest ( ) ; // TEMP: fire-and-forget iOS diagnostic
344+ return discoverGateways ( bundledSpecs ( ) , { nodes : [ ...seedNodeIps ( ) ] , fetchImpl : diagFetch } ) ;
280345}
0 commit comments