@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/README.md
CHANGED
|
@@ -175,7 +175,7 @@ Segment payloads are a **gzip-compressed JSON array** of rrweb events.
|
|
|
175
175
|
Custom rrweb events:
|
|
176
176
|
|
|
177
177
|
- `talaria-console` — console level + message/args (truncated)
|
|
178
|
-
- `talaria-network` — method,
|
|
178
|
+
- `talaria-network` — method, sanitized URL (no query by default), status, duration, failure metadata (no bodies / no auth headers)
|
|
179
179
|
|
|
180
180
|
Privacy defaults: `maskAllInputs: true`, password fields masked, `[data-talaria-mask]` blocked.
|
|
181
181
|
|
|
@@ -191,9 +191,33 @@ By default, completed `fetch` / XHR responses with status **500–599** are sent
|
|
|
191
191
|
|
|
192
192
|
Transport failures (CORS, offline, DNS — browser `TypeError: Failed to fetch`) are also promoted by default as:
|
|
193
193
|
|
|
194
|
-
`Network error:
|
|
194
|
+
`Network error: POST https://www.google-analytics.com/g/collect — TypeError: Failed to fetch`
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
**URL privacy:** network telemetry stores `origin + pathname` only (plus `hostname` / `pathname`). Query strings and fragments are stripped by default — analytics/ads URLs often carry `cid`, `sid`, `dl`, etc. Set `captureRequestQueryParameters: true` to keep query params after sensitive-key redaction (`token`, `api_key`, …).
|
|
197
|
+
|
|
198
|
+
Both **`fetch`** and **`XMLHttpRequest`** are instrumented (`http.transport`: `fetch` | `xhr`). Failure kinds are only claimed when detectable:
|
|
199
|
+
|
|
200
|
+
| `failure.kind` | Meaning |
|
|
201
|
+
| --- | --- |
|
|
202
|
+
| `http` | Completed response with a real status (e.g. 500) |
|
|
203
|
+
| `network` | No usable HTTP response (status 0 / Failed to fetch) — CORS, block, DNS, offline, etc. |
|
|
204
|
+
| `timeout` | `TimeoutError` or XHR `timeout` event |
|
|
205
|
+
| `abort` | `AbortError` / XHR abort — recorded in breadcrumbs, **not** promoted as events |
|
|
206
|
+
|
|
207
|
+
We **do not** classify status 0 as `cors` — browsers do not expose that reliably.
|
|
208
|
+
|
|
209
|
+
Promoted events use structured `extra`:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"http": { "method": "GET", "url": "https://ct.pinterest.com/user/", "hostname": "ct.pinterest.com", "pathname": "/user/", "transport": "xhr" },
|
|
214
|
+
"failure": { "kind": "network", "name": "NetworkError", "message": "XMLHttpRequest failed (status 0)" },
|
|
215
|
+
"durationMs": 158,
|
|
216
|
+
"aborted": false
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Crawler UAs are tagged (`bot=true`, `bot.name=Baiduspider`) without inventing a browser name.
|
|
197
221
|
|
|
198
222
|
```ts
|
|
199
223
|
Talaria.init({
|
|
@@ -202,8 +226,10 @@ Talaria.init({
|
|
|
202
226
|
environment: 'production',
|
|
203
227
|
// Default true — HTTP status promotion
|
|
204
228
|
captureFailedRequests: true,
|
|
205
|
-
// Default true —
|
|
229
|
+
// Default true — transport failures (fetch + XHR)
|
|
206
230
|
captureNetworkErrors: true,
|
|
231
|
+
// Default false — do not store ?query on network URLs
|
|
232
|
+
captureRequestQueryParameters: false,
|
|
207
233
|
// Default [[500, 599]]. CMS admin often wants 4xx too:
|
|
208
234
|
failedRequestStatusCodes: [[400, 599]],
|
|
209
235
|
// Extra URL substrings to skip (Talaria /events and /replays are always skipped)
|
|
@@ -213,6 +239,15 @@ Talaria.init({
|
|
|
213
239
|
|
|
214
240
|
`AbortError` is never promoted and is ignored by the global `unhandledrejection` handler. If a fetch failure is left unhandled after network-error promotion, the duplicate bare `TypeError` event is suppressed; if promotion is off, the exception is kept and correlated with the recent request URL/method/duration.
|
|
215
241
|
|
|
242
|
+
### Event timestamps
|
|
243
|
+
|
|
244
|
+
| Field | Meaning |
|
|
245
|
+
| --- | --- |
|
|
246
|
+
| `timestamp` | **Occurrence time** on the client (when capture began — before replay flush) |
|
|
247
|
+
| `createdAt` | **Ingest/storage time** on the server |
|
|
248
|
+
|
|
249
|
+
If replay upload runs before event ingest, `extra.sdk.queuedMs` records how long capture waited (ms). A large `createdAt - timestamp` gap is usually flush/queue delay or clock skew — not a wrong failure classification.
|
|
250
|
+
|
|
216
251
|
## Public API
|
|
217
252
|
|
|
218
253
|
| API | Description |
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EAEd,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -98,9 +98,46 @@ function isSdkInternalNoise(opts) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// src/utils/browser_context.ts
|
|
101
|
+
var BOT_RULES = [
|
|
102
|
+
{ name: "Baiduspider", re: /Baiduspider/i },
|
|
103
|
+
{ name: "Googlebot", re: /Googlebot/i },
|
|
104
|
+
{ name: "Bingbot", re: /bingbot/i },
|
|
105
|
+
{ name: "DuckDuckBot", re: /DuckDuckBot/i },
|
|
106
|
+
{ name: "YandexBot", re: /Yandex(Bot|Images)/i },
|
|
107
|
+
{ name: "Applebot", re: /Applebot/i },
|
|
108
|
+
{ name: "facebookexternalhit", re: /facebookexternalhit|Facebot/i },
|
|
109
|
+
{ name: "Twitterbot", re: /Twitterbot/i },
|
|
110
|
+
{ name: "LinkedInBot", re: /LinkedInBot/i },
|
|
111
|
+
{ name: "Slackbot", re: /Slackbot/i },
|
|
112
|
+
{ name: "Discordbot", re: /Discordbot/i },
|
|
113
|
+
{ name: "Bytespider", re: /Bytespider/i },
|
|
114
|
+
{ name: "PetalBot", re: /PetalBot/i },
|
|
115
|
+
{ name: "SemrushBot", re: /SemrushBot/i },
|
|
116
|
+
{ name: "AhrefsBot", re: /AhrefsBot/i },
|
|
117
|
+
{ name: "DotBot", re: /DotBot/i },
|
|
118
|
+
{ name: "GPTBot", re: /GPTBot/i },
|
|
119
|
+
{ name: "ClaudeBot", re: /ClaudeBot|anthropic-ai/i },
|
|
120
|
+
{ name: "Amazonbot", re: /Amazonbot/i },
|
|
121
|
+
{ name: "Sogou", re: /Sogou/i },
|
|
122
|
+
// Generic last — still mark as bot without inventing a browser name.
|
|
123
|
+
{ name: "bot", re: /\b(?:bot|crawler|spider|slurp)\b/i }
|
|
124
|
+
];
|
|
125
|
+
function detectBot(ua) {
|
|
126
|
+
if (!ua) return { bot: false };
|
|
127
|
+
for (const rule2 of BOT_RULES) {
|
|
128
|
+
if (rule2.re.test(ua)) {
|
|
129
|
+
return {
|
|
130
|
+
bot: true,
|
|
131
|
+
botName: rule2.name === "bot" ? void 0 : rule2.name
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { bot: false };
|
|
136
|
+
}
|
|
101
137
|
function parseBrowserContext(ua = typeof navigator !== "undefined" ? navigator.userAgent : "", language = typeof navigator !== "undefined" ? navigator.language : "") {
|
|
102
138
|
const userAgent = ua || "";
|
|
103
139
|
const lang = language || "";
|
|
140
|
+
const botInfo = detectBot(userAgent);
|
|
104
141
|
let name = "unknown";
|
|
105
142
|
let version = "";
|
|
106
143
|
const rules = [
|
|
@@ -111,18 +148,20 @@ function parseBrowserContext(ua = typeof navigator !== "undefined" ? navigator.u
|
|
|
111
148
|
{ name: "Chrome", re: /(?:Chrome|CriOS)\/([\d.]+)/ },
|
|
112
149
|
{ name: "Safari", re: /Version\/([\d.]+).*Safari/ }
|
|
113
150
|
];
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
151
|
+
if (!botInfo.bot) {
|
|
152
|
+
for (const rule2 of rules) {
|
|
153
|
+
const m = userAgent.match(rule2.re);
|
|
154
|
+
if (m) {
|
|
155
|
+
name = rule2.name;
|
|
156
|
+
version = m[1] ?? "";
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (name === "unknown" && /iPhone|iPad|iPod/.test(userAgent) && /AppleWebKit/.test(userAgent)) {
|
|
161
|
+
name = "Safari";
|
|
162
|
+
const m = userAgent.match(/OS ([\d_]+)/);
|
|
163
|
+
if (m?.[1]) version = m[1].replace(/_/g, ".");
|
|
120
164
|
}
|
|
121
|
-
}
|
|
122
|
-
if (name === "unknown" && /iPhone|iPad|iPod/.test(userAgent) && /AppleWebKit/.test(userAgent)) {
|
|
123
|
-
name = "Safari";
|
|
124
|
-
const m = userAgent.match(/OS ([\d_]+)/);
|
|
125
|
-
if (m?.[1]) version = m[1].replace(/_/g, ".");
|
|
126
165
|
}
|
|
127
166
|
let os = "unknown";
|
|
128
167
|
let osVersion = "";
|
|
@@ -168,7 +207,9 @@ function parseBrowserContext(ua = typeof navigator !== "undefined" ? navigator.u
|
|
|
168
207
|
osVersion,
|
|
169
208
|
device,
|
|
170
209
|
language: lang,
|
|
171
|
-
userAgent
|
|
210
|
+
userAgent,
|
|
211
|
+
bot: botInfo.bot,
|
|
212
|
+
...botInfo.botName ? { botName: botInfo.botName } : {}
|
|
172
213
|
};
|
|
173
214
|
}
|
|
174
215
|
function browserContextTags(ctx) {
|
|
@@ -177,11 +218,16 @@ function browserContextTags(ctx) {
|
|
|
177
218
|
"browser.version": ctx.version || "unknown",
|
|
178
219
|
"os.name": ctx.os,
|
|
179
220
|
"os.version": ctx.osVersion || "unknown",
|
|
180
|
-
device: ctx.device
|
|
221
|
+
device: ctx.device,
|
|
222
|
+
...ctx.bot ? {
|
|
223
|
+
bot: "true",
|
|
224
|
+
...ctx.botName ? { "bot.name": ctx.botName } : {}
|
|
225
|
+
} : {}
|
|
181
226
|
};
|
|
182
227
|
}
|
|
183
228
|
async function collectBrowserContext() {
|
|
184
229
|
const base = parseBrowserContext();
|
|
230
|
+
if (base.bot) return base;
|
|
185
231
|
const nav = typeof navigator !== "undefined" ? navigator : void 0;
|
|
186
232
|
const uad = nav?.userAgentData;
|
|
187
233
|
if (!uad) return base;
|
|
@@ -226,7 +272,7 @@ async function collectBrowserContext() {
|
|
|
226
272
|
|
|
227
273
|
// src/sdk_meta.ts
|
|
228
274
|
var SDK_NAME = "@newtalaria/browser";
|
|
229
|
-
var SDK_VERSION = "0.1.
|
|
275
|
+
var SDK_VERSION = "0.1.17";
|
|
230
276
|
|
|
231
277
|
// src/transport/serverpod.ts
|
|
232
278
|
var ServerpodTransport = class {
|
|
@@ -13450,6 +13496,40 @@ function redactUrl(raw) {
|
|
|
13450
13496
|
return raw.replace(SENSITIVE_QUERY, (_m, name) => `${name}=[Filtered]`);
|
|
13451
13497
|
}
|
|
13452
13498
|
}
|
|
13499
|
+
function sanitizeNetworkUrl(raw, opts) {
|
|
13500
|
+
const baseHref = opts?.baseHref ?? (typeof location !== "undefined" ? location.href : void 0);
|
|
13501
|
+
try {
|
|
13502
|
+
const parsed = new URL(raw, baseHref);
|
|
13503
|
+
const hostname = parsed.hostname;
|
|
13504
|
+
const pathname = parsed.pathname || "/";
|
|
13505
|
+
const originPath = `${parsed.origin}${pathname}`;
|
|
13506
|
+
if (!opts?.includeQuery) {
|
|
13507
|
+
return { url: originPath, hostname, pathname };
|
|
13508
|
+
}
|
|
13509
|
+
const redacted = new URL(redactUrl(parsed.toString()));
|
|
13510
|
+
const search = redacted.search || void 0;
|
|
13511
|
+
return {
|
|
13512
|
+
url: search ? `${originPath}${search}` : originPath,
|
|
13513
|
+
hostname,
|
|
13514
|
+
pathname,
|
|
13515
|
+
...search ? { search } : {}
|
|
13516
|
+
};
|
|
13517
|
+
} catch {
|
|
13518
|
+
const stripped = (raw.split(/[?#]/)[0] ?? raw).trim() || raw;
|
|
13519
|
+
if (!opts?.includeQuery) {
|
|
13520
|
+
return { url: stripped, hostname: "", pathname: stripped };
|
|
13521
|
+
}
|
|
13522
|
+
const redacted = redactUrl(raw);
|
|
13523
|
+
const qIndex = redacted.indexOf("?");
|
|
13524
|
+
const search = qIndex >= 0 ? redacted.slice(qIndex) : void 0;
|
|
13525
|
+
return {
|
|
13526
|
+
url: redacted.split("#")[0] || redacted,
|
|
13527
|
+
hostname: "",
|
|
13528
|
+
pathname: stripped,
|
|
13529
|
+
...search ? { search } : {}
|
|
13530
|
+
};
|
|
13531
|
+
}
|
|
13532
|
+
}
|
|
13453
13533
|
function defaultBlockSelector(extra) {
|
|
13454
13534
|
const parts = [
|
|
13455
13535
|
"[data-talaria-mask]",
|
|
@@ -13512,7 +13592,7 @@ function isAbortError(error) {
|
|
|
13512
13592
|
}
|
|
13513
13593
|
function isLikelyNetworkFetchError(error) {
|
|
13514
13594
|
if (!(error instanceof Error)) return false;
|
|
13515
|
-
if (error.name === "AbortError") return false;
|
|
13595
|
+
if (error.name === "AbortError" || error.name === "TimeoutError") return false;
|
|
13516
13596
|
if (error.name === "NetworkError") return true;
|
|
13517
13597
|
const msg = error.message.toLowerCase().trim();
|
|
13518
13598
|
return msg === "failed to fetch" || msg === "load failed" || msg === "networkerror when attempting to fetch resource." || msg.startsWith("networkerror") || msg.includes("failed to fetch");
|
|
@@ -13522,15 +13602,22 @@ function describeUnknownError(error) {
|
|
|
13522
13602
|
return {
|
|
13523
13603
|
errorName: error.name || "Error",
|
|
13524
13604
|
errorMessage: (error.message || "").slice(0, 500),
|
|
13525
|
-
aborted: error.name === "AbortError"
|
|
13605
|
+
aborted: error.name === "AbortError",
|
|
13606
|
+
timedOut: error.name === "TimeoutError"
|
|
13526
13607
|
};
|
|
13527
13608
|
}
|
|
13528
13609
|
return {
|
|
13529
13610
|
errorName: "Error",
|
|
13530
13611
|
errorMessage: String(error).slice(0, 500),
|
|
13531
|
-
aborted: false
|
|
13612
|
+
aborted: false,
|
|
13613
|
+
timedOut: false
|
|
13532
13614
|
};
|
|
13533
13615
|
}
|
|
13616
|
+
function classifyTransportFailure(opts) {
|
|
13617
|
+
if (opts.aborted || opts.errorName === "AbortError") return "abort";
|
|
13618
|
+
if (opts.timedOut || opts.errorName === "TimeoutError") return "timeout";
|
|
13619
|
+
return "network";
|
|
13620
|
+
}
|
|
13534
13621
|
|
|
13535
13622
|
// src/replay/hooks.ts
|
|
13536
13623
|
function installVisibilityResumeHook(onResume) {
|
|
@@ -13625,7 +13712,6 @@ function emitNetwork(meta) {
|
|
|
13625
13712
|
try {
|
|
13626
13713
|
record.addCustomEvent("talaria-network", {
|
|
13627
13714
|
...meta,
|
|
13628
|
-
url: redactUrl(meta.url),
|
|
13629
13715
|
timestamp: Date.now()
|
|
13630
13716
|
});
|
|
13631
13717
|
} catch {
|
|
@@ -13672,8 +13758,10 @@ function shouldPromoteFailedRequest(meta, opts) {
|
|
|
13672
13758
|
}
|
|
13673
13759
|
function shouldPromoteNetworkError(meta, opts) {
|
|
13674
13760
|
if (!opts.captureNetworkErrors) return false;
|
|
13675
|
-
if (meta.aborted) return false;
|
|
13676
|
-
if (meta.failureKind !== "network")
|
|
13761
|
+
if (meta.aborted || meta.failureKind === "abort") return false;
|
|
13762
|
+
if (meta.failureKind !== "network" && meta.failureKind !== "timeout") {
|
|
13763
|
+
return false;
|
|
13764
|
+
}
|
|
13677
13765
|
const ignore = buildFailedRequestIgnoreUrls(
|
|
13678
13766
|
opts.failedRequestIgnoreUrls,
|
|
13679
13767
|
opts.talariaBaseUrl
|
|
@@ -13689,14 +13777,28 @@ function enrichNetworkMeta(meta) {
|
|
|
13689
13777
|
failureKind: meta.ok === false ? "http" : meta.failureKind
|
|
13690
13778
|
};
|
|
13691
13779
|
}
|
|
13692
|
-
if (meta.ok === false || meta.errorName || meta.errorMessage) {
|
|
13780
|
+
if (meta.ok === false || meta.errorName || meta.errorMessage || meta.aborted) {
|
|
13781
|
+
const failureKind = meta.failureKind === "abort" || meta.failureKind === "timeout" || meta.failureKind === "network" ? meta.failureKind : classifyTransportFailure({
|
|
13782
|
+
aborted: meta.aborted,
|
|
13783
|
+
errorName: meta.errorName
|
|
13784
|
+
});
|
|
13693
13785
|
return {
|
|
13694
13786
|
...meta,
|
|
13695
|
-
failureKind
|
|
13787
|
+
failureKind,
|
|
13788
|
+
aborted: meta.aborted ?? failureKind === "abort"
|
|
13696
13789
|
};
|
|
13697
13790
|
}
|
|
13698
13791
|
return meta;
|
|
13699
13792
|
}
|
|
13793
|
+
function networkUrlParts(rawUrl, includeQuery) {
|
|
13794
|
+
const parts = sanitizeNetworkUrl(rawUrl, { includeQuery });
|
|
13795
|
+
return {
|
|
13796
|
+
url: parts.url,
|
|
13797
|
+
hostname: parts.hostname,
|
|
13798
|
+
pathname: parts.pathname,
|
|
13799
|
+
...parts.search ? { search: parts.search } : {}
|
|
13800
|
+
};
|
|
13801
|
+
}
|
|
13700
13802
|
function installNetworkHook(options = {}) {
|
|
13701
13803
|
const originalFetch = typeof fetch === "function" ? fetch.bind(globalThis) : null;
|
|
13702
13804
|
const XHR = typeof XMLHttpRequest !== "undefined" ? XMLHttpRequest : null;
|
|
@@ -13709,8 +13811,13 @@ function installNetworkHook(options = {}) {
|
|
|
13709
13811
|
failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? [],
|
|
13710
13812
|
talariaBaseUrl: options.talariaBaseUrl
|
|
13711
13813
|
};
|
|
13814
|
+
const includeQuery = options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false;
|
|
13712
13815
|
const handleMeta = (meta) => {
|
|
13713
|
-
const
|
|
13816
|
+
const { rawUrl, ...rest } = meta;
|
|
13817
|
+
const enriched = enrichNetworkMeta({
|
|
13818
|
+
...rest,
|
|
13819
|
+
...networkUrlParts(rawUrl, includeQuery)
|
|
13820
|
+
});
|
|
13714
13821
|
try {
|
|
13715
13822
|
emitNetwork(enriched);
|
|
13716
13823
|
options.onNetwork?.(enriched);
|
|
@@ -13726,12 +13833,14 @@ function installNetworkHook(options = {}) {
|
|
|
13726
13833
|
globalThis.fetch = async (input2, init) => {
|
|
13727
13834
|
const started = Date.now();
|
|
13728
13835
|
const method = (init?.method ?? (input2 instanceof Request ? input2.method : "GET")).toUpperCase();
|
|
13729
|
-
const
|
|
13836
|
+
const rawUrl = typeof input2 === "string" ? input2 : input2 instanceof URL ? input2.toString() : input2.url;
|
|
13730
13837
|
try {
|
|
13731
13838
|
const response = await originalFetch(input2, init);
|
|
13732
13839
|
handleMeta({
|
|
13733
13840
|
method,
|
|
13734
|
-
|
|
13841
|
+
rawUrl,
|
|
13842
|
+
url: rawUrl,
|
|
13843
|
+
transport: "fetch",
|
|
13735
13844
|
status: response.status,
|
|
13736
13845
|
durationMs: Date.now() - started,
|
|
13737
13846
|
ok: response.ok
|
|
@@ -13739,15 +13848,18 @@ function installNetworkHook(options = {}) {
|
|
|
13739
13848
|
return response;
|
|
13740
13849
|
} catch (error) {
|
|
13741
13850
|
const described = describeUnknownError(error);
|
|
13851
|
+
const failureKind = classifyTransportFailure(described);
|
|
13742
13852
|
handleMeta({
|
|
13743
13853
|
method,
|
|
13744
|
-
|
|
13854
|
+
rawUrl,
|
|
13855
|
+
url: rawUrl,
|
|
13856
|
+
transport: "fetch",
|
|
13745
13857
|
durationMs: Date.now() - started,
|
|
13746
13858
|
ok: false,
|
|
13747
13859
|
errorName: described.errorName,
|
|
13748
13860
|
errorMessage: described.errorMessage,
|
|
13749
13861
|
aborted: described.aborted,
|
|
13750
|
-
failureKind
|
|
13862
|
+
failureKind
|
|
13751
13863
|
});
|
|
13752
13864
|
throw error;
|
|
13753
13865
|
}
|
|
@@ -13761,19 +13873,44 @@ function installNetworkHook(options = {}) {
|
|
|
13761
13873
|
};
|
|
13762
13874
|
XHR.prototype.send = function(body) {
|
|
13763
13875
|
this.__talariaStarted = Date.now();
|
|
13876
|
+
this.__talariaTimedOut = false;
|
|
13877
|
+
this.__talariaAborted = false;
|
|
13878
|
+
this.addEventListener(
|
|
13879
|
+
"timeout",
|
|
13880
|
+
() => {
|
|
13881
|
+
this.__talariaTimedOut = true;
|
|
13882
|
+
},
|
|
13883
|
+
{ once: true }
|
|
13884
|
+
);
|
|
13885
|
+
this.addEventListener(
|
|
13886
|
+
"abort",
|
|
13887
|
+
() => {
|
|
13888
|
+
this.__talariaAborted = true;
|
|
13889
|
+
},
|
|
13890
|
+
{ once: true }
|
|
13891
|
+
);
|
|
13764
13892
|
const onDone = () => {
|
|
13765
13893
|
const status = this.status;
|
|
13766
|
-
const
|
|
13894
|
+
const transportFail = status === 0;
|
|
13895
|
+
const rawUrl = this.__talariaUrl ?? "";
|
|
13896
|
+
const aborted = !!this.__talariaAborted;
|
|
13897
|
+
const timedOut = !!this.__talariaTimedOut;
|
|
13898
|
+
const failureKind = transportFail ? classifyTransportFailure({ aborted, timedOut }) : void 0;
|
|
13899
|
+
const errorName = transportFail ? failureKind === "abort" ? "AbortError" : failureKind === "timeout" ? "TimeoutError" : "NetworkError" : void 0;
|
|
13900
|
+
const errorMessage = transportFail ? failureKind === "abort" ? "XMLHttpRequest aborted" : failureKind === "timeout" ? "XMLHttpRequest timed out" : "XMLHttpRequest failed (status 0)" : void 0;
|
|
13767
13901
|
handleMeta({
|
|
13768
13902
|
method: this.__talariaMethod ?? "GET",
|
|
13769
|
-
|
|
13903
|
+
rawUrl,
|
|
13904
|
+
url: rawUrl,
|
|
13905
|
+
transport: "xhr",
|
|
13770
13906
|
status,
|
|
13771
13907
|
durationMs: Date.now() - (this.__talariaStarted ?? Date.now()),
|
|
13772
13908
|
ok: status >= 200 && status < 400,
|
|
13773
|
-
...
|
|
13774
|
-
failureKind
|
|
13775
|
-
errorName
|
|
13776
|
-
errorMessage
|
|
13909
|
+
...transportFail ? {
|
|
13910
|
+
failureKind,
|
|
13911
|
+
errorName,
|
|
13912
|
+
errorMessage,
|
|
13913
|
+
aborted
|
|
13777
13914
|
} : {}
|
|
13778
13915
|
});
|
|
13779
13916
|
};
|
|
@@ -13789,28 +13926,49 @@ function installNetworkHook(options = {}) {
|
|
|
13789
13926
|
}
|
|
13790
13927
|
|
|
13791
13928
|
// src/client.ts
|
|
13929
|
+
function networkFailureTags(failure) {
|
|
13930
|
+
return {
|
|
13931
|
+
"http.method": failure.method || "GET",
|
|
13932
|
+
"network.failure_kind": failure.failureKind ?? "network",
|
|
13933
|
+
...failure.transport ? { "network.transport": failure.transport } : {},
|
|
13934
|
+
...failure.errorName ? { "network.error_name": failure.errorName } : {}
|
|
13935
|
+
};
|
|
13936
|
+
}
|
|
13937
|
+
function networkFailureExtra(failure) {
|
|
13938
|
+
const http = {
|
|
13939
|
+
method: failure.method || "GET",
|
|
13940
|
+
url: failure.url || "(unknown url)"
|
|
13941
|
+
};
|
|
13942
|
+
if (failure.hostname) http.hostname = failure.hostname;
|
|
13943
|
+
if (failure.pathname) http.pathname = failure.pathname;
|
|
13944
|
+
if (failure.search) http.search = failure.search;
|
|
13945
|
+
if (typeof failure.status === "number" && failure.status > 0) {
|
|
13946
|
+
http.status = failure.status;
|
|
13947
|
+
}
|
|
13948
|
+
if (failure.transport) http.transport = failure.transport;
|
|
13949
|
+
const fail = {
|
|
13950
|
+
kind: failure.failureKind ?? "network"
|
|
13951
|
+
};
|
|
13952
|
+
if (failure.errorName) fail.name = failure.errorName;
|
|
13953
|
+
if (failure.errorMessage) fail.message = failure.errorMessage;
|
|
13954
|
+
return {
|
|
13955
|
+
http,
|
|
13956
|
+
failure: fail,
|
|
13957
|
+
durationMs: failure.durationMs,
|
|
13958
|
+
aborted: failure.aborted ?? false,
|
|
13959
|
+
ok: failure.ok ?? false
|
|
13960
|
+
};
|
|
13961
|
+
}
|
|
13792
13962
|
function mergeNetworkFailureContext(context, failure) {
|
|
13793
|
-
const url = redactUrl(failure.url || "(unknown url)");
|
|
13794
13963
|
return {
|
|
13795
13964
|
...context,
|
|
13796
13965
|
tags: {
|
|
13797
13966
|
...context?.tags ?? {},
|
|
13798
|
-
|
|
13799
|
-
"network.failure_kind": "network",
|
|
13800
|
-
...failure.errorName ? { "network.error_name": failure.errorName } : {}
|
|
13967
|
+
...networkFailureTags(failure)
|
|
13801
13968
|
},
|
|
13802
13969
|
extra: {
|
|
13803
13970
|
...context?.extra ?? {},
|
|
13804
|
-
|
|
13805
|
-
method: failure.method,
|
|
13806
|
-
url,
|
|
13807
|
-
durationMs: failure.durationMs,
|
|
13808
|
-
ok: false,
|
|
13809
|
-
failureKind: "network",
|
|
13810
|
-
errorName: failure.errorName,
|
|
13811
|
-
errorMessage: failure.errorMessage,
|
|
13812
|
-
aborted: failure.aborted ?? false
|
|
13813
|
-
}
|
|
13971
|
+
...networkFailureExtra(failure)
|
|
13814
13972
|
}
|
|
13815
13973
|
};
|
|
13816
13974
|
}
|
|
@@ -13841,6 +13999,7 @@ function resolveOptions(options) {
|
|
|
13841
13999
|
disableDefaultIntegrations: options.disableDefaultIntegrations ?? false,
|
|
13842
14000
|
captureFailedRequests: options.captureFailedRequests ?? true,
|
|
13843
14001
|
captureNetworkErrors: options.captureNetworkErrors ?? true,
|
|
14002
|
+
includeNetworkUrlQuery: options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false,
|
|
13844
14003
|
failedRequestStatusCodes: options.failedRequestStatusCodes ?? [[500, 599]],
|
|
13845
14004
|
failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? []
|
|
13846
14005
|
};
|
|
@@ -13969,59 +14128,48 @@ var TalariaClient = class {
|
|
|
13969
14128
|
installNetworkHook({
|
|
13970
14129
|
captureFailedRequests: this.options.captureFailedRequests,
|
|
13971
14130
|
captureNetworkErrors: this.options.captureNetworkErrors,
|
|
14131
|
+
includeNetworkUrlQuery: this.options.includeNetworkUrlQuery,
|
|
13972
14132
|
failedRequestStatusCodes: this.options.failedRequestStatusCodes,
|
|
13973
14133
|
failedRequestIgnoreUrls: this.options.failedRequestIgnoreUrls,
|
|
13974
14134
|
talariaBaseUrl: this.options.baseUrl,
|
|
13975
14135
|
onNetwork: (meta) => {
|
|
13976
|
-
if (meta.failureKind === "network" && !meta.aborted) {
|
|
14136
|
+
if ((meta.failureKind === "network" || meta.failureKind === "timeout") && !meta.aborted) {
|
|
13977
14137
|
this.rememberNetworkFailure(meta, { promoted: false });
|
|
13978
14138
|
}
|
|
13979
14139
|
},
|
|
13980
14140
|
onFailedRequest: (meta) => {
|
|
13981
14141
|
const status = meta.status;
|
|
13982
14142
|
const method = meta.method || "GET";
|
|
13983
|
-
const url =
|
|
14143
|
+
const url = meta.url || "(unknown url)";
|
|
14144
|
+
const enriched = {
|
|
14145
|
+
...meta,
|
|
14146
|
+
failureKind: "http",
|
|
14147
|
+
aborted: false
|
|
14148
|
+
};
|
|
13984
14149
|
void this.captureMessage(
|
|
13985
14150
|
`HTTP ${status}: ${method} ${url}`,
|
|
13986
14151
|
status >= 500 ? "error" : "warning",
|
|
13987
14152
|
{
|
|
13988
14153
|
tags: {
|
|
13989
|
-
|
|
13990
|
-
"http.
|
|
13991
|
-
"network.failure_kind": "http"
|
|
14154
|
+
...networkFailureTags(enriched),
|
|
14155
|
+
"http.status_code": String(status)
|
|
13992
14156
|
},
|
|
13993
|
-
extra:
|
|
13994
|
-
url,
|
|
13995
|
-
durationMs: meta.durationMs,
|
|
13996
|
-
ok: meta.ok,
|
|
13997
|
-
failureKind: "http"
|
|
13998
|
-
}
|
|
14157
|
+
extra: networkFailureExtra(enriched)
|
|
13999
14158
|
}
|
|
14000
14159
|
);
|
|
14001
14160
|
},
|
|
14002
14161
|
onNetworkError: (meta) => {
|
|
14003
14162
|
this.rememberNetworkFailure(meta, { promoted: true });
|
|
14004
14163
|
const method = meta.method || "GET";
|
|
14005
|
-
const url =
|
|
14164
|
+
const url = meta.url || "(unknown url)";
|
|
14006
14165
|
const errLabel = meta.errorName && meta.errorMessage ? `${meta.errorName}: ${meta.errorMessage}` : meta.errorMessage || meta.errorName || "Failed to fetch";
|
|
14166
|
+
const prefix = meta.failureKind === "timeout" ? "Timeout error" : "Network error";
|
|
14007
14167
|
void this.captureMessage(
|
|
14008
|
-
|
|
14168
|
+
`${prefix}: ${method} ${url} \u2014 ${errLabel}`,
|
|
14009
14169
|
"error",
|
|
14010
14170
|
{
|
|
14011
|
-
tags:
|
|
14012
|
-
|
|
14013
|
-
"network.failure_kind": "network",
|
|
14014
|
-
...meta.errorName ? { "network.error_name": meta.errorName } : {}
|
|
14015
|
-
},
|
|
14016
|
-
extra: {
|
|
14017
|
-
url,
|
|
14018
|
-
durationMs: meta.durationMs,
|
|
14019
|
-
ok: false,
|
|
14020
|
-
failureKind: "network",
|
|
14021
|
-
errorName: meta.errorName,
|
|
14022
|
-
errorMessage: meta.errorMessage,
|
|
14023
|
-
aborted: false
|
|
14024
|
-
}
|
|
14171
|
+
tags: networkFailureTags(meta),
|
|
14172
|
+
extra: networkFailureExtra(meta)
|
|
14025
14173
|
}
|
|
14026
14174
|
);
|
|
14027
14175
|
}
|
|
@@ -14200,6 +14348,7 @@ var TalariaClient = class {
|
|
|
14200
14348
|
throw new Error("@newtalaria/browser: call Talaria.init() first");
|
|
14201
14349
|
}
|
|
14202
14350
|
if (this.ingestDisabled) return;
|
|
14351
|
+
const occurredAt = /* @__PURE__ */ new Date();
|
|
14203
14352
|
const isErrorLike = args.level === "error" || args.level === "fatal";
|
|
14204
14353
|
let errorClipOutcome = null;
|
|
14205
14354
|
let attemptedErrorClip = false;
|
|
@@ -14257,6 +14406,7 @@ var TalariaClient = class {
|
|
|
14257
14406
|
errorClipOutcome = { status: "failed", reason: "buffer_empty" };
|
|
14258
14407
|
}
|
|
14259
14408
|
}
|
|
14409
|
+
const queuedMs = Math.max(0, Date.now() - occurredAt.getTime());
|
|
14260
14410
|
const tags = applyReplayCaptureTags(
|
|
14261
14411
|
{
|
|
14262
14412
|
...this.browserContext ? browserContextTags(this.browserContext) : {},
|
|
@@ -14275,10 +14425,17 @@ var TalariaClient = class {
|
|
|
14275
14425
|
osVersion: this.browserContext.osVersion,
|
|
14276
14426
|
device: this.browserContext.device,
|
|
14277
14427
|
language: this.browserContext.language,
|
|
14278
|
-
userAgent: this.browserContext.userAgent
|
|
14428
|
+
userAgent: this.browserContext.userAgent,
|
|
14429
|
+
bot: this.browserContext.bot,
|
|
14430
|
+
...this.browserContext.botName ? { botName: this.browserContext.botName } : {}
|
|
14279
14431
|
}
|
|
14280
14432
|
} : {},
|
|
14281
|
-
sdk: {
|
|
14433
|
+
sdk: {
|
|
14434
|
+
name: SDK_NAME,
|
|
14435
|
+
version: SDK_VERSION,
|
|
14436
|
+
// Time spent in capture() before ingest (mostly replay flush).
|
|
14437
|
+
...queuedMs >= 50 ? { queuedMs } : {}
|
|
14438
|
+
},
|
|
14282
14439
|
...args.context?.extra
|
|
14283
14440
|
},
|
|
14284
14441
|
errorClipOutcome
|
|
@@ -14298,7 +14455,7 @@ var TalariaClient = class {
|
|
|
14298
14455
|
url: typeof location !== "undefined" ? location.href : void 0,
|
|
14299
14456
|
tags: Object.keys(tags).length ? tags : void 0,
|
|
14300
14457
|
extraJson: extra ? JSON.stringify(extra) : void 0,
|
|
14301
|
-
timestamp:
|
|
14458
|
+
timestamp: occurredAt.toISOString(),
|
|
14302
14459
|
keepalive: args.keepalive
|
|
14303
14460
|
});
|
|
14304
14461
|
if (replayId && replayId === this.linkableReplayId) {
|