@newtalaria/browser 0.1.15 → 0.1.17
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 +39 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/index.js +234 -77
- package/dist/index.js.map +2 -2
- package/dist/replay/hooks.d.ts +26 -4
- package/dist/replay/hooks.d.ts.map +1 -1
- package/dist/replay/privacy.d.ts +18 -1
- package/dist/replay/privacy.d.ts.map +1 -1
- package/dist/sdk_meta.d.ts +1 -1
- package/dist/talaria.browser.iife.js +234 -77
- package/dist/talaria.browser.iife.js.map +2 -2
- package/dist/types.d.ts +11 -0
- 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 +11 -0
- package/dist/utils/network_error.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/replay/hooks.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { FailedRequestStatusCode } from '../types.js';
|
|
2
|
+
/** How the request was issued. */
|
|
3
|
+
export type NetworkTransport = 'fetch' | 'xhr';
|
|
4
|
+
/**
|
|
5
|
+
* Failure classification for network telemetry.
|
|
6
|
+
* `cors` is intentionally absent — browsers do not expose CORS reliably
|
|
7
|
+
* (status 0 / Failed to fetch cover many causes).
|
|
8
|
+
*/
|
|
9
|
+
export type NetworkFailureKind = 'http' | 'network' | 'abort' | 'timeout';
|
|
2
10
|
export type Teardown = () => void;
|
|
3
11
|
/**
|
|
4
12
|
* Fire when the document becomes visible again (tab focus) or is restored
|
|
@@ -8,16 +16,21 @@ export type Teardown = () => void;
|
|
|
8
16
|
export declare function installVisibilityResumeHook(onResume: () => void): Teardown;
|
|
9
17
|
export interface NetworkMeta {
|
|
10
18
|
method: string;
|
|
19
|
+
/** Sanitized request URL (origin + pathname; query stripped by default). */
|
|
11
20
|
url: string;
|
|
21
|
+
hostname?: string;
|
|
22
|
+
pathname?: string;
|
|
23
|
+
/** Only when query capture is enabled. */
|
|
24
|
+
search?: string;
|
|
12
25
|
status?: number;
|
|
13
26
|
durationMs?: number;
|
|
14
27
|
ok?: boolean;
|
|
28
|
+
transport?: NetworkTransport;
|
|
15
29
|
/** Present on transport failures (fetch reject / XHR status 0). */
|
|
16
30
|
errorName?: string;
|
|
17
31
|
errorMessage?: string;
|
|
18
32
|
aborted?: boolean;
|
|
19
|
-
|
|
20
|
-
failureKind?: 'http' | 'network';
|
|
33
|
+
failureKind?: NetworkFailureKind;
|
|
21
34
|
}
|
|
22
35
|
export interface NetworkHookOptions {
|
|
23
36
|
/** Still emit rrweb breadcrumbs for all requests. */
|
|
@@ -37,6 +50,13 @@ export interface NetworkHookOptions {
|
|
|
37
50
|
captureFailedRequests?: boolean;
|
|
38
51
|
/** Promote CORS/offline/DNS fetch failures (default `true`). */
|
|
39
52
|
captureNetworkErrors?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Keep URL query strings in network telemetry (after sensitive-key redaction).
|
|
55
|
+
* Default `false`. Prefer `captureRequestQueryParameters` (same meaning).
|
|
56
|
+
*/
|
|
57
|
+
includeNetworkUrlQuery?: boolean;
|
|
58
|
+
/** Alias of `includeNetworkUrlQuery` — privacy-preserving default is `false`. */
|
|
59
|
+
captureRequestQueryParameters?: boolean;
|
|
40
60
|
failedRequestStatusCodes?: FailedRequestStatusCode[];
|
|
41
61
|
failedRequestIgnoreUrls?: string[];
|
|
42
62
|
/** SDK base URL — requests under this host + /events|/replays are never promoted. */
|
|
@@ -57,8 +77,8 @@ export declare function shouldPromoteFailedRequest(meta: NetworkMeta, opts: {
|
|
|
57
77
|
status: number;
|
|
58
78
|
};
|
|
59
79
|
/**
|
|
60
|
-
* Promote fetch rejects / XHR status-0 failures (
|
|
61
|
-
*
|
|
80
|
+
* Promote fetch rejects / XHR status-0 failures (offline, DNS, blocked, etc.).
|
|
81
|
+
* Abort is never promoted; timeout may be promoted as `failureKind: timeout`.
|
|
62
82
|
*/
|
|
63
83
|
export declare function shouldPromoteNetworkError(meta: NetworkMeta, opts: {
|
|
64
84
|
captureNetworkErrors: boolean;
|
|
@@ -66,6 +86,8 @@ export declare function shouldPromoteNetworkError(meta: NetworkMeta, opts: {
|
|
|
66
86
|
talariaBaseUrl?: string;
|
|
67
87
|
}): boolean;
|
|
68
88
|
export declare function enrichNetworkMeta(meta: NetworkMeta): NetworkMeta;
|
|
89
|
+
/** 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'>;
|
|
69
91
|
/** Capture fetch / XHR metadata (no bodies, no auth headers); optionally promote failures. */
|
|
70
92
|
export declare function installNetworkHook(options?: NetworkHookOptions): Teardown;
|
|
71
93
|
//# 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;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/replay/hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/replay/privacy.d.ts
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
/** Redact sensitive query params from a URL
|
|
1
|
+
/** Redact sensitive query params from a URL (keeps other query keys). */
|
|
2
2
|
export declare function redactUrl(raw: string): string;
|
|
3
|
+
export interface SanitizedNetworkUrl {
|
|
4
|
+
/** Safe URL for telemetry: origin + pathname (no query/hash by default). */
|
|
5
|
+
url: string;
|
|
6
|
+
hostname: string;
|
|
7
|
+
pathname: string;
|
|
8
|
+
/** Present only when query capture is opted in (after sensitive-key redaction). */
|
|
9
|
+
search?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Sanitize a request URL for network breadcrumbs / failure events.
|
|
13
|
+
* Default: drop query string and fragment (GA/ads URLs carry session ids).
|
|
14
|
+
* Opt-in `includeQuery` keeps search after `redactUrl` filtering.
|
|
15
|
+
*/
|
|
16
|
+
export declare function sanitizeNetworkUrl(raw: string, opts?: {
|
|
17
|
+
includeQuery?: boolean;
|
|
18
|
+
baseHref?: string;
|
|
19
|
+
}): SanitizedNetworkUrl;
|
|
3
20
|
export declare function defaultBlockSelector(extra?: string): string;
|
|
4
21
|
//# sourceMappingURL=privacy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/replay/privacy.ts"],"names":[],"mappings":"AAEA,
|
|
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"}
|
package/dist/sdk_meta.d.ts
CHANGED
|
@@ -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
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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.
|
|
277
|
+
var SDK_VERSION = "0.1.17";
|
|
232
278
|
|
|
233
279
|
// src/transport/serverpod.ts
|
|
234
280
|
var ServerpodTransport = class {
|
|
@@ -13452,6 +13498,40 @@
|
|
|
13452
13498
|
return raw.replace(SENSITIVE_QUERY, (_m, name) => `${name}=[Filtered]`);
|
|
13453
13499
|
}
|
|
13454
13500
|
}
|
|
13501
|
+
function sanitizeNetworkUrl(raw, opts) {
|
|
13502
|
+
const baseHref = opts?.baseHref ?? (typeof location !== "undefined" ? location.href : void 0);
|
|
13503
|
+
try {
|
|
13504
|
+
const parsed = new URL(raw, baseHref);
|
|
13505
|
+
const hostname = parsed.hostname;
|
|
13506
|
+
const pathname = parsed.pathname || "/";
|
|
13507
|
+
const originPath = `${parsed.origin}${pathname}`;
|
|
13508
|
+
if (!opts?.includeQuery) {
|
|
13509
|
+
return { url: originPath, hostname, pathname };
|
|
13510
|
+
}
|
|
13511
|
+
const redacted = new URL(redactUrl(parsed.toString()));
|
|
13512
|
+
const search = redacted.search || void 0;
|
|
13513
|
+
return {
|
|
13514
|
+
url: search ? `${originPath}${search}` : originPath,
|
|
13515
|
+
hostname,
|
|
13516
|
+
pathname,
|
|
13517
|
+
...search ? { search } : {}
|
|
13518
|
+
};
|
|
13519
|
+
} catch {
|
|
13520
|
+
const stripped = (raw.split(/[?#]/)[0] ?? raw).trim() || raw;
|
|
13521
|
+
if (!opts?.includeQuery) {
|
|
13522
|
+
return { url: stripped, hostname: "", pathname: stripped };
|
|
13523
|
+
}
|
|
13524
|
+
const redacted = redactUrl(raw);
|
|
13525
|
+
const qIndex = redacted.indexOf("?");
|
|
13526
|
+
const search = qIndex >= 0 ? redacted.slice(qIndex) : void 0;
|
|
13527
|
+
return {
|
|
13528
|
+
url: redacted.split("#")[0] || redacted,
|
|
13529
|
+
hostname: "",
|
|
13530
|
+
pathname: stripped,
|
|
13531
|
+
...search ? { search } : {}
|
|
13532
|
+
};
|
|
13533
|
+
}
|
|
13534
|
+
}
|
|
13455
13535
|
function defaultBlockSelector(extra) {
|
|
13456
13536
|
const parts = [
|
|
13457
13537
|
"[data-talaria-mask]",
|
|
@@ -13514,7 +13594,7 @@
|
|
|
13514
13594
|
}
|
|
13515
13595
|
function isLikelyNetworkFetchError(error) {
|
|
13516
13596
|
if (!(error instanceof Error)) return false;
|
|
13517
|
-
if (error.name === "AbortError") return false;
|
|
13597
|
+
if (error.name === "AbortError" || error.name === "TimeoutError") return false;
|
|
13518
13598
|
if (error.name === "NetworkError") return true;
|
|
13519
13599
|
const msg = error.message.toLowerCase().trim();
|
|
13520
13600
|
return msg === "failed to fetch" || msg === "load failed" || msg === "networkerror when attempting to fetch resource." || msg.startsWith("networkerror") || msg.includes("failed to fetch");
|
|
@@ -13524,15 +13604,22 @@
|
|
|
13524
13604
|
return {
|
|
13525
13605
|
errorName: error.name || "Error",
|
|
13526
13606
|
errorMessage: (error.message || "").slice(0, 500),
|
|
13527
|
-
aborted: error.name === "AbortError"
|
|
13607
|
+
aborted: error.name === "AbortError",
|
|
13608
|
+
timedOut: error.name === "TimeoutError"
|
|
13528
13609
|
};
|
|
13529
13610
|
}
|
|
13530
13611
|
return {
|
|
13531
13612
|
errorName: "Error",
|
|
13532
13613
|
errorMessage: String(error).slice(0, 500),
|
|
13533
|
-
aborted: false
|
|
13614
|
+
aborted: false,
|
|
13615
|
+
timedOut: false
|
|
13534
13616
|
};
|
|
13535
13617
|
}
|
|
13618
|
+
function classifyTransportFailure(opts) {
|
|
13619
|
+
if (opts.aborted || opts.errorName === "AbortError") return "abort";
|
|
13620
|
+
if (opts.timedOut || opts.errorName === "TimeoutError") return "timeout";
|
|
13621
|
+
return "network";
|
|
13622
|
+
}
|
|
13536
13623
|
|
|
13537
13624
|
// src/replay/hooks.ts
|
|
13538
13625
|
function installVisibilityResumeHook(onResume) {
|
|
@@ -13627,7 +13714,6 @@
|
|
|
13627
13714
|
try {
|
|
13628
13715
|
record.addCustomEvent("talaria-network", {
|
|
13629
13716
|
...meta,
|
|
13630
|
-
url: redactUrl(meta.url),
|
|
13631
13717
|
timestamp: Date.now()
|
|
13632
13718
|
});
|
|
13633
13719
|
} catch {
|
|
@@ -13674,8 +13760,10 @@
|
|
|
13674
13760
|
}
|
|
13675
13761
|
function shouldPromoteNetworkError(meta, opts) {
|
|
13676
13762
|
if (!opts.captureNetworkErrors) return false;
|
|
13677
|
-
if (meta.aborted) return false;
|
|
13678
|
-
if (meta.failureKind !== "network")
|
|
13763
|
+
if (meta.aborted || meta.failureKind === "abort") return false;
|
|
13764
|
+
if (meta.failureKind !== "network" && meta.failureKind !== "timeout") {
|
|
13765
|
+
return false;
|
|
13766
|
+
}
|
|
13679
13767
|
const ignore = buildFailedRequestIgnoreUrls(
|
|
13680
13768
|
opts.failedRequestIgnoreUrls,
|
|
13681
13769
|
opts.talariaBaseUrl
|
|
@@ -13691,14 +13779,28 @@
|
|
|
13691
13779
|
failureKind: meta.ok === false ? "http" : meta.failureKind
|
|
13692
13780
|
};
|
|
13693
13781
|
}
|
|
13694
|
-
if (meta.ok === false || meta.errorName || meta.errorMessage) {
|
|
13782
|
+
if (meta.ok === false || meta.errorName || meta.errorMessage || meta.aborted) {
|
|
13783
|
+
const failureKind = meta.failureKind === "abort" || meta.failureKind === "timeout" || meta.failureKind === "network" ? meta.failureKind : classifyTransportFailure({
|
|
13784
|
+
aborted: meta.aborted,
|
|
13785
|
+
errorName: meta.errorName
|
|
13786
|
+
});
|
|
13695
13787
|
return {
|
|
13696
13788
|
...meta,
|
|
13697
|
-
failureKind
|
|
13789
|
+
failureKind,
|
|
13790
|
+
aborted: meta.aborted ?? failureKind === "abort"
|
|
13698
13791
|
};
|
|
13699
13792
|
}
|
|
13700
13793
|
return meta;
|
|
13701
13794
|
}
|
|
13795
|
+
function networkUrlParts(rawUrl, includeQuery) {
|
|
13796
|
+
const parts = sanitizeNetworkUrl(rawUrl, { includeQuery });
|
|
13797
|
+
return {
|
|
13798
|
+
url: parts.url,
|
|
13799
|
+
hostname: parts.hostname,
|
|
13800
|
+
pathname: parts.pathname,
|
|
13801
|
+
...parts.search ? { search: parts.search } : {}
|
|
13802
|
+
};
|
|
13803
|
+
}
|
|
13702
13804
|
function installNetworkHook(options = {}) {
|
|
13703
13805
|
const originalFetch = typeof fetch === "function" ? fetch.bind(globalThis) : null;
|
|
13704
13806
|
const XHR = typeof XMLHttpRequest !== "undefined" ? XMLHttpRequest : null;
|
|
@@ -13711,8 +13813,13 @@
|
|
|
13711
13813
|
failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? [],
|
|
13712
13814
|
talariaBaseUrl: options.talariaBaseUrl
|
|
13713
13815
|
};
|
|
13816
|
+
const includeQuery = options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false;
|
|
13714
13817
|
const handleMeta = (meta) => {
|
|
13715
|
-
const
|
|
13818
|
+
const { rawUrl, ...rest } = meta;
|
|
13819
|
+
const enriched = enrichNetworkMeta({
|
|
13820
|
+
...rest,
|
|
13821
|
+
...networkUrlParts(rawUrl, includeQuery)
|
|
13822
|
+
});
|
|
13716
13823
|
try {
|
|
13717
13824
|
emitNetwork(enriched);
|
|
13718
13825
|
options.onNetwork?.(enriched);
|
|
@@ -13728,12 +13835,14 @@
|
|
|
13728
13835
|
globalThis.fetch = async (input2, init) => {
|
|
13729
13836
|
const started = Date.now();
|
|
13730
13837
|
const method = (init?.method ?? (input2 instanceof Request ? input2.method : "GET")).toUpperCase();
|
|
13731
|
-
const
|
|
13838
|
+
const rawUrl = typeof input2 === "string" ? input2 : input2 instanceof URL ? input2.toString() : input2.url;
|
|
13732
13839
|
try {
|
|
13733
13840
|
const response = await originalFetch(input2, init);
|
|
13734
13841
|
handleMeta({
|
|
13735
13842
|
method,
|
|
13736
|
-
|
|
13843
|
+
rawUrl,
|
|
13844
|
+
url: rawUrl,
|
|
13845
|
+
transport: "fetch",
|
|
13737
13846
|
status: response.status,
|
|
13738
13847
|
durationMs: Date.now() - started,
|
|
13739
13848
|
ok: response.ok
|
|
@@ -13741,15 +13850,18 @@
|
|
|
13741
13850
|
return response;
|
|
13742
13851
|
} catch (error) {
|
|
13743
13852
|
const described = describeUnknownError(error);
|
|
13853
|
+
const failureKind = classifyTransportFailure(described);
|
|
13744
13854
|
handleMeta({
|
|
13745
13855
|
method,
|
|
13746
|
-
|
|
13856
|
+
rawUrl,
|
|
13857
|
+
url: rawUrl,
|
|
13858
|
+
transport: "fetch",
|
|
13747
13859
|
durationMs: Date.now() - started,
|
|
13748
13860
|
ok: false,
|
|
13749
13861
|
errorName: described.errorName,
|
|
13750
13862
|
errorMessage: described.errorMessage,
|
|
13751
13863
|
aborted: described.aborted,
|
|
13752
|
-
failureKind
|
|
13864
|
+
failureKind
|
|
13753
13865
|
});
|
|
13754
13866
|
throw error;
|
|
13755
13867
|
}
|
|
@@ -13763,19 +13875,44 @@
|
|
|
13763
13875
|
};
|
|
13764
13876
|
XHR.prototype.send = function(body) {
|
|
13765
13877
|
this.__talariaStarted = Date.now();
|
|
13878
|
+
this.__talariaTimedOut = false;
|
|
13879
|
+
this.__talariaAborted = false;
|
|
13880
|
+
this.addEventListener(
|
|
13881
|
+
"timeout",
|
|
13882
|
+
() => {
|
|
13883
|
+
this.__talariaTimedOut = true;
|
|
13884
|
+
},
|
|
13885
|
+
{ once: true }
|
|
13886
|
+
);
|
|
13887
|
+
this.addEventListener(
|
|
13888
|
+
"abort",
|
|
13889
|
+
() => {
|
|
13890
|
+
this.__talariaAborted = true;
|
|
13891
|
+
},
|
|
13892
|
+
{ once: true }
|
|
13893
|
+
);
|
|
13766
13894
|
const onDone = () => {
|
|
13767
13895
|
const status = this.status;
|
|
13768
|
-
const
|
|
13896
|
+
const transportFail = status === 0;
|
|
13897
|
+
const rawUrl = this.__talariaUrl ?? "";
|
|
13898
|
+
const aborted = !!this.__talariaAborted;
|
|
13899
|
+
const timedOut = !!this.__talariaTimedOut;
|
|
13900
|
+
const failureKind = transportFail ? classifyTransportFailure({ aborted, timedOut }) : void 0;
|
|
13901
|
+
const errorName = transportFail ? failureKind === "abort" ? "AbortError" : failureKind === "timeout" ? "TimeoutError" : "NetworkError" : void 0;
|
|
13902
|
+
const errorMessage = transportFail ? failureKind === "abort" ? "XMLHttpRequest aborted" : failureKind === "timeout" ? "XMLHttpRequest timed out" : "XMLHttpRequest failed (status 0)" : void 0;
|
|
13769
13903
|
handleMeta({
|
|
13770
13904
|
method: this.__talariaMethod ?? "GET",
|
|
13771
|
-
|
|
13905
|
+
rawUrl,
|
|
13906
|
+
url: rawUrl,
|
|
13907
|
+
transport: "xhr",
|
|
13772
13908
|
status,
|
|
13773
13909
|
durationMs: Date.now() - (this.__talariaStarted ?? Date.now()),
|
|
13774
13910
|
ok: status >= 200 && status < 400,
|
|
13775
|
-
...
|
|
13776
|
-
failureKind
|
|
13777
|
-
errorName
|
|
13778
|
-
errorMessage
|
|
13911
|
+
...transportFail ? {
|
|
13912
|
+
failureKind,
|
|
13913
|
+
errorName,
|
|
13914
|
+
errorMessage,
|
|
13915
|
+
aborted
|
|
13779
13916
|
} : {}
|
|
13780
13917
|
});
|
|
13781
13918
|
};
|
|
@@ -13791,28 +13928,49 @@
|
|
|
13791
13928
|
}
|
|
13792
13929
|
|
|
13793
13930
|
// src/client.ts
|
|
13931
|
+
function networkFailureTags(failure) {
|
|
13932
|
+
return {
|
|
13933
|
+
"http.method": failure.method || "GET",
|
|
13934
|
+
"network.failure_kind": failure.failureKind ?? "network",
|
|
13935
|
+
...failure.transport ? { "network.transport": failure.transport } : {},
|
|
13936
|
+
...failure.errorName ? { "network.error_name": failure.errorName } : {}
|
|
13937
|
+
};
|
|
13938
|
+
}
|
|
13939
|
+
function networkFailureExtra(failure) {
|
|
13940
|
+
const http = {
|
|
13941
|
+
method: failure.method || "GET",
|
|
13942
|
+
url: failure.url || "(unknown url)"
|
|
13943
|
+
};
|
|
13944
|
+
if (failure.hostname) http.hostname = failure.hostname;
|
|
13945
|
+
if (failure.pathname) http.pathname = failure.pathname;
|
|
13946
|
+
if (failure.search) http.search = failure.search;
|
|
13947
|
+
if (typeof failure.status === "number" && failure.status > 0) {
|
|
13948
|
+
http.status = failure.status;
|
|
13949
|
+
}
|
|
13950
|
+
if (failure.transport) http.transport = failure.transport;
|
|
13951
|
+
const fail = {
|
|
13952
|
+
kind: failure.failureKind ?? "network"
|
|
13953
|
+
};
|
|
13954
|
+
if (failure.errorName) fail.name = failure.errorName;
|
|
13955
|
+
if (failure.errorMessage) fail.message = failure.errorMessage;
|
|
13956
|
+
return {
|
|
13957
|
+
http,
|
|
13958
|
+
failure: fail,
|
|
13959
|
+
durationMs: failure.durationMs,
|
|
13960
|
+
aborted: failure.aborted ?? false,
|
|
13961
|
+
ok: failure.ok ?? false
|
|
13962
|
+
};
|
|
13963
|
+
}
|
|
13794
13964
|
function mergeNetworkFailureContext(context, failure) {
|
|
13795
|
-
const url = redactUrl(failure.url || "(unknown url)");
|
|
13796
13965
|
return {
|
|
13797
13966
|
...context,
|
|
13798
13967
|
tags: {
|
|
13799
13968
|
...context?.tags ?? {},
|
|
13800
|
-
|
|
13801
|
-
"network.failure_kind": "network",
|
|
13802
|
-
...failure.errorName ? { "network.error_name": failure.errorName } : {}
|
|
13969
|
+
...networkFailureTags(failure)
|
|
13803
13970
|
},
|
|
13804
13971
|
extra: {
|
|
13805
13972
|
...context?.extra ?? {},
|
|
13806
|
-
|
|
13807
|
-
method: failure.method,
|
|
13808
|
-
url,
|
|
13809
|
-
durationMs: failure.durationMs,
|
|
13810
|
-
ok: false,
|
|
13811
|
-
failureKind: "network",
|
|
13812
|
-
errorName: failure.errorName,
|
|
13813
|
-
errorMessage: failure.errorMessage,
|
|
13814
|
-
aborted: failure.aborted ?? false
|
|
13815
|
-
}
|
|
13973
|
+
...networkFailureExtra(failure)
|
|
13816
13974
|
}
|
|
13817
13975
|
};
|
|
13818
13976
|
}
|
|
@@ -13843,6 +14001,7 @@
|
|
|
13843
14001
|
disableDefaultIntegrations: options.disableDefaultIntegrations ?? false,
|
|
13844
14002
|
captureFailedRequests: options.captureFailedRequests ?? true,
|
|
13845
14003
|
captureNetworkErrors: options.captureNetworkErrors ?? true,
|
|
14004
|
+
includeNetworkUrlQuery: options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false,
|
|
13846
14005
|
failedRequestStatusCodes: options.failedRequestStatusCodes ?? [[500, 599]],
|
|
13847
14006
|
failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? []
|
|
13848
14007
|
};
|
|
@@ -13971,59 +14130,48 @@
|
|
|
13971
14130
|
installNetworkHook({
|
|
13972
14131
|
captureFailedRequests: this.options.captureFailedRequests,
|
|
13973
14132
|
captureNetworkErrors: this.options.captureNetworkErrors,
|
|
14133
|
+
includeNetworkUrlQuery: this.options.includeNetworkUrlQuery,
|
|
13974
14134
|
failedRequestStatusCodes: this.options.failedRequestStatusCodes,
|
|
13975
14135
|
failedRequestIgnoreUrls: this.options.failedRequestIgnoreUrls,
|
|
13976
14136
|
talariaBaseUrl: this.options.baseUrl,
|
|
13977
14137
|
onNetwork: (meta) => {
|
|
13978
|
-
if (meta.failureKind === "network" && !meta.aborted) {
|
|
14138
|
+
if ((meta.failureKind === "network" || meta.failureKind === "timeout") && !meta.aborted) {
|
|
13979
14139
|
this.rememberNetworkFailure(meta, { promoted: false });
|
|
13980
14140
|
}
|
|
13981
14141
|
},
|
|
13982
14142
|
onFailedRequest: (meta) => {
|
|
13983
14143
|
const status = meta.status;
|
|
13984
14144
|
const method = meta.method || "GET";
|
|
13985
|
-
const url =
|
|
14145
|
+
const url = meta.url || "(unknown url)";
|
|
14146
|
+
const enriched = {
|
|
14147
|
+
...meta,
|
|
14148
|
+
failureKind: "http",
|
|
14149
|
+
aborted: false
|
|
14150
|
+
};
|
|
13986
14151
|
void this.captureMessage(
|
|
13987
14152
|
`HTTP ${status}: ${method} ${url}`,
|
|
13988
14153
|
status >= 500 ? "error" : "warning",
|
|
13989
14154
|
{
|
|
13990
14155
|
tags: {
|
|
13991
|
-
|
|
13992
|
-
"http.
|
|
13993
|
-
"network.failure_kind": "http"
|
|
14156
|
+
...networkFailureTags(enriched),
|
|
14157
|
+
"http.status_code": String(status)
|
|
13994
14158
|
},
|
|
13995
|
-
extra:
|
|
13996
|
-
url,
|
|
13997
|
-
durationMs: meta.durationMs,
|
|
13998
|
-
ok: meta.ok,
|
|
13999
|
-
failureKind: "http"
|
|
14000
|
-
}
|
|
14159
|
+
extra: networkFailureExtra(enriched)
|
|
14001
14160
|
}
|
|
14002
14161
|
);
|
|
14003
14162
|
},
|
|
14004
14163
|
onNetworkError: (meta) => {
|
|
14005
14164
|
this.rememberNetworkFailure(meta, { promoted: true });
|
|
14006
14165
|
const method = meta.method || "GET";
|
|
14007
|
-
const url =
|
|
14166
|
+
const url = meta.url || "(unknown url)";
|
|
14008
14167
|
const errLabel = meta.errorName && meta.errorMessage ? `${meta.errorName}: ${meta.errorMessage}` : meta.errorMessage || meta.errorName || "Failed to fetch";
|
|
14168
|
+
const prefix = meta.failureKind === "timeout" ? "Timeout error" : "Network error";
|
|
14009
14169
|
void this.captureMessage(
|
|
14010
|
-
|
|
14170
|
+
`${prefix}: ${method} ${url} \u2014 ${errLabel}`,
|
|
14011
14171
|
"error",
|
|
14012
14172
|
{
|
|
14013
|
-
tags:
|
|
14014
|
-
|
|
14015
|
-
"network.failure_kind": "network",
|
|
14016
|
-
...meta.errorName ? { "network.error_name": meta.errorName } : {}
|
|
14017
|
-
},
|
|
14018
|
-
extra: {
|
|
14019
|
-
url,
|
|
14020
|
-
durationMs: meta.durationMs,
|
|
14021
|
-
ok: false,
|
|
14022
|
-
failureKind: "network",
|
|
14023
|
-
errorName: meta.errorName,
|
|
14024
|
-
errorMessage: meta.errorMessage,
|
|
14025
|
-
aborted: false
|
|
14026
|
-
}
|
|
14173
|
+
tags: networkFailureTags(meta),
|
|
14174
|
+
extra: networkFailureExtra(meta)
|
|
14027
14175
|
}
|
|
14028
14176
|
);
|
|
14029
14177
|
}
|
|
@@ -14202,6 +14350,7 @@
|
|
|
14202
14350
|
throw new Error("@newtalaria/browser: call Talaria.init() first");
|
|
14203
14351
|
}
|
|
14204
14352
|
if (this.ingestDisabled) return;
|
|
14353
|
+
const occurredAt = /* @__PURE__ */ new Date();
|
|
14205
14354
|
const isErrorLike = args.level === "error" || args.level === "fatal";
|
|
14206
14355
|
let errorClipOutcome = null;
|
|
14207
14356
|
let attemptedErrorClip = false;
|
|
@@ -14259,6 +14408,7 @@
|
|
|
14259
14408
|
errorClipOutcome = { status: "failed", reason: "buffer_empty" };
|
|
14260
14409
|
}
|
|
14261
14410
|
}
|
|
14411
|
+
const queuedMs = Math.max(0, Date.now() - occurredAt.getTime());
|
|
14262
14412
|
const tags = applyReplayCaptureTags(
|
|
14263
14413
|
{
|
|
14264
14414
|
...this.browserContext ? browserContextTags(this.browserContext) : {},
|
|
@@ -14277,10 +14427,17 @@
|
|
|
14277
14427
|
osVersion: this.browserContext.osVersion,
|
|
14278
14428
|
device: this.browserContext.device,
|
|
14279
14429
|
language: this.browserContext.language,
|
|
14280
|
-
userAgent: this.browserContext.userAgent
|
|
14430
|
+
userAgent: this.browserContext.userAgent,
|
|
14431
|
+
bot: this.browserContext.bot,
|
|
14432
|
+
...this.browserContext.botName ? { botName: this.browserContext.botName } : {}
|
|
14281
14433
|
}
|
|
14282
14434
|
} : {},
|
|
14283
|
-
sdk: {
|
|
14435
|
+
sdk: {
|
|
14436
|
+
name: SDK_NAME,
|
|
14437
|
+
version: SDK_VERSION,
|
|
14438
|
+
// Time spent in capture() before ingest (mostly replay flush).
|
|
14439
|
+
...queuedMs >= 50 ? { queuedMs } : {}
|
|
14440
|
+
},
|
|
14284
14441
|
...args.context?.extra
|
|
14285
14442
|
},
|
|
14286
14443
|
errorClipOutcome
|
|
@@ -14300,7 +14457,7 @@
|
|
|
14300
14457
|
url: typeof location !== "undefined" ? location.href : void 0,
|
|
14301
14458
|
tags: Object.keys(tags).length ? tags : void 0,
|
|
14302
14459
|
extraJson: extra ? JSON.stringify(extra) : void 0,
|
|
14303
|
-
timestamp:
|
|
14460
|
+
timestamp: occurredAt.toISOString(),
|
|
14304
14461
|
keepalive: args.keepalive
|
|
14305
14462
|
});
|
|
14306
14463
|
if (replayId && replayId === this.linkableReplayId) {
|