From cd6fb7cf91deb6e55de8236f7105132fc060766c Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 1 Jun 2026 16:47:39 -0500 Subject: [PATCH] fix(interceptor): guard XHR double-wrap and thread currentAttr into regex fallback --- src/interceptor.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/interceptor.js b/src/interceptor.js index 4e0d46a..f8fc4fe 100644 --- a/src/interceptor.js +++ b/src/interceptor.js @@ -14,7 +14,7 @@ export function startRequestInterceptor({ prevValue, currentAttr, onTrain, onPar let warnedFor = null; const handle = (text, url) => { - const parsed = parseTrainResponse(text, url); + const parsed = parseTrainResponse(text, url, currentAttr); if (!parsed) { if (warnedFor !== url) { warnedFor = url; @@ -22,7 +22,7 @@ export function startRequestInterceptor({ prevValue, currentAttr, onTrain, onPar } return; } - if (parsed.attr !== currentAttr) return; + if (parsed.attr && currentAttr && parsed.attr !== currentAttr) return; const delta = parsed.newValue - lastValue; lastValue = parsed.newValue; if (delta > 0) onTrain({ attr: parsed.attr, delta, ts: Date.now() }); @@ -36,7 +36,7 @@ export function startRequestInterceptor({ prevValue, currentAttr, onTrain, onPar }; } -function parseTrainResponse(text, url) { +function parseTrainResponse(text, url, fallbackAttr) { // Try JSON try { const j = JSON.parse(text); @@ -48,20 +48,21 @@ function parseTrainResponse(text, url) { } } catch { /* not JSON */ } - // Fallback: scan text for a number formatted like an attribute. + // Regex fallback: scan text for a number formatted like an attribute. + // If we find one and the caller passed a fallbackAttr, use it; otherwise + // the caller can choose to ignore the result. const m = text.match(/(\d{1,3}(?:,\d{3})+|\d{4,})/); if (m) { const newValue = parseInt(m[1].replace(/,/g, ''), 10); if (Number.isFinite(newValue) && newValue > 0) { - // Without a confirmed attr, fall back to whatever currentAttr - // the caller passed in. - return { newValue, attr: null }; + return { newValue, attr: fallbackAttr || null }; } } return null; } function wrapXhr(handle) { + if (XMLHttpRequest.prototype.send.__tatWrapped) return; const origOpen = XMLHttpRequest.prototype.open; const origSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url) { @@ -74,6 +75,7 @@ function wrapXhr(handle) { }); return origSend.apply(this, arguments); }; + XMLHttpRequest.prototype.send.__tatWrapped = true; } function wrapFetch(handle) {