@diegotsi/flint-react 1.0.2 → 1.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 +77 -464
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -130
- package/dist/index.d.ts +6 -130
- package/dist/index.js +67 -455
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -30,9 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
Flint: () => import_flint_core9.Flint,
|
|
33
34
|
FlintModal: () => FlintModal,
|
|
34
35
|
FlintWidget: () => FlintWidget,
|
|
35
|
-
flint: () => flint
|
|
36
|
+
flint: () => import_flint_core9.flint
|
|
36
37
|
});
|
|
37
38
|
module.exports = __toCommonJS(index_exports);
|
|
38
39
|
|
|
@@ -40,437 +41,8 @@ module.exports = __toCommonJS(index_exports);
|
|
|
40
41
|
var import_react2 = require("react");
|
|
41
42
|
var import_react_i18next = require("react-i18next");
|
|
42
43
|
|
|
43
|
-
//
|
|
44
|
-
var
|
|
45
|
-
async function fetchWithRetry(url, init, retries = 3, baseDelay = 1e3) {
|
|
46
|
-
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
47
|
-
try {
|
|
48
|
-
const res = await fetch(url, init);
|
|
49
|
-
if (res.ok || attempt === retries) return res;
|
|
50
|
-
if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;
|
|
51
|
-
} catch (err) {
|
|
52
|
-
if (attempt === retries) throw err;
|
|
53
|
-
}
|
|
54
|
-
await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));
|
|
55
|
-
}
|
|
56
|
-
throw new Error("Max retries exceeded");
|
|
57
|
-
}
|
|
58
|
-
async function submitReport(serverUrl, projectKey, payload, screenshot) {
|
|
59
|
-
const url = `${serverUrl.replace(/\/$/, "")}/api/v1/bug-reports`;
|
|
60
|
-
let body;
|
|
61
|
-
const headers = {
|
|
62
|
-
"x-project-key": projectKey
|
|
63
|
-
};
|
|
64
|
-
if (screenshot) {
|
|
65
|
-
const form = new FormData();
|
|
66
|
-
form.append("reporterId", payload.reporterId);
|
|
67
|
-
form.append("reporterName", payload.reporterName);
|
|
68
|
-
if (payload.reporterEmail) form.append("reporterEmail", payload.reporterEmail);
|
|
69
|
-
form.append("description", payload.description);
|
|
70
|
-
if (payload.expectedBehavior) form.append("expectedBehavior", payload.expectedBehavior);
|
|
71
|
-
if (payload.stepsToReproduce) form.append("stepsToReproduce", JSON.stringify(payload.stepsToReproduce));
|
|
72
|
-
if (payload.externalReplayUrl) form.append("externalReplayUrl", payload.externalReplayUrl);
|
|
73
|
-
if (payload.additionalContext) form.append("additionalContext", payload.additionalContext);
|
|
74
|
-
form.append("severity", payload.severity);
|
|
75
|
-
if (payload.url) form.append("url", payload.url);
|
|
76
|
-
if (payload.meta) form.append("meta", JSON.stringify(payload.meta));
|
|
77
|
-
form.append("screenshot", screenshot);
|
|
78
|
-
body = form;
|
|
79
|
-
} else {
|
|
80
|
-
body = JSON.stringify(payload);
|
|
81
|
-
headers["Content-Type"] = "application/json";
|
|
82
|
-
}
|
|
83
|
-
const res = await fetchWithRetry(url, { method: "POST", headers, body });
|
|
84
|
-
if (!res.ok) {
|
|
85
|
-
const err = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
86
|
-
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
87
|
-
}
|
|
88
|
-
return res.json();
|
|
89
|
-
}
|
|
90
|
-
async function submitReplay(serverUrl, projectKey, reportId, events) {
|
|
91
|
-
const json = JSON.stringify(events);
|
|
92
|
-
const encoded = new TextEncoder().encode(json);
|
|
93
|
-
const compressed = (0, import_fflate.gzipSync)(encoded);
|
|
94
|
-
const url = `${serverUrl.replace(/\/$/, "")}/api/v1/bug-reports/${reportId}/replay`;
|
|
95
|
-
await fetch(url, {
|
|
96
|
-
method: "POST",
|
|
97
|
-
headers: {
|
|
98
|
-
"x-project-key": projectKey,
|
|
99
|
-
"Content-Type": "application/octet-stream"
|
|
100
|
-
},
|
|
101
|
-
body: compressed.buffer
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
var MAX_ENTRIES = 50;
|
|
105
|
-
function createConsoleCollector() {
|
|
106
|
-
const entries = [];
|
|
107
|
-
let active = false;
|
|
108
|
-
const originals = {
|
|
109
|
-
log: console.log.bind(console),
|
|
110
|
-
warn: console.warn.bind(console),
|
|
111
|
-
error: console.error.bind(console)
|
|
112
|
-
};
|
|
113
|
-
let origOnerror = null;
|
|
114
|
-
let origUnhandled = null;
|
|
115
|
-
function push(level, args) {
|
|
116
|
-
let str;
|
|
117
|
-
try {
|
|
118
|
-
str = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
|
|
119
|
-
} catch {
|
|
120
|
-
str = String(args[0]);
|
|
121
|
-
}
|
|
122
|
-
if (str.length > 500) str = str.slice(0, 500) + "\u2026";
|
|
123
|
-
entries.push({ level, args: str, timestamp: Date.now() });
|
|
124
|
-
if (entries.length > MAX_ENTRIES) entries.shift();
|
|
125
|
-
}
|
|
126
|
-
return {
|
|
127
|
-
start() {
|
|
128
|
-
if (active) return;
|
|
129
|
-
active = true;
|
|
130
|
-
console.log = (...args) => {
|
|
131
|
-
push("log", args);
|
|
132
|
-
originals.log(...args);
|
|
133
|
-
};
|
|
134
|
-
console.warn = (...args) => {
|
|
135
|
-
push("warn", args);
|
|
136
|
-
originals.warn(...args);
|
|
137
|
-
};
|
|
138
|
-
console.error = (...args) => {
|
|
139
|
-
push("error", args);
|
|
140
|
-
originals.error(...args);
|
|
141
|
-
};
|
|
142
|
-
origOnerror = window.onerror;
|
|
143
|
-
window.onerror = (msg, src, line, col, err) => {
|
|
144
|
-
push("error", [err?.message ?? String(msg), `${src}:${line}:${col}`]);
|
|
145
|
-
if (typeof origOnerror === "function") return origOnerror(msg, src, line, col, err);
|
|
146
|
-
return false;
|
|
147
|
-
};
|
|
148
|
-
origUnhandled = window.onunhandledrejection;
|
|
149
|
-
window.onunhandledrejection = (event) => {
|
|
150
|
-
const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);
|
|
151
|
-
push("error", ["UnhandledRejection:", reason]);
|
|
152
|
-
if (typeof origUnhandled === "function") origUnhandled.call(window, event);
|
|
153
|
-
};
|
|
154
|
-
},
|
|
155
|
-
stop() {
|
|
156
|
-
if (!active) return;
|
|
157
|
-
active = false;
|
|
158
|
-
console.log = originals.log;
|
|
159
|
-
console.warn = originals.warn;
|
|
160
|
-
console.error = originals.error;
|
|
161
|
-
window.onerror = origOnerror;
|
|
162
|
-
window.onunhandledrejection = origUnhandled;
|
|
163
|
-
},
|
|
164
|
-
getEntries() {
|
|
165
|
-
return [...entries];
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
function collectEnvironment() {
|
|
170
|
-
const ua = navigator.userAgent;
|
|
171
|
-
let browser = "Unknown";
|
|
172
|
-
const chromeM = ua.match(/Chrome\/(\d+)/);
|
|
173
|
-
const firefoxM = ua.match(/Firefox\/(\d+)/);
|
|
174
|
-
const edgeM = ua.match(/Edg\/(\d+)/);
|
|
175
|
-
const safariM = ua.match(/Version\/(\d+)/);
|
|
176
|
-
const operaM = ua.match(/OPR\/(\d+)/);
|
|
177
|
-
if (operaM) {
|
|
178
|
-
browser = `Opera ${operaM[1]}`;
|
|
179
|
-
} else if (edgeM) {
|
|
180
|
-
browser = `Edge ${edgeM[1]}`;
|
|
181
|
-
} else if (chromeM && !/Edg|OPR/.test(ua)) {
|
|
182
|
-
browser = `Chrome ${chromeM[1]}`;
|
|
183
|
-
} else if (firefoxM) {
|
|
184
|
-
browser = `Firefox ${firefoxM[1]}`;
|
|
185
|
-
} else if (safariM && /Safari\//.test(ua)) {
|
|
186
|
-
browser = `Safari ${safariM[1]}`;
|
|
187
|
-
}
|
|
188
|
-
let os = "Unknown";
|
|
189
|
-
const macM = ua.match(/Mac OS X (\d+[._]\d+)/);
|
|
190
|
-
const winM = ua.match(/Windows NT (\d+\.\d+)/);
|
|
191
|
-
const androidM = ua.match(/Android (\d+)/);
|
|
192
|
-
const iosM = ua.match(/iPhone OS (\d+[._]\d+)/);
|
|
193
|
-
if (macM) {
|
|
194
|
-
os = `macOS ${macM[1].replace("_", ".")}`;
|
|
195
|
-
} else if (winM) {
|
|
196
|
-
const winMap = {
|
|
197
|
-
"10.0": "10/11",
|
|
198
|
-
"6.3": "8.1",
|
|
199
|
-
"6.2": "8",
|
|
200
|
-
"6.1": "7"
|
|
201
|
-
};
|
|
202
|
-
os = `Windows ${winMap[winM[1]] ?? winM[1]}`;
|
|
203
|
-
} else if (androidM) {
|
|
204
|
-
os = `Android ${androidM[1]}`;
|
|
205
|
-
} else if (iosM) {
|
|
206
|
-
os = `iOS ${iosM[1].replace(/_/g, ".")}`;
|
|
207
|
-
} else if (/Linux/.test(ua)) {
|
|
208
|
-
os = "Linux";
|
|
209
|
-
}
|
|
210
|
-
return {
|
|
211
|
-
browser,
|
|
212
|
-
os,
|
|
213
|
-
viewport: `${window.innerWidth}x${window.innerHeight}`,
|
|
214
|
-
screen: `${screen.width}x${screen.height}`,
|
|
215
|
-
language: navigator.language,
|
|
216
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
217
|
-
online: navigator.onLine
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
function createFrustrationCollector(opts) {
|
|
221
|
-
const threshold = opts?.rageClickThreshold ?? 3;
|
|
222
|
-
const window2 = opts?.rageClickWindow ?? 500;
|
|
223
|
-
const errorThreshold = opts?.errorLoopThreshold ?? 3;
|
|
224
|
-
const errorWindow = opts?.errorLoopWindow ?? 3e4;
|
|
225
|
-
const deadClicksEnabled = opts?.enableDeadClicks ?? true;
|
|
226
|
-
const events = [];
|
|
227
|
-
const listeners2 = /* @__PURE__ */ new Set();
|
|
228
|
-
const clickHistory = [];
|
|
229
|
-
const errorCounts = /* @__PURE__ */ new Map();
|
|
230
|
-
let clickHandler = null;
|
|
231
|
-
let origConsoleError = null;
|
|
232
|
-
function emit2(event) {
|
|
233
|
-
events.push(event);
|
|
234
|
-
if (events.length > 50) events.shift();
|
|
235
|
-
for (const cb of listeners2) cb(event);
|
|
236
|
-
}
|
|
237
|
-
function getCSSSelector(el) {
|
|
238
|
-
if (el.id) return `#${el.id}`;
|
|
239
|
-
const tag = el.tagName.toLowerCase();
|
|
240
|
-
const cls = [...el.classList].slice(0, 3).join(".");
|
|
241
|
-
if (cls) return `${tag}.${cls}`;
|
|
242
|
-
return tag;
|
|
243
|
-
}
|
|
244
|
-
function isInteractive(el) {
|
|
245
|
-
const tag = el.tagName.toLowerCase();
|
|
246
|
-
if (["a", "button", "input", "select", "textarea", "label", "summary"].includes(tag)) return true;
|
|
247
|
-
if (el.getAttribute("role") === "button" || el.getAttribute("tabindex")) return true;
|
|
248
|
-
if (el.onclick || el.getAttribute("onclick")) return true;
|
|
249
|
-
if (el.closest("a, button, [role=button], [onclick]")) return true;
|
|
250
|
-
try {
|
|
251
|
-
if (getComputedStyle(el).cursor === "pointer") return true;
|
|
252
|
-
} catch {
|
|
253
|
-
}
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
function handleClick(e) {
|
|
257
|
-
const target = e.target;
|
|
258
|
-
if (!target) return;
|
|
259
|
-
const now = Date.now();
|
|
260
|
-
clickHistory.push({ target, time: now });
|
|
261
|
-
while (clickHistory.length > 0 && now - clickHistory[0].time > 1e3) {
|
|
262
|
-
clickHistory.shift();
|
|
263
|
-
}
|
|
264
|
-
const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window2);
|
|
265
|
-
if (recentOnSame.length >= threshold) {
|
|
266
|
-
emit2({
|
|
267
|
-
type: "rage_click",
|
|
268
|
-
timestamp: now,
|
|
269
|
-
target: getCSSSelector(target),
|
|
270
|
-
details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,
|
|
271
|
-
url: globalThis.location?.href ?? ""
|
|
272
|
-
});
|
|
273
|
-
clickHistory.length = 0;
|
|
274
|
-
}
|
|
275
|
-
if (deadClicksEnabled && !isInteractive(target)) {
|
|
276
|
-
emit2({
|
|
277
|
-
type: "dead_click",
|
|
278
|
-
timestamp: now,
|
|
279
|
-
target: getCSSSelector(target),
|
|
280
|
-
details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,
|
|
281
|
-
url: globalThis.location?.href ?? ""
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
function patchConsoleError() {
|
|
286
|
-
origConsoleError = console.error;
|
|
287
|
-
console.error = (...args) => {
|
|
288
|
-
origConsoleError?.apply(console, args);
|
|
289
|
-
const key = String(args[0]).slice(0, 100);
|
|
290
|
-
const now = Date.now();
|
|
291
|
-
const existing = errorCounts.get(key);
|
|
292
|
-
if (existing && now - existing.firstSeen < errorWindow) {
|
|
293
|
-
existing.count++;
|
|
294
|
-
if (existing.count >= errorThreshold) {
|
|
295
|
-
emit2({
|
|
296
|
-
type: "error_loop",
|
|
297
|
-
timestamp: now,
|
|
298
|
-
target: "console",
|
|
299
|
-
details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1e3)}s: ${key}`,
|
|
300
|
-
url: globalThis.location?.href ?? ""
|
|
301
|
-
});
|
|
302
|
-
errorCounts.delete(key);
|
|
303
|
-
}
|
|
304
|
-
} else {
|
|
305
|
-
errorCounts.set(key, { count: 1, firstSeen: now });
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
return {
|
|
310
|
-
start() {
|
|
311
|
-
clickHandler = handleClick;
|
|
312
|
-
document.addEventListener("click", clickHandler, true);
|
|
313
|
-
patchConsoleError();
|
|
314
|
-
},
|
|
315
|
-
stop() {
|
|
316
|
-
if (clickHandler) document.removeEventListener("click", clickHandler, true);
|
|
317
|
-
if (origConsoleError) console.error = origConsoleError;
|
|
318
|
-
clickHistory.length = 0;
|
|
319
|
-
errorCounts.clear();
|
|
320
|
-
},
|
|
321
|
-
getEvents() {
|
|
322
|
-
return [...events];
|
|
323
|
-
},
|
|
324
|
-
onFrustration(callback) {
|
|
325
|
-
listeners2.add(callback);
|
|
326
|
-
return () => listeners2.delete(callback);
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
var MAX_ENTRIES2 = 50;
|
|
331
|
-
var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
|
|
332
|
-
"browser-intake-datadoghq.com",
|
|
333
|
-
"rum.browser-intake-datadoghq.com",
|
|
334
|
-
"logs.browser-intake-datadoghq.com",
|
|
335
|
-
"session-replay.browser-intake-datadoghq.com"
|
|
336
|
-
]);
|
|
337
|
-
function isBlockedUrl(url, extra) {
|
|
338
|
-
try {
|
|
339
|
-
const host = new URL(url, location.href).hostname;
|
|
340
|
-
const all = [...BLOCKED_HOSTS, ...extra];
|
|
341
|
-
return all.some((b) => host === b || host.endsWith("." + b));
|
|
342
|
-
} catch {
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
function truncateUrl(url) {
|
|
347
|
-
try {
|
|
348
|
-
const u = new URL(url, location.href);
|
|
349
|
-
const base = `${u.origin}${u.pathname}`;
|
|
350
|
-
return base.length > 200 ? base.slice(0, 200) + "\u2026" : base;
|
|
351
|
-
} catch {
|
|
352
|
-
return url.length > 200 ? url.slice(0, 200) + "\u2026" : url;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
function createNetworkCollector(extraBlockedHosts = []) {
|
|
356
|
-
const entries = [];
|
|
357
|
-
const blocked = new Set(extraBlockedHosts);
|
|
358
|
-
let origFetch = null;
|
|
359
|
-
let origXHROpen = null;
|
|
360
|
-
let active = false;
|
|
361
|
-
function push(entry) {
|
|
362
|
-
entries.push(entry);
|
|
363
|
-
if (entries.length > MAX_ENTRIES2) entries.shift();
|
|
364
|
-
}
|
|
365
|
-
return {
|
|
366
|
-
start() {
|
|
367
|
-
if (active) return;
|
|
368
|
-
active = true;
|
|
369
|
-
origFetch = window.fetch;
|
|
370
|
-
window.fetch = async (input, init) => {
|
|
371
|
-
const method = (init?.method ?? "GET").toUpperCase();
|
|
372
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
373
|
-
const startTime = Date.now();
|
|
374
|
-
const res = await origFetch.call(window, input, init);
|
|
375
|
-
if (res.status >= 400 && !isBlockedUrl(url, blocked)) {
|
|
376
|
-
push({
|
|
377
|
-
method,
|
|
378
|
-
url: truncateUrl(url),
|
|
379
|
-
status: res.status,
|
|
380
|
-
duration: Date.now() - startTime,
|
|
381
|
-
timestamp: startTime
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
return res;
|
|
385
|
-
};
|
|
386
|
-
origXHROpen = XMLHttpRequest.prototype.open;
|
|
387
|
-
XMLHttpRequest.prototype.open = function(method, url, async, username, password) {
|
|
388
|
-
const startTime = Date.now();
|
|
389
|
-
const urlStr = typeof url === "string" ? url : url.href;
|
|
390
|
-
this.addEventListener("load", () => {
|
|
391
|
-
if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {
|
|
392
|
-
push({
|
|
393
|
-
method: method.toUpperCase(),
|
|
394
|
-
url: truncateUrl(urlStr),
|
|
395
|
-
status: this.status,
|
|
396
|
-
duration: Date.now() - startTime,
|
|
397
|
-
timestamp: startTime
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
return origXHROpen.apply(this, [method, url, async ?? true, username, password]);
|
|
402
|
-
};
|
|
403
|
-
},
|
|
404
|
-
stop() {
|
|
405
|
-
if (!active) return;
|
|
406
|
-
active = false;
|
|
407
|
-
if (origFetch) window.fetch = origFetch;
|
|
408
|
-
if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;
|
|
409
|
-
},
|
|
410
|
-
getEntries() {
|
|
411
|
-
return [...entries];
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
var state = { user: void 0, sessionReplay: void 0 };
|
|
416
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
417
|
-
function emit() {
|
|
418
|
-
for (const l of listeners) l();
|
|
419
|
-
}
|
|
420
|
-
function subscribe(listener) {
|
|
421
|
-
listeners.add(listener);
|
|
422
|
-
return () => listeners.delete(listener);
|
|
423
|
-
}
|
|
424
|
-
function getSnapshot() {
|
|
425
|
-
return state;
|
|
426
|
-
}
|
|
427
|
-
var flint = {
|
|
428
|
-
setUser(user) {
|
|
429
|
-
state = { ...state, user: user ?? void 0 };
|
|
430
|
-
emit();
|
|
431
|
-
},
|
|
432
|
-
setSessionReplay(url) {
|
|
433
|
-
state = { ...state, sessionReplay: url ?? void 0 };
|
|
434
|
-
emit();
|
|
435
|
-
}
|
|
436
|
-
};
|
|
437
|
-
var light = {
|
|
438
|
-
background: "rgba(255,255,255,0.90)",
|
|
439
|
-
backgroundSecondary: "rgba(249,250,251,0.75)",
|
|
440
|
-
accent: "#2563eb",
|
|
441
|
-
accentHover: "#1d4ed8",
|
|
442
|
-
text: "#111827",
|
|
443
|
-
textMuted: "#6b7280",
|
|
444
|
-
border: "rgba(255,255,255,0.9)",
|
|
445
|
-
shadow: "0 32px 80px rgba(0,0,0,0.18), 0 8px 32px rgba(0,0,0,0.1), 0 0 0 1px rgba(0,0,0,0.04)",
|
|
446
|
-
buttonText: "#ffffff",
|
|
447
|
-
backdropFilter: "blur(32px) saturate(1.8)"
|
|
448
|
-
};
|
|
449
|
-
var dark = {
|
|
450
|
-
background: "rgba(15,20,35,0.88)",
|
|
451
|
-
backgroundSecondary: "rgba(5,8,18,0.65)",
|
|
452
|
-
accent: "#4d8aff",
|
|
453
|
-
accentHover: "#3b6fdb",
|
|
454
|
-
text: "#dde3ef",
|
|
455
|
-
textMuted: "#6b7a93",
|
|
456
|
-
border: "rgba(255,255,255,0.08)",
|
|
457
|
-
shadow: "0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)",
|
|
458
|
-
buttonText: "#ffffff",
|
|
459
|
-
backdropFilter: "blur(32px) saturate(1.6)"
|
|
460
|
-
};
|
|
461
|
-
function resolveTheme(theme) {
|
|
462
|
-
if (theme === "dark") return dark;
|
|
463
|
-
if (theme === "light") return light;
|
|
464
|
-
const override = theme;
|
|
465
|
-
return {
|
|
466
|
-
...light,
|
|
467
|
-
background: override.background ?? light.background,
|
|
468
|
-
accent: override.accent ?? light.accent,
|
|
469
|
-
accentHover: override.accent ?? light.accentHover,
|
|
470
|
-
text: override.text ?? light.text,
|
|
471
|
-
border: override.border ?? light.border
|
|
472
|
-
};
|
|
473
|
-
}
|
|
44
|
+
// src/api.ts
|
|
45
|
+
var import_flint_core = require("@diegotsi/flint-core");
|
|
474
46
|
|
|
475
47
|
// src/ScreenAnnotator.tsx
|
|
476
48
|
var import_modern_screenshot = require("modern-screenshot");
|
|
@@ -614,6 +186,9 @@ function ScreenAnnotator({ zIndex, onCapture, onCancel }) {
|
|
|
614
186
|
);
|
|
615
187
|
}
|
|
616
188
|
|
|
189
|
+
// src/theme.ts
|
|
190
|
+
var import_flint_core2 = require("@diegotsi/flint-core");
|
|
191
|
+
|
|
617
192
|
// src/FlintModal.tsx
|
|
618
193
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
619
194
|
var SEVERITIES = ["P1", "P2", "P3", "P4"];
|
|
@@ -670,6 +245,7 @@ function FlintModal({
|
|
|
670
245
|
getEnvironment,
|
|
671
246
|
getConsoleLogs,
|
|
672
247
|
getNetworkErrors,
|
|
248
|
+
getFormErrors,
|
|
673
249
|
getReplayEvents,
|
|
674
250
|
getExternalReplayUrl,
|
|
675
251
|
initialSelection = "",
|
|
@@ -680,7 +256,7 @@ function FlintModal({
|
|
|
680
256
|
onError
|
|
681
257
|
}) {
|
|
682
258
|
const { t } = (0, import_react_i18next.useTranslation)();
|
|
683
|
-
const colors = resolveTheme(theme);
|
|
259
|
+
const colors = (0, import_flint_core2.resolveTheme)(theme);
|
|
684
260
|
const isDark = theme === "dark";
|
|
685
261
|
const [severity, setSeverity] = (0, import_react2.useState)("P2");
|
|
686
262
|
const [description, setDescription] = (0, import_react2.useState)("");
|
|
@@ -728,7 +304,8 @@ function FlintModal({
|
|
|
728
304
|
...meta,
|
|
729
305
|
environment: getEnvironment(),
|
|
730
306
|
consoleLogs: getConsoleLogs(),
|
|
731
|
-
networkErrors: getNetworkErrors()
|
|
307
|
+
networkErrors: getNetworkErrors(),
|
|
308
|
+
formErrors: getFormErrors()
|
|
732
309
|
};
|
|
733
310
|
if (isText) {
|
|
734
311
|
collectedMeta.textIssue = {
|
|
@@ -757,13 +334,13 @@ function FlintModal({
|
|
|
757
334
|
}
|
|
758
335
|
}
|
|
759
336
|
try {
|
|
760
|
-
const res = await submitReport(serverUrl, projectKey, payload, !isText ? screenshot ?? void 0 : void 0);
|
|
337
|
+
const res = await (0, import_flint_core.submitReport)(serverUrl, projectKey, payload, !isText ? screenshot ?? void 0 : void 0);
|
|
761
338
|
setResult(res);
|
|
762
339
|
setStatus("success");
|
|
763
340
|
onSuccess?.(res);
|
|
764
341
|
const events = getReplayEvents();
|
|
765
342
|
if (events.length > 0) {
|
|
766
|
-
submitReplay(serverUrl, projectKey, res.id, events).catch(() => {
|
|
343
|
+
(0, import_flint_core.submitReplay)(serverUrl, projectKey, res.id, events).catch(() => {
|
|
767
344
|
});
|
|
768
345
|
}
|
|
769
346
|
} catch (err) {
|
|
@@ -1603,9 +1180,25 @@ function CheckIcon({ size = 20 }) {
|
|
|
1603
1180
|
}
|
|
1604
1181
|
|
|
1605
1182
|
// src/FlintWidget.tsx
|
|
1183
|
+
var import_flint_core10 = require("@diegotsi/flint-core");
|
|
1606
1184
|
var import_react4 = require("react");
|
|
1607
1185
|
var import_react_i18next3 = require("react-i18next");
|
|
1608
1186
|
|
|
1187
|
+
// src/collectors/console.ts
|
|
1188
|
+
var import_flint_core3 = require("@diegotsi/flint-core");
|
|
1189
|
+
|
|
1190
|
+
// src/collectors/environment.ts
|
|
1191
|
+
var import_flint_core4 = require("@diegotsi/flint-core");
|
|
1192
|
+
|
|
1193
|
+
// src/collectors/formErrors.ts
|
|
1194
|
+
var import_flint_core5 = require("@diegotsi/flint-core");
|
|
1195
|
+
|
|
1196
|
+
// src/collectors/frustration.ts
|
|
1197
|
+
var import_flint_core6 = require("@diegotsi/flint-core");
|
|
1198
|
+
|
|
1199
|
+
// src/collectors/network.ts
|
|
1200
|
+
var import_flint_core7 = require("@diegotsi/flint-core");
|
|
1201
|
+
|
|
1609
1202
|
// src/i18n/index.ts
|
|
1610
1203
|
var import_i18next = require("i18next");
|
|
1611
1204
|
var import_react_i18next2 = require("react-i18next");
|
|
@@ -1656,9 +1249,11 @@ widgetI18n.use(import_react_i18next2.initReactI18next).init({
|
|
|
1656
1249
|
var i18n_default = widgetI18n;
|
|
1657
1250
|
|
|
1658
1251
|
// src/store.ts
|
|
1252
|
+
var import_flint_core8 = require("@diegotsi/flint-core");
|
|
1659
1253
|
var import_react3 = require("react");
|
|
1254
|
+
var import_flint_core9 = require("@diegotsi/flint-core");
|
|
1660
1255
|
function useFlintStore() {
|
|
1661
|
-
return (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot);
|
|
1256
|
+
return (0, import_react3.useSyncExternalStore)(import_flint_core8.subscribe, import_flint_core8.getSnapshot);
|
|
1662
1257
|
}
|
|
1663
1258
|
|
|
1664
1259
|
// src/FlintWidget.tsx
|
|
@@ -1686,6 +1281,7 @@ function WidgetContent({
|
|
|
1686
1281
|
enableScreenshot = true,
|
|
1687
1282
|
enableConsole = true,
|
|
1688
1283
|
enableNetwork = true,
|
|
1284
|
+
enableFormErrors = true,
|
|
1689
1285
|
enableFrustration = false,
|
|
1690
1286
|
autoReportFrustration = false,
|
|
1691
1287
|
onBeforeSubmit,
|
|
@@ -1720,7 +1316,7 @@ function WidgetContent({
|
|
|
1720
1316
|
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
1721
1317
|
const [hovered, setHovered] = (0, import_react4.useState)(false);
|
|
1722
1318
|
const pendingSelection = (0, import_react4.useRef)("");
|
|
1723
|
-
const colors = resolveTheme(theme);
|
|
1319
|
+
const colors = (0, import_flint_core2.resolveTheme)(theme);
|
|
1724
1320
|
const [selectionTooltip, setSelectionTooltip] = (0, import_react4.useState)(null);
|
|
1725
1321
|
const tooltipRef = (0, import_react4.useRef)(null);
|
|
1726
1322
|
const triggerRef = (0, import_react4.useRef)(null);
|
|
@@ -1763,31 +1359,42 @@ function WidgetContent({
|
|
|
1763
1359
|
setOpen(true);
|
|
1764
1360
|
onOpen?.();
|
|
1765
1361
|
};
|
|
1362
|
+
const globalInit = import_flint_core10.Flint.isInitialized();
|
|
1363
|
+
const global = import_flint_core10.Flint.getInstance();
|
|
1766
1364
|
const consoleCollector = (0, import_react4.useRef)(null);
|
|
1767
1365
|
const networkCollector = (0, import_react4.useRef)(null);
|
|
1768
1366
|
const replayEvents = (0, import_react4.useRef)([]);
|
|
1769
1367
|
const stopReplay = (0, import_react4.useRef)(null);
|
|
1770
|
-
|
|
1771
|
-
consoleCollector.current = createConsoleCollector();
|
|
1772
|
-
consoleCollector.current.start();
|
|
1773
|
-
}
|
|
1774
|
-
if (enableNetwork && !networkCollector.current) {
|
|
1775
|
-
const flintHost = (() => {
|
|
1776
|
-
try {
|
|
1777
|
-
return new URL(serverUrl).hostname;
|
|
1778
|
-
} catch {
|
|
1779
|
-
return "";
|
|
1780
|
-
}
|
|
1781
|
-
})();
|
|
1782
|
-
networkCollector.current = createNetworkCollector(flintHost ? [flintHost] : []);
|
|
1783
|
-
networkCollector.current.start();
|
|
1784
|
-
}
|
|
1368
|
+
const formErrorCollector = (0, import_react4.useRef)(null);
|
|
1785
1369
|
const frustrationCollector = (0, import_react4.useRef)(null);
|
|
1786
|
-
if (
|
|
1787
|
-
|
|
1788
|
-
|
|
1370
|
+
if (!globalInit) {
|
|
1371
|
+
if (enableConsole && !consoleCollector.current) {
|
|
1372
|
+
consoleCollector.current = (0, import_flint_core3.createConsoleCollector)();
|
|
1373
|
+
consoleCollector.current.start();
|
|
1374
|
+
}
|
|
1375
|
+
if (enableNetwork && !networkCollector.current) {
|
|
1376
|
+
const flintHost = (() => {
|
|
1377
|
+
try {
|
|
1378
|
+
return new URL(serverUrl).hostname;
|
|
1379
|
+
} catch {
|
|
1380
|
+
return "";
|
|
1381
|
+
}
|
|
1382
|
+
})();
|
|
1383
|
+
networkCollector.current = (0, import_flint_core7.createNetworkCollector)(flintHost ? [flintHost] : []);
|
|
1384
|
+
networkCollector.current.start();
|
|
1385
|
+
}
|
|
1386
|
+
if (enableFormErrors && !formErrorCollector.current) {
|
|
1387
|
+
formErrorCollector.current = (0, import_flint_core5.createFormErrorCollector)();
|
|
1388
|
+
formErrorCollector.current.start();
|
|
1389
|
+
(0, import_flint_core9._setFormErrorCollector)(formErrorCollector.current);
|
|
1390
|
+
}
|
|
1391
|
+
if (enableFrustration && !frustrationCollector.current) {
|
|
1392
|
+
frustrationCollector.current = (0, import_flint_core6.createFrustrationCollector)();
|
|
1393
|
+
frustrationCollector.current.start();
|
|
1394
|
+
}
|
|
1789
1395
|
}
|
|
1790
1396
|
(0, import_react4.useEffect)(() => {
|
|
1397
|
+
if (globalInit) return;
|
|
1791
1398
|
let cancelled = false;
|
|
1792
1399
|
if (enableReplay) {
|
|
1793
1400
|
import("rrweb").then(({ record }) => {
|
|
@@ -1808,15 +1415,18 @@ function WidgetContent({
|
|
|
1808
1415
|
cancelled = true;
|
|
1809
1416
|
consoleCollector.current?.stop();
|
|
1810
1417
|
networkCollector.current?.stop();
|
|
1418
|
+
formErrorCollector.current?.stop();
|
|
1419
|
+
(0, import_flint_core9._setFormErrorCollector)(null);
|
|
1811
1420
|
frustrationCollector.current?.stop();
|
|
1812
1421
|
stopReplay.current?.();
|
|
1813
1422
|
};
|
|
1814
|
-
}, [enableReplay]);
|
|
1423
|
+
}, [enableReplay, globalInit]);
|
|
1815
1424
|
(0, import_react4.useEffect)(() => {
|
|
1425
|
+
if (globalInit) return;
|
|
1816
1426
|
if (!enableFrustration || !autoReportFrustration || !frustrationCollector.current) return;
|
|
1817
1427
|
const unsubscribe = frustrationCollector.current.onFrustration(async (event) => {
|
|
1818
1428
|
const user2 = resolvedUser;
|
|
1819
|
-
await submitReport(serverUrl, projectKey, {
|
|
1429
|
+
await (0, import_flint_core.submitReport)(serverUrl, projectKey, {
|
|
1820
1430
|
reporterId: user2?.id ?? "anonymous",
|
|
1821
1431
|
reporterName: user2?.name ?? "Anonymous",
|
|
1822
1432
|
reporterEmail: user2?.email,
|
|
@@ -1824,16 +1434,17 @@ function WidgetContent({
|
|
|
1824
1434
|
severity: event.type === "error_loop" ? "P1" : event.type === "rage_click" ? "P2" : "P3",
|
|
1825
1435
|
url: event.url,
|
|
1826
1436
|
meta: {
|
|
1827
|
-
environment: collectEnvironment(),
|
|
1437
|
+
environment: (0, import_flint_core4.collectEnvironment)(),
|
|
1828
1438
|
consoleLogs: consoleCollector.current?.getEntries() ?? [],
|
|
1829
1439
|
networkErrors: networkCollector.current?.getEntries() ?? [],
|
|
1440
|
+
formErrors: formErrorCollector.current?.getEntries() ?? [],
|
|
1830
1441
|
frustrationEvent: event
|
|
1831
1442
|
}
|
|
1832
1443
|
}).catch(() => {
|
|
1833
1444
|
});
|
|
1834
1445
|
});
|
|
1835
1446
|
return unsubscribe;
|
|
1836
|
-
}, [enableFrustration, autoReportFrustration]);
|
|
1447
|
+
}, [enableFrustration, autoReportFrustration, globalInit]);
|
|
1837
1448
|
const label = buttonLabel ?? t("buttonLabel");
|
|
1838
1449
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1839
1450
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
@@ -1930,10 +1541,11 @@ function WidgetContent({
|
|
|
1930
1541
|
onClose?.();
|
|
1931
1542
|
pendingSelection.current = "";
|
|
1932
1543
|
},
|
|
1933
|
-
getEnvironment: collectEnvironment,
|
|
1934
|
-
getConsoleLogs: () => consoleCollector.current?.getEntries() ?? [],
|
|
1935
|
-
getNetworkErrors: () => networkCollector.current?.getEntries() ?? [],
|
|
1936
|
-
|
|
1544
|
+
getEnvironment: import_flint_core4.collectEnvironment,
|
|
1545
|
+
getConsoleLogs: () => globalInit ? global?.console?.getEntries() ?? [] : consoleCollector.current?.getEntries() ?? [],
|
|
1546
|
+
getNetworkErrors: () => globalInit ? global?.network?.getEntries() ?? [] : networkCollector.current?.getEntries() ?? [],
|
|
1547
|
+
getFormErrors: () => globalInit ? global?.formErrors?.getEntries() ?? [] : formErrorCollector.current?.getEntries() ?? [],
|
|
1548
|
+
getReplayEvents: () => globalInit ? import_flint_core10.Flint.getReplayEvents() : [...replayEvents.current],
|
|
1937
1549
|
getExternalReplayUrl,
|
|
1938
1550
|
initialSelection: pendingSelection.current,
|
|
1939
1551
|
enableScreenshot,
|
|
@@ -1986,6 +1598,7 @@ function SparkIcon2() {
|
|
|
1986
1598
|
}
|
|
1987
1599
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1988
1600
|
0 && (module.exports = {
|
|
1601
|
+
Flint,
|
|
1989
1602
|
FlintModal,
|
|
1990
1603
|
FlintWidget,
|
|
1991
1604
|
flint
|