@newtalaria/browser 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -10
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/index.js +292 -79
- package/dist/index.js.map +2 -2
- package/dist/replay/hooks.d.ts +61 -8
- package/dist/replay/hooks.d.ts.map +1 -1
- package/dist/replay/privacy.d.ts +1 -1
- package/dist/replay/privacy.d.ts.map +1 -1
- package/dist/sdk_meta.d.ts +1 -1
- package/dist/talaria.browser.iife.js +292 -79
- package/dist/talaria.browser.iife.js.map +2 -2
- package/dist/types.d.ts +18 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/browser_context.d.ts +10 -0
- package/dist/utils/browser_context.d.ts.map +1 -1
- package/dist/utils/network_error.d.ts +16 -0
- package/dist/utils/network_error.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -98,9 +98,46 @@ function isSdkInternalNoise(opts) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// src/utils/browser_context.ts
|
|
101
|
+
var BOT_RULES = [
|
|
102
|
+
{ name: "Baiduspider", re: /Baiduspider/i },
|
|
103
|
+
{ name: "Googlebot", re: /Googlebot/i },
|
|
104
|
+
{ name: "Bingbot", re: /bingbot/i },
|
|
105
|
+
{ name: "DuckDuckBot", re: /DuckDuckBot/i },
|
|
106
|
+
{ name: "YandexBot", re: /Yandex(Bot|Images)/i },
|
|
107
|
+
{ name: "Applebot", re: /Applebot/i },
|
|
108
|
+
{ name: "facebookexternalhit", re: /facebookexternalhit|Facebot/i },
|
|
109
|
+
{ name: "Twitterbot", re: /Twitterbot/i },
|
|
110
|
+
{ name: "LinkedInBot", re: /LinkedInBot/i },
|
|
111
|
+
{ name: "Slackbot", re: /Slackbot/i },
|
|
112
|
+
{ name: "Discordbot", re: /Discordbot/i },
|
|
113
|
+
{ name: "Bytespider", re: /Bytespider/i },
|
|
114
|
+
{ name: "PetalBot", re: /PetalBot/i },
|
|
115
|
+
{ name: "SemrushBot", re: /SemrushBot/i },
|
|
116
|
+
{ name: "AhrefsBot", re: /AhrefsBot/i },
|
|
117
|
+
{ name: "DotBot", re: /DotBot/i },
|
|
118
|
+
{ name: "GPTBot", re: /GPTBot/i },
|
|
119
|
+
{ name: "ClaudeBot", re: /ClaudeBot|anthropic-ai/i },
|
|
120
|
+
{ name: "Amazonbot", re: /Amazonbot/i },
|
|
121
|
+
{ name: "Sogou", re: /Sogou/i },
|
|
122
|
+
// Generic last — still mark as bot without inventing a browser name.
|
|
123
|
+
{ name: "bot", re: /\b(?:bot|crawler|spider|slurp)\b/i }
|
|
124
|
+
];
|
|
125
|
+
function detectBot(ua) {
|
|
126
|
+
if (!ua) return { bot: false };
|
|
127
|
+
for (const rule2 of BOT_RULES) {
|
|
128
|
+
if (rule2.re.test(ua)) {
|
|
129
|
+
return {
|
|
130
|
+
bot: true,
|
|
131
|
+
botName: rule2.name === "bot" ? void 0 : rule2.name
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { bot: false };
|
|
136
|
+
}
|
|
101
137
|
function parseBrowserContext(ua = typeof navigator !== "undefined" ? navigator.userAgent : "", language = typeof navigator !== "undefined" ? navigator.language : "") {
|
|
102
138
|
const userAgent = ua || "";
|
|
103
139
|
const lang = language || "";
|
|
140
|
+
const botInfo = detectBot(userAgent);
|
|
104
141
|
let name = "unknown";
|
|
105
142
|
let version = "";
|
|
106
143
|
const rules = [
|
|
@@ -111,18 +148,20 @@ function parseBrowserContext(ua = typeof navigator !== "undefined" ? navigator.u
|
|
|
111
148
|
{ name: "Chrome", re: /(?:Chrome|CriOS)\/([\d.]+)/ },
|
|
112
149
|
{ name: "Safari", re: /Version\/([\d.]+).*Safari/ }
|
|
113
150
|
];
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
151
|
+
if (!botInfo.bot) {
|
|
152
|
+
for (const rule2 of rules) {
|
|
153
|
+
const m = userAgent.match(rule2.re);
|
|
154
|
+
if (m) {
|
|
155
|
+
name = rule2.name;
|
|
156
|
+
version = m[1] ?? "";
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (name === "unknown" && /iPhone|iPad|iPod/.test(userAgent) && /AppleWebKit/.test(userAgent)) {
|
|
161
|
+
name = "Safari";
|
|
162
|
+
const m = userAgent.match(/OS ([\d_]+)/);
|
|
163
|
+
if (m?.[1]) version = m[1].replace(/_/g, ".");
|
|
120
164
|
}
|
|
121
|
-
}
|
|
122
|
-
if (name === "unknown" && /iPhone|iPad|iPod/.test(userAgent) && /AppleWebKit/.test(userAgent)) {
|
|
123
|
-
name = "Safari";
|
|
124
|
-
const m = userAgent.match(/OS ([\d_]+)/);
|
|
125
|
-
if (m?.[1]) version = m[1].replace(/_/g, ".");
|
|
126
165
|
}
|
|
127
166
|
let os = "unknown";
|
|
128
167
|
let osVersion = "";
|
|
@@ -168,7 +207,9 @@ function parseBrowserContext(ua = typeof navigator !== "undefined" ? navigator.u
|
|
|
168
207
|
osVersion,
|
|
169
208
|
device,
|
|
170
209
|
language: lang,
|
|
171
|
-
userAgent
|
|
210
|
+
userAgent,
|
|
211
|
+
bot: botInfo.bot,
|
|
212
|
+
...botInfo.botName ? { botName: botInfo.botName } : {}
|
|
172
213
|
};
|
|
173
214
|
}
|
|
174
215
|
function browserContextTags(ctx) {
|
|
@@ -177,11 +218,16 @@ function browserContextTags(ctx) {
|
|
|
177
218
|
"browser.version": ctx.version || "unknown",
|
|
178
219
|
"os.name": ctx.os,
|
|
179
220
|
"os.version": ctx.osVersion || "unknown",
|
|
180
|
-
device: ctx.device
|
|
221
|
+
device: ctx.device,
|
|
222
|
+
...ctx.bot ? {
|
|
223
|
+
bot: "true",
|
|
224
|
+
...ctx.botName ? { "bot.name": ctx.botName } : {}
|
|
225
|
+
} : {}
|
|
181
226
|
};
|
|
182
227
|
}
|
|
183
228
|
async function collectBrowserContext() {
|
|
184
229
|
const base = parseBrowserContext();
|
|
230
|
+
if (base.bot) return base;
|
|
185
231
|
const nav = typeof navigator !== "undefined" ? navigator : void 0;
|
|
186
232
|
const uad = nav?.userAgentData;
|
|
187
233
|
if (!uad) return base;
|
|
@@ -226,7 +272,7 @@ async function collectBrowserContext() {
|
|
|
226
272
|
|
|
227
273
|
// src/sdk_meta.ts
|
|
228
274
|
var SDK_NAME = "@newtalaria/browser";
|
|
229
|
-
var SDK_VERSION = "0.1.
|
|
275
|
+
var SDK_VERSION = "0.1.18";
|
|
230
276
|
|
|
231
277
|
// src/transport/serverpod.ts
|
|
232
278
|
var ServerpodTransport = class {
|
|
@@ -13436,18 +13482,22 @@ var { freezePage } = record;
|
|
|
13436
13482
|
var { takeFullSnapshot } = record;
|
|
13437
13483
|
|
|
13438
13484
|
// src/replay/privacy.ts
|
|
13439
|
-
var
|
|
13485
|
+
var SENSITIVE_QUERY_KEY = /^(?:token|secret|password|auth|api[_-]?key|access[_-]?token|gclid|fbclid|gcl_au|msclkid|_ga(?:_.*)?|cid|sid)$/i;
|
|
13486
|
+
var SENSITIVE_QUERY_LEGACY = /(?:^|[?&])(token|secret|password|auth|api[_-]?key|access[_-]?token|gclid|fbclid|gcl_au|msclkid|_ga(?:_[^=&#]*)?|cid|sid)=([^&#]*)/gi;
|
|
13487
|
+
function isSensitiveQueryKey(key) {
|
|
13488
|
+
return SENSITIVE_QUERY_KEY.test(key);
|
|
13489
|
+
}
|
|
13440
13490
|
function redactUrl(raw) {
|
|
13441
13491
|
try {
|
|
13442
13492
|
const url = new URL(raw, typeof location !== "undefined" ? location.href : void 0);
|
|
13443
13493
|
for (const key of [...url.searchParams.keys()]) {
|
|
13444
|
-
if (
|
|
13494
|
+
if (isSensitiveQueryKey(key)) {
|
|
13445
13495
|
url.searchParams.set(key, "[Filtered]");
|
|
13446
13496
|
}
|
|
13447
13497
|
}
|
|
13448
13498
|
return url.toString();
|
|
13449
13499
|
} catch {
|
|
13450
|
-
return raw.replace(
|
|
13500
|
+
return raw.replace(SENSITIVE_QUERY_LEGACY, (_m, name) => `${name}=[Filtered]`);
|
|
13451
13501
|
}
|
|
13452
13502
|
}
|
|
13453
13503
|
function sanitizeNetworkUrl(raw, opts) {
|
|
@@ -13544,27 +13594,40 @@ function startRecorder(options) {
|
|
|
13544
13594
|
function isAbortError(error) {
|
|
13545
13595
|
return error instanceof Error && error.name === "AbortError";
|
|
13546
13596
|
}
|
|
13597
|
+
function isTimeoutError(error) {
|
|
13598
|
+
return error instanceof Error && error.name === "TimeoutError";
|
|
13599
|
+
}
|
|
13547
13600
|
function isLikelyNetworkFetchError(error) {
|
|
13548
13601
|
if (!(error instanceof Error)) return false;
|
|
13549
|
-
if (error.name === "AbortError") return false;
|
|
13602
|
+
if (error.name === "AbortError" || error.name === "TimeoutError") return false;
|
|
13550
13603
|
if (error.name === "NetworkError") return true;
|
|
13551
13604
|
const msg = error.message.toLowerCase().trim();
|
|
13552
13605
|
return msg === "failed to fetch" || msg === "load failed" || msg === "networkerror when attempting to fetch resource." || msg.startsWith("networkerror") || msg.includes("failed to fetch");
|
|
13553
13606
|
}
|
|
13607
|
+
function isCorrelatableTransportError(error) {
|
|
13608
|
+
return isLikelyNetworkFetchError(error) || isTimeoutError(error);
|
|
13609
|
+
}
|
|
13554
13610
|
function describeUnknownError(error) {
|
|
13555
13611
|
if (error instanceof Error) {
|
|
13556
13612
|
return {
|
|
13557
13613
|
errorName: error.name || "Error",
|
|
13558
13614
|
errorMessage: (error.message || "").slice(0, 500),
|
|
13559
|
-
aborted: error.name === "AbortError"
|
|
13615
|
+
aborted: error.name === "AbortError",
|
|
13616
|
+
timedOut: error.name === "TimeoutError"
|
|
13560
13617
|
};
|
|
13561
13618
|
}
|
|
13562
13619
|
return {
|
|
13563
13620
|
errorName: "Error",
|
|
13564
13621
|
errorMessage: String(error).slice(0, 500),
|
|
13565
|
-
aborted: false
|
|
13622
|
+
aborted: false,
|
|
13623
|
+
timedOut: false
|
|
13566
13624
|
};
|
|
13567
13625
|
}
|
|
13626
|
+
function classifyTransportFailure(opts) {
|
|
13627
|
+
if (opts.aborted || opts.errorName === "AbortError") return "abort";
|
|
13628
|
+
if (opts.timedOut || opts.errorName === "TimeoutError") return "timeout";
|
|
13629
|
+
return "network";
|
|
13630
|
+
}
|
|
13568
13631
|
|
|
13569
13632
|
// src/replay/hooks.ts
|
|
13570
13633
|
function installVisibilityResumeHook(onResume) {
|
|
@@ -13691,6 +13754,56 @@ function urlMatchesIgnoreList(url, ignoreUrls) {
|
|
|
13691
13754
|
}
|
|
13692
13755
|
return false;
|
|
13693
13756
|
}
|
|
13757
|
+
function normalizeNetworkOrigin(raw) {
|
|
13758
|
+
const trimmed = (raw || "").trim();
|
|
13759
|
+
if (!trimmed || trimmed === "*") return trimmed;
|
|
13760
|
+
try {
|
|
13761
|
+
return new URL(trimmed).origin;
|
|
13762
|
+
} catch {
|
|
13763
|
+
try {
|
|
13764
|
+
return new URL(`https://${trimmed}`).origin;
|
|
13765
|
+
} catch {
|
|
13766
|
+
return trimmed.replace(/\/+$/, "");
|
|
13767
|
+
}
|
|
13768
|
+
}
|
|
13769
|
+
}
|
|
13770
|
+
function resolvePageOrigin(pageOrigin) {
|
|
13771
|
+
if (pageOrigin) {
|
|
13772
|
+
try {
|
|
13773
|
+
return new URL(pageOrigin).origin;
|
|
13774
|
+
} catch {
|
|
13775
|
+
return pageOrigin.replace(/\/+$/, "");
|
|
13776
|
+
}
|
|
13777
|
+
}
|
|
13778
|
+
if (typeof location !== "undefined" && location.origin) {
|
|
13779
|
+
return location.origin;
|
|
13780
|
+
}
|
|
13781
|
+
return "";
|
|
13782
|
+
}
|
|
13783
|
+
function resolveRequestOrigin(rawUrl, pageOrigin) {
|
|
13784
|
+
const base = pageOrigin || (typeof location !== "undefined" ? location.href : void 0);
|
|
13785
|
+
try {
|
|
13786
|
+
return new URL(rawUrl, base).origin;
|
|
13787
|
+
} catch {
|
|
13788
|
+
return "";
|
|
13789
|
+
}
|
|
13790
|
+
}
|
|
13791
|
+
function isAllowedNetworkOrigin(requestOrigin, opts) {
|
|
13792
|
+
if (!requestOrigin) return false;
|
|
13793
|
+
const allow = opts.networkErrorOrigins.map(normalizeNetworkOrigin);
|
|
13794
|
+
if (allow.includes("*")) return true;
|
|
13795
|
+
const page = resolvePageOrigin(opts.pageOrigin);
|
|
13796
|
+
if (page && requestOrigin === page) return true;
|
|
13797
|
+
return allow.includes(requestOrigin);
|
|
13798
|
+
}
|
|
13799
|
+
function classifyNetworkParty(requestOrigin, pageOrigin) {
|
|
13800
|
+
const page = resolvePageOrigin(pageOrigin);
|
|
13801
|
+
if (page && requestOrigin && requestOrigin === page) return "first_party";
|
|
13802
|
+
return "third_party";
|
|
13803
|
+
}
|
|
13804
|
+
function isHttpOkStatus(status) {
|
|
13805
|
+
return status >= 200 && status < 300;
|
|
13806
|
+
}
|
|
13694
13807
|
function shouldPromoteFailedRequest(meta, opts) {
|
|
13695
13808
|
if (!opts.captureFailedRequests) return false;
|
|
13696
13809
|
if (typeof meta.status !== "number" || Number.isNaN(meta.status)) return false;
|
|
@@ -13701,17 +13814,33 @@ function shouldPromoteFailedRequest(meta, opts) {
|
|
|
13701
13814
|
opts.talariaBaseUrl
|
|
13702
13815
|
);
|
|
13703
13816
|
if (urlMatchesIgnoreList(meta.url || "", ignore)) return false;
|
|
13817
|
+
const requestOrigin = meta.origin || resolveRequestOrigin(meta.url || "", opts.pageOrigin);
|
|
13818
|
+
if (!isAllowedNetworkOrigin(requestOrigin, {
|
|
13819
|
+
networkErrorOrigins: opts.networkErrorOrigins ?? [],
|
|
13820
|
+
pageOrigin: opts.pageOrigin
|
|
13821
|
+
})) {
|
|
13822
|
+
return false;
|
|
13823
|
+
}
|
|
13704
13824
|
return true;
|
|
13705
13825
|
}
|
|
13706
13826
|
function shouldPromoteNetworkError(meta, opts) {
|
|
13707
13827
|
if (!opts.captureNetworkErrors) return false;
|
|
13708
|
-
if (meta.aborted) return false;
|
|
13709
|
-
if (meta.failureKind !== "network")
|
|
13828
|
+
if (meta.aborted || meta.failureKind === "abort") return false;
|
|
13829
|
+
if (meta.failureKind !== "network" && meta.failureKind !== "timeout") {
|
|
13830
|
+
return false;
|
|
13831
|
+
}
|
|
13710
13832
|
const ignore = buildFailedRequestIgnoreUrls(
|
|
13711
13833
|
opts.failedRequestIgnoreUrls,
|
|
13712
13834
|
opts.talariaBaseUrl
|
|
13713
13835
|
);
|
|
13714
13836
|
if (urlMatchesIgnoreList(meta.url || "", ignore)) return false;
|
|
13837
|
+
const requestOrigin = meta.origin || resolveRequestOrigin(meta.url || "", opts.pageOrigin);
|
|
13838
|
+
if (!isAllowedNetworkOrigin(requestOrigin, {
|
|
13839
|
+
networkErrorOrigins: opts.networkErrorOrigins ?? [],
|
|
13840
|
+
pageOrigin: opts.pageOrigin
|
|
13841
|
+
})) {
|
|
13842
|
+
return false;
|
|
13843
|
+
}
|
|
13715
13844
|
return true;
|
|
13716
13845
|
}
|
|
13717
13846
|
function enrichNetworkMeta(meta) {
|
|
@@ -13722,18 +13851,27 @@ function enrichNetworkMeta(meta) {
|
|
|
13722
13851
|
failureKind: meta.ok === false ? "http" : meta.failureKind
|
|
13723
13852
|
};
|
|
13724
13853
|
}
|
|
13725
|
-
|
|
13854
|
+
const hasTransportSignal = !!meta.errorName || !!meta.errorMessage || !!meta.aborted || meta.failureKind === "abort" || meta.failureKind === "timeout" || meta.failureKind === "network";
|
|
13855
|
+
if (hasTransportSignal) {
|
|
13856
|
+
const failureKind = meta.failureKind === "abort" || meta.failureKind === "timeout" || meta.failureKind === "network" ? meta.failureKind : classifyTransportFailure({
|
|
13857
|
+
aborted: meta.aborted,
|
|
13858
|
+
errorName: meta.errorName
|
|
13859
|
+
});
|
|
13726
13860
|
return {
|
|
13727
13861
|
...meta,
|
|
13728
|
-
failureKind
|
|
13862
|
+
failureKind,
|
|
13863
|
+
aborted: meta.aborted ?? failureKind === "abort"
|
|
13729
13864
|
};
|
|
13730
13865
|
}
|
|
13731
13866
|
return meta;
|
|
13732
13867
|
}
|
|
13733
|
-
function networkUrlParts(rawUrl, includeQuery) {
|
|
13734
|
-
const
|
|
13868
|
+
function networkUrlParts(rawUrl, includeQuery, pageOrigin) {
|
|
13869
|
+
const baseHref = pageOrigin || (typeof location !== "undefined" ? location.href : void 0);
|
|
13870
|
+
const parts = sanitizeNetworkUrl(rawUrl, { includeQuery, baseHref });
|
|
13871
|
+
const origin = resolveRequestOrigin(rawUrl, pageOrigin);
|
|
13735
13872
|
return {
|
|
13736
13873
|
url: parts.url,
|
|
13874
|
+
origin: origin || void 0,
|
|
13737
13875
|
hostname: parts.hostname,
|
|
13738
13876
|
pathname: parts.pathname,
|
|
13739
13877
|
...parts.search ? { search: parts.search } : {}
|
|
@@ -13749,14 +13887,22 @@ function installNetworkHook(options = {}) {
|
|
|
13749
13887
|
captureNetworkErrors: options.captureNetworkErrors ?? true,
|
|
13750
13888
|
failedRequestStatusCodes: options.failedRequestStatusCodes ?? [[500, 599]],
|
|
13751
13889
|
failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? [],
|
|
13890
|
+
networkErrorOrigins: options.networkErrorOrigins ?? [],
|
|
13891
|
+
pageOrigin: options.pageOrigin,
|
|
13752
13892
|
talariaBaseUrl: options.talariaBaseUrl
|
|
13753
13893
|
};
|
|
13754
|
-
const includeQuery = options.includeNetworkUrlQuery ?? false;
|
|
13894
|
+
const includeQuery = options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false;
|
|
13755
13895
|
const handleMeta = (meta) => {
|
|
13756
13896
|
const { rawUrl, ...rest } = meta;
|
|
13897
|
+
const parts = networkUrlParts(rawUrl, includeQuery, matchOpts.pageOrigin);
|
|
13898
|
+
const party = classifyNetworkParty(
|
|
13899
|
+
parts.origin || "",
|
|
13900
|
+
matchOpts.pageOrigin
|
|
13901
|
+
);
|
|
13757
13902
|
const enriched = enrichNetworkMeta({
|
|
13758
13903
|
...rest,
|
|
13759
|
-
...
|
|
13904
|
+
...parts,
|
|
13905
|
+
party
|
|
13760
13906
|
});
|
|
13761
13907
|
try {
|
|
13762
13908
|
emitNetwork(enriched);
|
|
@@ -13776,27 +13922,32 @@ function installNetworkHook(options = {}) {
|
|
|
13776
13922
|
const rawUrl = typeof input2 === "string" ? input2 : input2 instanceof URL ? input2.toString() : input2.url;
|
|
13777
13923
|
try {
|
|
13778
13924
|
const response = await originalFetch(input2, init);
|
|
13925
|
+
const opaque = response.type === "opaque" || response.type === "opaqueredirect";
|
|
13779
13926
|
handleMeta({
|
|
13780
13927
|
method,
|
|
13781
13928
|
rawUrl,
|
|
13782
13929
|
url: rawUrl,
|
|
13930
|
+
transport: "fetch",
|
|
13783
13931
|
status: response.status,
|
|
13784
13932
|
durationMs: Date.now() - started,
|
|
13785
|
-
|
|
13933
|
+
// Opaque responses resolve successfully with status 0 — not a failure.
|
|
13934
|
+
ok: opaque ? true : response.ok
|
|
13786
13935
|
});
|
|
13787
13936
|
return response;
|
|
13788
13937
|
} catch (error) {
|
|
13789
13938
|
const described = describeUnknownError(error);
|
|
13939
|
+
const failureKind = classifyTransportFailure(described);
|
|
13790
13940
|
handleMeta({
|
|
13791
13941
|
method,
|
|
13792
13942
|
rawUrl,
|
|
13793
13943
|
url: rawUrl,
|
|
13944
|
+
transport: "fetch",
|
|
13794
13945
|
durationMs: Date.now() - started,
|
|
13795
13946
|
ok: false,
|
|
13796
13947
|
errorName: described.errorName,
|
|
13797
13948
|
errorMessage: described.errorMessage,
|
|
13798
13949
|
aborted: described.aborted,
|
|
13799
|
-
failureKind
|
|
13950
|
+
failureKind
|
|
13800
13951
|
});
|
|
13801
13952
|
throw error;
|
|
13802
13953
|
}
|
|
@@ -13810,25 +13961,48 @@ function installNetworkHook(options = {}) {
|
|
|
13810
13961
|
};
|
|
13811
13962
|
XHR.prototype.send = function(body) {
|
|
13812
13963
|
this.__talariaStarted = Date.now();
|
|
13964
|
+
this.__talariaTimedOut = false;
|
|
13965
|
+
this.__talariaAborted = false;
|
|
13966
|
+
this.addEventListener(
|
|
13967
|
+
"timeout",
|
|
13968
|
+
() => {
|
|
13969
|
+
this.__talariaTimedOut = true;
|
|
13970
|
+
},
|
|
13971
|
+
{ once: true }
|
|
13972
|
+
);
|
|
13973
|
+
this.addEventListener(
|
|
13974
|
+
"abort",
|
|
13975
|
+
() => {
|
|
13976
|
+
this.__talariaAborted = true;
|
|
13977
|
+
},
|
|
13978
|
+
{ once: true }
|
|
13979
|
+
);
|
|
13813
13980
|
const onDone = () => {
|
|
13814
13981
|
const status = this.status;
|
|
13815
|
-
const
|
|
13982
|
+
const transportFail = status === 0;
|
|
13816
13983
|
const rawUrl = this.__talariaUrl ?? "";
|
|
13984
|
+
const aborted = !!this.__talariaAborted;
|
|
13985
|
+
const timedOut = !!this.__talariaTimedOut;
|
|
13986
|
+
const failureKind = transportFail ? classifyTransportFailure({ aborted, timedOut }) : void 0;
|
|
13987
|
+
const errorName = transportFail ? failureKind === "abort" ? "AbortError" : failureKind === "timeout" ? "TimeoutError" : "NetworkError" : void 0;
|
|
13988
|
+
const errorMessage = transportFail ? failureKind === "abort" ? "XMLHttpRequest aborted" : failureKind === "timeout" ? "XMLHttpRequest timed out" : "XMLHttpRequest failed (status 0)" : void 0;
|
|
13817
13989
|
handleMeta({
|
|
13818
13990
|
method: this.__talariaMethod ?? "GET",
|
|
13819
13991
|
rawUrl,
|
|
13820
13992
|
url: rawUrl,
|
|
13993
|
+
transport: "xhr",
|
|
13821
13994
|
status,
|
|
13822
13995
|
durationMs: Date.now() - (this.__talariaStarted ?? Date.now()),
|
|
13823
|
-
ok: status
|
|
13824
|
-
...
|
|
13825
|
-
failureKind
|
|
13826
|
-
errorName
|
|
13827
|
-
errorMessage
|
|
13996
|
+
ok: isHttpOkStatus(status),
|
|
13997
|
+
...transportFail ? {
|
|
13998
|
+
failureKind,
|
|
13999
|
+
errorName,
|
|
14000
|
+
errorMessage,
|
|
14001
|
+
aborted
|
|
13828
14002
|
} : {}
|
|
13829
14003
|
});
|
|
13830
14004
|
};
|
|
13831
|
-
this.addEventListener("loadend", onDone);
|
|
14005
|
+
this.addEventListener("loadend", onDone, { once: true });
|
|
13832
14006
|
return originalSend.call(this, body);
|
|
13833
14007
|
};
|
|
13834
14008
|
}
|
|
@@ -13840,20 +14014,54 @@ function installNetworkHook(options = {}) {
|
|
|
13840
14014
|
}
|
|
13841
14015
|
|
|
13842
14016
|
// src/client.ts
|
|
13843
|
-
function
|
|
14017
|
+
function networkExceptionClass(failure) {
|
|
14018
|
+
if (failure.failureKind === "http") return "HttpError";
|
|
14019
|
+
if (failure.failureKind === "timeout") return "TimeoutError";
|
|
14020
|
+
return "NetworkError";
|
|
14021
|
+
}
|
|
14022
|
+
function networkFailureTags(failure) {
|
|
13844
14023
|
return {
|
|
14024
|
+
"http.method": failure.method || "GET",
|
|
14025
|
+
"network.failure_kind": failure.failureKind ?? "network",
|
|
14026
|
+
...failure.party ? { "network.party": failure.party } : {},
|
|
14027
|
+
...failure.transport ? { "network.transport": failure.transport } : {},
|
|
14028
|
+
...failure.errorName ? { "network.error_name": failure.errorName } : {}
|
|
14029
|
+
};
|
|
14030
|
+
}
|
|
14031
|
+
function networkFailureExtra(failure) {
|
|
14032
|
+
const http = {
|
|
13845
14033
|
method: failure.method || "GET",
|
|
13846
|
-
url: failure.url || "(unknown url)"
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
14034
|
+
url: failure.url || "(unknown url)"
|
|
14035
|
+
};
|
|
14036
|
+
if (failure.origin) http.origin = failure.origin;
|
|
14037
|
+
if (failure.hostname) http.hostname = failure.hostname;
|
|
14038
|
+
if (failure.pathname) http.pathname = failure.pathname;
|
|
14039
|
+
if (failure.search) http.search = failure.search;
|
|
14040
|
+
if (typeof failure.status === "number" && failure.status > 0) {
|
|
14041
|
+
http.status = failure.status;
|
|
14042
|
+
}
|
|
14043
|
+
if (failure.transport) http.transport = failure.transport;
|
|
14044
|
+
const fail = {
|
|
14045
|
+
kind: failure.failureKind ?? "network"
|
|
14046
|
+
};
|
|
14047
|
+
if (failure.errorName) fail.name = failure.errorName;
|
|
14048
|
+
if (failure.errorMessage) fail.message = failure.errorMessage;
|
|
14049
|
+
const statusCode = typeof failure.status === "number" && failure.status > 0 ? failure.status : null;
|
|
14050
|
+
return {
|
|
14051
|
+
http,
|
|
14052
|
+
failure: fail,
|
|
14053
|
+
network: {
|
|
14054
|
+
party: failure.party ?? "third_party",
|
|
14055
|
+
durationMs: failure.durationMs,
|
|
14056
|
+
aborted: failure.aborted ?? false,
|
|
14057
|
+
ok: failure.ok ?? false
|
|
14058
|
+
},
|
|
14059
|
+
// Top-level for server fingerprint compatibility.
|
|
14060
|
+
status_code: statusCode,
|
|
14061
|
+
exception_class: networkExceptionClass(failure),
|
|
13851
14062
|
durationMs: failure.durationMs,
|
|
13852
|
-
ok: failure.ok ?? false,
|
|
13853
|
-
failureKind: failure.failureKind ?? "network",
|
|
13854
14063
|
aborted: failure.aborted ?? false,
|
|
13855
|
-
|
|
13856
|
-
...failure.errorMessage ? { errorMessage: failure.errorMessage } : {}
|
|
14064
|
+
ok: failure.ok ?? false
|
|
13857
14065
|
};
|
|
13858
14066
|
}
|
|
13859
14067
|
function mergeNetworkFailureContext(context, failure) {
|
|
@@ -13861,13 +14069,11 @@ function mergeNetworkFailureContext(context, failure) {
|
|
|
13861
14069
|
...context,
|
|
13862
14070
|
tags: {
|
|
13863
14071
|
...context?.tags ?? {},
|
|
13864
|
-
|
|
13865
|
-
"network.failure_kind": "network",
|
|
13866
|
-
...failure.errorName ? { "network.error_name": failure.errorName } : {}
|
|
14072
|
+
...networkFailureTags(failure)
|
|
13867
14073
|
},
|
|
13868
14074
|
extra: {
|
|
13869
14075
|
...context?.extra ?? {},
|
|
13870
|
-
|
|
14076
|
+
...networkFailureExtra(failure)
|
|
13871
14077
|
}
|
|
13872
14078
|
};
|
|
13873
14079
|
}
|
|
@@ -13898,7 +14104,8 @@ function resolveOptions(options) {
|
|
|
13898
14104
|
disableDefaultIntegrations: options.disableDefaultIntegrations ?? false,
|
|
13899
14105
|
captureFailedRequests: options.captureFailedRequests ?? true,
|
|
13900
14106
|
captureNetworkErrors: options.captureNetworkErrors ?? true,
|
|
13901
|
-
|
|
14107
|
+
networkErrorOrigins: options.networkErrorOrigins ?? [],
|
|
14108
|
+
includeNetworkUrlQuery: options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false,
|
|
13902
14109
|
failedRequestStatusCodes: options.failedRequestStatusCodes ?? [[500, 599]],
|
|
13903
14110
|
failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? []
|
|
13904
14111
|
};
|
|
@@ -14027,12 +14234,14 @@ var TalariaClient = class {
|
|
|
14027
14234
|
installNetworkHook({
|
|
14028
14235
|
captureFailedRequests: this.options.captureFailedRequests,
|
|
14029
14236
|
captureNetworkErrors: this.options.captureNetworkErrors,
|
|
14237
|
+
networkErrorOrigins: this.options.networkErrorOrigins,
|
|
14238
|
+
pageOrigin: typeof location !== "undefined" ? location.origin : void 0,
|
|
14030
14239
|
includeNetworkUrlQuery: this.options.includeNetworkUrlQuery,
|
|
14031
14240
|
failedRequestStatusCodes: this.options.failedRequestStatusCodes,
|
|
14032
14241
|
failedRequestIgnoreUrls: this.options.failedRequestIgnoreUrls,
|
|
14033
14242
|
talariaBaseUrl: this.options.baseUrl,
|
|
14034
14243
|
onNetwork: (meta) => {
|
|
14035
|
-
if (meta.failureKind === "network" && !meta.aborted) {
|
|
14244
|
+
if ((meta.failureKind === "network" || meta.failureKind === "timeout") && !meta.aborted) {
|
|
14036
14245
|
this.rememberNetworkFailure(meta, { promoted: false });
|
|
14037
14246
|
}
|
|
14038
14247
|
},
|
|
@@ -14040,22 +14249,20 @@ var TalariaClient = class {
|
|
|
14040
14249
|
const status = meta.status;
|
|
14041
14250
|
const method = meta.method || "GET";
|
|
14042
14251
|
const url = meta.url || "(unknown url)";
|
|
14252
|
+
const enriched = {
|
|
14253
|
+
...meta,
|
|
14254
|
+
failureKind: "http",
|
|
14255
|
+
aborted: false
|
|
14256
|
+
};
|
|
14043
14257
|
void this.captureMessage(
|
|
14044
14258
|
`HTTP ${status}: ${method} ${url}`,
|
|
14045
14259
|
status >= 500 ? "error" : "warning",
|
|
14046
14260
|
{
|
|
14047
14261
|
tags: {
|
|
14048
|
-
|
|
14049
|
-
"http.
|
|
14050
|
-
"network.failure_kind": "http"
|
|
14262
|
+
...networkFailureTags(enriched),
|
|
14263
|
+
"http.status_code": String(status)
|
|
14051
14264
|
},
|
|
14052
|
-
extra:
|
|
14053
|
-
...networkFailureExtra({
|
|
14054
|
-
...meta,
|
|
14055
|
-
failureKind: "http",
|
|
14056
|
-
aborted: false
|
|
14057
|
-
})
|
|
14058
|
-
}
|
|
14265
|
+
extra: networkFailureExtra(enriched)
|
|
14059
14266
|
}
|
|
14060
14267
|
);
|
|
14061
14268
|
},
|
|
@@ -14064,20 +14271,13 @@ var TalariaClient = class {
|
|
|
14064
14271
|
const method = meta.method || "GET";
|
|
14065
14272
|
const url = meta.url || "(unknown url)";
|
|
14066
14273
|
const errLabel = meta.errorName && meta.errorMessage ? `${meta.errorName}: ${meta.errorMessage}` : meta.errorMessage || meta.errorName || "Failed to fetch";
|
|
14274
|
+
const prefix = meta.failureKind === "timeout" ? "Timeout error" : "Network error";
|
|
14067
14275
|
void this.captureMessage(
|
|
14068
|
-
|
|
14276
|
+
`${prefix}: ${method} ${url} \u2014 ${errLabel}`,
|
|
14069
14277
|
"error",
|
|
14070
14278
|
{
|
|
14071
|
-
tags:
|
|
14072
|
-
|
|
14073
|
-
"network.failure_kind": "network",
|
|
14074
|
-
...meta.errorName ? { "network.error_name": meta.errorName } : {}
|
|
14075
|
-
},
|
|
14076
|
-
extra: networkFailureExtra({
|
|
14077
|
-
...meta,
|
|
14078
|
-
failureKind: "network",
|
|
14079
|
-
aborted: false
|
|
14080
|
-
})
|
|
14279
|
+
tags: networkFailureTags(meta),
|
|
14280
|
+
extra: networkFailureExtra(meta)
|
|
14081
14281
|
}
|
|
14082
14282
|
);
|
|
14083
14283
|
}
|
|
@@ -14129,6 +14329,7 @@ var TalariaClient = class {
|
|
|
14129
14329
|
}
|
|
14130
14330
|
const correlated = this.consumeCorrelatedNetworkFailure(err);
|
|
14131
14331
|
if (correlated?.promoted) return;
|
|
14332
|
+
if (correlated && correlated.party === "third_party") return;
|
|
14132
14333
|
const mergedContext = correlated ? mergeNetworkFailureContext(context, correlated) : context;
|
|
14133
14334
|
this.capturing = true;
|
|
14134
14335
|
try {
|
|
@@ -14333,7 +14534,9 @@ var TalariaClient = class {
|
|
|
14333
14534
|
osVersion: this.browserContext.osVersion,
|
|
14334
14535
|
device: this.browserContext.device,
|
|
14335
14536
|
language: this.browserContext.language,
|
|
14336
|
-
userAgent: this.browserContext.userAgent
|
|
14537
|
+
userAgent: this.browserContext.userAgent,
|
|
14538
|
+
bot: this.browserContext.bot,
|
|
14539
|
+
...this.browserContext.botName ? { botName: this.browserContext.botName } : {}
|
|
14337
14540
|
}
|
|
14338
14541
|
} : {},
|
|
14339
14542
|
sdk: {
|
|
@@ -14609,6 +14812,13 @@ var TalariaClient = class {
|
|
|
14609
14812
|
);
|
|
14610
14813
|
}
|
|
14611
14814
|
rememberNetworkFailure(meta, opts) {
|
|
14815
|
+
if (this.options) {
|
|
14816
|
+
const ignore = buildFailedRequestIgnoreUrls(
|
|
14817
|
+
this.options.failedRequestIgnoreUrls,
|
|
14818
|
+
this.options.baseUrl
|
|
14819
|
+
);
|
|
14820
|
+
if (urlMatchesIgnoreList(meta.url || "", ignore)) return;
|
|
14821
|
+
}
|
|
14612
14822
|
const now = Date.now();
|
|
14613
14823
|
this.pruneRecentNetworkFailures(now);
|
|
14614
14824
|
for (let i = this.recentNetworkFailures.length - 1; i >= 0; i--) {
|
|
@@ -14619,6 +14829,8 @@ var TalariaClient = class {
|
|
|
14619
14829
|
existing.errorName = meta.errorName ?? existing.errorName;
|
|
14620
14830
|
existing.aborted = meta.aborted ?? existing.aborted;
|
|
14621
14831
|
existing.failureKind = meta.failureKind ?? existing.failureKind;
|
|
14832
|
+
existing.party = meta.party ?? existing.party;
|
|
14833
|
+
existing.origin = meta.origin ?? existing.origin;
|
|
14622
14834
|
return;
|
|
14623
14835
|
}
|
|
14624
14836
|
}
|
|
@@ -14629,11 +14841,12 @@ var TalariaClient = class {
|
|
|
14629
14841
|
});
|
|
14630
14842
|
}
|
|
14631
14843
|
/**
|
|
14632
|
-
* Find a recent transport failure for a bare fetch TypeError.
|
|
14844
|
+
* Find a recent transport failure for a bare fetch TypeError / TimeoutError.
|
|
14633
14845
|
* Consumes the entry so a later duplicate path cannot reuse it.
|
|
14634
14846
|
*/
|
|
14635
14847
|
consumeCorrelatedNetworkFailure(error) {
|
|
14636
|
-
if (!
|
|
14848
|
+
if (!isCorrelatableTransportError(error)) return null;
|
|
14849
|
+
const wantKind = isTimeoutError(error) ? "timeout" : "network";
|
|
14637
14850
|
const now = Date.now();
|
|
14638
14851
|
this.pruneRecentNetworkFailures(now);
|
|
14639
14852
|
const takeAt = (index2) => {
|
|
@@ -14642,14 +14855,14 @@ var TalariaClient = class {
|
|
|
14642
14855
|
};
|
|
14643
14856
|
for (let i = this.recentNetworkFailures.length - 1; i >= 0; i--) {
|
|
14644
14857
|
const failure = this.recentNetworkFailures[i];
|
|
14645
|
-
if (failure.aborted || failure.failureKind !==
|
|
14858
|
+
if (failure.aborted || failure.failureKind !== wantKind) continue;
|
|
14646
14859
|
if (failure.errorMessage && failure.errorMessage === error.message) {
|
|
14647
14860
|
return takeAt(i);
|
|
14648
14861
|
}
|
|
14649
14862
|
}
|
|
14650
14863
|
for (let i = this.recentNetworkFailures.length - 1; i >= 0; i--) {
|
|
14651
14864
|
const failure = this.recentNetworkFailures[i];
|
|
14652
|
-
if (failure.aborted || failure.failureKind !==
|
|
14865
|
+
if (failure.aborted || failure.failureKind !== wantKind) continue;
|
|
14653
14866
|
return takeAt(i);
|
|
14654
14867
|
}
|
|
14655
14868
|
return null;
|