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