@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/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
- for (const rule2 of rules) {
115
- const m = userAgent.match(rule2.re);
116
- if (m) {
117
- name = rule2.name;
118
- version = m[1] ?? "";
119
- break;
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.16";
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 SENSITIVE_QUERY = /(?:^|[?&])(token|secret|password|auth|api[_-]?key|access[_-]?token)=([^&#]*)/gi;
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 (/token|secret|password|auth|api[_-]?key|access[_-]?token/i.test(key)) {
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(SENSITIVE_QUERY, (_m, name) => `${name}=[Filtered]`);
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") return false;
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
- if (meta.ok === false || meta.errorName || meta.errorMessage) {
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: "network"
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 parts = sanitizeNetworkUrl(rawUrl, { includeQuery });
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
- ...networkUrlParts(rawUrl, includeQuery)
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
- ok: response.ok
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: "network"
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 networkFail = status === 0;
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 >= 200 && status < 400,
13824
- ...networkFail ? {
13825
- failureKind: "network",
13826
- errorName: "NetworkError",
13827
- errorMessage: "XMLHttpRequest failed (status 0)"
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 networkFailureExtra(failure) {
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
- ...failure.hostname ? { hostname: failure.hostname } : {},
13848
- ...failure.pathname ? { pathname: failure.pathname } : {},
13849
- ...failure.search ? { search: failure.search } : {},
13850
- ...typeof failure.status === "number" ? { status: failure.status } : {},
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
- ...failure.errorName ? { errorName: failure.errorName } : {},
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
- "http.method": failure.method || "GET",
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
- network: networkFailureExtra(failure)
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
- includeNetworkUrlQuery: options.includeNetworkUrlQuery ?? false,
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
- "http.status_code": String(status),
14049
- "http.method": method,
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
- `Network error: ${method} ${url} \u2014 ${errLabel}`,
14276
+ `${prefix}: ${method} ${url} \u2014 ${errLabel}`,
14069
14277
  "error",
14070
14278
  {
14071
- tags: {
14072
- "http.method": method,
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 (!isLikelyNetworkFetchError(error)) return null;
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 !== "network") continue;
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 !== "network") continue;
14865
+ if (failure.aborted || failure.failureKind !== wantKind) continue;
14653
14866
  return takeAt(i);
14654
14867
  }
14655
14868
  return null;