@diegotsi/flint-core 1.6.1 → 1.8.0
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/index.cjs +243 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -1
- package/dist/index.d.ts +71 -1
- package/dist/index.js +242 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -64,6 +64,7 @@ __export(index_exports, {
|
|
|
64
64
|
collectEnvironment: () => collectEnvironment,
|
|
65
65
|
createConsoleCollector: () => createConsoleCollector,
|
|
66
66
|
createDatadogReplayProvider: () => createDatadogReplayProvider,
|
|
67
|
+
createErrorCaptureCollector: () => createErrorCaptureCollector,
|
|
67
68
|
createFormErrorCollector: () => createFormErrorCollector,
|
|
68
69
|
createFrustrationCollector: () => createFrustrationCollector,
|
|
69
70
|
createNetworkCollector: () => createNetworkCollector,
|
|
@@ -111,6 +112,8 @@ async function submitReport(serverUrl, projectKey, payload, screenshot) {
|
|
|
111
112
|
form.append("severity", payload.severity);
|
|
112
113
|
if (payload.url) form.append("url", payload.url);
|
|
113
114
|
if (payload.meta) form.append("meta", JSON.stringify(payload.meta));
|
|
115
|
+
if (payload.appVersion) form.append("appVersion", payload.appVersion);
|
|
116
|
+
if (payload.release) form.append("release", payload.release);
|
|
114
117
|
form.append("screenshot", screenshot);
|
|
115
118
|
body = form;
|
|
116
119
|
} else {
|
|
@@ -139,8 +142,7 @@ async function submitReplay(serverUrl, projectKey, reportId, events) {
|
|
|
139
142
|
});
|
|
140
143
|
}
|
|
141
144
|
|
|
142
|
-
// src/
|
|
143
|
-
var MAX_ENTRIES = 50;
|
|
145
|
+
// src/sanitize.ts
|
|
144
146
|
var SENSITIVE_PATTERNS = [
|
|
145
147
|
/(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|bearer)\s*[:=]\s*["']?[^\s"',]{4,}/gi,
|
|
146
148
|
/\b(sk-[a-zA-Z0-9_-]{20,})\b/g,
|
|
@@ -159,6 +161,9 @@ function sanitize(str) {
|
|
|
159
161
|
}
|
|
160
162
|
return result;
|
|
161
163
|
}
|
|
164
|
+
|
|
165
|
+
// src/collectors/console.ts
|
|
166
|
+
var MAX_ENTRIES = 50;
|
|
162
167
|
function createConsoleCollector() {
|
|
163
168
|
const entries = [];
|
|
164
169
|
let active = false;
|
|
@@ -278,6 +283,216 @@ function collectEnvironment() {
|
|
|
278
283
|
};
|
|
279
284
|
}
|
|
280
285
|
|
|
286
|
+
// src/collectors/errorCapture.ts
|
|
287
|
+
var MAX_MESSAGE = 1e3;
|
|
288
|
+
var MAX_STACK = 8e3;
|
|
289
|
+
var MAX_BATCH = 20;
|
|
290
|
+
var MAX_PAYLOAD_BYTES = 6e4;
|
|
291
|
+
var PER_KEY_PER_MINUTE = 10;
|
|
292
|
+
var GLOBAL_PAGE_CAP = 100;
|
|
293
|
+
var FLUSH_INTERVAL_MS = 5e3;
|
|
294
|
+
var THROTTLE_WINDOW_MS = 6e4;
|
|
295
|
+
var BREADCRUMB_CONSOLE = 10;
|
|
296
|
+
var BREADCRUMB_NETWORK = 10;
|
|
297
|
+
var DEFAULT_IGNORE = [/ResizeObserver loop/i, /^Script error\.?$/];
|
|
298
|
+
var EXTENSION_URL = /(chrome|moz|safari|safari-web)-extension:\/\//;
|
|
299
|
+
function normalizeForKey(message) {
|
|
300
|
+
return message.toLowerCase().replace(/\d+/g, "#").replace(/\s+/g, " ").trim().slice(0, 200);
|
|
301
|
+
}
|
|
302
|
+
function topFrame(stack) {
|
|
303
|
+
if (!stack) return "";
|
|
304
|
+
const lines = stack.split("\n").map((l) => l.trim());
|
|
305
|
+
return lines.find((l) => l.startsWith("at ") || /@/.test(l)) ?? "";
|
|
306
|
+
}
|
|
307
|
+
function createErrorCaptureCollector(options) {
|
|
308
|
+
const sampleRate = options.sampleRate ?? 1;
|
|
309
|
+
const ignoreList = [...DEFAULT_IGNORE, ...options.ignoreErrors ?? []];
|
|
310
|
+
const sessionId = `s_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
311
|
+
let active = false;
|
|
312
|
+
let queue = [];
|
|
313
|
+
let interval = null;
|
|
314
|
+
let sentCount = 0;
|
|
315
|
+
let environment;
|
|
316
|
+
const keyTimestamps = /* @__PURE__ */ new Map();
|
|
317
|
+
function debugLog2(...args) {
|
|
318
|
+
if (options.debug) console.log("[Flint]", ...args);
|
|
319
|
+
}
|
|
320
|
+
function isIgnored(message, stack, frameUrl) {
|
|
321
|
+
for (const entry of ignoreList) {
|
|
322
|
+
if (typeof entry === "string") {
|
|
323
|
+
if (message.includes(entry)) return true;
|
|
324
|
+
} else if (entry.test(message)) {
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (EXTENSION_URL.test(frameUrl ?? "") || EXTENSION_URL.test(stack ?? "")) return true;
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
function isThrottled(localKey) {
|
|
332
|
+
const now = Date.now();
|
|
333
|
+
const stamps = (keyTimestamps.get(localKey) ?? []).filter((t) => now - t < THROTTLE_WINDOW_MS);
|
|
334
|
+
if (stamps.length >= PER_KEY_PER_MINUTE) {
|
|
335
|
+
keyTimestamps.set(localKey, stamps);
|
|
336
|
+
return true;
|
|
337
|
+
}
|
|
338
|
+
stamps.push(now);
|
|
339
|
+
keyTimestamps.set(localKey, stamps);
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
function capture(input) {
|
|
343
|
+
try {
|
|
344
|
+
if (sentCount + queue.length >= GLOBAL_PAGE_CAP) return;
|
|
345
|
+
let message = input.message.slice(0, MAX_MESSAGE);
|
|
346
|
+
if (isIgnored(message, input.stack, input.frameUrl)) return;
|
|
347
|
+
if (sampleRate < 1 && Math.random() >= sampleRate) return;
|
|
348
|
+
const localKey = `${input.type}|${normalizeForKey(message)}|${topFrame(input.stack)}`;
|
|
349
|
+
if (isThrottled(localKey)) return;
|
|
350
|
+
const pending = queue.find((e) => e._localKey === localKey);
|
|
351
|
+
if (pending) {
|
|
352
|
+
pending.count += 1;
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
message = sanitize(message);
|
|
356
|
+
const stack = input.stack ? sanitize(input.stack.slice(0, MAX_STACK)) : void 0;
|
|
357
|
+
if (!environment && options.getEnvironment) {
|
|
358
|
+
try {
|
|
359
|
+
environment = options.getEnvironment();
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const user = options.getUser?.();
|
|
364
|
+
let event = {
|
|
365
|
+
type: input.type,
|
|
366
|
+
message,
|
|
367
|
+
errorClass: input.errorClass,
|
|
368
|
+
stack,
|
|
369
|
+
url: typeof location !== "undefined" ? location.href : "",
|
|
370
|
+
timestamp: Date.now(),
|
|
371
|
+
release: options.release,
|
|
372
|
+
appVersion: options.appVersion,
|
|
373
|
+
userId: user?.id,
|
|
374
|
+
sessionId,
|
|
375
|
+
browser: environment?.browser,
|
|
376
|
+
os: environment?.os,
|
|
377
|
+
count: 1,
|
|
378
|
+
breadcrumbs: options.getBreadcrumbs ? {
|
|
379
|
+
consoleLogs: options.getBreadcrumbs().consoleLogs.slice(-BREADCRUMB_CONSOLE),
|
|
380
|
+
networkErrors: options.getBreadcrumbs().networkErrors.slice(-BREADCRUMB_NETWORK)
|
|
381
|
+
} : void 0,
|
|
382
|
+
_localKey: localKey
|
|
383
|
+
};
|
|
384
|
+
if (options.beforeSend) {
|
|
385
|
+
try {
|
|
386
|
+
const result = options.beforeSend(event);
|
|
387
|
+
if (!result) return;
|
|
388
|
+
event = { ...result, _localKey: localKey };
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
queue.push(event);
|
|
393
|
+
debugLog2("Error captured", input.errorClass, message);
|
|
394
|
+
if (queue.length >= MAX_BATCH) flush(false);
|
|
395
|
+
} catch {
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function serialize(events) {
|
|
399
|
+
const wire = events.map(({ _localKey, ...e }) => e);
|
|
400
|
+
let body = JSON.stringify(wire);
|
|
401
|
+
if (body.length > MAX_PAYLOAD_BYTES) {
|
|
402
|
+
body = JSON.stringify(wire.map((e) => ({ ...e, breadcrumbs: void 0 })));
|
|
403
|
+
if (body.length > MAX_PAYLOAD_BYTES) {
|
|
404
|
+
body = JSON.stringify(wire.map((e) => ({ ...e, breadcrumbs: void 0, stack: e.stack?.slice(0, 2e3) })));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return body;
|
|
408
|
+
}
|
|
409
|
+
function flush(unloading) {
|
|
410
|
+
if (queue.length === 0) return;
|
|
411
|
+
const events = queue;
|
|
412
|
+
queue = [];
|
|
413
|
+
sentCount += events.length;
|
|
414
|
+
const base = options.serverUrl.replace(/\/$/, "");
|
|
415
|
+
const url = `${base}/api/v1/error-events?project_key=${encodeURIComponent(options.projectKey)}`;
|
|
416
|
+
const body = serialize(events);
|
|
417
|
+
if (unloading && typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
418
|
+
const ok = navigator.sendBeacon(url, body);
|
|
419
|
+
debugLog2("Flushed via beacon", events.length, ok);
|
|
420
|
+
if (ok) return;
|
|
421
|
+
}
|
|
422
|
+
fetch(url, {
|
|
423
|
+
method: "POST",
|
|
424
|
+
headers: { "Content-Type": "text/plain;charset=UTF-8" },
|
|
425
|
+
body,
|
|
426
|
+
keepalive: unloading
|
|
427
|
+
}).catch(() => {
|
|
428
|
+
});
|
|
429
|
+
debugLog2("Flushed via fetch", events.length);
|
|
430
|
+
}
|
|
431
|
+
function onError(event) {
|
|
432
|
+
const e = event;
|
|
433
|
+
if (typeof e.message !== "string" && !(e.error instanceof Error)) return;
|
|
434
|
+
const err = e.error instanceof Error ? e.error : void 0;
|
|
435
|
+
capture({
|
|
436
|
+
type: "error",
|
|
437
|
+
message: err?.message ?? (e.message || "Unknown error"),
|
|
438
|
+
errorClass: err?.name ?? "Error",
|
|
439
|
+
stack: err?.stack,
|
|
440
|
+
frameUrl: e.filename
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
function onRejection(event) {
|
|
444
|
+
const reason = event.reason;
|
|
445
|
+
const err = reason instanceof Error ? reason : void 0;
|
|
446
|
+
let message;
|
|
447
|
+
if (err) {
|
|
448
|
+
message = err.message;
|
|
449
|
+
} else {
|
|
450
|
+
try {
|
|
451
|
+
message = typeof reason === "string" ? reason : JSON.stringify(reason);
|
|
452
|
+
} catch {
|
|
453
|
+
message = String(reason);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
capture({
|
|
457
|
+
type: "unhandledrejection",
|
|
458
|
+
message: message || "Unhandled promise rejection",
|
|
459
|
+
errorClass: err?.name ?? "UnhandledRejection",
|
|
460
|
+
stack: err?.stack
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
function onPageHide() {
|
|
464
|
+
flush(true);
|
|
465
|
+
}
|
|
466
|
+
function onVisibilityChange() {
|
|
467
|
+
if (document.visibilityState === "hidden") flush(true);
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
start() {
|
|
471
|
+
if (active) return;
|
|
472
|
+
active = true;
|
|
473
|
+
window.addEventListener("error", onError, { capture: true });
|
|
474
|
+
window.addEventListener("unhandledrejection", onRejection, { capture: true });
|
|
475
|
+
window.addEventListener("pagehide", onPageHide);
|
|
476
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
477
|
+
interval = setInterval(() => flush(false), FLUSH_INTERVAL_MS);
|
|
478
|
+
},
|
|
479
|
+
stop() {
|
|
480
|
+
if (!active) return;
|
|
481
|
+
active = false;
|
|
482
|
+
window.removeEventListener("error", onError, { capture: true });
|
|
483
|
+
window.removeEventListener("unhandledrejection", onRejection, { capture: true });
|
|
484
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
485
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
486
|
+
if (interval) clearInterval(interval);
|
|
487
|
+
interval = null;
|
|
488
|
+
flush(false);
|
|
489
|
+
},
|
|
490
|
+
flush() {
|
|
491
|
+
flush(false);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
281
496
|
// src/collectors/formErrors.ts
|
|
282
497
|
var MAX_ENTRIES2 = 30;
|
|
283
498
|
var POST_SUBMIT_CHECK_MS = 300;
|
|
@@ -975,6 +1190,8 @@ function init(config) {
|
|
|
975
1190
|
enableFormErrors = true,
|
|
976
1191
|
enableFrustration = false,
|
|
977
1192
|
autoReportFrustration = false,
|
|
1193
|
+
enableErrorMonitoring = true,
|
|
1194
|
+
errorMonitoring: errorMonitoringOpts,
|
|
978
1195
|
enableReplay = false,
|
|
979
1196
|
replayBufferMs = DEFAULT_REPLAY_BUFFER_MS,
|
|
980
1197
|
blockedHosts = [],
|
|
@@ -1010,6 +1227,23 @@ function init(config) {
|
|
|
1010
1227
|
}
|
|
1011
1228
|
const frustrationCol = enableFrustration ? createFrustrationCollector(frustrationOpts) : null;
|
|
1012
1229
|
frustrationCol?.start();
|
|
1230
|
+
const errorCaptureCol = enableErrorMonitoring ? createErrorCaptureCollector({
|
|
1231
|
+
serverUrl: config.serverUrl,
|
|
1232
|
+
projectKey: config.projectKey,
|
|
1233
|
+
release: config.release,
|
|
1234
|
+
appVersion: config.appVersion,
|
|
1235
|
+
debug: config.debug,
|
|
1236
|
+
sampleRate: errorMonitoringOpts?.sampleRate,
|
|
1237
|
+
ignoreErrors: errorMonitoringOpts?.ignoreErrors,
|
|
1238
|
+
beforeSend: errorMonitoringOpts?.beforeSend,
|
|
1239
|
+
getUser: () => getSnapshot().user ?? config.user,
|
|
1240
|
+
getBreadcrumbs: () => ({
|
|
1241
|
+
consoleLogs: consoleCol?.getEntries() ?? [],
|
|
1242
|
+
networkErrors: networkCol?.getEntries() ?? []
|
|
1243
|
+
}),
|
|
1244
|
+
getEnvironment: _collectors?.environment ?? collectEnvironment
|
|
1245
|
+
}) : null;
|
|
1246
|
+
errorCaptureCol?.start();
|
|
1013
1247
|
if (config.user) {
|
|
1014
1248
|
flint.setUser(config.user);
|
|
1015
1249
|
}
|
|
@@ -1019,7 +1253,8 @@ function init(config) {
|
|
|
1019
1253
|
console: !!consoleCol,
|
|
1020
1254
|
network: !!networkCol,
|
|
1021
1255
|
formErrors: !!formErrorsCol,
|
|
1022
|
-
frustration: !!frustrationCol
|
|
1256
|
+
frustration: !!frustrationCol,
|
|
1257
|
+
errorCapture: !!errorCaptureCol
|
|
1023
1258
|
});
|
|
1024
1259
|
instance = {
|
|
1025
1260
|
config,
|
|
@@ -1027,6 +1262,7 @@ function init(config) {
|
|
|
1027
1262
|
network: networkCol,
|
|
1028
1263
|
formErrors: formErrorsCol,
|
|
1029
1264
|
frustration: frustrationCol,
|
|
1265
|
+
errorCapture: errorCaptureCol,
|
|
1030
1266
|
replayEvents,
|
|
1031
1267
|
stopReplay: null
|
|
1032
1268
|
};
|
|
@@ -1056,6 +1292,8 @@ function init(config) {
|
|
|
1056
1292
|
severity: event.type === "error_loop" ? "P1" : event.type === "rage_click" ? "P2" : "P3",
|
|
1057
1293
|
url: event.url,
|
|
1058
1294
|
source: "auto_capture",
|
|
1295
|
+
appVersion: config.appVersion,
|
|
1296
|
+
release: config.release,
|
|
1059
1297
|
meta: {
|
|
1060
1298
|
...config.meta,
|
|
1061
1299
|
environment: getEnvironment(),
|
|
@@ -1105,6 +1343,7 @@ function shutdown() {
|
|
|
1105
1343
|
instance.formErrors?.stop();
|
|
1106
1344
|
_setFormErrorCollector(null);
|
|
1107
1345
|
instance.frustration?.stop();
|
|
1346
|
+
instance.errorCapture?.stop();
|
|
1108
1347
|
instance.stopReplay?.();
|
|
1109
1348
|
instance = null;
|
|
1110
1349
|
}
|
|
@@ -1196,6 +1435,7 @@ function resolveTheme(theme) {
|
|
|
1196
1435
|
collectEnvironment,
|
|
1197
1436
|
createConsoleCollector,
|
|
1198
1437
|
createDatadogReplayProvider,
|
|
1438
|
+
createErrorCaptureCollector,
|
|
1199
1439
|
createFormErrorCollector,
|
|
1200
1440
|
createFrustrationCollector,
|
|
1201
1441
|
createNetworkCollector,
|