diff --git a/client_sdks/devconnect-react-native/src/client.ts b/client_sdks/devconnect-react-native/src/client.ts index a44fdff..4e2b160 100644 --- a/client_sdks/devconnect-react-native/src/client.ts +++ b/client_sdks/devconnect-react-native/src/client.ts @@ -490,6 +490,7 @@ export class DevConnect { * own duplicate report if the count is > 0. */ private fetchInFlight: Map = new Map(); + private fetchStackCount = 0; private constructor(config: DevConnectConfig & { resolvedHost: string }) { this.config = { @@ -920,8 +921,38 @@ export class DevConnect { dc.fetchInFlight.set(fetchKey, (dc.fetchInFlight.get(fetchKey) ?? 0) + 1); dc.send('client:network:request_start', { requestId, method, url, startTime, requestHeaders: reqHeaders, requestBody, source, via: 'fetch' }); + let responsePromise: Promise; + dc.fetchStackCount++; try { - const response = await originalFetch(input, init); + responsePromise = originalFetch(input, init); + } catch (err: any) { + dc.send('client:network:request_complete', { + requestId, + method, + url, + statusCode: 0, + startTime, + endTime: Date.now(), + duration: Date.now() - startTime, + requestHeaders: reqHeaders, + requestBody, + error: err?.message ?? String(err), + source, + via: 'fetch', + }); + const currentCount = dc.fetchInFlight.get(fetchKey) ?? 0; + if (currentCount <= 1) { + dc.fetchInFlight.delete(fetchKey); + } else { + dc.fetchInFlight.set(fetchKey, currentCount - 1); + } + throw err; + } finally { + dc.fetchStackCount--; + } + + try { + const response = await responsePromise; const clone = response.clone(); let responseBody: any; try { const text = await clone.text(); try { responseBody = JSON.parse(text); } catch (_) { responseBody = text; } } catch (_) {} @@ -968,6 +999,7 @@ export class DevConnect { function PatchedXHR(this: any) { const xhr = new OriginalXHR(); + const isFetchXhr = dc.fetchStackCount > 0; const requestId = generateId(); let method = 'GET', url = '', startTime = 0; const reqHeaders: Record = {}; @@ -1006,7 +1038,7 @@ export class DevConnect { // Skip the start report if the fetch interceptor already // covers this call (see handleLoadEnd for the matching skip). const xhrKey = `${method}\0${url}`; - const isFetchRequest = (dc.fetchInFlight.get(xhrKey) ?? 0) > 0; + const isFetchRequest = isFetchXhr || (dc.fetchInFlight.get(xhrKey) ?? 0) > 0; if (!isFetchRequest) { dc.send('client:network:request_start', { requestId, method, url, startTime, requestHeaders: reqHeaders, requestBody, source: classifyUrl(url), via: 'xhr' }); } @@ -1021,7 +1053,7 @@ export class DevConnect { // XHR path) with different requestIds, which the server // cannot merge downstream. const xhrKey = `${method}\0${url}`; - const isFetchRequest = (dc.fetchInFlight.get(xhrKey) ?? 0) > 0; + const isFetchRequest = isFetchXhr || (dc.fetchInFlight.get(xhrKey) ?? 0) > 0; if (isFetchRequest) { return; }