nuxt-error-tracker 0.1.16 → 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/dist/module.d.mts +9 -0
- package/dist/module.d.ts +9 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -0
- package/dist/runtime/plugin.client.js +85 -3
- package/package.json +1 -1
package/dist/module.d.mts
CHANGED
|
@@ -5,6 +5,15 @@ interface ModuleOptions {
|
|
|
5
5
|
enabled?: boolean;
|
|
6
6
|
/** Wrap window.fetch to capture failed requests (network errors + 5xx). Default true. */
|
|
7
7
|
captureFetch?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* On a failed request (HTTP >= 400 or a network error), attach full request/
|
|
10
|
+
* response detail (method, url, status, timing, headers, bodies) to the fetch
|
|
11
|
+
* breadcrumb's `data` for precise debugging. Sensitive headers (authorization/
|
|
12
|
+
* cookie/…) are redacted; bodies are truncated. Only failed requests carry
|
|
13
|
+
* detail — successful ones stay lightweight. Default true; set false to keep
|
|
14
|
+
* fetch breadcrumbs message-only (e.g. to avoid capturing request payloads).
|
|
15
|
+
*/
|
|
16
|
+
captureFetchDetails?: boolean;
|
|
8
17
|
/** Patch console.error to capture logged-but-not-thrown errors. Default true. */
|
|
9
18
|
captureConsole?: boolean;
|
|
10
19
|
/**
|
package/dist/module.d.ts
CHANGED
|
@@ -5,6 +5,15 @@ interface ModuleOptions {
|
|
|
5
5
|
enabled?: boolean;
|
|
6
6
|
/** Wrap window.fetch to capture failed requests (network errors + 5xx). Default true. */
|
|
7
7
|
captureFetch?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* On a failed request (HTTP >= 400 or a network error), attach full request/
|
|
10
|
+
* response detail (method, url, status, timing, headers, bodies) to the fetch
|
|
11
|
+
* breadcrumb's `data` for precise debugging. Sensitive headers (authorization/
|
|
12
|
+
* cookie/…) are redacted; bodies are truncated. Only failed requests carry
|
|
13
|
+
* detail — successful ones stay lightweight. Default true; set false to keep
|
|
14
|
+
* fetch breadcrumbs message-only (e.g. to avoid capturing request payloads).
|
|
15
|
+
*/
|
|
16
|
+
captureFetchDetails?: boolean;
|
|
8
17
|
/** Patch console.error to capture logged-but-not-thrown errors. Default true. */
|
|
9
18
|
captureConsole?: boolean;
|
|
10
19
|
/**
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -15,6 +15,7 @@ const module = defineNuxtModule({
|
|
|
15
15
|
appVersion: "unknown",
|
|
16
16
|
enabled: true,
|
|
17
17
|
captureFetch: true,
|
|
18
|
+
captureFetchDetails: true,
|
|
18
19
|
captureConsole: true,
|
|
19
20
|
captureInputs: false,
|
|
20
21
|
flushDelay: 2e3,
|
|
@@ -30,6 +31,7 @@ const module = defineNuxtModule({
|
|
|
30
31
|
publicKey: options.publicKey,
|
|
31
32
|
appVersion: options.appVersion ?? "unknown",
|
|
32
33
|
captureFetch: options.captureFetch !== false,
|
|
34
|
+
captureFetchDetails: options.captureFetchDetails !== false,
|
|
33
35
|
captureConsole: options.captureConsole !== false,
|
|
34
36
|
captureInputs: options.captureInputs === true,
|
|
35
37
|
flushDelay: posInt(options.flushDelay, 2e3),
|
|
@@ -199,18 +199,92 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
199
199
|
});
|
|
200
200
|
if (config.captureFetch && typeof window.fetch === "function") {
|
|
201
201
|
const originalFetch = window.fetch.bind(window);
|
|
202
|
+
const REDACTED = "[redacted]";
|
|
203
|
+
const SENSITIVE_HEADER = /^(authorization|cookie|set-cookie|proxy-authorization|x-api-key|x-auth-token|x-csrf-token|x-xsrf-token)$/i;
|
|
204
|
+
const MAX_BODY = 2048;
|
|
205
|
+
const MAX_HEADER_VAL = 256;
|
|
206
|
+
const trunc = (s, max) => s.length > max ? `${s.slice(0, max)}\u2026[+${s.length - max}]` : s;
|
|
207
|
+
const headersToObj = (h) => {
|
|
208
|
+
const out = {};
|
|
209
|
+
if (!h) return out;
|
|
210
|
+
try {
|
|
211
|
+
const entries = h instanceof Headers ? [...h.entries()] : Array.isArray(h) ? h : Object.entries(h);
|
|
212
|
+
for (const [k, v] of entries) {
|
|
213
|
+
out[k] = SENSITIVE_HEADER.test(k) ? REDACTED : trunc(String(v), MAX_HEADER_VAL);
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
};
|
|
219
|
+
const reqHeaders = (input, init) => {
|
|
220
|
+
const base = input instanceof Request ? headersToObj(input.headers) : {};
|
|
221
|
+
return { ...base, ...headersToObj(init?.headers) };
|
|
222
|
+
};
|
|
223
|
+
const readReqBody = async (input, init) => {
|
|
224
|
+
try {
|
|
225
|
+
const body = init?.body;
|
|
226
|
+
if (body != null) {
|
|
227
|
+
if (typeof body === "string") return trunc(body, MAX_BODY);
|
|
228
|
+
if (body instanceof URLSearchParams)
|
|
229
|
+
return trunc(body.toString(), MAX_BODY);
|
|
230
|
+
if (typeof FormData !== "undefined" && body instanceof FormData)
|
|
231
|
+
return "[FormData]";
|
|
232
|
+
if (typeof Blob !== "undefined" && body instanceof Blob)
|
|
233
|
+
return `[Blob ${body.size}b]`;
|
|
234
|
+
if (body instanceof ArrayBuffer)
|
|
235
|
+
return `[ArrayBuffer ${body.byteLength}b]`;
|
|
236
|
+
return "[stream]";
|
|
237
|
+
}
|
|
238
|
+
if (input instanceof Request) {
|
|
239
|
+
const t = await input.clone().text();
|
|
240
|
+
return t ? trunc(t, MAX_BODY) : void 0;
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
return void 0;
|
|
245
|
+
};
|
|
246
|
+
const readResBody = async (res) => {
|
|
247
|
+
try {
|
|
248
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
249
|
+
if (!/json|text|xml|form-urlencoded/i.test(ct))
|
|
250
|
+
return `[${ct || "binary"}]`;
|
|
251
|
+
const len = Number(res.headers.get("content-length") ?? 0);
|
|
252
|
+
if (len > MAX_BODY * 8) return `[too large ${len}b]`;
|
|
253
|
+
const t = await res.clone().text();
|
|
254
|
+
return t ? trunc(t, MAX_BODY) : void 0;
|
|
255
|
+
} catch {
|
|
256
|
+
}
|
|
257
|
+
return void 0;
|
|
258
|
+
};
|
|
202
259
|
window.fetch = async (input, init) => {
|
|
203
260
|
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
204
261
|
const reqUrl = input instanceof Request ? input.url : String(input);
|
|
205
262
|
const isOwn = reqUrl.startsWith(config.endpoint);
|
|
263
|
+
const startedAt = Date.now();
|
|
206
264
|
try {
|
|
207
265
|
const res = await originalFetch(input, init);
|
|
208
266
|
if (!isOwn) {
|
|
267
|
+
const failed = res.status >= 400;
|
|
268
|
+
let data;
|
|
269
|
+
if (failed && config.captureFetchDetails) {
|
|
270
|
+
data = {
|
|
271
|
+
method,
|
|
272
|
+
url: reqUrl,
|
|
273
|
+
status: res.status,
|
|
274
|
+
statusText: res.statusText,
|
|
275
|
+
durationMs: Date.now() - startedAt,
|
|
276
|
+
requestHeaders: reqHeaders(input, init),
|
|
277
|
+
requestBody: await readReqBody(input, init),
|
|
278
|
+
responseHeaders: headersToObj(res.headers),
|
|
279
|
+
responseBody: await readResBody(res)
|
|
280
|
+
};
|
|
281
|
+
}
|
|
209
282
|
addCrumb({
|
|
210
283
|
type: "fetch",
|
|
211
284
|
category: "http",
|
|
212
|
-
level:
|
|
213
|
-
message: `${method} ${reqUrl} \u2192 ${res.status}
|
|
285
|
+
level: failed ? "error" : "info",
|
|
286
|
+
message: `${method} ${reqUrl} \u2192 ${res.status}`,
|
|
287
|
+
data
|
|
214
288
|
});
|
|
215
289
|
if (res.status >= 500) {
|
|
216
290
|
capture(
|
|
@@ -227,7 +301,15 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
227
301
|
type: "fetch",
|
|
228
302
|
category: "http",
|
|
229
303
|
level: "error",
|
|
230
|
-
message: `${method} ${reqUrl} \u2192 failed: ${reason}
|
|
304
|
+
message: `${method} ${reqUrl} \u2192 failed: ${reason}`,
|
|
305
|
+
data: config.captureFetchDetails ? {
|
|
306
|
+
method,
|
|
307
|
+
url: reqUrl,
|
|
308
|
+
error: reason,
|
|
309
|
+
durationMs: Date.now() - startedAt,
|
|
310
|
+
requestHeaders: reqHeaders(input, init),
|
|
311
|
+
requestBody: await readReqBody(input, init)
|
|
312
|
+
} : void 0
|
|
231
313
|
});
|
|
232
314
|
capture(
|
|
233
315
|
new Error(`Fetch failed ${method} ${reqUrl}: ${reason}`),
|