@diegotsi/flint-core 0.1.0 → 0.1.2

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 CHANGED
@@ -215,93 +215,6 @@ function collectEnvironment() {
215
215
  };
216
216
  }
217
217
 
218
- // src/collectors/network.ts
219
- var MAX_ENTRIES2 = 50;
220
- var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
221
- "browser-intake-datadoghq.com",
222
- "rum.browser-intake-datadoghq.com",
223
- "logs.browser-intake-datadoghq.com",
224
- "session-replay.browser-intake-datadoghq.com"
225
- ]);
226
- function isBlockedUrl(url, extra) {
227
- try {
228
- const host = new URL(url, location.href).hostname;
229
- const all = [...BLOCKED_HOSTS, ...extra];
230
- return all.some((b) => host === b || host.endsWith("." + b));
231
- } catch {
232
- return false;
233
- }
234
- }
235
- function truncateUrl(url) {
236
- try {
237
- const u = new URL(url, location.href);
238
- const base = `${u.origin}${u.pathname}`;
239
- return base.length > 200 ? base.slice(0, 200) + "\u2026" : base;
240
- } catch {
241
- return url.length > 200 ? url.slice(0, 200) + "\u2026" : url;
242
- }
243
- }
244
- function createNetworkCollector(extraBlockedHosts = []) {
245
- const entries = [];
246
- const blocked = new Set(extraBlockedHosts);
247
- let origFetch = null;
248
- let origXHROpen = null;
249
- let active = false;
250
- function push(entry) {
251
- entries.push(entry);
252
- if (entries.length > MAX_ENTRIES2) entries.shift();
253
- }
254
- return {
255
- start() {
256
- if (active) return;
257
- active = true;
258
- origFetch = window.fetch;
259
- window.fetch = async (input, init) => {
260
- const method = (init?.method ?? "GET").toUpperCase();
261
- const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
262
- const startTime = Date.now();
263
- const res = await origFetch.call(window, input, init);
264
- if (res.status >= 400 && !isBlockedUrl(url, blocked)) {
265
- push({
266
- method,
267
- url: truncateUrl(url),
268
- status: res.status,
269
- duration: Date.now() - startTime,
270
- timestamp: startTime
271
- });
272
- }
273
- return res;
274
- };
275
- origXHROpen = XMLHttpRequest.prototype.open;
276
- XMLHttpRequest.prototype.open = function(method, url, async, username, password) {
277
- const startTime = Date.now();
278
- const urlStr = typeof url === "string" ? url : url.href;
279
- this.addEventListener("load", () => {
280
- if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {
281
- push({
282
- method: method.toUpperCase(),
283
- url: truncateUrl(urlStr),
284
- status: this.status,
285
- duration: Date.now() - startTime,
286
- timestamp: startTime
287
- });
288
- }
289
- });
290
- return origXHROpen.apply(this, [method, url, async ?? true, username, password]);
291
- };
292
- },
293
- stop() {
294
- if (!active) return;
295
- active = false;
296
- if (origFetch) window.fetch = origFetch;
297
- if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;
298
- },
299
- getEntries() {
300
- return [...entries];
301
- }
302
- };
303
- }
304
-
305
218
  // src/collectors/frustration.ts
306
219
  function createFrustrationCollector(opts) {
307
220
  const threshold = opts?.rageClickThreshold ?? 3;
@@ -347,9 +260,7 @@ function createFrustrationCollector(opts) {
347
260
  while (clickHistory.length > 0 && now - clickHistory[0].time > 1e3) {
348
261
  clickHistory.shift();
349
262
  }
350
- const recentOnSame = clickHistory.filter(
351
- (c) => c.target === target && now - c.time < window2
352
- );
263
+ const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window2);
353
264
  if (recentOnSame.length >= threshold) {
354
265
  emit2({
355
266
  type: "rage_click",
@@ -416,6 +327,93 @@ function createFrustrationCollector(opts) {
416
327
  };
417
328
  }
418
329
 
330
+ // src/collectors/network.ts
331
+ var MAX_ENTRIES2 = 50;
332
+ var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
333
+ "browser-intake-datadoghq.com",
334
+ "rum.browser-intake-datadoghq.com",
335
+ "logs.browser-intake-datadoghq.com",
336
+ "session-replay.browser-intake-datadoghq.com"
337
+ ]);
338
+ function isBlockedUrl(url, extra) {
339
+ try {
340
+ const host = new URL(url, location.href).hostname;
341
+ const all = [...BLOCKED_HOSTS, ...extra];
342
+ return all.some((b) => host === b || host.endsWith("." + b));
343
+ } catch {
344
+ return false;
345
+ }
346
+ }
347
+ function truncateUrl(url) {
348
+ try {
349
+ const u = new URL(url, location.href);
350
+ const base = `${u.origin}${u.pathname}`;
351
+ return base.length > 200 ? base.slice(0, 200) + "\u2026" : base;
352
+ } catch {
353
+ return url.length > 200 ? url.slice(0, 200) + "\u2026" : url;
354
+ }
355
+ }
356
+ function createNetworkCollector(extraBlockedHosts = []) {
357
+ const entries = [];
358
+ const blocked = new Set(extraBlockedHosts);
359
+ let origFetch = null;
360
+ let origXHROpen = null;
361
+ let active = false;
362
+ function push(entry) {
363
+ entries.push(entry);
364
+ if (entries.length > MAX_ENTRIES2) entries.shift();
365
+ }
366
+ return {
367
+ start() {
368
+ if (active) return;
369
+ active = true;
370
+ origFetch = window.fetch;
371
+ window.fetch = async (input, init) => {
372
+ const method = (init?.method ?? "GET").toUpperCase();
373
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
374
+ const startTime = Date.now();
375
+ const res = await origFetch.call(window, input, init);
376
+ if (res.status >= 400 && !isBlockedUrl(url, blocked)) {
377
+ push({
378
+ method,
379
+ url: truncateUrl(url),
380
+ status: res.status,
381
+ duration: Date.now() - startTime,
382
+ timestamp: startTime
383
+ });
384
+ }
385
+ return res;
386
+ };
387
+ origXHROpen = XMLHttpRequest.prototype.open;
388
+ XMLHttpRequest.prototype.open = function(method, url, async, username, password) {
389
+ const startTime = Date.now();
390
+ const urlStr = typeof url === "string" ? url : url.href;
391
+ this.addEventListener("load", () => {
392
+ if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {
393
+ push({
394
+ method: method.toUpperCase(),
395
+ url: truncateUrl(urlStr),
396
+ status: this.status,
397
+ duration: Date.now() - startTime,
398
+ timestamp: startTime
399
+ });
400
+ }
401
+ });
402
+ return origXHROpen.apply(this, [method, url, async ?? true, username, password]);
403
+ };
404
+ },
405
+ stop() {
406
+ if (!active) return;
407
+ active = false;
408
+ if (origFetch) window.fetch = origFetch;
409
+ if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;
410
+ },
411
+ getEntries() {
412
+ return [...entries];
413
+ }
414
+ };
415
+ }
416
+
419
417
  // src/store.ts
420
418
  var state = { user: void 0, sessionReplay: void 0 };
421
419
  var listeners = /* @__PURE__ */ new Set();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/network.ts","../src/collectors/frustration.ts","../src/store.ts","../src/theme.ts"],"sourcesContent":["// Collectors\n\n// Types (re-export)\nexport type {\n ConsoleEntry,\n EnvironmentInfo,\n FlintUser,\n FlintWidgetProps,\n NetworkEntry,\n ReportPayload,\n ReportResult,\n Severity,\n Theme,\n ThemeOverride,\n} from \"@flint/types\";\n// API\nexport { submitReplay, submitReport } from \"./api.js\";\n// Collector types\nexport type { ConsoleCollector } from \"./collectors/console.js\";\nexport { createConsoleCollector } from \"./collectors/console.js\";\nexport { collectEnvironment } from \"./collectors/environment.js\";\nexport type { NetworkCollector } from \"./collectors/network.js\";\nexport { createNetworkCollector } from \"./collectors/network.js\";\nexport type { FrustrationCollector, FrustrationEvent } from \"./collectors/frustration.js\";\nexport { createFrustrationCollector } from \"./collectors/frustration.js\";\nexport type { FlintState } from \"./store.js\";\n// State\nexport { flint, getSnapshot, subscribe } from \"./store.js\";\nexport type { ResolvedTheme } from \"./theme.js\";\n// Theme\nexport { resolveTheme } from \"./theme.js\";\n","import type { ReportPayload, ReportResult } from \"@flint/types\";\nimport { gzipSync } from \"fflate\";\n\nasync function fetchWithRetry(url: string, init: RequestInit, retries = 3, baseDelay = 1000): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || attempt === retries) return res;\n // Don't retry client errors (4xx) except 429\n if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;\n } catch (err) {\n if (attempt === retries) throw err;\n }\n await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));\n }\n throw new Error(\"Max retries exceeded\");\n}\n\nexport async function submitReport(\n serverUrl: string,\n projectKey: string,\n payload: ReportPayload,\n screenshot?: File,\n): Promise<ReportResult> {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports`;\n\n let body: BodyInit;\n const headers: Record<string, string> = {\n \"x-project-key\": projectKey,\n };\n\n if (screenshot) {\n const form = new FormData();\n form.append(\"reporterId\", payload.reporterId);\n form.append(\"reporterName\", payload.reporterName);\n if (payload.reporterEmail) form.append(\"reporterEmail\", payload.reporterEmail);\n form.append(\"description\", payload.description);\n if (payload.expectedBehavior) form.append(\"expectedBehavior\", payload.expectedBehavior);\n if (payload.stepsToReproduce) form.append(\"stepsToReproduce\", JSON.stringify(payload.stepsToReproduce));\n if (payload.externalReplayUrl) form.append(\"externalReplayUrl\", payload.externalReplayUrl);\n if (payload.additionalContext) form.append(\"additionalContext\", payload.additionalContext);\n form.append(\"severity\", payload.severity);\n if (payload.url) form.append(\"url\", payload.url);\n if (payload.meta) form.append(\"meta\", JSON.stringify(payload.meta));\n form.append(\"screenshot\", screenshot);\n body = form;\n } else {\n body = JSON.stringify(payload);\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchWithRetry(url, { method: \"POST\", headers, body });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<ReportResult>;\n}\n\nexport async function submitReplay(\n serverUrl: string,\n projectKey: string,\n reportId: string,\n events: unknown[],\n): Promise<void> {\n const json = JSON.stringify(events);\n const encoded = new TextEncoder().encode(json);\n const compressed = gzipSync(encoded);\n\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports/${reportId}/replay`;\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"x-project-key\": projectKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: compressed.buffer as ArrayBuffer,\n });\n}\n","import type { ConsoleEntry } from \"@flint/types\";\n\nconst MAX_ENTRIES = 50;\n\nexport interface ConsoleCollector {\n start(): void;\n stop(): void;\n getEntries(): ConsoleEntry[];\n}\n\nexport function createConsoleCollector(): ConsoleCollector {\n const entries: ConsoleEntry[] = [];\n let active = false;\n\n const originals = {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n };\n\n let origOnerror: typeof window.onerror = null;\n let origUnhandled: typeof window.onunhandledrejection = null;\n\n function push(level: ConsoleEntry[\"level\"], args: unknown[]) {\n let str: string;\n try {\n str = args.map((a) => (typeof a === \"string\" ? a : JSON.stringify(a))).join(\" \");\n } catch {\n str = String(args[0]);\n }\n if (str.length > 500) str = str.slice(0, 500) + \"\\u2026\";\n entries.push({ level, args: str, timestamp: Date.now() });\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n console.log = (...args: unknown[]) => {\n push(\"log\", args);\n originals.log(...args);\n };\n console.warn = (...args: unknown[]) => {\n push(\"warn\", args);\n originals.warn(...args);\n };\n console.error = (...args: unknown[]) => {\n push(\"error\", args);\n originals.error(...args);\n };\n\n origOnerror = window.onerror;\n window.onerror = (msg, src, line, col, err) => {\n push(\"error\", [err?.message ?? String(msg), `${src}:${line}:${col}`]);\n if (typeof origOnerror === \"function\") return origOnerror(msg, src, line, col, err);\n return false;\n };\n\n origUnhandled = window.onunhandledrejection;\n window.onunhandledrejection = (event) => {\n const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n push(\"error\", [\"UnhandledRejection:\", reason]);\n if (typeof origUnhandled === \"function\") origUnhandled.call(window, event);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n console.log = originals.log;\n console.warn = originals.warn;\n console.error = originals.error;\n window.onerror = origOnerror;\n window.onunhandledrejection = origUnhandled;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { EnvironmentInfo } from \"@flint/types\";\n\nexport function collectEnvironment(): EnvironmentInfo {\n const ua = navigator.userAgent;\n\n // -- Browser --\n let browser = \"Unknown\";\n const chromeM = ua.match(/Chrome\\/(\\d+)/);\n const firefoxM = ua.match(/Firefox\\/(\\d+)/);\n const edgeM = ua.match(/Edg\\/(\\d+)/);\n const safariM = ua.match(/Version\\/(\\d+)/);\n const operaM = ua.match(/OPR\\/(\\d+)/);\n\n if (operaM) {\n browser = `Opera ${operaM[1]}`;\n } else if (edgeM) {\n browser = `Edge ${edgeM[1]}`;\n } else if (chromeM && !/Edg|OPR/.test(ua)) {\n browser = `Chrome ${chromeM[1]}`;\n } else if (firefoxM) {\n browser = `Firefox ${firefoxM[1]}`;\n } else if (safariM && /Safari\\//.test(ua)) {\n browser = `Safari ${safariM[1]}`;\n }\n\n // -- OS --\n let os = \"Unknown\";\n const macM = ua.match(/Mac OS X (\\d+[._]\\d+)/);\n const winM = ua.match(/Windows NT (\\d+\\.\\d+)/);\n const androidM = ua.match(/Android (\\d+)/);\n const iosM = ua.match(/iPhone OS (\\d+[._]\\d+)/);\n\n if (macM) {\n os = `macOS ${macM[1].replace(\"_\", \".\")}`;\n } else if (winM) {\n const winMap: Record<string, string> = {\n \"10.0\": \"10/11\",\n \"6.3\": \"8.1\",\n \"6.2\": \"8\",\n \"6.1\": \"7\",\n };\n os = `Windows ${winMap[winM[1]] ?? winM[1]}`;\n } else if (androidM) {\n os = `Android ${androidM[1]}`;\n } else if (iosM) {\n os = `iOS ${iosM[1].replace(/_/g, \".\")}`;\n } else if (/Linux/.test(ua)) {\n os = \"Linux\";\n }\n\n return {\n browser,\n os,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n screen: `${screen.width}x${screen.height}`,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n online: navigator.onLine,\n };\n}\n","import type { NetworkEntry } from \"@flint/types\";\n\nconst MAX_ENTRIES = 50;\n\nconst BLOCKED_HOSTS = new Set([\n \"browser-intake-datadoghq.com\",\n \"rum.browser-intake-datadoghq.com\",\n \"logs.browser-intake-datadoghq.com\",\n \"session-replay.browser-intake-datadoghq.com\",\n]);\n\nfunction isBlockedUrl(url: string, extra: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n const all = [...BLOCKED_HOSTS, ...extra];\n return all.some((b) => host === b || host.endsWith(\".\" + b));\n } catch {\n return false;\n }\n}\n\nexport interface NetworkCollector {\n start(): void;\n stop(): void;\n getEntries(): NetworkEntry[];\n}\n\nfunction truncateUrl(url: string): string {\n try {\n const u = new URL(url, location.href);\n const base = `${u.origin}${u.pathname}`;\n return base.length > 200 ? base.slice(0, 200) + \"\\u2026\" : base;\n } catch {\n return url.length > 200 ? url.slice(0, 200) + \"\\u2026\" : url;\n }\n}\n\nexport function createNetworkCollector(extraBlockedHosts: string[] = []): NetworkCollector {\n const entries: NetworkEntry[] = [];\n const blocked = new Set(extraBlockedHosts);\n let origFetch: typeof window.fetch | null = null;\n let origXHROpen: typeof XMLHttpRequest.prototype.open | null = null;\n let active = false;\n\n function push(entry: NetworkEntry) {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // -- Patch fetch --\n origFetch = window.fetch;\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const method = ((init?.method ?? \"GET\") as string).toUpperCase();\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n const startTime = Date.now();\n const res = await origFetch!.call(window, input, init);\n if (res.status >= 400 && !isBlockedUrl(url, blocked)) {\n push({\n method,\n url: truncateUrl(url),\n status: res.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n return res;\n };\n\n // -- Patch XHR --\n origXHROpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null,\n ) {\n const startTime = Date.now();\n const urlStr = typeof url === \"string\" ? url : (url as URL).href;\n\n this.addEventListener(\"load\", () => {\n if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {\n push({\n method: method.toUpperCase(),\n url: truncateUrl(urlStr),\n status: this.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n });\n\n return origXHROpen!.apply(this, [method, url, async ?? true, username, password] as Parameters<\n typeof XMLHttpRequest.prototype.open\n >);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (origFetch) window.fetch = origFetch;\n if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","export interface FrustrationEvent {\n type: \"rage_click\" | \"dead_click\" | \"error_loop\";\n timestamp: number;\n target: string;\n details: string;\n url: string;\n}\n\nexport interface FrustrationCollector {\n start(): void;\n stop(): void;\n getEvents(): FrustrationEvent[];\n onFrustration(callback: (event: FrustrationEvent) => void): () => void;\n}\n\nexport function createFrustrationCollector(opts?: {\n rageClickThreshold?: number;\n rageClickWindow?: number;\n errorLoopThreshold?: number;\n errorLoopWindow?: number;\n enableDeadClicks?: boolean;\n}): FrustrationCollector {\n const threshold = opts?.rageClickThreshold ?? 3;\n const window = opts?.rageClickWindow ?? 500;\n const errorThreshold = opts?.errorLoopThreshold ?? 3;\n const errorWindow = opts?.errorLoopWindow ?? 30_000;\n const deadClicksEnabled = opts?.enableDeadClicks ?? true;\n\n const events: FrustrationEvent[] = [];\n const listeners = new Set<(event: FrustrationEvent) => void>();\n const clickHistory: { target: EventTarget | null; time: number }[] = [];\n const errorCounts = new Map<string, { count: number; firstSeen: number }>();\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n let origConsoleError: typeof console.error | null = null;\n\n function emit(event: FrustrationEvent) {\n events.push(event);\n if (events.length > 50) events.shift();\n for (const cb of listeners) cb(event);\n }\n\n function getCSSSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = [...el.classList].slice(0, 3).join(\".\");\n if (cls) return `${tag}.${cls}`;\n return tag;\n }\n\n function isInteractive(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if ([\"a\", \"button\", \"input\", \"select\", \"textarea\", \"label\", \"summary\"].includes(tag)) return true;\n if (el.getAttribute(\"role\") === \"button\" || el.getAttribute(\"tabindex\")) return true;\n if (el.onclick || el.getAttribute(\"onclick\")) return true;\n if (el.closest(\"a, button, [role=button], [onclick]\")) return true;\n try { if (getComputedStyle(el).cursor === \"pointer\") return true; } catch {}\n return false;\n }\n\n function handleClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (!target) return;\n const now = Date.now();\n\n // --- Rage click detection ---\n clickHistory.push({ target, time: now });\n // Remove old clicks\n while (clickHistory.length > 0 && now - clickHistory[0].time > 1000) {\n clickHistory.shift();\n }\n const recentOnSame = clickHistory.filter(\n (c) => c.target === target && now - c.time < window\n );\n if (recentOnSame.length >= threshold) {\n emit({\n type: \"rage_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,\n url: globalThis.location?.href ?? \"\",\n });\n clickHistory.length = 0;\n }\n\n // --- Dead click detection ---\n if (deadClicksEnabled && !isInteractive(target)) {\n emit({\n type: \"dead_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,\n url: globalThis.location?.href ?? \"\",\n });\n }\n }\n\n function patchConsoleError() {\n origConsoleError = console.error;\n console.error = (...args: unknown[]) => {\n origConsoleError?.apply(console, args);\n // Check for error loop\n const key = String(args[0]).slice(0, 100);\n const now = Date.now();\n const existing = errorCounts.get(key);\n if (existing && now - existing.firstSeen < errorWindow) {\n existing.count++;\n if (existing.count >= errorThreshold) {\n emit({\n type: \"error_loop\",\n timestamp: now,\n target: \"console\",\n details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1000)}s: ${key}`,\n url: globalThis.location?.href ?? \"\",\n });\n errorCounts.delete(key);\n }\n } else {\n errorCounts.set(key, { count: 1, firstSeen: now });\n }\n };\n }\n\n return {\n start() {\n clickHandler = handleClick;\n document.addEventListener(\"click\", clickHandler, true);\n patchConsoleError();\n },\n stop() {\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n if (origConsoleError) console.error = origConsoleError;\n clickHistory.length = 0;\n errorCounts.clear();\n },\n getEvents() { return [...events]; },\n onFrustration(callback) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n };\n}\n","import type { FlintUser } from \"@flint/types\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\nlet state: FlintState = { user: undefined, sessionReplay: undefined };\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const l of listeners) l();\n}\n\nexport function subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getSnapshot(): FlintState {\n return state;\n}\n\nexport const flint = {\n setUser(user: FlintUser | null) {\n state = { ...state, user: user ?? undefined };\n emit();\n },\n setSessionReplay(url: string | (() => string) | null) {\n state = { ...state, sessionReplay: url ?? undefined };\n emit();\n },\n};\n","import type { Theme, ThemeOverride } from \"@flint/types\";\n\nexport interface ResolvedTheme {\n background: string;\n backgroundSecondary: string;\n accent: string;\n accentHover: string;\n text: string;\n textMuted: string;\n border: string;\n shadow: string;\n buttonText: string;\n backdropFilter: string;\n}\n\nconst light: ResolvedTheme = {\n background: \"rgba(255,255,255,0.90)\",\n backgroundSecondary: \"rgba(249,250,251,0.75)\",\n accent: \"#2563eb\",\n accentHover: \"#1d4ed8\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n border: \"rgba(255,255,255,0.9)\",\n 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)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.8)\",\n};\n\nconst dark: ResolvedTheme = {\n background: \"rgba(15,20,35,0.88)\",\n backgroundSecondary: \"rgba(5,8,18,0.65)\",\n accent: \"#4d8aff\",\n accentHover: \"#3b6fdb\",\n text: \"#dde3ef\",\n textMuted: \"#6b7a93\",\n border: \"rgba(255,255,255,0.08)\",\n shadow: \"0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.6)\",\n};\n\nexport function resolveTheme(theme: Theme): ResolvedTheme {\n if (theme === \"dark\") return dark;\n if (theme === \"light\") return light;\n // ThemeOverride — merge over light base\n const override = theme as ThemeOverride;\n return {\n ...light,\n background: override.background ?? light.background,\n accent: override.accent ?? light.accent,\n accentHover: override.accent ?? light.accentHover,\n text: override.text ?? light.text,\n border: override.border ?? light.border,\n };\n}\n"],"mappings":"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","names":["MAX_ENTRIES","window","listeners","emit"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/frustration.ts","../src/collectors/network.ts","../src/store.ts","../src/theme.ts"],"sourcesContent":["// Collectors\n\n// Types (re-export)\nexport type {\n ConsoleEntry,\n EnvironmentInfo,\n FlintUser,\n FlintWidgetProps,\n NetworkEntry,\n ReportPayload,\n ReportResult,\n Severity,\n Theme,\n ThemeOverride,\n} from \"./types.js\";\n// API\nexport { submitReplay, submitReport } from \"./api.js\";\n// Collector types\nexport type { ConsoleCollector } from \"./collectors/console.js\";\nexport { createConsoleCollector } from \"./collectors/console.js\";\nexport { collectEnvironment } from \"./collectors/environment.js\";\nexport type { FrustrationCollector, FrustrationEvent } from \"./collectors/frustration.js\";\nexport { createFrustrationCollector } from \"./collectors/frustration.js\";\nexport type { NetworkCollector } from \"./collectors/network.js\";\nexport { createNetworkCollector } from \"./collectors/network.js\";\nexport type { FlintState } from \"./store.js\";\n// State\nexport { flint, getSnapshot, subscribe } from \"./store.js\";\nexport type { ResolvedTheme } from \"./theme.js\";\n// Theme\nexport { resolveTheme } from \"./theme.js\";\n","import type { ReportPayload, ReportResult } from \"./types.js\";\nimport { gzipSync } from \"fflate\";\n\nasync function fetchWithRetry(url: string, init: RequestInit, retries = 3, baseDelay = 1000): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || attempt === retries) return res;\n // Don't retry client errors (4xx) except 429\n if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;\n } catch (err) {\n if (attempt === retries) throw err;\n }\n await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));\n }\n throw new Error(\"Max retries exceeded\");\n}\n\nexport async function submitReport(\n serverUrl: string,\n projectKey: string,\n payload: ReportPayload,\n screenshot?: File,\n): Promise<ReportResult> {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports`;\n\n let body: BodyInit;\n const headers: Record<string, string> = {\n \"x-project-key\": projectKey,\n };\n\n if (screenshot) {\n const form = new FormData();\n form.append(\"reporterId\", payload.reporterId);\n form.append(\"reporterName\", payload.reporterName);\n if (payload.reporterEmail) form.append(\"reporterEmail\", payload.reporterEmail);\n form.append(\"description\", payload.description);\n if (payload.expectedBehavior) form.append(\"expectedBehavior\", payload.expectedBehavior);\n if (payload.stepsToReproduce) form.append(\"stepsToReproduce\", JSON.stringify(payload.stepsToReproduce));\n if (payload.externalReplayUrl) form.append(\"externalReplayUrl\", payload.externalReplayUrl);\n if (payload.additionalContext) form.append(\"additionalContext\", payload.additionalContext);\n form.append(\"severity\", payload.severity);\n if (payload.url) form.append(\"url\", payload.url);\n if (payload.meta) form.append(\"meta\", JSON.stringify(payload.meta));\n form.append(\"screenshot\", screenshot);\n body = form;\n } else {\n body = JSON.stringify(payload);\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchWithRetry(url, { method: \"POST\", headers, body });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<ReportResult>;\n}\n\nexport async function submitReplay(\n serverUrl: string,\n projectKey: string,\n reportId: string,\n events: unknown[],\n): Promise<void> {\n const json = JSON.stringify(events);\n const encoded = new TextEncoder().encode(json);\n const compressed = gzipSync(encoded);\n\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports/${reportId}/replay`;\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"x-project-key\": projectKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: compressed.buffer as ArrayBuffer,\n });\n}\n","import type { ConsoleEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\nexport interface ConsoleCollector {\n start(): void;\n stop(): void;\n getEntries(): ConsoleEntry[];\n}\n\nexport function createConsoleCollector(): ConsoleCollector {\n const entries: ConsoleEntry[] = [];\n let active = false;\n\n const originals = {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n };\n\n let origOnerror: typeof window.onerror = null;\n let origUnhandled: typeof window.onunhandledrejection = null;\n\n function push(level: ConsoleEntry[\"level\"], args: unknown[]) {\n let str: string;\n try {\n str = args.map((a) => (typeof a === \"string\" ? a : JSON.stringify(a))).join(\" \");\n } catch {\n str = String(args[0]);\n }\n if (str.length > 500) str = str.slice(0, 500) + \"\\u2026\";\n entries.push({ level, args: str, timestamp: Date.now() });\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n console.log = (...args: unknown[]) => {\n push(\"log\", args);\n originals.log(...args);\n };\n console.warn = (...args: unknown[]) => {\n push(\"warn\", args);\n originals.warn(...args);\n };\n console.error = (...args: unknown[]) => {\n push(\"error\", args);\n originals.error(...args);\n };\n\n origOnerror = window.onerror;\n window.onerror = (msg, src, line, col, err) => {\n push(\"error\", [err?.message ?? String(msg), `${src}:${line}:${col}`]);\n if (typeof origOnerror === \"function\") return origOnerror(msg, src, line, col, err);\n return false;\n };\n\n origUnhandled = window.onunhandledrejection;\n window.onunhandledrejection = (event) => {\n const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n push(\"error\", [\"UnhandledRejection:\", reason]);\n if (typeof origUnhandled === \"function\") origUnhandled.call(window, event);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n console.log = originals.log;\n console.warn = originals.warn;\n console.error = originals.error;\n window.onerror = origOnerror;\n window.onunhandledrejection = origUnhandled;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { EnvironmentInfo } from \"../types.js\";\n\nexport function collectEnvironment(): EnvironmentInfo {\n const ua = navigator.userAgent;\n\n // -- Browser --\n let browser = \"Unknown\";\n const chromeM = ua.match(/Chrome\\/(\\d+)/);\n const firefoxM = ua.match(/Firefox\\/(\\d+)/);\n const edgeM = ua.match(/Edg\\/(\\d+)/);\n const safariM = ua.match(/Version\\/(\\d+)/);\n const operaM = ua.match(/OPR\\/(\\d+)/);\n\n if (operaM) {\n browser = `Opera ${operaM[1]}`;\n } else if (edgeM) {\n browser = `Edge ${edgeM[1]}`;\n } else if (chromeM && !/Edg|OPR/.test(ua)) {\n browser = `Chrome ${chromeM[1]}`;\n } else if (firefoxM) {\n browser = `Firefox ${firefoxM[1]}`;\n } else if (safariM && /Safari\\//.test(ua)) {\n browser = `Safari ${safariM[1]}`;\n }\n\n // -- OS --\n let os = \"Unknown\";\n const macM = ua.match(/Mac OS X (\\d+[._]\\d+)/);\n const winM = ua.match(/Windows NT (\\d+\\.\\d+)/);\n const androidM = ua.match(/Android (\\d+)/);\n const iosM = ua.match(/iPhone OS (\\d+[._]\\d+)/);\n\n if (macM) {\n os = `macOS ${macM[1].replace(\"_\", \".\")}`;\n } else if (winM) {\n const winMap: Record<string, string> = {\n \"10.0\": \"10/11\",\n \"6.3\": \"8.1\",\n \"6.2\": \"8\",\n \"6.1\": \"7\",\n };\n os = `Windows ${winMap[winM[1]] ?? winM[1]}`;\n } else if (androidM) {\n os = `Android ${androidM[1]}`;\n } else if (iosM) {\n os = `iOS ${iosM[1].replace(/_/g, \".\")}`;\n } else if (/Linux/.test(ua)) {\n os = \"Linux\";\n }\n\n return {\n browser,\n os,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n screen: `${screen.width}x${screen.height}`,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n online: navigator.onLine,\n };\n}\n","export interface FrustrationEvent {\n type: \"rage_click\" | \"dead_click\" | \"error_loop\";\n timestamp: number;\n target: string;\n details: string;\n url: string;\n}\n\nexport interface FrustrationCollector {\n start(): void;\n stop(): void;\n getEvents(): FrustrationEvent[];\n onFrustration(callback: (event: FrustrationEvent) => void): () => void;\n}\n\nexport function createFrustrationCollector(opts?: {\n rageClickThreshold?: number;\n rageClickWindow?: number;\n errorLoopThreshold?: number;\n errorLoopWindow?: number;\n enableDeadClicks?: boolean;\n}): FrustrationCollector {\n const threshold = opts?.rageClickThreshold ?? 3;\n const window = opts?.rageClickWindow ?? 500;\n const errorThreshold = opts?.errorLoopThreshold ?? 3;\n const errorWindow = opts?.errorLoopWindow ?? 30_000;\n const deadClicksEnabled = opts?.enableDeadClicks ?? true;\n\n const events: FrustrationEvent[] = [];\n const listeners = new Set<(event: FrustrationEvent) => void>();\n const clickHistory: { target: EventTarget | null; time: number }[] = [];\n const errorCounts = new Map<string, { count: number; firstSeen: number }>();\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n let origConsoleError: typeof console.error | null = null;\n\n function emit(event: FrustrationEvent) {\n events.push(event);\n if (events.length > 50) events.shift();\n for (const cb of listeners) cb(event);\n }\n\n function getCSSSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = [...el.classList].slice(0, 3).join(\".\");\n if (cls) return `${tag}.${cls}`;\n return tag;\n }\n\n function isInteractive(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if ([\"a\", \"button\", \"input\", \"select\", \"textarea\", \"label\", \"summary\"].includes(tag)) return true;\n if (el.getAttribute(\"role\") === \"button\" || el.getAttribute(\"tabindex\")) return true;\n if (el.onclick || el.getAttribute(\"onclick\")) return true;\n if (el.closest(\"a, button, [role=button], [onclick]\")) return true;\n try {\n if (getComputedStyle(el).cursor === \"pointer\") return true;\n } catch {\n /* getComputedStyle may throw */\n }\n return false;\n }\n\n function handleClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (!target) return;\n const now = Date.now();\n\n // --- Rage click detection ---\n clickHistory.push({ target, time: now });\n // Remove old clicks\n while (clickHistory.length > 0 && now - clickHistory[0].time > 1000) {\n clickHistory.shift();\n }\n const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window);\n if (recentOnSame.length >= threshold) {\n emit({\n type: \"rage_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,\n url: globalThis.location?.href ?? \"\",\n });\n clickHistory.length = 0;\n }\n\n // --- Dead click detection ---\n if (deadClicksEnabled && !isInteractive(target)) {\n emit({\n type: \"dead_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,\n url: globalThis.location?.href ?? \"\",\n });\n }\n }\n\n function patchConsoleError() {\n origConsoleError = console.error;\n console.error = (...args: unknown[]) => {\n origConsoleError?.apply(console, args);\n // Check for error loop\n const key = String(args[0]).slice(0, 100);\n const now = Date.now();\n const existing = errorCounts.get(key);\n if (existing && now - existing.firstSeen < errorWindow) {\n existing.count++;\n if (existing.count >= errorThreshold) {\n emit({\n type: \"error_loop\",\n timestamp: now,\n target: \"console\",\n details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1000)}s: ${key}`,\n url: globalThis.location?.href ?? \"\",\n });\n errorCounts.delete(key);\n }\n } else {\n errorCounts.set(key, { count: 1, firstSeen: now });\n }\n };\n }\n\n return {\n start() {\n clickHandler = handleClick;\n document.addEventListener(\"click\", clickHandler, true);\n patchConsoleError();\n },\n stop() {\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n if (origConsoleError) console.error = origConsoleError;\n clickHistory.length = 0;\n errorCounts.clear();\n },\n getEvents() {\n return [...events];\n },\n onFrustration(callback) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n };\n}\n","import type { NetworkEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\nconst BLOCKED_HOSTS = new Set([\n \"browser-intake-datadoghq.com\",\n \"rum.browser-intake-datadoghq.com\",\n \"logs.browser-intake-datadoghq.com\",\n \"session-replay.browser-intake-datadoghq.com\",\n]);\n\nfunction isBlockedUrl(url: string, extra: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n const all = [...BLOCKED_HOSTS, ...extra];\n return all.some((b) => host === b || host.endsWith(\".\" + b));\n } catch {\n return false;\n }\n}\n\nexport interface NetworkCollector {\n start(): void;\n stop(): void;\n getEntries(): NetworkEntry[];\n}\n\nfunction truncateUrl(url: string): string {\n try {\n const u = new URL(url, location.href);\n const base = `${u.origin}${u.pathname}`;\n return base.length > 200 ? base.slice(0, 200) + \"\\u2026\" : base;\n } catch {\n return url.length > 200 ? url.slice(0, 200) + \"\\u2026\" : url;\n }\n}\n\nexport function createNetworkCollector(extraBlockedHosts: string[] = []): NetworkCollector {\n const entries: NetworkEntry[] = [];\n const blocked = new Set(extraBlockedHosts);\n let origFetch: typeof window.fetch | null = null;\n let origXHROpen: typeof XMLHttpRequest.prototype.open | null = null;\n let active = false;\n\n function push(entry: NetworkEntry) {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // -- Patch fetch --\n origFetch = window.fetch;\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const method = ((init?.method ?? \"GET\") as string).toUpperCase();\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n const startTime = Date.now();\n const res = await origFetch!.call(window, input, init);\n if (res.status >= 400 && !isBlockedUrl(url, blocked)) {\n push({\n method,\n url: truncateUrl(url),\n status: res.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n return res;\n };\n\n // -- Patch XHR --\n origXHROpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null,\n ) {\n const startTime = Date.now();\n const urlStr = typeof url === \"string\" ? url : (url as URL).href;\n\n this.addEventListener(\"load\", () => {\n if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {\n push({\n method: method.toUpperCase(),\n url: truncateUrl(urlStr),\n status: this.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n });\n\n return origXHROpen!.apply(this, [method, url, async ?? true, username, password] as Parameters<\n typeof XMLHttpRequest.prototype.open\n >);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (origFetch) window.fetch = origFetch;\n if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { FlintUser } from \"./types.js\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\nlet state: FlintState = { user: undefined, sessionReplay: undefined };\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const l of listeners) l();\n}\n\nexport function subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getSnapshot(): FlintState {\n return state;\n}\n\nexport const flint = {\n setUser(user: FlintUser | null) {\n state = { ...state, user: user ?? undefined };\n emit();\n },\n setSessionReplay(url: string | (() => string) | null) {\n state = { ...state, sessionReplay: url ?? undefined };\n emit();\n },\n};\n","import type { Theme, ThemeOverride } from \"./types.js\";\n\nexport interface ResolvedTheme {\n background: string;\n backgroundSecondary: string;\n accent: string;\n accentHover: string;\n text: string;\n textMuted: string;\n border: string;\n shadow: string;\n buttonText: string;\n backdropFilter: string;\n}\n\nconst light: ResolvedTheme = {\n background: \"rgba(255,255,255,0.90)\",\n backgroundSecondary: \"rgba(249,250,251,0.75)\",\n accent: \"#2563eb\",\n accentHover: \"#1d4ed8\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n border: \"rgba(255,255,255,0.9)\",\n 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)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.8)\",\n};\n\nconst dark: ResolvedTheme = {\n background: \"rgba(15,20,35,0.88)\",\n backgroundSecondary: \"rgba(5,8,18,0.65)\",\n accent: \"#4d8aff\",\n accentHover: \"#3b6fdb\",\n text: \"#dde3ef\",\n textMuted: \"#6b7a93\",\n border: \"rgba(255,255,255,0.08)\",\n shadow: \"0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.6)\",\n};\n\nexport function resolveTheme(theme: Theme): ResolvedTheme {\n if (theme === \"dark\") return dark;\n if (theme === \"light\") return light;\n // ThemeOverride — merge over light base\n const override = theme as ThemeOverride;\n return {\n ...light,\n background: override.background ?? light.background,\n accent: override.accent ?? light.accent,\n accentHover: override.accent ?? light.accentHover,\n text: override.text ?? light.text,\n border: override.border ?? light.border,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,oBAAyB;AAEzB,eAAe,eAAe,KAAa,MAAmB,UAAU,GAAG,YAAY,KAAyB;AAC9G,WAAS,UAAU,GAAG,WAAW,SAAS,WAAW;AACnD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,KAAK,IAAI;AACjC,UAAI,IAAI,MAAM,YAAY,QAAS,QAAO;AAE1C,UAAI,IAAI,UAAU,OAAO,IAAI,SAAS,OAAO,IAAI,WAAW,IAAK,QAAO;AAAA,IAC1E,SAAS,KAAK;AACZ,UAAI,YAAY,QAAS,OAAM;AAAA,IACjC;AACA,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,YAAY,KAAK,OAAO,CAAC;AAAA,EAClE;AACA,QAAM,IAAI,MAAM,sBAAsB;AACxC;AAEA,eAAsB,aACpB,WACA,YACA,SACA,YACuB;AACvB,QAAM,MAAM,GAAG,UAAU,QAAQ,OAAO,EAAE,CAAC;AAE3C,MAAI;AACJ,QAAM,UAAkC;AAAA,IACtC,iBAAiB;AAAA,EACnB;AAEA,MAAI,YAAY;AACd,UAAM,OAAO,IAAI,SAAS;AAC1B,SAAK,OAAO,cAAc,QAAQ,UAAU;AAC5C,SAAK,OAAO,gBAAgB,QAAQ,YAAY;AAChD,QAAI,QAAQ,cAAe,MAAK,OAAO,iBAAiB,QAAQ,aAAa;AAC7E,SAAK,OAAO,eAAe,QAAQ,WAAW;AAC9C,QAAI,QAAQ,iBAAkB,MAAK,OAAO,oBAAoB,QAAQ,gBAAgB;AACtF,QAAI,QAAQ,iBAAkB,MAAK,OAAO,oBAAoB,KAAK,UAAU,QAAQ,gBAAgB,CAAC;AACtG,QAAI,QAAQ,kBAAmB,MAAK,OAAO,qBAAqB,QAAQ,iBAAiB;AACzF,QAAI,QAAQ,kBAAmB,MAAK,OAAO,qBAAqB,QAAQ,iBAAiB;AACzF,SAAK,OAAO,YAAY,QAAQ,QAAQ;AACxC,QAAI,QAAQ,IAAK,MAAK,OAAO,OAAO,QAAQ,GAAG;AAC/C,QAAI,QAAQ,KAAM,MAAK,OAAO,QAAQ,KAAK,UAAU,QAAQ,IAAI,CAAC;AAClE,SAAK,OAAO,cAAc,UAAU;AACpC,WAAO;AAAA,EACT,OAAO;AACL,WAAO,KAAK,UAAU,OAAO;AAC7B,YAAQ,cAAc,IAAI;AAAA,EAC5B;AAEA,QAAM,MAAM,MAAM,eAAe,KAAK,EAAE,QAAQ,QAAQ,SAAS,KAAK,CAAC;AAEvE,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,gBAAgB,EAAE;AACrE,UAAM,IAAI,MAAO,IAA2B,SAAS,QAAQ,IAAI,MAAM,EAAE;AAAA,EAC3E;AAEA,SAAO,IAAI,KAAK;AAClB;AAEA,eAAsB,aACpB,WACA,YACA,UACA,QACe;AACf,QAAM,OAAO,KAAK,UAAU,MAAM;AAClC,QAAM,UAAU,IAAI,YAAY,EAAE,OAAO,IAAI;AAC7C,QAAM,iBAAa,wBAAS,OAAO;AAEnC,QAAM,MAAM,GAAG,UAAU,QAAQ,OAAO,EAAE,CAAC,uBAAuB,QAAQ;AAC1E,QAAM,MAAM,KAAK;AAAA,IACf,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,WAAW;AAAA,EACnB,CAAC;AACH;;;AC9EA,IAAM,cAAc;AAQb,SAAS,yBAA2C;AACzD,QAAM,UAA0B,CAAC;AACjC,MAAI,SAAS;AAEb,QAAM,YAAY;AAAA,IAChB,KAAK,QAAQ,IAAI,KAAK,OAAO;AAAA,IAC7B,MAAM,QAAQ,KAAK,KAAK,OAAO;AAAA,IAC/B,OAAO,QAAQ,MAAM,KAAK,OAAO;AAAA,EACnC;AAEA,MAAI,cAAqC;AACzC,MAAI,gBAAoD;AAExD,WAAS,KAAK,OAA8B,MAAiB;AAC3D,QAAI;AACJ,QAAI;AACF,YAAM,KAAK,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,CAAC,CAAE,EAAE,KAAK,GAAG;AAAA,IACjF,QAAQ;AACN,YAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACtB;AACA,QAAI,IAAI,SAAS,IAAK,OAAM,IAAI,MAAM,GAAG,GAAG,IAAI;AAChD,YAAQ,KAAK,EAAE,OAAO,MAAM,KAAK,WAAW,KAAK,IAAI,EAAE,CAAC;AACxD,QAAI,QAAQ,SAAS,YAAa,SAAQ,MAAM;AAAA,EAClD;AAEA,SAAO;AAAA,IACL,QAAQ;AACN,UAAI,OAAQ;AACZ,eAAS;AAET,cAAQ,MAAM,IAAI,SAAoB;AACpC,aAAK,OAAO,IAAI;AAChB,kBAAU,IAAI,GAAG,IAAI;AAAA,MACvB;AACA,cAAQ,OAAO,IAAI,SAAoB;AACrC,aAAK,QAAQ,IAAI;AACjB,kBAAU,KAAK,GAAG,IAAI;AAAA,MACxB;AACA,cAAQ,QAAQ,IAAI,SAAoB;AACtC,aAAK,SAAS,IAAI;AAClB,kBAAU,MAAM,GAAG,IAAI;AAAA,MACzB;AAEA,oBAAc,OAAO;AACrB,aAAO,UAAU,CAAC,KAAK,KAAK,MAAM,KAAK,QAAQ;AAC7C,aAAK,SAAS,CAAC,KAAK,WAAW,OAAO,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;AACpE,YAAI,OAAO,gBAAgB,WAAY,QAAO,YAAY,KAAK,KAAK,MAAM,KAAK,GAAG;AAClF,eAAO;AAAA,MACT;AAEA,sBAAgB,OAAO;AACvB,aAAO,uBAAuB,CAAC,UAAU;AACvC,cAAM,SAAS,MAAM,kBAAkB,QAAQ,MAAM,OAAO,UAAU,KAAK,UAAU,MAAM,MAAM;AACjG,aAAK,SAAS,CAAC,uBAAuB,MAAM,CAAC;AAC7C,YAAI,OAAO,kBAAkB,WAAY,eAAc,KAAK,QAAQ,KAAK;AAAA,MAC3E;AAAA,IACF;AAAA,IAEA,OAAO;AACL,UAAI,CAAC,OAAQ;AACb,eAAS;AACT,cAAQ,MAAM,UAAU;AACxB,cAAQ,OAAO,UAAU;AACzB,cAAQ,QAAQ,UAAU;AAC1B,aAAO,UAAU;AACjB,aAAO,uBAAuB;AAAA,IAChC;AAAA,IAEA,aAAa;AACX,aAAO,CAAC,GAAG,OAAO;AAAA,IACpB;AAAA,EACF;AACF;;;AChFO,SAAS,qBAAsC;AACpD,QAAM,KAAK,UAAU;AAGrB,MAAI,UAAU;AACd,QAAM,UAAU,GAAG,MAAM,eAAe;AACxC,QAAM,WAAW,GAAG,MAAM,gBAAgB;AAC1C,QAAM,QAAQ,GAAG,MAAM,YAAY;AACnC,QAAM,UAAU,GAAG,MAAM,gBAAgB;AACzC,QAAM,SAAS,GAAG,MAAM,YAAY;AAEpC,MAAI,QAAQ;AACV,cAAU,SAAS,OAAO,CAAC,CAAC;AAAA,EAC9B,WAAW,OAAO;AAChB,cAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,EAC5B,WAAW,WAAW,CAAC,UAAU,KAAK,EAAE,GAAG;AACzC,cAAU,UAAU,QAAQ,CAAC,CAAC;AAAA,EAChC,WAAW,UAAU;AACnB,cAAU,WAAW,SAAS,CAAC,CAAC;AAAA,EAClC,WAAW,WAAW,WAAW,KAAK,EAAE,GAAG;AACzC,cAAU,UAAU,QAAQ,CAAC,CAAC;AAAA,EAChC;AAGA,MAAI,KAAK;AACT,QAAM,OAAO,GAAG,MAAM,uBAAuB;AAC7C,QAAM,OAAO,GAAG,MAAM,uBAAuB;AAC7C,QAAM,WAAW,GAAG,MAAM,eAAe;AACzC,QAAM,OAAO,GAAG,MAAM,wBAAwB;AAE9C,MAAI,MAAM;AACR,SAAK,SAAS,KAAK,CAAC,EAAE,QAAQ,KAAK,GAAG,CAAC;AAAA,EACzC,WAAW,MAAM;AACf,UAAM,SAAiC;AAAA,MACrC,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AACA,SAAK,WAAW,OAAO,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;AAAA,EAC5C,WAAW,UAAU;AACnB,SAAK,WAAW,SAAS,CAAC,CAAC;AAAA,EAC7B,WAAW,MAAM;AACf,SAAK,OAAO,KAAK,CAAC,EAAE,QAAQ,MAAM,GAAG,CAAC;AAAA,EACxC,WAAW,QAAQ,KAAK,EAAE,GAAG;AAC3B,SAAK;AAAA,EACP;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,UAAU,GAAG,OAAO,UAAU,IAAI,OAAO,WAAW;AAAA,IACpD,QAAQ,GAAG,OAAO,KAAK,IAAI,OAAO,MAAM;AAAA,IACxC,UAAU,UAAU;AAAA,IACpB,UAAU,KAAK,eAAe,EAAE,gBAAgB,EAAE;AAAA,IAClD,QAAQ,UAAU;AAAA,EACpB;AACF;;;AC5CO,SAAS,2BAA2B,MAMlB;AACvB,QAAM,YAAY,MAAM,sBAAsB;AAC9C,QAAMA,UAAS,MAAM,mBAAmB;AACxC,QAAM,iBAAiB,MAAM,sBAAsB;AACnD,QAAM,cAAc,MAAM,mBAAmB;AAC7C,QAAM,oBAAoB,MAAM,oBAAoB;AAEpD,QAAM,SAA6B,CAAC;AACpC,QAAMC,aAAY,oBAAI,IAAuC;AAC7D,QAAM,eAA+D,CAAC;AACtE,QAAM,cAAc,oBAAI,IAAkD;AAC1E,MAAI,eAAiD;AACrD,MAAI,mBAAgD;AAEpD,WAASC,MAAK,OAAyB;AACrC,WAAO,KAAK,KAAK;AACjB,QAAI,OAAO,SAAS,GAAI,QAAO,MAAM;AACrC,eAAW,MAAMD,WAAW,IAAG,KAAK;AAAA,EACtC;AAEA,WAAS,eAAe,IAAqB;AAC3C,QAAI,GAAG,GAAI,QAAO,IAAI,GAAG,EAAE;AAC3B,UAAM,MAAM,GAAG,QAAQ,YAAY;AACnC,UAAM,MAAM,CAAC,GAAG,GAAG,SAAS,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAClD,QAAI,IAAK,QAAO,GAAG,GAAG,IAAI,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,WAAS,cAAc,IAA0B;AAC/C,UAAM,MAAM,GAAG,QAAQ,YAAY;AACnC,QAAI,CAAC,KAAK,UAAU,SAAS,UAAU,YAAY,SAAS,SAAS,EAAE,SAAS,GAAG,EAAG,QAAO;AAC7F,QAAI,GAAG,aAAa,MAAM,MAAM,YAAY,GAAG,aAAa,UAAU,EAAG,QAAO;AAChF,QAAI,GAAG,WAAW,GAAG,aAAa,SAAS,EAAG,QAAO;AACrD,QAAI,GAAG,QAAQ,qCAAqC,EAAG,QAAO;AAC9D,QAAI;AACF,UAAI,iBAAiB,EAAE,EAAE,WAAW,UAAW,QAAO;AAAA,IACxD,QAAQ;AAAA,IAER;AACA,WAAO;AAAA,EACT;AAEA,WAAS,YAAY,GAAe;AAClC,UAAM,SAAS,EAAE;AACjB,QAAI,CAAC,OAAQ;AACb,UAAM,MAAM,KAAK,IAAI;AAGrB,iBAAa,KAAK,EAAE,QAAQ,MAAM,IAAI,CAAC;AAEvC,WAAO,aAAa,SAAS,KAAK,MAAM,aAAa,CAAC,EAAE,OAAO,KAAM;AACnE,mBAAa,MAAM;AAAA,IACrB;AACA,UAAM,eAAe,aAAa,OAAO,CAAC,MAAM,EAAE,WAAW,UAAU,MAAM,EAAE,OAAOD,OAAM;AAC5F,QAAI,aAAa,UAAU,WAAW;AACpC,MAAAE,MAAK;AAAA,QACH,MAAM;AAAA,QACN,WAAW;AAAA,QACX,QAAQ,eAAe,MAAM;AAAA,QAC7B,SAAS,GAAG,aAAa,MAAM,cAAc,MAAM,aAAa,CAAC,EAAE,IAAI;AAAA,QACvE,KAAK,WAAW,UAAU,QAAQ;AAAA,MACpC,CAAC;AACD,mBAAa,SAAS;AAAA,IACxB;AAGA,QAAI,qBAAqB,CAAC,cAAc,MAAM,GAAG;AAC/C,MAAAA,MAAK;AAAA,QACH,MAAM;AAAA,QACN,WAAW;AAAA,QACX,QAAQ,eAAe,MAAM;AAAA,QAC7B,SAAS,6BAA6B,OAAO,QAAQ,YAAY,CAAC;AAAA,QAClE,KAAK,WAAW,UAAU,QAAQ;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,WAAS,oBAAoB;AAC3B,uBAAmB,QAAQ;AAC3B,YAAQ,QAAQ,IAAI,SAAoB;AACtC,wBAAkB,MAAM,SAAS,IAAI;AAErC,YAAM,MAAM,OAAO,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,GAAG;AACxC,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,WAAW,YAAY,IAAI,GAAG;AACpC,UAAI,YAAY,MAAM,SAAS,YAAY,aAAa;AACtD,iBAAS;AACT,YAAI,SAAS,SAAS,gBAAgB;AACpC,UAAAA,MAAK;AAAA,YACH,MAAM;AAAA,YACN,WAAW;AAAA,YACX,QAAQ;AAAA,YACR,SAAS,cAAc,SAAS,KAAK,QAAQ,KAAK,OAAO,MAAM,SAAS,aAAa,GAAI,CAAC,MAAM,GAAG;AAAA,YACnG,KAAK,WAAW,UAAU,QAAQ;AAAA,UACpC,CAAC;AACD,sBAAY,OAAO,GAAG;AAAA,QACxB;AAAA,MACF,OAAO;AACL,oBAAY,IAAI,KAAK,EAAE,OAAO,GAAG,WAAW,IAAI,CAAC;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ;AACN,qBAAe;AACf,eAAS,iBAAiB,SAAS,cAAc,IAAI;AACrD,wBAAkB;AAAA,IACpB;AAAA,IACA,OAAO;AACL,UAAI,aAAc,UAAS,oBAAoB,SAAS,cAAc,IAAI;AAC1E,UAAI,iBAAkB,SAAQ,QAAQ;AACtC,mBAAa,SAAS;AACtB,kBAAY,MAAM;AAAA,IACpB;AAAA,IACA,YAAY;AACV,aAAO,CAAC,GAAG,MAAM;AAAA,IACnB;AAAA,IACA,cAAc,UAAU;AACtB,MAAAD,WAAU,IAAI,QAAQ;AACtB,aAAO,MAAMA,WAAU,OAAO,QAAQ;AAAA,IACxC;AAAA,EACF;AACF;;;AC9IA,IAAME,eAAc;AAEpB,IAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,aAAa,KAAa,OAA6B;AAC9D,MAAI;AACF,UAAM,OAAO,IAAI,IAAI,KAAK,SAAS,IAAI,EAAE;AACzC,UAAM,MAAM,CAAC,GAAG,eAAe,GAAG,KAAK;AACvC,WAAO,IAAI,KAAK,CAAC,MAAM,SAAS,KAAK,KAAK,SAAS,MAAM,CAAC,CAAC;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,SAAS,YAAY,KAAqB;AACxC,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI;AACpC,UAAM,OAAO,GAAG,EAAE,MAAM,GAAG,EAAE,QAAQ;AACrC,WAAO,KAAK,SAAS,MAAM,KAAK,MAAM,GAAG,GAAG,IAAI,WAAW;AAAA,EAC7D,QAAQ;AACN,WAAO,IAAI,SAAS,MAAM,IAAI,MAAM,GAAG,GAAG,IAAI,WAAW;AAAA,EAC3D;AACF;AAEO,SAAS,uBAAuB,oBAA8B,CAAC,GAAqB;AACzF,QAAM,UAA0B,CAAC;AACjC,QAAM,UAAU,IAAI,IAAI,iBAAiB;AACzC,MAAI,YAAwC;AAC5C,MAAI,cAA2D;AAC/D,MAAI,SAAS;AAEb,WAAS,KAAK,OAAqB;AACjC,YAAQ,KAAK,KAAK;AAClB,QAAI,QAAQ,SAASA,aAAa,SAAQ,MAAM;AAAA,EAClD;AAEA,SAAO;AAAA,IACL,QAAQ;AACN,UAAI,OAAQ;AACZ,eAAS;AAGT,kBAAY,OAAO;AACnB,aAAO,QAAQ,OAAO,OAA0B,SAA0C;AACxF,cAAM,UAAW,MAAM,UAAU,OAAkB,YAAY;AAC/D,cAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,iBAAiB,MAAM,MAAM,OAAQ,MAAkB;AACvG,cAAM,YAAY,KAAK,IAAI;AAC3B,cAAM,MAAM,MAAM,UAAW,KAAK,QAAQ,OAAO,IAAI;AACrD,YAAI,IAAI,UAAU,OAAO,CAAC,aAAa,KAAK,OAAO,GAAG;AACpD,eAAK;AAAA,YACH;AAAA,YACA,KAAK,YAAY,GAAG;AAAA,YACpB,QAAQ,IAAI;AAAA,YACZ,UAAU,KAAK,IAAI,IAAI;AAAA,YACvB,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AACA,eAAO;AAAA,MACT;AAGA,oBAAc,eAAe,UAAU;AACvC,qBAAe,UAAU,OAAO,SAC9B,QACA,KACA,OACA,UACA,UACA;AACA,cAAM,YAAY,KAAK,IAAI;AAC3B,cAAM,SAAS,OAAO,QAAQ,WAAW,MAAO,IAAY;AAE5D,aAAK,iBAAiB,QAAQ,MAAM;AAClC,cAAI,KAAK,UAAU,OAAO,CAAC,aAAa,QAAQ,OAAO,GAAG;AACxD,iBAAK;AAAA,cACH,QAAQ,OAAO,YAAY;AAAA,cAC3B,KAAK,YAAY,MAAM;AAAA,cACvB,QAAQ,KAAK;AAAA,cACb,UAAU,KAAK,IAAI,IAAI;AAAA,cACvB,WAAW;AAAA,YACb,CAAC;AAAA,UACH;AAAA,QACF,CAAC;AAED,eAAO,YAAa,MAAM,MAAM,CAAC,QAAQ,KAAK,SAAS,MAAM,UAAU,QAAQ,CAE9E;AAAA,MACH;AAAA,IACF;AAAA,IAEA,OAAO;AACL,UAAI,CAAC,OAAQ;AACb,eAAS;AACT,UAAI,UAAW,QAAO,QAAQ;AAC9B,UAAI,YAAa,gBAAe,UAAU,OAAO;AAAA,IACnD;AAAA,IAEA,aAAa;AACX,aAAO,CAAC,GAAG,OAAO;AAAA,IACpB;AAAA,EACF;AACF;;;AC3GA,IAAI,QAAoB,EAAE,MAAM,QAAW,eAAe,OAAU;AACpE,IAAM,YAAY,oBAAI,IAAgB;AAEtC,SAAS,OAAO;AACd,aAAW,KAAK,UAAW,GAAE;AAC/B;AAEO,SAAS,UAAU,UAAsB;AAC9C,YAAU,IAAI,QAAQ;AACtB,SAAO,MAAM,UAAU,OAAO,QAAQ;AACxC;AAEO,SAAS,cAA0B;AACxC,SAAO;AACT;AAEO,IAAM,QAAQ;AAAA,EACnB,QAAQ,MAAwB;AAC9B,YAAQ,EAAE,GAAG,OAAO,MAAM,QAAQ,OAAU;AAC5C,SAAK;AAAA,EACP;AAAA,EACA,iBAAiB,KAAqC;AACpD,YAAQ,EAAE,GAAG,OAAO,eAAe,OAAO,OAAU;AACpD,SAAK;AAAA,EACP;AACF;;;ACjBA,IAAM,QAAuB;AAAA,EAC3B,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,MAAM;AAAA,EACN,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,gBAAgB;AAClB;AAEA,IAAM,OAAsB;AAAA,EAC1B,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,MAAM;AAAA,EACN,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,gBAAgB;AAClB;AAEO,SAAS,aAAa,OAA6B;AACxD,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,UAAU,QAAS,QAAO;AAE9B,QAAM,WAAW;AACjB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY,SAAS,cAAc,MAAM;AAAA,IACzC,QAAQ,SAAS,UAAU,MAAM;AAAA,IACjC,aAAa,SAAS,UAAU,MAAM;AAAA,IACtC,MAAM,SAAS,QAAQ,MAAM;AAAA,IAC7B,QAAQ,SAAS,UAAU,MAAM;AAAA,EACnC;AACF;","names":["window","listeners","emit","MAX_ENTRIES"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,91 @@
1
- import { ReportPayload, ReportResult, ConsoleEntry, EnvironmentInfo, NetworkEntry, FlintUser, Theme } from '@flint/types';
2
- export { ConsoleEntry, EnvironmentInfo, FlintUser, FlintWidgetProps, NetworkEntry, ReportPayload, ReportResult, Severity, Theme, ThemeOverride } from '@flint/types';
1
+ type Severity = "P1" | "P2" | "P3" | "P4";
2
+ interface EnvironmentInfo {
3
+ browser: string;
4
+ os: string;
5
+ viewport: string;
6
+ screen: string;
7
+ language: string;
8
+ timezone: string;
9
+ online: boolean;
10
+ }
11
+ interface ConsoleEntry {
12
+ level: "log" | "warn" | "error";
13
+ args: string;
14
+ timestamp: number;
15
+ }
16
+ interface NetworkEntry {
17
+ method: string;
18
+ url: string;
19
+ status: number;
20
+ duration: number;
21
+ timestamp: number;
22
+ }
23
+ type Locale = "pt-BR" | "en-US";
24
+ type Theme = "light" | "dark" | ThemeOverride;
25
+ interface ThemeOverride {
26
+ background?: string;
27
+ accent?: string;
28
+ text?: string;
29
+ border?: string;
30
+ }
31
+ interface FlintUser {
32
+ id: string;
33
+ name: string;
34
+ email?: string;
35
+ }
36
+ interface FlintExtraFields {
37
+ sessionReplay?: string | (() => string);
38
+ [key: string]: unknown;
39
+ }
40
+ interface FlintWidgetProps {
41
+ projectKey: string;
42
+ serverUrl: string;
43
+ user?: FlintUser;
44
+ meta?: Record<string, unknown>;
45
+ extraFields?: FlintExtraFields;
46
+ buttonLabel?: string;
47
+ locale?: Locale;
48
+ theme?: Theme;
49
+ zIndex?: number;
50
+ statusPageUrl?: string;
51
+ datadogSite?: string;
52
+ enableReplay?: boolean;
53
+ enableScreenshot?: boolean;
54
+ enableConsole?: boolean;
55
+ enableNetwork?: boolean;
56
+ enableFrustration?: boolean;
57
+ autoReportFrustration?: boolean;
58
+ onBeforeSubmit?: (payload: ReportPayload) => boolean | Promise<boolean>;
59
+ onSuccess?: (result: ReportResult) => void;
60
+ onError?: (error: Error) => void;
61
+ onOpen?: () => void;
62
+ onClose?: () => void;
63
+ }
64
+ interface ReportPayload {
65
+ reporterId: string;
66
+ reporterName: string;
67
+ reporterEmail?: string;
68
+ description: string;
69
+ expectedBehavior?: string;
70
+ stepsToReproduce?: {
71
+ action: string;
72
+ result: string;
73
+ }[];
74
+ externalReplayUrl?: string;
75
+ additionalContext?: string;
76
+ severity: Severity;
77
+ url?: string;
78
+ meta?: Record<string, unknown>;
79
+ label?: string;
80
+ }
81
+ interface ReportResult {
82
+ id: string;
83
+ status: string;
84
+ githubIssueUrl?: string | null;
85
+ slackMessageUrl?: string | null;
86
+ isDuplicate: boolean;
87
+ duplicateOfId?: string | null;
88
+ }
3
89
 
4
90
  declare function submitReport(serverUrl: string, projectKey: string, payload: ReportPayload, screenshot?: File): Promise<ReportResult>;
5
91
  declare function submitReplay(serverUrl: string, projectKey: string, reportId: string, events: unknown[]): Promise<void>;
@@ -13,13 +99,6 @@ declare function createConsoleCollector(): ConsoleCollector;
13
99
 
14
100
  declare function collectEnvironment(): EnvironmentInfo;
15
101
 
16
- interface NetworkCollector {
17
- start(): void;
18
- stop(): void;
19
- getEntries(): NetworkEntry[];
20
- }
21
- declare function createNetworkCollector(extraBlockedHosts?: string[]): NetworkCollector;
22
-
23
102
  interface FrustrationEvent {
24
103
  type: "rage_click" | "dead_click" | "error_loop";
25
104
  timestamp: number;
@@ -41,6 +120,13 @@ declare function createFrustrationCollector(opts?: {
41
120
  enableDeadClicks?: boolean;
42
121
  }): FrustrationCollector;
43
122
 
123
+ interface NetworkCollector {
124
+ start(): void;
125
+ stop(): void;
126
+ getEntries(): NetworkEntry[];
127
+ }
128
+ declare function createNetworkCollector(extraBlockedHosts?: string[]): NetworkCollector;
129
+
44
130
  interface FlintState {
45
131
  user: FlintUser | undefined;
46
132
  sessionReplay: string | (() => string) | undefined;
@@ -66,4 +152,4 @@ interface ResolvedTheme {
66
152
  }
67
153
  declare function resolveTheme(theme: Theme): ResolvedTheme;
68
154
 
69
- export { type ConsoleCollector, type FlintState, type FrustrationCollector, type FrustrationEvent, type NetworkCollector, type ResolvedTheme, collectEnvironment, createConsoleCollector, createFrustrationCollector, createNetworkCollector, flint, getSnapshot, resolveTheme, submitReplay, submitReport, subscribe };
155
+ export { type ConsoleCollector, type ConsoleEntry, type EnvironmentInfo, type FlintState, type FlintUser, type FlintWidgetProps, type FrustrationCollector, type FrustrationEvent, type NetworkCollector, type NetworkEntry, type ReportPayload, type ReportResult, type ResolvedTheme, type Severity, type Theme, type ThemeOverride, collectEnvironment, createConsoleCollector, createFrustrationCollector, createNetworkCollector, flint, getSnapshot, resolveTheme, submitReplay, submitReport, subscribe };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,91 @@
1
- import { ReportPayload, ReportResult, ConsoleEntry, EnvironmentInfo, NetworkEntry, FlintUser, Theme } from '@flint/types';
2
- export { ConsoleEntry, EnvironmentInfo, FlintUser, FlintWidgetProps, NetworkEntry, ReportPayload, ReportResult, Severity, Theme, ThemeOverride } from '@flint/types';
1
+ type Severity = "P1" | "P2" | "P3" | "P4";
2
+ interface EnvironmentInfo {
3
+ browser: string;
4
+ os: string;
5
+ viewport: string;
6
+ screen: string;
7
+ language: string;
8
+ timezone: string;
9
+ online: boolean;
10
+ }
11
+ interface ConsoleEntry {
12
+ level: "log" | "warn" | "error";
13
+ args: string;
14
+ timestamp: number;
15
+ }
16
+ interface NetworkEntry {
17
+ method: string;
18
+ url: string;
19
+ status: number;
20
+ duration: number;
21
+ timestamp: number;
22
+ }
23
+ type Locale = "pt-BR" | "en-US";
24
+ type Theme = "light" | "dark" | ThemeOverride;
25
+ interface ThemeOverride {
26
+ background?: string;
27
+ accent?: string;
28
+ text?: string;
29
+ border?: string;
30
+ }
31
+ interface FlintUser {
32
+ id: string;
33
+ name: string;
34
+ email?: string;
35
+ }
36
+ interface FlintExtraFields {
37
+ sessionReplay?: string | (() => string);
38
+ [key: string]: unknown;
39
+ }
40
+ interface FlintWidgetProps {
41
+ projectKey: string;
42
+ serverUrl: string;
43
+ user?: FlintUser;
44
+ meta?: Record<string, unknown>;
45
+ extraFields?: FlintExtraFields;
46
+ buttonLabel?: string;
47
+ locale?: Locale;
48
+ theme?: Theme;
49
+ zIndex?: number;
50
+ statusPageUrl?: string;
51
+ datadogSite?: string;
52
+ enableReplay?: boolean;
53
+ enableScreenshot?: boolean;
54
+ enableConsole?: boolean;
55
+ enableNetwork?: boolean;
56
+ enableFrustration?: boolean;
57
+ autoReportFrustration?: boolean;
58
+ onBeforeSubmit?: (payload: ReportPayload) => boolean | Promise<boolean>;
59
+ onSuccess?: (result: ReportResult) => void;
60
+ onError?: (error: Error) => void;
61
+ onOpen?: () => void;
62
+ onClose?: () => void;
63
+ }
64
+ interface ReportPayload {
65
+ reporterId: string;
66
+ reporterName: string;
67
+ reporterEmail?: string;
68
+ description: string;
69
+ expectedBehavior?: string;
70
+ stepsToReproduce?: {
71
+ action: string;
72
+ result: string;
73
+ }[];
74
+ externalReplayUrl?: string;
75
+ additionalContext?: string;
76
+ severity: Severity;
77
+ url?: string;
78
+ meta?: Record<string, unknown>;
79
+ label?: string;
80
+ }
81
+ interface ReportResult {
82
+ id: string;
83
+ status: string;
84
+ githubIssueUrl?: string | null;
85
+ slackMessageUrl?: string | null;
86
+ isDuplicate: boolean;
87
+ duplicateOfId?: string | null;
88
+ }
3
89
 
4
90
  declare function submitReport(serverUrl: string, projectKey: string, payload: ReportPayload, screenshot?: File): Promise<ReportResult>;
5
91
  declare function submitReplay(serverUrl: string, projectKey: string, reportId: string, events: unknown[]): Promise<void>;
@@ -13,13 +99,6 @@ declare function createConsoleCollector(): ConsoleCollector;
13
99
 
14
100
  declare function collectEnvironment(): EnvironmentInfo;
15
101
 
16
- interface NetworkCollector {
17
- start(): void;
18
- stop(): void;
19
- getEntries(): NetworkEntry[];
20
- }
21
- declare function createNetworkCollector(extraBlockedHosts?: string[]): NetworkCollector;
22
-
23
102
  interface FrustrationEvent {
24
103
  type: "rage_click" | "dead_click" | "error_loop";
25
104
  timestamp: number;
@@ -41,6 +120,13 @@ declare function createFrustrationCollector(opts?: {
41
120
  enableDeadClicks?: boolean;
42
121
  }): FrustrationCollector;
43
122
 
123
+ interface NetworkCollector {
124
+ start(): void;
125
+ stop(): void;
126
+ getEntries(): NetworkEntry[];
127
+ }
128
+ declare function createNetworkCollector(extraBlockedHosts?: string[]): NetworkCollector;
129
+
44
130
  interface FlintState {
45
131
  user: FlintUser | undefined;
46
132
  sessionReplay: string | (() => string) | undefined;
@@ -66,4 +152,4 @@ interface ResolvedTheme {
66
152
  }
67
153
  declare function resolveTheme(theme: Theme): ResolvedTheme;
68
154
 
69
- export { type ConsoleCollector, type FlintState, type FrustrationCollector, type FrustrationEvent, type NetworkCollector, type ResolvedTheme, collectEnvironment, createConsoleCollector, createFrustrationCollector, createNetworkCollector, flint, getSnapshot, resolveTheme, submitReplay, submitReport, subscribe };
155
+ export { type ConsoleCollector, type ConsoleEntry, type EnvironmentInfo, type FlintState, type FlintUser, type FlintWidgetProps, type FrustrationCollector, type FrustrationEvent, type NetworkCollector, type NetworkEntry, type ReportPayload, type ReportResult, type ResolvedTheme, type Severity, type Theme, type ThemeOverride, collectEnvironment, createConsoleCollector, createFrustrationCollector, createNetworkCollector, flint, getSnapshot, resolveTheme, submitReplay, submitReport, subscribe };
package/dist/index.js CHANGED
@@ -180,93 +180,6 @@ function collectEnvironment() {
180
180
  };
181
181
  }
182
182
 
183
- // src/collectors/network.ts
184
- var MAX_ENTRIES2 = 50;
185
- var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
186
- "browser-intake-datadoghq.com",
187
- "rum.browser-intake-datadoghq.com",
188
- "logs.browser-intake-datadoghq.com",
189
- "session-replay.browser-intake-datadoghq.com"
190
- ]);
191
- function isBlockedUrl(url, extra) {
192
- try {
193
- const host = new URL(url, location.href).hostname;
194
- const all = [...BLOCKED_HOSTS, ...extra];
195
- return all.some((b) => host === b || host.endsWith("." + b));
196
- } catch {
197
- return false;
198
- }
199
- }
200
- function truncateUrl(url) {
201
- try {
202
- const u = new URL(url, location.href);
203
- const base = `${u.origin}${u.pathname}`;
204
- return base.length > 200 ? base.slice(0, 200) + "\u2026" : base;
205
- } catch {
206
- return url.length > 200 ? url.slice(0, 200) + "\u2026" : url;
207
- }
208
- }
209
- function createNetworkCollector(extraBlockedHosts = []) {
210
- const entries = [];
211
- const blocked = new Set(extraBlockedHosts);
212
- let origFetch = null;
213
- let origXHROpen = null;
214
- let active = false;
215
- function push(entry) {
216
- entries.push(entry);
217
- if (entries.length > MAX_ENTRIES2) entries.shift();
218
- }
219
- return {
220
- start() {
221
- if (active) return;
222
- active = true;
223
- origFetch = window.fetch;
224
- window.fetch = async (input, init) => {
225
- const method = (init?.method ?? "GET").toUpperCase();
226
- const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
227
- const startTime = Date.now();
228
- const res = await origFetch.call(window, input, init);
229
- if (res.status >= 400 && !isBlockedUrl(url, blocked)) {
230
- push({
231
- method,
232
- url: truncateUrl(url),
233
- status: res.status,
234
- duration: Date.now() - startTime,
235
- timestamp: startTime
236
- });
237
- }
238
- return res;
239
- };
240
- origXHROpen = XMLHttpRequest.prototype.open;
241
- XMLHttpRequest.prototype.open = function(method, url, async, username, password) {
242
- const startTime = Date.now();
243
- const urlStr = typeof url === "string" ? url : url.href;
244
- this.addEventListener("load", () => {
245
- if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {
246
- push({
247
- method: method.toUpperCase(),
248
- url: truncateUrl(urlStr),
249
- status: this.status,
250
- duration: Date.now() - startTime,
251
- timestamp: startTime
252
- });
253
- }
254
- });
255
- return origXHROpen.apply(this, [method, url, async ?? true, username, password]);
256
- };
257
- },
258
- stop() {
259
- if (!active) return;
260
- active = false;
261
- if (origFetch) window.fetch = origFetch;
262
- if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;
263
- },
264
- getEntries() {
265
- return [...entries];
266
- }
267
- };
268
- }
269
-
270
183
  // src/collectors/frustration.ts
271
184
  function createFrustrationCollector(opts) {
272
185
  const threshold = opts?.rageClickThreshold ?? 3;
@@ -312,9 +225,7 @@ function createFrustrationCollector(opts) {
312
225
  while (clickHistory.length > 0 && now - clickHistory[0].time > 1e3) {
313
226
  clickHistory.shift();
314
227
  }
315
- const recentOnSame = clickHistory.filter(
316
- (c) => c.target === target && now - c.time < window2
317
- );
228
+ const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window2);
318
229
  if (recentOnSame.length >= threshold) {
319
230
  emit2({
320
231
  type: "rage_click",
@@ -381,6 +292,93 @@ function createFrustrationCollector(opts) {
381
292
  };
382
293
  }
383
294
 
295
+ // src/collectors/network.ts
296
+ var MAX_ENTRIES2 = 50;
297
+ var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
298
+ "browser-intake-datadoghq.com",
299
+ "rum.browser-intake-datadoghq.com",
300
+ "logs.browser-intake-datadoghq.com",
301
+ "session-replay.browser-intake-datadoghq.com"
302
+ ]);
303
+ function isBlockedUrl(url, extra) {
304
+ try {
305
+ const host = new URL(url, location.href).hostname;
306
+ const all = [...BLOCKED_HOSTS, ...extra];
307
+ return all.some((b) => host === b || host.endsWith("." + b));
308
+ } catch {
309
+ return false;
310
+ }
311
+ }
312
+ function truncateUrl(url) {
313
+ try {
314
+ const u = new URL(url, location.href);
315
+ const base = `${u.origin}${u.pathname}`;
316
+ return base.length > 200 ? base.slice(0, 200) + "\u2026" : base;
317
+ } catch {
318
+ return url.length > 200 ? url.slice(0, 200) + "\u2026" : url;
319
+ }
320
+ }
321
+ function createNetworkCollector(extraBlockedHosts = []) {
322
+ const entries = [];
323
+ const blocked = new Set(extraBlockedHosts);
324
+ let origFetch = null;
325
+ let origXHROpen = null;
326
+ let active = false;
327
+ function push(entry) {
328
+ entries.push(entry);
329
+ if (entries.length > MAX_ENTRIES2) entries.shift();
330
+ }
331
+ return {
332
+ start() {
333
+ if (active) return;
334
+ active = true;
335
+ origFetch = window.fetch;
336
+ window.fetch = async (input, init) => {
337
+ const method = (init?.method ?? "GET").toUpperCase();
338
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
339
+ const startTime = Date.now();
340
+ const res = await origFetch.call(window, input, init);
341
+ if (res.status >= 400 && !isBlockedUrl(url, blocked)) {
342
+ push({
343
+ method,
344
+ url: truncateUrl(url),
345
+ status: res.status,
346
+ duration: Date.now() - startTime,
347
+ timestamp: startTime
348
+ });
349
+ }
350
+ return res;
351
+ };
352
+ origXHROpen = XMLHttpRequest.prototype.open;
353
+ XMLHttpRequest.prototype.open = function(method, url, async, username, password) {
354
+ const startTime = Date.now();
355
+ const urlStr = typeof url === "string" ? url : url.href;
356
+ this.addEventListener("load", () => {
357
+ if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {
358
+ push({
359
+ method: method.toUpperCase(),
360
+ url: truncateUrl(urlStr),
361
+ status: this.status,
362
+ duration: Date.now() - startTime,
363
+ timestamp: startTime
364
+ });
365
+ }
366
+ });
367
+ return origXHROpen.apply(this, [method, url, async ?? true, username, password]);
368
+ };
369
+ },
370
+ stop() {
371
+ if (!active) return;
372
+ active = false;
373
+ if (origFetch) window.fetch = origFetch;
374
+ if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;
375
+ },
376
+ getEntries() {
377
+ return [...entries];
378
+ }
379
+ };
380
+ }
381
+
384
382
  // src/store.ts
385
383
  var state = { user: void 0, sessionReplay: void 0 };
386
384
  var listeners = /* @__PURE__ */ new Set();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/network.ts","../src/collectors/frustration.ts","../src/store.ts","../src/theme.ts"],"sourcesContent":["import type { ReportPayload, ReportResult } from \"@flint/types\";\nimport { gzipSync } from \"fflate\";\n\nasync function fetchWithRetry(url: string, init: RequestInit, retries = 3, baseDelay = 1000): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || attempt === retries) return res;\n // Don't retry client errors (4xx) except 429\n if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;\n } catch (err) {\n if (attempt === retries) throw err;\n }\n await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));\n }\n throw new Error(\"Max retries exceeded\");\n}\n\nexport async function submitReport(\n serverUrl: string,\n projectKey: string,\n payload: ReportPayload,\n screenshot?: File,\n): Promise<ReportResult> {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports`;\n\n let body: BodyInit;\n const headers: Record<string, string> = {\n \"x-project-key\": projectKey,\n };\n\n if (screenshot) {\n const form = new FormData();\n form.append(\"reporterId\", payload.reporterId);\n form.append(\"reporterName\", payload.reporterName);\n if (payload.reporterEmail) form.append(\"reporterEmail\", payload.reporterEmail);\n form.append(\"description\", payload.description);\n if (payload.expectedBehavior) form.append(\"expectedBehavior\", payload.expectedBehavior);\n if (payload.stepsToReproduce) form.append(\"stepsToReproduce\", JSON.stringify(payload.stepsToReproduce));\n if (payload.externalReplayUrl) form.append(\"externalReplayUrl\", payload.externalReplayUrl);\n if (payload.additionalContext) form.append(\"additionalContext\", payload.additionalContext);\n form.append(\"severity\", payload.severity);\n if (payload.url) form.append(\"url\", payload.url);\n if (payload.meta) form.append(\"meta\", JSON.stringify(payload.meta));\n form.append(\"screenshot\", screenshot);\n body = form;\n } else {\n body = JSON.stringify(payload);\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchWithRetry(url, { method: \"POST\", headers, body });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<ReportResult>;\n}\n\nexport async function submitReplay(\n serverUrl: string,\n projectKey: string,\n reportId: string,\n events: unknown[],\n): Promise<void> {\n const json = JSON.stringify(events);\n const encoded = new TextEncoder().encode(json);\n const compressed = gzipSync(encoded);\n\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports/${reportId}/replay`;\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"x-project-key\": projectKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: compressed.buffer as ArrayBuffer,\n });\n}\n","import type { ConsoleEntry } from \"@flint/types\";\n\nconst MAX_ENTRIES = 50;\n\nexport interface ConsoleCollector {\n start(): void;\n stop(): void;\n getEntries(): ConsoleEntry[];\n}\n\nexport function createConsoleCollector(): ConsoleCollector {\n const entries: ConsoleEntry[] = [];\n let active = false;\n\n const originals = {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n };\n\n let origOnerror: typeof window.onerror = null;\n let origUnhandled: typeof window.onunhandledrejection = null;\n\n function push(level: ConsoleEntry[\"level\"], args: unknown[]) {\n let str: string;\n try {\n str = args.map((a) => (typeof a === \"string\" ? a : JSON.stringify(a))).join(\" \");\n } catch {\n str = String(args[0]);\n }\n if (str.length > 500) str = str.slice(0, 500) + \"\\u2026\";\n entries.push({ level, args: str, timestamp: Date.now() });\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n console.log = (...args: unknown[]) => {\n push(\"log\", args);\n originals.log(...args);\n };\n console.warn = (...args: unknown[]) => {\n push(\"warn\", args);\n originals.warn(...args);\n };\n console.error = (...args: unknown[]) => {\n push(\"error\", args);\n originals.error(...args);\n };\n\n origOnerror = window.onerror;\n window.onerror = (msg, src, line, col, err) => {\n push(\"error\", [err?.message ?? String(msg), `${src}:${line}:${col}`]);\n if (typeof origOnerror === \"function\") return origOnerror(msg, src, line, col, err);\n return false;\n };\n\n origUnhandled = window.onunhandledrejection;\n window.onunhandledrejection = (event) => {\n const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n push(\"error\", [\"UnhandledRejection:\", reason]);\n if (typeof origUnhandled === \"function\") origUnhandled.call(window, event);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n console.log = originals.log;\n console.warn = originals.warn;\n console.error = originals.error;\n window.onerror = origOnerror;\n window.onunhandledrejection = origUnhandled;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { EnvironmentInfo } from \"@flint/types\";\n\nexport function collectEnvironment(): EnvironmentInfo {\n const ua = navigator.userAgent;\n\n // -- Browser --\n let browser = \"Unknown\";\n const chromeM = ua.match(/Chrome\\/(\\d+)/);\n const firefoxM = ua.match(/Firefox\\/(\\d+)/);\n const edgeM = ua.match(/Edg\\/(\\d+)/);\n const safariM = ua.match(/Version\\/(\\d+)/);\n const operaM = ua.match(/OPR\\/(\\d+)/);\n\n if (operaM) {\n browser = `Opera ${operaM[1]}`;\n } else if (edgeM) {\n browser = `Edge ${edgeM[1]}`;\n } else if (chromeM && !/Edg|OPR/.test(ua)) {\n browser = `Chrome ${chromeM[1]}`;\n } else if (firefoxM) {\n browser = `Firefox ${firefoxM[1]}`;\n } else if (safariM && /Safari\\//.test(ua)) {\n browser = `Safari ${safariM[1]}`;\n }\n\n // -- OS --\n let os = \"Unknown\";\n const macM = ua.match(/Mac OS X (\\d+[._]\\d+)/);\n const winM = ua.match(/Windows NT (\\d+\\.\\d+)/);\n const androidM = ua.match(/Android (\\d+)/);\n const iosM = ua.match(/iPhone OS (\\d+[._]\\d+)/);\n\n if (macM) {\n os = `macOS ${macM[1].replace(\"_\", \".\")}`;\n } else if (winM) {\n const winMap: Record<string, string> = {\n \"10.0\": \"10/11\",\n \"6.3\": \"8.1\",\n \"6.2\": \"8\",\n \"6.1\": \"7\",\n };\n os = `Windows ${winMap[winM[1]] ?? winM[1]}`;\n } else if (androidM) {\n os = `Android ${androidM[1]}`;\n } else if (iosM) {\n os = `iOS ${iosM[1].replace(/_/g, \".\")}`;\n } else if (/Linux/.test(ua)) {\n os = \"Linux\";\n }\n\n return {\n browser,\n os,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n screen: `${screen.width}x${screen.height}`,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n online: navigator.onLine,\n };\n}\n","import type { NetworkEntry } from \"@flint/types\";\n\nconst MAX_ENTRIES = 50;\n\nconst BLOCKED_HOSTS = new Set([\n \"browser-intake-datadoghq.com\",\n \"rum.browser-intake-datadoghq.com\",\n \"logs.browser-intake-datadoghq.com\",\n \"session-replay.browser-intake-datadoghq.com\",\n]);\n\nfunction isBlockedUrl(url: string, extra: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n const all = [...BLOCKED_HOSTS, ...extra];\n return all.some((b) => host === b || host.endsWith(\".\" + b));\n } catch {\n return false;\n }\n}\n\nexport interface NetworkCollector {\n start(): void;\n stop(): void;\n getEntries(): NetworkEntry[];\n}\n\nfunction truncateUrl(url: string): string {\n try {\n const u = new URL(url, location.href);\n const base = `${u.origin}${u.pathname}`;\n return base.length > 200 ? base.slice(0, 200) + \"\\u2026\" : base;\n } catch {\n return url.length > 200 ? url.slice(0, 200) + \"\\u2026\" : url;\n }\n}\n\nexport function createNetworkCollector(extraBlockedHosts: string[] = []): NetworkCollector {\n const entries: NetworkEntry[] = [];\n const blocked = new Set(extraBlockedHosts);\n let origFetch: typeof window.fetch | null = null;\n let origXHROpen: typeof XMLHttpRequest.prototype.open | null = null;\n let active = false;\n\n function push(entry: NetworkEntry) {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // -- Patch fetch --\n origFetch = window.fetch;\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const method = ((init?.method ?? \"GET\") as string).toUpperCase();\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n const startTime = Date.now();\n const res = await origFetch!.call(window, input, init);\n if (res.status >= 400 && !isBlockedUrl(url, blocked)) {\n push({\n method,\n url: truncateUrl(url),\n status: res.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n return res;\n };\n\n // -- Patch XHR --\n origXHROpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null,\n ) {\n const startTime = Date.now();\n const urlStr = typeof url === \"string\" ? url : (url as URL).href;\n\n this.addEventListener(\"load\", () => {\n if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {\n push({\n method: method.toUpperCase(),\n url: truncateUrl(urlStr),\n status: this.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n });\n\n return origXHROpen!.apply(this, [method, url, async ?? true, username, password] as Parameters<\n typeof XMLHttpRequest.prototype.open\n >);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (origFetch) window.fetch = origFetch;\n if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","export interface FrustrationEvent {\n type: \"rage_click\" | \"dead_click\" | \"error_loop\";\n timestamp: number;\n target: string;\n details: string;\n url: string;\n}\n\nexport interface FrustrationCollector {\n start(): void;\n stop(): void;\n getEvents(): FrustrationEvent[];\n onFrustration(callback: (event: FrustrationEvent) => void): () => void;\n}\n\nexport function createFrustrationCollector(opts?: {\n rageClickThreshold?: number;\n rageClickWindow?: number;\n errorLoopThreshold?: number;\n errorLoopWindow?: number;\n enableDeadClicks?: boolean;\n}): FrustrationCollector {\n const threshold = opts?.rageClickThreshold ?? 3;\n const window = opts?.rageClickWindow ?? 500;\n const errorThreshold = opts?.errorLoopThreshold ?? 3;\n const errorWindow = opts?.errorLoopWindow ?? 30_000;\n const deadClicksEnabled = opts?.enableDeadClicks ?? true;\n\n const events: FrustrationEvent[] = [];\n const listeners = new Set<(event: FrustrationEvent) => void>();\n const clickHistory: { target: EventTarget | null; time: number }[] = [];\n const errorCounts = new Map<string, { count: number; firstSeen: number }>();\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n let origConsoleError: typeof console.error | null = null;\n\n function emit(event: FrustrationEvent) {\n events.push(event);\n if (events.length > 50) events.shift();\n for (const cb of listeners) cb(event);\n }\n\n function getCSSSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = [...el.classList].slice(0, 3).join(\".\");\n if (cls) return `${tag}.${cls}`;\n return tag;\n }\n\n function isInteractive(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if ([\"a\", \"button\", \"input\", \"select\", \"textarea\", \"label\", \"summary\"].includes(tag)) return true;\n if (el.getAttribute(\"role\") === \"button\" || el.getAttribute(\"tabindex\")) return true;\n if (el.onclick || el.getAttribute(\"onclick\")) return true;\n if (el.closest(\"a, button, [role=button], [onclick]\")) return true;\n try { if (getComputedStyle(el).cursor === \"pointer\") return true; } catch {}\n return false;\n }\n\n function handleClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (!target) return;\n const now = Date.now();\n\n // --- Rage click detection ---\n clickHistory.push({ target, time: now });\n // Remove old clicks\n while (clickHistory.length > 0 && now - clickHistory[0].time > 1000) {\n clickHistory.shift();\n }\n const recentOnSame = clickHistory.filter(\n (c) => c.target === target && now - c.time < window\n );\n if (recentOnSame.length >= threshold) {\n emit({\n type: \"rage_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,\n url: globalThis.location?.href ?? \"\",\n });\n clickHistory.length = 0;\n }\n\n // --- Dead click detection ---\n if (deadClicksEnabled && !isInteractive(target)) {\n emit({\n type: \"dead_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,\n url: globalThis.location?.href ?? \"\",\n });\n }\n }\n\n function patchConsoleError() {\n origConsoleError = console.error;\n console.error = (...args: unknown[]) => {\n origConsoleError?.apply(console, args);\n // Check for error loop\n const key = String(args[0]).slice(0, 100);\n const now = Date.now();\n const existing = errorCounts.get(key);\n if (existing && now - existing.firstSeen < errorWindow) {\n existing.count++;\n if (existing.count >= errorThreshold) {\n emit({\n type: \"error_loop\",\n timestamp: now,\n target: \"console\",\n details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1000)}s: ${key}`,\n url: globalThis.location?.href ?? \"\",\n });\n errorCounts.delete(key);\n }\n } else {\n errorCounts.set(key, { count: 1, firstSeen: now });\n }\n };\n }\n\n return {\n start() {\n clickHandler = handleClick;\n document.addEventListener(\"click\", clickHandler, true);\n patchConsoleError();\n },\n stop() {\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n if (origConsoleError) console.error = origConsoleError;\n clickHistory.length = 0;\n errorCounts.clear();\n },\n getEvents() { return [...events]; },\n onFrustration(callback) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n };\n}\n","import type { FlintUser } from \"@flint/types\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\nlet state: FlintState = { user: undefined, sessionReplay: undefined };\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const l of listeners) l();\n}\n\nexport function subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getSnapshot(): FlintState {\n return state;\n}\n\nexport const flint = {\n setUser(user: FlintUser | null) {\n state = { ...state, user: user ?? undefined };\n emit();\n },\n setSessionReplay(url: string | (() => string) | null) {\n state = { ...state, sessionReplay: url ?? undefined };\n emit();\n },\n};\n","import type { Theme, ThemeOverride } from \"@flint/types\";\n\nexport interface ResolvedTheme {\n background: string;\n backgroundSecondary: string;\n accent: string;\n accentHover: string;\n text: string;\n textMuted: string;\n border: string;\n shadow: string;\n buttonText: string;\n backdropFilter: string;\n}\n\nconst light: ResolvedTheme = {\n background: \"rgba(255,255,255,0.90)\",\n backgroundSecondary: \"rgba(249,250,251,0.75)\",\n accent: \"#2563eb\",\n accentHover: \"#1d4ed8\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n border: \"rgba(255,255,255,0.9)\",\n 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)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.8)\",\n};\n\nconst dark: ResolvedTheme = {\n background: \"rgba(15,20,35,0.88)\",\n backgroundSecondary: \"rgba(5,8,18,0.65)\",\n accent: \"#4d8aff\",\n accentHover: \"#3b6fdb\",\n text: \"#dde3ef\",\n textMuted: \"#6b7a93\",\n border: \"rgba(255,255,255,0.08)\",\n shadow: \"0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.6)\",\n};\n\nexport function resolveTheme(theme: Theme): ResolvedTheme {\n if (theme === \"dark\") return dark;\n if (theme === \"light\") return light;\n // ThemeOverride — merge over light base\n const override = theme as ThemeOverride;\n return {\n ...light,\n background: override.background ?? light.background,\n accent: override.accent ?? light.accent,\n accentHover: override.accent ?? light.accentHover,\n text: override.text ?? light.text,\n border: override.border ?? light.border,\n };\n}\n"],"mappings":"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","names":["MAX_ENTRIES","window","listeners","emit"]}
1
+ {"version":3,"sources":["../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/frustration.ts","../src/collectors/network.ts","../src/store.ts","../src/theme.ts"],"sourcesContent":["import type { ReportPayload, ReportResult } from \"./types.js\";\nimport { gzipSync } from \"fflate\";\n\nasync function fetchWithRetry(url: string, init: RequestInit, retries = 3, baseDelay = 1000): Promise<Response> {\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n if (res.ok || attempt === retries) return res;\n // Don't retry client errors (4xx) except 429\n if (res.status >= 400 && res.status < 500 && res.status !== 429) return res;\n } catch (err) {\n if (attempt === retries) throw err;\n }\n await new Promise((r) => setTimeout(r, baseDelay * 2 ** attempt));\n }\n throw new Error(\"Max retries exceeded\");\n}\n\nexport async function submitReport(\n serverUrl: string,\n projectKey: string,\n payload: ReportPayload,\n screenshot?: File,\n): Promise<ReportResult> {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports`;\n\n let body: BodyInit;\n const headers: Record<string, string> = {\n \"x-project-key\": projectKey,\n };\n\n if (screenshot) {\n const form = new FormData();\n form.append(\"reporterId\", payload.reporterId);\n form.append(\"reporterName\", payload.reporterName);\n if (payload.reporterEmail) form.append(\"reporterEmail\", payload.reporterEmail);\n form.append(\"description\", payload.description);\n if (payload.expectedBehavior) form.append(\"expectedBehavior\", payload.expectedBehavior);\n if (payload.stepsToReproduce) form.append(\"stepsToReproduce\", JSON.stringify(payload.stepsToReproduce));\n if (payload.externalReplayUrl) form.append(\"externalReplayUrl\", payload.externalReplayUrl);\n if (payload.additionalContext) form.append(\"additionalContext\", payload.additionalContext);\n form.append(\"severity\", payload.severity);\n if (payload.url) form.append(\"url\", payload.url);\n if (payload.meta) form.append(\"meta\", JSON.stringify(payload.meta));\n form.append(\"screenshot\", screenshot);\n body = form;\n } else {\n body = JSON.stringify(payload);\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchWithRetry(url, { method: \"POST\", headers, body });\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: \"Unknown error\" }));\n throw new Error((err as { error?: string }).error ?? `HTTP ${res.status}`);\n }\n\n return res.json() as Promise<ReportResult>;\n}\n\nexport async function submitReplay(\n serverUrl: string,\n projectKey: string,\n reportId: string,\n events: unknown[],\n): Promise<void> {\n const json = JSON.stringify(events);\n const encoded = new TextEncoder().encode(json);\n const compressed = gzipSync(encoded);\n\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/bug-reports/${reportId}/replay`;\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"x-project-key\": projectKey,\n \"Content-Type\": \"application/octet-stream\",\n },\n body: compressed.buffer as ArrayBuffer,\n });\n}\n","import type { ConsoleEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\nexport interface ConsoleCollector {\n start(): void;\n stop(): void;\n getEntries(): ConsoleEntry[];\n}\n\nexport function createConsoleCollector(): ConsoleCollector {\n const entries: ConsoleEntry[] = [];\n let active = false;\n\n const originals = {\n log: console.log.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n };\n\n let origOnerror: typeof window.onerror = null;\n let origUnhandled: typeof window.onunhandledrejection = null;\n\n function push(level: ConsoleEntry[\"level\"], args: unknown[]) {\n let str: string;\n try {\n str = args.map((a) => (typeof a === \"string\" ? a : JSON.stringify(a))).join(\" \");\n } catch {\n str = String(args[0]);\n }\n if (str.length > 500) str = str.slice(0, 500) + \"\\u2026\";\n entries.push({ level, args: str, timestamp: Date.now() });\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n console.log = (...args: unknown[]) => {\n push(\"log\", args);\n originals.log(...args);\n };\n console.warn = (...args: unknown[]) => {\n push(\"warn\", args);\n originals.warn(...args);\n };\n console.error = (...args: unknown[]) => {\n push(\"error\", args);\n originals.error(...args);\n };\n\n origOnerror = window.onerror;\n window.onerror = (msg, src, line, col, err) => {\n push(\"error\", [err?.message ?? String(msg), `${src}:${line}:${col}`]);\n if (typeof origOnerror === \"function\") return origOnerror(msg, src, line, col, err);\n return false;\n };\n\n origUnhandled = window.onunhandledrejection;\n window.onunhandledrejection = (event) => {\n const reason = event.reason instanceof Error ? event.reason.message : JSON.stringify(event.reason);\n push(\"error\", [\"UnhandledRejection:\", reason]);\n if (typeof origUnhandled === \"function\") origUnhandled.call(window, event);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n console.log = originals.log;\n console.warn = originals.warn;\n console.error = originals.error;\n window.onerror = origOnerror;\n window.onunhandledrejection = origUnhandled;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { EnvironmentInfo } from \"../types.js\";\n\nexport function collectEnvironment(): EnvironmentInfo {\n const ua = navigator.userAgent;\n\n // -- Browser --\n let browser = \"Unknown\";\n const chromeM = ua.match(/Chrome\\/(\\d+)/);\n const firefoxM = ua.match(/Firefox\\/(\\d+)/);\n const edgeM = ua.match(/Edg\\/(\\d+)/);\n const safariM = ua.match(/Version\\/(\\d+)/);\n const operaM = ua.match(/OPR\\/(\\d+)/);\n\n if (operaM) {\n browser = `Opera ${operaM[1]}`;\n } else if (edgeM) {\n browser = `Edge ${edgeM[1]}`;\n } else if (chromeM && !/Edg|OPR/.test(ua)) {\n browser = `Chrome ${chromeM[1]}`;\n } else if (firefoxM) {\n browser = `Firefox ${firefoxM[1]}`;\n } else if (safariM && /Safari\\//.test(ua)) {\n browser = `Safari ${safariM[1]}`;\n }\n\n // -- OS --\n let os = \"Unknown\";\n const macM = ua.match(/Mac OS X (\\d+[._]\\d+)/);\n const winM = ua.match(/Windows NT (\\d+\\.\\d+)/);\n const androidM = ua.match(/Android (\\d+)/);\n const iosM = ua.match(/iPhone OS (\\d+[._]\\d+)/);\n\n if (macM) {\n os = `macOS ${macM[1].replace(\"_\", \".\")}`;\n } else if (winM) {\n const winMap: Record<string, string> = {\n \"10.0\": \"10/11\",\n \"6.3\": \"8.1\",\n \"6.2\": \"8\",\n \"6.1\": \"7\",\n };\n os = `Windows ${winMap[winM[1]] ?? winM[1]}`;\n } else if (androidM) {\n os = `Android ${androidM[1]}`;\n } else if (iosM) {\n os = `iOS ${iosM[1].replace(/_/g, \".\")}`;\n } else if (/Linux/.test(ua)) {\n os = \"Linux\";\n }\n\n return {\n browser,\n os,\n viewport: `${window.innerWidth}x${window.innerHeight}`,\n screen: `${screen.width}x${screen.height}`,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n online: navigator.onLine,\n };\n}\n","export interface FrustrationEvent {\n type: \"rage_click\" | \"dead_click\" | \"error_loop\";\n timestamp: number;\n target: string;\n details: string;\n url: string;\n}\n\nexport interface FrustrationCollector {\n start(): void;\n stop(): void;\n getEvents(): FrustrationEvent[];\n onFrustration(callback: (event: FrustrationEvent) => void): () => void;\n}\n\nexport function createFrustrationCollector(opts?: {\n rageClickThreshold?: number;\n rageClickWindow?: number;\n errorLoopThreshold?: number;\n errorLoopWindow?: number;\n enableDeadClicks?: boolean;\n}): FrustrationCollector {\n const threshold = opts?.rageClickThreshold ?? 3;\n const window = opts?.rageClickWindow ?? 500;\n const errorThreshold = opts?.errorLoopThreshold ?? 3;\n const errorWindow = opts?.errorLoopWindow ?? 30_000;\n const deadClicksEnabled = opts?.enableDeadClicks ?? true;\n\n const events: FrustrationEvent[] = [];\n const listeners = new Set<(event: FrustrationEvent) => void>();\n const clickHistory: { target: EventTarget | null; time: number }[] = [];\n const errorCounts = new Map<string, { count: number; firstSeen: number }>();\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n let origConsoleError: typeof console.error | null = null;\n\n function emit(event: FrustrationEvent) {\n events.push(event);\n if (events.length > 50) events.shift();\n for (const cb of listeners) cb(event);\n }\n\n function getCSSSelector(el: Element): string {\n if (el.id) return `#${el.id}`;\n const tag = el.tagName.toLowerCase();\n const cls = [...el.classList].slice(0, 3).join(\".\");\n if (cls) return `${tag}.${cls}`;\n return tag;\n }\n\n function isInteractive(el: HTMLElement): boolean {\n const tag = el.tagName.toLowerCase();\n if ([\"a\", \"button\", \"input\", \"select\", \"textarea\", \"label\", \"summary\"].includes(tag)) return true;\n if (el.getAttribute(\"role\") === \"button\" || el.getAttribute(\"tabindex\")) return true;\n if (el.onclick || el.getAttribute(\"onclick\")) return true;\n if (el.closest(\"a, button, [role=button], [onclick]\")) return true;\n try {\n if (getComputedStyle(el).cursor === \"pointer\") return true;\n } catch {\n /* getComputedStyle may throw */\n }\n return false;\n }\n\n function handleClick(e: MouseEvent) {\n const target = e.target as HTMLElement;\n if (!target) return;\n const now = Date.now();\n\n // --- Rage click detection ---\n clickHistory.push({ target, time: now });\n // Remove old clicks\n while (clickHistory.length > 0 && now - clickHistory[0].time > 1000) {\n clickHistory.shift();\n }\n const recentOnSame = clickHistory.filter((c) => c.target === target && now - c.time < window);\n if (recentOnSame.length >= threshold) {\n emit({\n type: \"rage_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `${recentOnSame.length} clicks in ${now - recentOnSame[0].time}ms`,\n url: globalThis.location?.href ?? \"\",\n });\n clickHistory.length = 0;\n }\n\n // --- Dead click detection ---\n if (deadClicksEnabled && !isInteractive(target)) {\n emit({\n type: \"dead_click\",\n timestamp: now,\n target: getCSSSelector(target),\n details: `Click on non-interactive <${target.tagName.toLowerCase()}>`,\n url: globalThis.location?.href ?? \"\",\n });\n }\n }\n\n function patchConsoleError() {\n origConsoleError = console.error;\n console.error = (...args: unknown[]) => {\n origConsoleError?.apply(console, args);\n // Check for error loop\n const key = String(args[0]).slice(0, 100);\n const now = Date.now();\n const existing = errorCounts.get(key);\n if (existing && now - existing.firstSeen < errorWindow) {\n existing.count++;\n if (existing.count >= errorThreshold) {\n emit({\n type: \"error_loop\",\n timestamp: now,\n target: \"console\",\n details: `Same error ${existing.count}x in ${Math.round((now - existing.firstSeen) / 1000)}s: ${key}`,\n url: globalThis.location?.href ?? \"\",\n });\n errorCounts.delete(key);\n }\n } else {\n errorCounts.set(key, { count: 1, firstSeen: now });\n }\n };\n }\n\n return {\n start() {\n clickHandler = handleClick;\n document.addEventListener(\"click\", clickHandler, true);\n patchConsoleError();\n },\n stop() {\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n if (origConsoleError) console.error = origConsoleError;\n clickHistory.length = 0;\n errorCounts.clear();\n },\n getEvents() {\n return [...events];\n },\n onFrustration(callback) {\n listeners.add(callback);\n return () => listeners.delete(callback);\n },\n };\n}\n","import type { NetworkEntry } from \"../types.js\";\n\nconst MAX_ENTRIES = 50;\n\nconst BLOCKED_HOSTS = new Set([\n \"browser-intake-datadoghq.com\",\n \"rum.browser-intake-datadoghq.com\",\n \"logs.browser-intake-datadoghq.com\",\n \"session-replay.browser-intake-datadoghq.com\",\n]);\n\nfunction isBlockedUrl(url: string, extra: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n const all = [...BLOCKED_HOSTS, ...extra];\n return all.some((b) => host === b || host.endsWith(\".\" + b));\n } catch {\n return false;\n }\n}\n\nexport interface NetworkCollector {\n start(): void;\n stop(): void;\n getEntries(): NetworkEntry[];\n}\n\nfunction truncateUrl(url: string): string {\n try {\n const u = new URL(url, location.href);\n const base = `${u.origin}${u.pathname}`;\n return base.length > 200 ? base.slice(0, 200) + \"\\u2026\" : base;\n } catch {\n return url.length > 200 ? url.slice(0, 200) + \"\\u2026\" : url;\n }\n}\n\nexport function createNetworkCollector(extraBlockedHosts: string[] = []): NetworkCollector {\n const entries: NetworkEntry[] = [];\n const blocked = new Set(extraBlockedHosts);\n let origFetch: typeof window.fetch | null = null;\n let origXHROpen: typeof XMLHttpRequest.prototype.open | null = null;\n let active = false;\n\n function push(entry: NetworkEntry) {\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // -- Patch fetch --\n origFetch = window.fetch;\n window.fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const method = ((init?.method ?? \"GET\") as string).toUpperCase();\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.href : (input as Request).url;\n const startTime = Date.now();\n const res = await origFetch!.call(window, input, init);\n if (res.status >= 400 && !isBlockedUrl(url, blocked)) {\n push({\n method,\n url: truncateUrl(url),\n status: res.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n return res;\n };\n\n // -- Patch XHR --\n origXHROpen = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (\n method: string,\n url: string | URL,\n async?: boolean,\n username?: string | null,\n password?: string | null,\n ) {\n const startTime = Date.now();\n const urlStr = typeof url === \"string\" ? url : (url as URL).href;\n\n this.addEventListener(\"load\", () => {\n if (this.status >= 400 && !isBlockedUrl(urlStr, blocked)) {\n push({\n method: method.toUpperCase(),\n url: truncateUrl(urlStr),\n status: this.status,\n duration: Date.now() - startTime,\n timestamp: startTime,\n });\n }\n });\n\n return origXHROpen!.apply(this, [method, url, async ?? true, username, password] as Parameters<\n typeof XMLHttpRequest.prototype.open\n >);\n };\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (origFetch) window.fetch = origFetch;\n if (origXHROpen) XMLHttpRequest.prototype.open = origXHROpen;\n },\n\n getEntries() {\n return [...entries];\n },\n };\n}\n","import type { FlintUser } from \"./types.js\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\nlet state: FlintState = { user: undefined, sessionReplay: undefined };\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const l of listeners) l();\n}\n\nexport function subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n\nexport function getSnapshot(): FlintState {\n return state;\n}\n\nexport const flint = {\n setUser(user: FlintUser | null) {\n state = { ...state, user: user ?? undefined };\n emit();\n },\n setSessionReplay(url: string | (() => string) | null) {\n state = { ...state, sessionReplay: url ?? undefined };\n emit();\n },\n};\n","import type { Theme, ThemeOverride } from \"./types.js\";\n\nexport interface ResolvedTheme {\n background: string;\n backgroundSecondary: string;\n accent: string;\n accentHover: string;\n text: string;\n textMuted: string;\n border: string;\n shadow: string;\n buttonText: string;\n backdropFilter: string;\n}\n\nconst light: ResolvedTheme = {\n background: \"rgba(255,255,255,0.90)\",\n backgroundSecondary: \"rgba(249,250,251,0.75)\",\n accent: \"#2563eb\",\n accentHover: \"#1d4ed8\",\n text: \"#111827\",\n textMuted: \"#6b7280\",\n border: \"rgba(255,255,255,0.9)\",\n 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)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.8)\",\n};\n\nconst dark: ResolvedTheme = {\n background: \"rgba(15,20,35,0.88)\",\n backgroundSecondary: \"rgba(5,8,18,0.65)\",\n accent: \"#4d8aff\",\n accentHover: \"#3b6fdb\",\n text: \"#dde3ef\",\n textMuted: \"#6b7a93\",\n border: \"rgba(255,255,255,0.08)\",\n shadow: \"0 24px 60px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.04)\",\n buttonText: \"#ffffff\",\n backdropFilter: \"blur(32px) saturate(1.6)\",\n};\n\nexport function resolveTheme(theme: Theme): ResolvedTheme {\n if (theme === \"dark\") return dark;\n if (theme === \"light\") return light;\n // ThemeOverride — merge over light base\n const override = theme as ThemeOverride;\n return {\n ...light,\n background: override.background ?? light.background,\n accent: override.accent ?? light.accent,\n accentHover: override.accent ?? light.accentHover,\n text: override.text ?? light.text,\n border: override.border ?? light.border,\n };\n}\n"],"mappings":"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","names":["window","listeners","emit","MAX_ENTRIES"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegotsi/flint-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -18,7 +18,6 @@
18
18
  "typecheck": "tsc --noEmit"
19
19
  },
20
20
  "dependencies": {
21
- "@flint/types": "workspace:*",
22
21
  "fflate": "^0.8.2"
23
22
  },
24
23
  "devDependencies": {