@newtalaria/browser 0.1.17 → 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.
@@ -1,4 +1,11 @@
1
1
  import type { FailedRequestStatusCode } from '../types.js';
2
+ /**
3
+ * Fetch / XHR instrumentation for replay breadcrumbs and optional error promotion.
4
+ *
5
+ * Policy (see package README): always emit `talaria-network` breadcrumbs; promote
6
+ * to Talaria events only for same-origin or `networkErrorOrigins` (never invent
7
+ * a `cors` failure kind from status 0 / Failed to fetch).
8
+ */
2
9
  /** How the request was issued. */
3
10
  export type NetworkTransport = 'fetch' | 'xhr';
4
11
  /**
@@ -7,6 +14,8 @@ export type NetworkTransport = 'fetch' | 'xhr';
7
14
  * (status 0 / Failed to fetch cover many causes).
8
15
  */
9
16
  export type NetworkFailureKind = 'http' | 'network' | 'abort' | 'timeout';
17
+ /** Same-origin vs cross-origin relative to the page. */
18
+ export type NetworkParty = 'first_party' | 'third_party';
10
19
  export type Teardown = () => void;
11
20
  /**
12
21
  * Fire when the document becomes visible again (tab focus) or is restored
@@ -18,6 +27,8 @@ export interface NetworkMeta {
18
27
  method: string;
19
28
  /** Sanitized request URL (origin + pathname; query stripped by default). */
20
29
  url: string;
30
+ /** Request origin (scheme + host + port). */
31
+ origin?: string;
21
32
  hostname?: string;
22
33
  pathname?: string;
23
34
  /** Only when query capture is enabled. */
@@ -31,6 +42,8 @@ export interface NetworkMeta {
31
42
  errorMessage?: string;
32
43
  aborted?: boolean;
33
44
  failureKind?: NetworkFailureKind;
45
+ /** Same-origin (`first_party`) vs cross-origin (`third_party`). */
46
+ party?: NetworkParty;
34
47
  }
35
48
  export interface NetworkHookOptions {
36
49
  /** Still emit rrweb breadcrumbs for all requests. */
@@ -48,8 +61,17 @@ export interface NetworkHookOptions {
48
61
  */
49
62
  onNetworkError?: (meta: NetworkMeta) => void;
50
63
  captureFailedRequests?: boolean;
51
- /** Promote CORS/offline/DNS fetch failures (default `true`). */
64
+ /** Promote transport failures for first-party / allowlisted origins (default `true`). */
52
65
  captureNetworkErrors?: boolean;
66
+ /**
67
+ * Extra origins eligible for error promotion (exact origin strings).
68
+ * Same-origin is always eligible. `['*']` promotes all origins.
69
+ */
70
+ networkErrorOrigins?: string[];
71
+ /**
72
+ * Override page origin (tests / non-browser). Defaults to `location.origin`.
73
+ */
74
+ pageOrigin?: string;
53
75
  /**
54
76
  * Keep URL query strings in network telemetry (after sensitive-key redaction).
55
77
  * Default `false`. Prefer `captureRequestQueryParameters` (same meaning).
@@ -68,10 +90,28 @@ export declare function statusMatches(status: number, codes: FailedRequestStatus
68
90
  /** Shared ignore list for HTTP + network failure promotion. */
69
91
  export declare function buildFailedRequestIgnoreUrls(failedRequestIgnoreUrls: string[], talariaBaseUrl?: string): string[];
70
92
  export declare function urlMatchesIgnoreList(url: string, ignoreUrls: string[]): boolean;
93
+ /** Normalize an allowlist entry to an origin string (`https://host:port`). */
94
+ export declare function normalizeNetworkOrigin(raw: string): string;
95
+ export declare function resolvePageOrigin(pageOrigin?: string): string;
96
+ /** Resolve the request origin from a (possibly relative) URL. */
97
+ export declare function resolveRequestOrigin(rawUrl: string, pageOrigin?: string): string;
98
+ /**
99
+ * Whether a request origin may be promoted to an error event.
100
+ * Same-origin and `networkErrorOrigins` entries (or `*`) are allowed.
101
+ */
102
+ export declare function isAllowedNetworkOrigin(requestOrigin: string, opts: {
103
+ networkErrorOrigins: string[];
104
+ pageOrigin?: string;
105
+ }): boolean;
106
+ export declare function classifyNetworkParty(requestOrigin: string, pageOrigin?: string): NetworkParty;
107
+ /** HTTP success = 2xx (aligned for fetch and XHR). */
108
+ export declare function isHttpOkStatus(status: number): boolean;
71
109
  export declare function shouldPromoteFailedRequest(meta: NetworkMeta, opts: {
72
110
  captureFailedRequests: boolean;
73
111
  failedRequestStatusCodes: FailedRequestStatusCode[];
74
112
  failedRequestIgnoreUrls: string[];
113
+ networkErrorOrigins?: string[];
114
+ pageOrigin?: string;
75
115
  talariaBaseUrl?: string;
76
116
  }): meta is NetworkMeta & {
77
117
  status: number;
@@ -79,15 +119,18 @@ export declare function shouldPromoteFailedRequest(meta: NetworkMeta, opts: {
79
119
  /**
80
120
  * Promote fetch rejects / XHR status-0 failures (offline, DNS, blocked, etc.).
81
121
  * Abort is never promoted; timeout may be promoted as `failureKind: timeout`.
122
+ * Third-party origins are skipped unless allowlisted (or `*`).
82
123
  */
83
124
  export declare function shouldPromoteNetworkError(meta: NetworkMeta, opts: {
84
125
  captureNetworkErrors: boolean;
85
126
  failedRequestIgnoreUrls: string[];
127
+ networkErrorOrigins?: string[];
128
+ pageOrigin?: string;
86
129
  talariaBaseUrl?: string;
87
130
  }): boolean;
88
131
  export declare function enrichNetworkMeta(meta: NetworkMeta): NetworkMeta;
89
132
  /** Apply privacy sanitization to a raw request URL for network meta. */
90
- export declare function networkUrlParts(rawUrl: string, includeQuery: boolean): Pick<NetworkMeta, 'url' | 'hostname' | 'pathname' | 'search'>;
133
+ export declare function networkUrlParts(rawUrl: string, includeQuery: boolean, pageOrigin?: string): Pick<NetworkMeta, 'url' | 'origin' | 'hostname' | 'pathname' | 'search'>;
91
134
  /** Capture fetch / XHR metadata (no bodies, no auth headers); optionally promote failures. */
92
135
  export declare function installNetworkHook(options?: NetworkHookOptions): Teardown;
93
136
  //# sourceMappingURL=hooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/replay/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAM3D,kCAAkC;AAClC,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,CAAC;AAE/C;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAE1E,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;AAElC;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,QAAQ,CA4B1E;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnE;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IAC7C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gEAAgE;IAChE,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,iFAAiF;IACjF,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,wBAAwB,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACrD,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC,qFAAqF;IACrF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAwCD,2EAA2E;AAC3E,wBAAgB,kBAAkB,IAAI,QAAQ,CA8B7C;AAaD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,uBAAuB,EAAE,GAC/B,OAAO,CAUT;AAED,+DAA+D;AAC/D,wBAAgB,4BAA4B,CAC1C,uBAAuB,EAAE,MAAM,EAAE,EACjC,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,EAAE,CASV;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAM/E;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE;IACJ,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wBAAwB,EAAE,uBAAuB,EAAE,CAAC;IACpD,uBAAuB,EAAE,MAAM,EAAE,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACA,IAAI,IAAI,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAc1C;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE;IACJ,oBAAoB,EAAE,OAAO,CAAC;IAC9B,uBAAuB,EAAE,MAAM,EAAE,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACA,OAAO,CAcT;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CA+BhE;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,GACpB,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC,CAQ/D;AAED,8FAA8F;AAC9F,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,kBAAuB,GAAG,QAAQ,CA8K7E"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/replay/hooks.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- /** Redact sensitive query params from a URL (keeps other query keys). */
1
+ /** Redact sensitive / tracking query params from a URL (keeps other query keys). */
2
2
  export declare function redactUrl(raw: string): string;
3
3
  export interface SanitizedNetworkUrl {
4
4
  /** Safe URL for telemetry: origin + pathname (no query/hash by default). */
@@ -1 +1 @@
1
- {"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/replay/privacy.ts"],"names":[],"mappings":"AAEA,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAY7C;AAED,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,mBAAmB,CAsCrB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAU3D"}
1
+ {"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/replay/privacy.ts"],"names":[],"mappings":"AAUA,oFAAoF;AACpF,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAY7C;AAED,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,mBAAmB,CAsCrB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAU3D"}
@@ -1,4 +1,4 @@
1
1
  /** Bumped with package.json version for event metadata. */
2
2
  export declare const SDK_NAME = "@newtalaria/browser";
3
- export declare const SDK_VERSION = "0.1.17";
3
+ export declare const SDK_VERSION = "0.1.18";
4
4
  //# sourceMappingURL=sdk_meta.d.ts.map
@@ -274,7 +274,7 @@
274
274
 
275
275
  // src/sdk_meta.ts
276
276
  var SDK_NAME = "@newtalaria/browser";
277
- var SDK_VERSION = "0.1.17";
277
+ var SDK_VERSION = "0.1.18";
278
278
 
279
279
  // src/transport/serverpod.ts
280
280
  var ServerpodTransport = class {
@@ -13484,18 +13484,22 @@
13484
13484
  var { takeFullSnapshot } = record;
13485
13485
 
13486
13486
  // src/replay/privacy.ts
13487
- 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
+ }
13488
13492
  function redactUrl(raw) {
13489
13493
  try {
13490
13494
  const url = new URL(raw, typeof location !== "undefined" ? location.href : void 0);
13491
13495
  for (const key of [...url.searchParams.keys()]) {
13492
- if (/token|secret|password|auth|api[_-]?key|access[_-]?token/i.test(key)) {
13496
+ if (isSensitiveQueryKey(key)) {
13493
13497
  url.searchParams.set(key, "[Filtered]");
13494
13498
  }
13495
13499
  }
13496
13500
  return url.toString();
13497
13501
  } catch {
13498
- return raw.replace(SENSITIVE_QUERY, (_m, name) => `${name}=[Filtered]`);
13502
+ return raw.replace(SENSITIVE_QUERY_LEGACY, (_m, name) => `${name}=[Filtered]`);
13499
13503
  }
13500
13504
  }
13501
13505
  function sanitizeNetworkUrl(raw, opts) {
@@ -13592,6 +13596,9 @@
13592
13596
  function isAbortError(error) {
13593
13597
  return error instanceof Error && error.name === "AbortError";
13594
13598
  }
13599
+ function isTimeoutError(error) {
13600
+ return error instanceof Error && error.name === "TimeoutError";
13601
+ }
13595
13602
  function isLikelyNetworkFetchError(error) {
13596
13603
  if (!(error instanceof Error)) return false;
13597
13604
  if (error.name === "AbortError" || error.name === "TimeoutError") return false;
@@ -13599,6 +13606,9 @@
13599
13606
  const msg = error.message.toLowerCase().trim();
13600
13607
  return msg === "failed to fetch" || msg === "load failed" || msg === "networkerror when attempting to fetch resource." || msg.startsWith("networkerror") || msg.includes("failed to fetch");
13601
13608
  }
13609
+ function isCorrelatableTransportError(error) {
13610
+ return isLikelyNetworkFetchError(error) || isTimeoutError(error);
13611
+ }
13602
13612
  function describeUnknownError(error) {
13603
13613
  if (error instanceof Error) {
13604
13614
  return {
@@ -13746,6 +13756,56 @@
13746
13756
  }
13747
13757
  return false;
13748
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
+ }
13749
13809
  function shouldPromoteFailedRequest(meta, opts) {
13750
13810
  if (!opts.captureFailedRequests) return false;
13751
13811
  if (typeof meta.status !== "number" || Number.isNaN(meta.status)) return false;
@@ -13756,6 +13816,13 @@
13756
13816
  opts.talariaBaseUrl
13757
13817
  );
13758
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
+ }
13759
13826
  return true;
13760
13827
  }
13761
13828
  function shouldPromoteNetworkError(meta, opts) {
@@ -13769,6 +13836,13 @@
13769
13836
  opts.talariaBaseUrl
13770
13837
  );
13771
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
+ }
13772
13846
  return true;
13773
13847
  }
13774
13848
  function enrichNetworkMeta(meta) {
@@ -13779,7 +13853,8 @@
13779
13853
  failureKind: meta.ok === false ? "http" : meta.failureKind
13780
13854
  };
13781
13855
  }
13782
- if (meta.ok === false || meta.errorName || meta.errorMessage || meta.aborted) {
13856
+ const hasTransportSignal = !!meta.errorName || !!meta.errorMessage || !!meta.aborted || meta.failureKind === "abort" || meta.failureKind === "timeout" || meta.failureKind === "network";
13857
+ if (hasTransportSignal) {
13783
13858
  const failureKind = meta.failureKind === "abort" || meta.failureKind === "timeout" || meta.failureKind === "network" ? meta.failureKind : classifyTransportFailure({
13784
13859
  aborted: meta.aborted,
13785
13860
  errorName: meta.errorName
@@ -13792,10 +13867,13 @@
13792
13867
  }
13793
13868
  return meta;
13794
13869
  }
13795
- function networkUrlParts(rawUrl, includeQuery) {
13796
- 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);
13797
13874
  return {
13798
13875
  url: parts.url,
13876
+ origin: origin || void 0,
13799
13877
  hostname: parts.hostname,
13800
13878
  pathname: parts.pathname,
13801
13879
  ...parts.search ? { search: parts.search } : {}
@@ -13811,14 +13889,22 @@
13811
13889
  captureNetworkErrors: options.captureNetworkErrors ?? true,
13812
13890
  failedRequestStatusCodes: options.failedRequestStatusCodes ?? [[500, 599]],
13813
13891
  failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? [],
13892
+ networkErrorOrigins: options.networkErrorOrigins ?? [],
13893
+ pageOrigin: options.pageOrigin,
13814
13894
  talariaBaseUrl: options.talariaBaseUrl
13815
13895
  };
13816
13896
  const includeQuery = options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false;
13817
13897
  const handleMeta = (meta) => {
13818
13898
  const { rawUrl, ...rest } = meta;
13899
+ const parts = networkUrlParts(rawUrl, includeQuery, matchOpts.pageOrigin);
13900
+ const party = classifyNetworkParty(
13901
+ parts.origin || "",
13902
+ matchOpts.pageOrigin
13903
+ );
13819
13904
  const enriched = enrichNetworkMeta({
13820
13905
  ...rest,
13821
- ...networkUrlParts(rawUrl, includeQuery)
13906
+ ...parts,
13907
+ party
13822
13908
  });
13823
13909
  try {
13824
13910
  emitNetwork(enriched);
@@ -13838,6 +13924,7 @@
13838
13924
  const rawUrl = typeof input2 === "string" ? input2 : input2 instanceof URL ? input2.toString() : input2.url;
13839
13925
  try {
13840
13926
  const response = await originalFetch(input2, init);
13927
+ const opaque = response.type === "opaque" || response.type === "opaqueredirect";
13841
13928
  handleMeta({
13842
13929
  method,
13843
13930
  rawUrl,
@@ -13845,7 +13932,8 @@
13845
13932
  transport: "fetch",
13846
13933
  status: response.status,
13847
13934
  durationMs: Date.now() - started,
13848
- ok: response.ok
13935
+ // Opaque responses resolve successfully with status 0 — not a failure.
13936
+ ok: opaque ? true : response.ok
13849
13937
  });
13850
13938
  return response;
13851
13939
  } catch (error) {
@@ -13907,7 +13995,7 @@
13907
13995
  transport: "xhr",
13908
13996
  status,
13909
13997
  durationMs: Date.now() - (this.__talariaStarted ?? Date.now()),
13910
- ok: status >= 200 && status < 400,
13998
+ ok: isHttpOkStatus(status),
13911
13999
  ...transportFail ? {
13912
14000
  failureKind,
13913
14001
  errorName,
@@ -13916,7 +14004,7 @@
13916
14004
  } : {}
13917
14005
  });
13918
14006
  };
13919
- this.addEventListener("loadend", onDone);
14007
+ this.addEventListener("loadend", onDone, { once: true });
13920
14008
  return originalSend.call(this, body);
13921
14009
  };
13922
14010
  }
@@ -13928,10 +14016,16 @@
13928
14016
  }
13929
14017
 
13930
14018
  // src/client.ts
14019
+ function networkExceptionClass(failure) {
14020
+ if (failure.failureKind === "http") return "HttpError";
14021
+ if (failure.failureKind === "timeout") return "TimeoutError";
14022
+ return "NetworkError";
14023
+ }
13931
14024
  function networkFailureTags(failure) {
13932
14025
  return {
13933
14026
  "http.method": failure.method || "GET",
13934
14027
  "network.failure_kind": failure.failureKind ?? "network",
14028
+ ...failure.party ? { "network.party": failure.party } : {},
13935
14029
  ...failure.transport ? { "network.transport": failure.transport } : {},
13936
14030
  ...failure.errorName ? { "network.error_name": failure.errorName } : {}
13937
14031
  };
@@ -13941,6 +14035,7 @@
13941
14035
  method: failure.method || "GET",
13942
14036
  url: failure.url || "(unknown url)"
13943
14037
  };
14038
+ if (failure.origin) http.origin = failure.origin;
13944
14039
  if (failure.hostname) http.hostname = failure.hostname;
13945
14040
  if (failure.pathname) http.pathname = failure.pathname;
13946
14041
  if (failure.search) http.search = failure.search;
@@ -13953,9 +14048,19 @@
13953
14048
  };
13954
14049
  if (failure.errorName) fail.name = failure.errorName;
13955
14050
  if (failure.errorMessage) fail.message = failure.errorMessage;
14051
+ const statusCode = typeof failure.status === "number" && failure.status > 0 ? failure.status : null;
13956
14052
  return {
13957
14053
  http,
13958
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),
13959
14064
  durationMs: failure.durationMs,
13960
14065
  aborted: failure.aborted ?? false,
13961
14066
  ok: failure.ok ?? false
@@ -14001,6 +14106,7 @@
14001
14106
  disableDefaultIntegrations: options.disableDefaultIntegrations ?? false,
14002
14107
  captureFailedRequests: options.captureFailedRequests ?? true,
14003
14108
  captureNetworkErrors: options.captureNetworkErrors ?? true,
14109
+ networkErrorOrigins: options.networkErrorOrigins ?? [],
14004
14110
  includeNetworkUrlQuery: options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false,
14005
14111
  failedRequestStatusCodes: options.failedRequestStatusCodes ?? [[500, 599]],
14006
14112
  failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? []
@@ -14130,6 +14236,8 @@
14130
14236
  installNetworkHook({
14131
14237
  captureFailedRequests: this.options.captureFailedRequests,
14132
14238
  captureNetworkErrors: this.options.captureNetworkErrors,
14239
+ networkErrorOrigins: this.options.networkErrorOrigins,
14240
+ pageOrigin: typeof location !== "undefined" ? location.origin : void 0,
14133
14241
  includeNetworkUrlQuery: this.options.includeNetworkUrlQuery,
14134
14242
  failedRequestStatusCodes: this.options.failedRequestStatusCodes,
14135
14243
  failedRequestIgnoreUrls: this.options.failedRequestIgnoreUrls,
@@ -14223,6 +14331,7 @@
14223
14331
  }
14224
14332
  const correlated = this.consumeCorrelatedNetworkFailure(err);
14225
14333
  if (correlated?.promoted) return;
14334
+ if (correlated && correlated.party === "third_party") return;
14226
14335
  const mergedContext = correlated ? mergeNetworkFailureContext(context, correlated) : context;
14227
14336
  this.capturing = true;
14228
14337
  try {
@@ -14705,6 +14814,13 @@
14705
14814
  );
14706
14815
  }
14707
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
+ }
14708
14824
  const now = Date.now();
14709
14825
  this.pruneRecentNetworkFailures(now);
14710
14826
  for (let i = this.recentNetworkFailures.length - 1; i >= 0; i--) {
@@ -14715,6 +14831,8 @@
14715
14831
  existing.errorName = meta.errorName ?? existing.errorName;
14716
14832
  existing.aborted = meta.aborted ?? existing.aborted;
14717
14833
  existing.failureKind = meta.failureKind ?? existing.failureKind;
14834
+ existing.party = meta.party ?? existing.party;
14835
+ existing.origin = meta.origin ?? existing.origin;
14718
14836
  return;
14719
14837
  }
14720
14838
  }
@@ -14725,11 +14843,12 @@
14725
14843
  });
14726
14844
  }
14727
14845
  /**
14728
- * Find a recent transport failure for a bare fetch TypeError.
14846
+ * Find a recent transport failure for a bare fetch TypeError / TimeoutError.
14729
14847
  * Consumes the entry so a later duplicate path cannot reuse it.
14730
14848
  */
14731
14849
  consumeCorrelatedNetworkFailure(error) {
14732
- if (!isLikelyNetworkFetchError(error)) return null;
14850
+ if (!isCorrelatableTransportError(error)) return null;
14851
+ const wantKind = isTimeoutError(error) ? "timeout" : "network";
14733
14852
  const now = Date.now();
14734
14853
  this.pruneRecentNetworkFailures(now);
14735
14854
  const takeAt = (index2) => {
@@ -14738,14 +14857,14 @@
14738
14857
  };
14739
14858
  for (let i = this.recentNetworkFailures.length - 1; i >= 0; i--) {
14740
14859
  const failure = this.recentNetworkFailures[i];
14741
- if (failure.aborted || failure.failureKind !== "network") continue;
14860
+ if (failure.aborted || failure.failureKind !== wantKind) continue;
14742
14861
  if (failure.errorMessage && failure.errorMessage === error.message) {
14743
14862
  return takeAt(i);
14744
14863
  }
14745
14864
  }
14746
14865
  for (let i = this.recentNetworkFailures.length - 1; i >= 0; i--) {
14747
14866
  const failure = this.recentNetworkFailures[i];
14748
- if (failure.aborted || failure.failureKind !== "network") continue;
14867
+ if (failure.aborted || failure.failureKind !== wantKind) continue;
14749
14868
  return takeAt(i);
14750
14869
  }
14751
14870
  return null;