@diegotsi/flint-core 0.1.2 → 0.5.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 +414 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -18
- package/dist/index.d.ts +127 -18
- package/dist/index.js +411 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,8 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
Flint: () => Flint,
|
|
24
|
+
_setFormErrorCollector: () => _setFormErrorCollector,
|
|
23
25
|
collectEnvironment: () => collectEnvironment,
|
|
24
26
|
createConsoleCollector: () => createConsoleCollector,
|
|
27
|
+
createFormErrorCollector: () => createFormErrorCollector,
|
|
25
28
|
createFrustrationCollector: () => createFrustrationCollector,
|
|
26
29
|
createNetworkCollector: () => createNetworkCollector,
|
|
27
30
|
flint: () => flint,
|
|
@@ -35,10 +38,10 @@ module.exports = __toCommonJS(index_exports);
|
|
|
35
38
|
|
|
36
39
|
// src/api.ts
|
|
37
40
|
var import_fflate = require("fflate");
|
|
38
|
-
async function fetchWithRetry(url,
|
|
41
|
+
async function fetchWithRetry(url, init2, retries = 3, baseDelay = 1e3) {
|
|
39
42
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
40
43
|
try {
|
|
41
|
-
const res = await fetch(url,
|
|
44
|
+
const res = await fetch(url, init2);
|
|
42
45
|
if (res.ok || attempt === retries) return res;
|
|
43
46
|
if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;
|
|
44
47
|
} catch (err) {
|
|
@@ -97,6 +100,24 @@ async function submitReplay(serverUrl, projectKey, reportId, events) {
|
|
|
97
100
|
|
|
98
101
|
// src/collectors/console.ts
|
|
99
102
|
var MAX_ENTRIES = 50;
|
|
103
|
+
var SENSITIVE_PATTERNS = [
|
|
104
|
+
/(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|bearer)\s*[:=]\s*["']?[^\s"',]{4,}/gi,
|
|
105
|
+
/\b(sk-[a-zA-Z0-9_-]{20,})\b/g,
|
|
106
|
+
// API keys (e.g. sk-ant-...)
|
|
107
|
+
/\b(ghp_[a-zA-Z0-9]{36,})\b/g,
|
|
108
|
+
// GitHub tokens
|
|
109
|
+
/\b(xoxb-[a-zA-Z0-9-]+)\b/g,
|
|
110
|
+
// Slack tokens
|
|
111
|
+
/\b(eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,})\b/g
|
|
112
|
+
// JWTs
|
|
113
|
+
];
|
|
114
|
+
function sanitize(str) {
|
|
115
|
+
let result = str;
|
|
116
|
+
for (const pattern of SENSITIVE_PATTERNS) {
|
|
117
|
+
result = result.replace(pattern, "[REDACTED]");
|
|
118
|
+
}
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
100
121
|
function createConsoleCollector() {
|
|
101
122
|
const entries = [];
|
|
102
123
|
let active = false;
|
|
@@ -115,6 +136,7 @@ function createConsoleCollector() {
|
|
|
115
136
|
str = String(args[0]);
|
|
116
137
|
}
|
|
117
138
|
if (str.length > 500) str = str.slice(0, 500) + "\u2026";
|
|
139
|
+
str = sanitize(str);
|
|
118
140
|
entries.push({ level, args: str, timestamp: Date.now() });
|
|
119
141
|
if (entries.length > MAX_ENTRIES) entries.shift();
|
|
120
142
|
}
|
|
@@ -215,6 +237,236 @@ function collectEnvironment() {
|
|
|
215
237
|
};
|
|
216
238
|
}
|
|
217
239
|
|
|
240
|
+
// src/collectors/formErrors.ts
|
|
241
|
+
var MAX_ENTRIES2 = 30;
|
|
242
|
+
var POST_SUBMIT_CHECK_MS = 300;
|
|
243
|
+
var SILENT_SUBMIT_WINDOW_MS = 400;
|
|
244
|
+
function getFormId(form) {
|
|
245
|
+
if (form.id) return `#${form.id}`;
|
|
246
|
+
if (form.getAttribute("name")) return `form[name="${form.getAttribute("name")}"]`;
|
|
247
|
+
if (form.action && form.action !== location.href) return form.action;
|
|
248
|
+
return `form:${Array.from(document.forms).indexOf(form)}`;
|
|
249
|
+
}
|
|
250
|
+
function getFieldLabel(field, root) {
|
|
251
|
+
const ariaLabel = field.getAttribute("aria-label");
|
|
252
|
+
if (ariaLabel) return ariaLabel;
|
|
253
|
+
const id = field.id;
|
|
254
|
+
if (id) {
|
|
255
|
+
const label = root.querySelector(`label[for="${id}"]`);
|
|
256
|
+
if (label?.textContent) return label.textContent.trim();
|
|
257
|
+
}
|
|
258
|
+
const parentLabel = field.closest("label");
|
|
259
|
+
if (parentLabel?.textContent) {
|
|
260
|
+
const text = parentLabel.textContent.trim();
|
|
261
|
+
if (text.length < 60) return text;
|
|
262
|
+
}
|
|
263
|
+
return field.name || field.placeholder || field.tagName.toLowerCase();
|
|
264
|
+
}
|
|
265
|
+
function getErrorMessage(field) {
|
|
266
|
+
const errId = field.getAttribute("aria-errormessage");
|
|
267
|
+
if (errId) {
|
|
268
|
+
const el = document.getElementById(errId);
|
|
269
|
+
if (el?.textContent) return el.textContent.trim();
|
|
270
|
+
}
|
|
271
|
+
const descId = field.getAttribute("aria-describedby");
|
|
272
|
+
if (descId) {
|
|
273
|
+
for (const id of descId.split(/\s+/)) {
|
|
274
|
+
const el = document.getElementById(id);
|
|
275
|
+
if (el?.textContent?.trim()) return el.textContent.trim();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {
|
|
279
|
+
if (field.validationMessage) return field.validationMessage;
|
|
280
|
+
}
|
|
281
|
+
const next = field.nextElementSibling;
|
|
282
|
+
if (next?.getAttribute("role") === "alert" && next.textContent) {
|
|
283
|
+
return next.textContent.trim();
|
|
284
|
+
}
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
function collectInvalidFields(root) {
|
|
288
|
+
const fields = [];
|
|
289
|
+
const invalidEls = root.querySelectorAll('[aria-invalid="true"], :invalid');
|
|
290
|
+
for (const el of invalidEls) {
|
|
291
|
+
if (el instanceof HTMLFormElement) continue;
|
|
292
|
+
if (el.tagName === "FIELDSET") continue;
|
|
293
|
+
fields.push({
|
|
294
|
+
name: getFieldLabel(el, root),
|
|
295
|
+
message: getErrorMessage(el)
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
return fields;
|
|
299
|
+
}
|
|
300
|
+
function collectVisibleErrors(root) {
|
|
301
|
+
const fields = [];
|
|
302
|
+
const seen = /* @__PURE__ */ new Set();
|
|
303
|
+
const alerts = root.querySelectorAll('[role="alert"]');
|
|
304
|
+
for (const el of alerts) {
|
|
305
|
+
const text = el.textContent?.trim();
|
|
306
|
+
if (!text || text.length > 200) continue;
|
|
307
|
+
if (seen.has(text)) continue;
|
|
308
|
+
seen.add(text);
|
|
309
|
+
fields.push({ name: "alert", message: text });
|
|
310
|
+
}
|
|
311
|
+
const errorEls = root.querySelectorAll(
|
|
312
|
+
"[data-error], [data-field-error], .error-message, .field-error, .form-error"
|
|
313
|
+
);
|
|
314
|
+
for (const el of errorEls) {
|
|
315
|
+
const text = el.textContent?.trim();
|
|
316
|
+
if (!text || text.length > 200) continue;
|
|
317
|
+
if (seen.has(text)) continue;
|
|
318
|
+
seen.add(text);
|
|
319
|
+
fields.push({ name: "error", message: text });
|
|
320
|
+
}
|
|
321
|
+
return fields;
|
|
322
|
+
}
|
|
323
|
+
function isSubmitButton(el) {
|
|
324
|
+
if (el.tagName === "BUTTON") {
|
|
325
|
+
const type = el.getAttribute("type");
|
|
326
|
+
return type === "submit" || type === null || type === "";
|
|
327
|
+
}
|
|
328
|
+
if (el.tagName === "INPUT") {
|
|
329
|
+
return el.type === "submit";
|
|
330
|
+
}
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
function findClosestForm(el) {
|
|
334
|
+
if ("form" in el && el.form) {
|
|
335
|
+
return el.form;
|
|
336
|
+
}
|
|
337
|
+
return el.closest("form");
|
|
338
|
+
}
|
|
339
|
+
function createFormErrorCollector() {
|
|
340
|
+
const entries = [];
|
|
341
|
+
const attemptCounts = /* @__PURE__ */ new Map();
|
|
342
|
+
let active = false;
|
|
343
|
+
let submitHandler = null;
|
|
344
|
+
let invalidHandler = null;
|
|
345
|
+
let clickHandler = null;
|
|
346
|
+
let lastSubmitTime = 0;
|
|
347
|
+
const recentRecords = /* @__PURE__ */ new Map();
|
|
348
|
+
const DEDUP_MS = 500;
|
|
349
|
+
function push(entry) {
|
|
350
|
+
const key = `${entry.formId}:${entry.timestamp}`;
|
|
351
|
+
const last = recentRecords.get(entry.formId);
|
|
352
|
+
if (last && entry.timestamp - last < DEDUP_MS) return;
|
|
353
|
+
recentRecords.set(entry.formId, entry.timestamp);
|
|
354
|
+
entries.push(entry);
|
|
355
|
+
if (entries.length > MAX_ENTRIES2) entries.shift();
|
|
356
|
+
}
|
|
357
|
+
function recordFormErrors(form, type) {
|
|
358
|
+
const formId = getFormId(form);
|
|
359
|
+
let fields = collectInvalidFields(form);
|
|
360
|
+
if (fields.length === 0) {
|
|
361
|
+
fields = collectVisibleErrors(form);
|
|
362
|
+
}
|
|
363
|
+
if (fields.length === 0) return;
|
|
364
|
+
const count = (attemptCounts.get(formId) ?? 0) + 1;
|
|
365
|
+
attemptCounts.set(formId, count);
|
|
366
|
+
push({
|
|
367
|
+
type,
|
|
368
|
+
formId,
|
|
369
|
+
fields,
|
|
370
|
+
attemptNumber: count,
|
|
371
|
+
url: location.href,
|
|
372
|
+
timestamp: Date.now()
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function handleSubmitButtonClick(button) {
|
|
376
|
+
const form = findClosestForm(button);
|
|
377
|
+
setTimeout(() => {
|
|
378
|
+
if (!active) return;
|
|
379
|
+
if (Date.now() - lastSubmitTime < SILENT_SUBMIT_WINDOW_MS) return;
|
|
380
|
+
const root = form ?? document.body;
|
|
381
|
+
const formId = form ? getFormId(form) : `page:${location.pathname}`;
|
|
382
|
+
let fields = form ? collectInvalidFields(form) : [];
|
|
383
|
+
if (fields.length === 0) {
|
|
384
|
+
fields = collectVisibleErrors(root);
|
|
385
|
+
}
|
|
386
|
+
if (fields.length === 0 && form) {
|
|
387
|
+
fields = collectVisibleErrors(document.body);
|
|
388
|
+
}
|
|
389
|
+
if (fields.length === 0) return;
|
|
390
|
+
const count = (attemptCounts.get(formId) ?? 0) + 1;
|
|
391
|
+
attemptCounts.set(formId, count);
|
|
392
|
+
push({
|
|
393
|
+
type: "silent_submit",
|
|
394
|
+
formId,
|
|
395
|
+
fields,
|
|
396
|
+
attemptNumber: count,
|
|
397
|
+
url: location.href,
|
|
398
|
+
timestamp: Date.now()
|
|
399
|
+
});
|
|
400
|
+
}, SILENT_SUBMIT_WINDOW_MS);
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
start() {
|
|
404
|
+
if (active) return;
|
|
405
|
+
active = true;
|
|
406
|
+
submitHandler = (e) => {
|
|
407
|
+
lastSubmitTime = Date.now();
|
|
408
|
+
const form = e.target;
|
|
409
|
+
if (!(form instanceof HTMLFormElement)) return;
|
|
410
|
+
setTimeout(() => {
|
|
411
|
+
if (!active) return;
|
|
412
|
+
recordFormErrors(form, "validation_failed");
|
|
413
|
+
}, POST_SUBMIT_CHECK_MS);
|
|
414
|
+
};
|
|
415
|
+
document.addEventListener("submit", submitHandler, true);
|
|
416
|
+
invalidHandler = (e) => {
|
|
417
|
+
lastSubmitTime = Date.now();
|
|
418
|
+
const field = e.target;
|
|
419
|
+
const form = field.closest("form");
|
|
420
|
+
if (!form) return;
|
|
421
|
+
setTimeout(() => {
|
|
422
|
+
if (!active) return;
|
|
423
|
+
recordFormErrors(form, "validation_failed");
|
|
424
|
+
}, POST_SUBMIT_CHECK_MS);
|
|
425
|
+
};
|
|
426
|
+
document.addEventListener("invalid", invalidHandler, true);
|
|
427
|
+
clickHandler = (e) => {
|
|
428
|
+
const target = e.target;
|
|
429
|
+
if (!target) return;
|
|
430
|
+
const button = target.closest("button, input[type=submit]");
|
|
431
|
+
if (!button) return;
|
|
432
|
+
if (!isSubmitButton(button)) return;
|
|
433
|
+
handleSubmitButtonClick(button);
|
|
434
|
+
};
|
|
435
|
+
document.addEventListener("click", clickHandler, true);
|
|
436
|
+
},
|
|
437
|
+
stop() {
|
|
438
|
+
if (!active) return;
|
|
439
|
+
active = false;
|
|
440
|
+
if (submitHandler) document.removeEventListener("submit", submitHandler, true);
|
|
441
|
+
if (invalidHandler) document.removeEventListener("invalid", invalidHandler, true);
|
|
442
|
+
if (clickHandler) document.removeEventListener("click", clickHandler, true);
|
|
443
|
+
attemptCounts.clear();
|
|
444
|
+
recentRecords.clear();
|
|
445
|
+
},
|
|
446
|
+
getEntries() {
|
|
447
|
+
return [...entries];
|
|
448
|
+
},
|
|
449
|
+
report(formId, errors) {
|
|
450
|
+
const fields = [];
|
|
451
|
+
for (const [name, err] of Object.entries(errors)) {
|
|
452
|
+
if (!err) continue;
|
|
453
|
+
fields.push({ name, message: err.message });
|
|
454
|
+
}
|
|
455
|
+
if (fields.length === 0) return;
|
|
456
|
+
const count = (attemptCounts.get(formId) ?? 0) + 1;
|
|
457
|
+
attemptCounts.set(formId, count);
|
|
458
|
+
push({
|
|
459
|
+
type: "validation_failed",
|
|
460
|
+
formId,
|
|
461
|
+
fields,
|
|
462
|
+
attemptNumber: count,
|
|
463
|
+
url: location.href,
|
|
464
|
+
timestamp: Date.now()
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
218
470
|
// src/collectors/frustration.ts
|
|
219
471
|
function createFrustrationCollector(opts) {
|
|
220
472
|
const threshold = opts?.rageClickThreshold ?? 3;
|
|
@@ -328,7 +580,7 @@ function createFrustrationCollector(opts) {
|
|
|
328
580
|
}
|
|
329
581
|
|
|
330
582
|
// src/collectors/network.ts
|
|
331
|
-
var
|
|
583
|
+
var MAX_ENTRIES3 = 50;
|
|
332
584
|
var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
|
|
333
585
|
"browser-intake-datadoghq.com",
|
|
334
586
|
"rum.browser-intake-datadoghq.com",
|
|
@@ -361,18 +613,18 @@ function createNetworkCollector(extraBlockedHosts = []) {
|
|
|
361
613
|
let active = false;
|
|
362
614
|
function push(entry) {
|
|
363
615
|
entries.push(entry);
|
|
364
|
-
if (entries.length >
|
|
616
|
+
if (entries.length > MAX_ENTRIES3) entries.shift();
|
|
365
617
|
}
|
|
366
618
|
return {
|
|
367
619
|
start() {
|
|
368
620
|
if (active) return;
|
|
369
621
|
active = true;
|
|
370
622
|
origFetch = window.fetch;
|
|
371
|
-
window.fetch = async (input,
|
|
372
|
-
const method = (
|
|
623
|
+
window.fetch = async (input, init2) => {
|
|
624
|
+
const method = (init2?.method ?? "GET").toUpperCase();
|
|
373
625
|
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
374
626
|
const startTime = Date.now();
|
|
375
|
-
const res = await origFetch.call(window, input,
|
|
627
|
+
const res = await origFetch.call(window, input, init2);
|
|
376
628
|
if (res.status >= 400 && !isBlockedUrl(url, blocked)) {
|
|
377
629
|
push({
|
|
378
630
|
method,
|
|
@@ -415,6 +667,10 @@ function createNetworkCollector(extraBlockedHosts = []) {
|
|
|
415
667
|
}
|
|
416
668
|
|
|
417
669
|
// src/store.ts
|
|
670
|
+
var formErrorCollectorRef = null;
|
|
671
|
+
function _setFormErrorCollector(collector) {
|
|
672
|
+
formErrorCollectorRef = collector;
|
|
673
|
+
}
|
|
418
674
|
var state = { user: void 0, sessionReplay: void 0 };
|
|
419
675
|
var listeners = /* @__PURE__ */ new Set();
|
|
420
676
|
function emit() {
|
|
@@ -435,7 +691,155 @@ var flint = {
|
|
|
435
691
|
setSessionReplay(url) {
|
|
436
692
|
state = { ...state, sessionReplay: url ?? void 0 };
|
|
437
693
|
emit();
|
|
694
|
+
},
|
|
695
|
+
/**
|
|
696
|
+
* Report a form validation failure with exact field-level errors.
|
|
697
|
+
* Call this from your form library's error callback.
|
|
698
|
+
*
|
|
699
|
+
* @example
|
|
700
|
+
* // React Hook Form + Zod
|
|
701
|
+
* const onSubmit = handleSubmit(onValid, (errors) => {
|
|
702
|
+
* flint.reportFormError('checkout-form', errors);
|
|
703
|
+
* });
|
|
704
|
+
*/
|
|
705
|
+
reportFormError(formId, errors) {
|
|
706
|
+
formErrorCollectorRef?.report(formId, errors);
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// src/singleton.ts
|
|
711
|
+
var DEFAULT_REPLAY_BUFFER_MS = 6e4;
|
|
712
|
+
var instance = null;
|
|
713
|
+
function init(config) {
|
|
714
|
+
if (instance) {
|
|
715
|
+
console.warn("[Flint] Already initialized. Call Flint.shutdown() first to re-initialize.");
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const {
|
|
719
|
+
enableConsole = true,
|
|
720
|
+
enableNetwork = true,
|
|
721
|
+
enableFormErrors = true,
|
|
722
|
+
enableFrustration = false,
|
|
723
|
+
autoReportFrustration = false,
|
|
724
|
+
enableReplay = false,
|
|
725
|
+
replayBufferMs = DEFAULT_REPLAY_BUFFER_MS,
|
|
726
|
+
blockedHosts = [],
|
|
727
|
+
frustration: frustrationOpts,
|
|
728
|
+
onFrustration,
|
|
729
|
+
_replayRecorder
|
|
730
|
+
} = config;
|
|
731
|
+
const flintHost = (() => {
|
|
732
|
+
try {
|
|
733
|
+
return new URL(config.serverUrl).hostname;
|
|
734
|
+
} catch {
|
|
735
|
+
return "";
|
|
736
|
+
}
|
|
737
|
+
})();
|
|
738
|
+
const consoleCol = enableConsole ? createConsoleCollector() : null;
|
|
739
|
+
consoleCol?.start();
|
|
740
|
+
const networkCol = enableNetwork ? createNetworkCollector([...blockedHosts, ...flintHost ? [flintHost] : []]) : null;
|
|
741
|
+
networkCol?.start();
|
|
742
|
+
const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;
|
|
743
|
+
if (formErrorsCol) {
|
|
744
|
+
formErrorsCol.start();
|
|
745
|
+
_setFormErrorCollector(formErrorsCol);
|
|
746
|
+
}
|
|
747
|
+
const frustrationCol = enableFrustration ? createFrustrationCollector(frustrationOpts) : null;
|
|
748
|
+
frustrationCol?.start();
|
|
749
|
+
if (config.user) {
|
|
750
|
+
flint.setUser(config.user);
|
|
438
751
|
}
|
|
752
|
+
const replayEvents = [];
|
|
753
|
+
let stopReplay = null;
|
|
754
|
+
instance = {
|
|
755
|
+
config,
|
|
756
|
+
console: consoleCol,
|
|
757
|
+
network: networkCol,
|
|
758
|
+
formErrors: formErrorsCol,
|
|
759
|
+
frustration: frustrationCol,
|
|
760
|
+
replayEvents,
|
|
761
|
+
stopReplay: null
|
|
762
|
+
};
|
|
763
|
+
if (enableReplay && _replayRecorder) {
|
|
764
|
+
_replayRecorder((event) => {
|
|
765
|
+
replayEvents.push(event);
|
|
766
|
+
const cutoff = Date.now() - replayBufferMs;
|
|
767
|
+
while (replayEvents.length > 0 && replayEvents[0].timestamp < cutoff) {
|
|
768
|
+
replayEvents.shift();
|
|
769
|
+
}
|
|
770
|
+
}).then((stop) => {
|
|
771
|
+
stopReplay = stop ?? null;
|
|
772
|
+
if (instance) instance.stopReplay = stopReplay;
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
if (frustrationCol && autoReportFrustration) {
|
|
776
|
+
frustrationCol.onFrustration(async (event) => {
|
|
777
|
+
onFrustration?.(event);
|
|
778
|
+
const user = getSnapshot().user ?? config.user;
|
|
779
|
+
await submitReport(config.serverUrl, config.projectKey, {
|
|
780
|
+
reporterId: user?.id ?? "anonymous",
|
|
781
|
+
reporterName: user?.name ?? "Anonymous",
|
|
782
|
+
reporterEmail: user?.email,
|
|
783
|
+
description: `[Auto-detected] ${event.type.replace(/_/g, " ")}: ${event.details}`,
|
|
784
|
+
severity: event.type === "error_loop" ? "P1" : event.type === "rage_click" ? "P2" : "P3",
|
|
785
|
+
url: event.url,
|
|
786
|
+
meta: {
|
|
787
|
+
...config.meta,
|
|
788
|
+
environment: collectEnvironment(),
|
|
789
|
+
consoleLogs: consoleCol?.getEntries() ?? [],
|
|
790
|
+
networkErrors: networkCol?.getEntries() ?? [],
|
|
791
|
+
formErrors: formErrorsCol?.getEntries() ?? [],
|
|
792
|
+
frustrationEvent: event
|
|
793
|
+
}
|
|
794
|
+
}).catch(() => {
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
} else if (frustrationCol && onFrustration) {
|
|
798
|
+
frustrationCol.onFrustration(onFrustration);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function shutdown() {
|
|
802
|
+
if (!instance) return;
|
|
803
|
+
instance.console?.stop();
|
|
804
|
+
instance.network?.stop();
|
|
805
|
+
instance.formErrors?.stop();
|
|
806
|
+
_setFormErrorCollector(null);
|
|
807
|
+
instance.frustration?.stop();
|
|
808
|
+
instance.stopReplay?.();
|
|
809
|
+
instance = null;
|
|
810
|
+
}
|
|
811
|
+
function isInitialized() {
|
|
812
|
+
return instance !== null;
|
|
813
|
+
}
|
|
814
|
+
function getInstance() {
|
|
815
|
+
return instance;
|
|
816
|
+
}
|
|
817
|
+
function getMeta(extraMeta) {
|
|
818
|
+
return {
|
|
819
|
+
...extraMeta,
|
|
820
|
+
environment: collectEnvironment(),
|
|
821
|
+
consoleLogs: instance?.console?.getEntries() ?? [],
|
|
822
|
+
networkErrors: instance?.network?.getEntries() ?? [],
|
|
823
|
+
formErrors: instance?.formErrors?.getEntries() ?? []
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
function getReplayEvents() {
|
|
827
|
+
return instance ? [...instance.replayEvents] : [];
|
|
828
|
+
}
|
|
829
|
+
function getConfig() {
|
|
830
|
+
return instance?.config ?? null;
|
|
831
|
+
}
|
|
832
|
+
var Flint = {
|
|
833
|
+
init,
|
|
834
|
+
shutdown,
|
|
835
|
+
isInitialized,
|
|
836
|
+
getInstance,
|
|
837
|
+
getMeta,
|
|
838
|
+
getReplayEvents,
|
|
839
|
+
getConfig,
|
|
840
|
+
setUser: flint.setUser,
|
|
841
|
+
setSessionReplay: flint.setSessionReplay,
|
|
842
|
+
reportFormError: flint.reportFormError
|
|
439
843
|
};
|
|
440
844
|
|
|
441
845
|
// src/theme.ts
|
|
@@ -478,8 +882,11 @@ function resolveTheme(theme) {
|
|
|
478
882
|
}
|
|
479
883
|
// Annotate the CommonJS export names for ESM import in node:
|
|
480
884
|
0 && (module.exports = {
|
|
885
|
+
Flint,
|
|
886
|
+
_setFormErrorCollector,
|
|
481
887
|
collectEnvironment,
|
|
482
888
|
createConsoleCollector,
|
|
889
|
+
createFormErrorCollector,
|
|
483
890
|
createFrustrationCollector,
|
|
484
891
|
createNetworkCollector,
|
|
485
892
|
flint,
|