@diegotsi/flint-core 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-HVSD45YR.js +29 -0
- package/dist/chunk-HVSD45YR.js.map +1 -0
- package/dist/datadog-I3QKI6Q3.js +9 -0
- package/dist/datadog-I3QKI6Q3.js.map +1 -0
- package/dist/index.cjs +90 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -50
- package/dist/index.d.ts +48 -50
- package/dist/index.js +52 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/integrations/datadog.ts
|
|
2
|
+
var DATADOG_BLOCKED_HOSTS = [
|
|
3
|
+
"browser-intake-datadoghq.com",
|
|
4
|
+
"rum.browser-intake-datadoghq.com",
|
|
5
|
+
"logs.browser-intake-datadoghq.com",
|
|
6
|
+
"session-replay.browser-intake-datadoghq.com"
|
|
7
|
+
];
|
|
8
|
+
function createDatadogReplayProvider(site) {
|
|
9
|
+
return () => {
|
|
10
|
+
try {
|
|
11
|
+
const ddRum = window.DD_RUM;
|
|
12
|
+
const ctx = ddRum?.getInternalContext?.();
|
|
13
|
+
if (ctx?.session_id) {
|
|
14
|
+
const ts = Date.now();
|
|
15
|
+
const fromTs = ts - 3e4;
|
|
16
|
+
const toTs = ts + 5e3;
|
|
17
|
+
return `https://${site}/rum/replay/sessions/${ctx.session_id}?from_ts=${fromTs}&to_ts=${toTs}&tab=replay&live=false`;
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
return void 0;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
DATADOG_BLOCKED_HOSTS,
|
|
27
|
+
createDatadogReplayProvider
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=chunk-HVSD45YR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/integrations/datadog.ts"],"sourcesContent":["/**\n * Datadog RUM integration — opt-in helper.\n *\n * Usage:\n * import { Flint } from \"@diegotsi/flint-core\";\n * import { createDatadogReplayProvider, DATADOG_BLOCKED_HOSTS } from \"@diegotsi/flint-core\";\n *\n * Flint.init({\n * projectKey: \"...\",\n * serverUrl: \"...\",\n * externalReplayProvider: createDatadogReplayProvider(\"app.datadoghq.com\"),\n * blockedHosts: DATADOG_BLOCKED_HOSTS,\n * });\n */\n\n/** Datadog intake hosts to exclude from network capture. */\nexport const DATADOG_BLOCKED_HOSTS = [\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\n/**\n * Creates an `externalReplayProvider` that reads the current Datadog RUM\n * session and returns a deep link to the Session Replay viewer.\n */\nexport function createDatadogReplayProvider(site: string): () => string | undefined {\n return () => {\n try {\n const ddRum = (window as unknown as Record<string, unknown>).DD_RUM as\n | { getInternalContext?: () => { session_id?: string } | undefined }\n | undefined;\n const ctx = ddRum?.getInternalContext?.();\n if (ctx?.session_id) {\n const ts = Date.now();\n const fromTs = ts - 30_000;\n const toTs = ts + 5_000;\n return `https://${site}/rum/replay/sessions/${ctx.session_id}?from_ts=${fromTs}&to_ts=${toTs}&tab=replay&live=false`;\n }\n } catch {\n // DD_RUM not available — silently skip\n }\n return undefined;\n };\n}\n"],"mappings":";AAgBO,IAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAMO,SAAS,4BAA4B,MAAwC;AAClF,SAAO,MAAM;AACX,QAAI;AACF,YAAM,QAAS,OAA8C;AAG7D,YAAM,MAAM,OAAO,qBAAqB;AACxC,UAAI,KAAK,YAAY;AACnB,cAAM,KAAK,KAAK,IAAI;AACpB,cAAM,SAAS,KAAK;AACpB,cAAM,OAAO,KAAK;AAClB,eAAO,WAAW,IAAI,wBAAwB,IAAI,UAAU,YAAY,MAAM,UAAU,IAAI;AAAA,MAC9F;AAAA,IACF,QAAQ;AAAA,IAER;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,17 +20,53 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
22
|
|
|
23
|
+
// src/integrations/datadog.ts
|
|
24
|
+
var datadog_exports = {};
|
|
25
|
+
__export(datadog_exports, {
|
|
26
|
+
DATADOG_BLOCKED_HOSTS: () => DATADOG_BLOCKED_HOSTS,
|
|
27
|
+
createDatadogReplayProvider: () => createDatadogReplayProvider
|
|
28
|
+
});
|
|
29
|
+
function createDatadogReplayProvider(site) {
|
|
30
|
+
return () => {
|
|
31
|
+
try {
|
|
32
|
+
const ddRum = window.DD_RUM;
|
|
33
|
+
const ctx = ddRum?.getInternalContext?.();
|
|
34
|
+
if (ctx?.session_id) {
|
|
35
|
+
const ts = Date.now();
|
|
36
|
+
const fromTs = ts - 3e4;
|
|
37
|
+
const toTs = ts + 5e3;
|
|
38
|
+
return `https://${site}/rum/replay/sessions/${ctx.session_id}?from_ts=${fromTs}&to_ts=${toTs}&tab=replay&live=false`;
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
return void 0;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
var DATADOG_BLOCKED_HOSTS;
|
|
46
|
+
var init_datadog = __esm({
|
|
47
|
+
"src/integrations/datadog.ts"() {
|
|
48
|
+
"use strict";
|
|
49
|
+
DATADOG_BLOCKED_HOSTS = [
|
|
50
|
+
"browser-intake-datadoghq.com",
|
|
51
|
+
"rum.browser-intake-datadoghq.com",
|
|
52
|
+
"logs.browser-intake-datadoghq.com",
|
|
53
|
+
"session-replay.browser-intake-datadoghq.com"
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
20
58
|
// src/index.ts
|
|
21
59
|
var index_exports = {};
|
|
22
60
|
__export(index_exports, {
|
|
61
|
+
DATADOG_BLOCKED_HOSTS: () => DATADOG_BLOCKED_HOSTS,
|
|
23
62
|
Flint: () => Flint,
|
|
24
63
|
_setFormErrorCollector: () => _setFormErrorCollector,
|
|
25
64
|
collectEnvironment: () => collectEnvironment,
|
|
26
65
|
createConsoleCollector: () => createConsoleCollector,
|
|
66
|
+
createDatadogReplayProvider: () => createDatadogReplayProvider,
|
|
27
67
|
createFormErrorCollector: () => createFormErrorCollector,
|
|
28
68
|
createFrustrationCollector: () => createFrustrationCollector,
|
|
29
69
|
createNetworkCollector: () => createNetworkCollector,
|
|
30
|
-
flint: () => flint,
|
|
31
70
|
getSnapshot: () => getSnapshot,
|
|
32
71
|
resolveTheme: () => resolveTheme,
|
|
33
72
|
submitReplay: () => submitReplay,
|
|
@@ -581,17 +620,10 @@ function createFrustrationCollector(opts) {
|
|
|
581
620
|
|
|
582
621
|
// src/collectors/network.ts
|
|
583
622
|
var MAX_ENTRIES3 = 50;
|
|
584
|
-
|
|
585
|
-
"browser-intake-datadoghq.com",
|
|
586
|
-
"rum.browser-intake-datadoghq.com",
|
|
587
|
-
"logs.browser-intake-datadoghq.com",
|
|
588
|
-
"session-replay.browser-intake-datadoghq.com"
|
|
589
|
-
]);
|
|
590
|
-
function isBlockedUrl(url, extra) {
|
|
623
|
+
function isBlockedUrl(url, blocked) {
|
|
591
624
|
try {
|
|
592
625
|
const host = new URL(url, location.href).hostname;
|
|
593
|
-
|
|
594
|
-
return all.some((b) => host === b || host.endsWith("." + b));
|
|
626
|
+
return [...blocked].some((b) => host === b || host.endsWith("." + b));
|
|
595
627
|
} catch {
|
|
596
628
|
return false;
|
|
597
629
|
}
|
|
@@ -666,6 +698,9 @@ function createNetworkCollector(extraBlockedHosts = []) {
|
|
|
666
698
|
};
|
|
667
699
|
}
|
|
668
700
|
|
|
701
|
+
// src/index.ts
|
|
702
|
+
init_datadog();
|
|
703
|
+
|
|
669
704
|
// src/store.ts
|
|
670
705
|
var formErrorCollectorRef = null;
|
|
671
706
|
function _setFormErrorCollector(collector) {
|
|
@@ -729,7 +764,8 @@ function init(config) {
|
|
|
729
764
|
blockedHosts = [],
|
|
730
765
|
frustration: frustrationOpts,
|
|
731
766
|
onFrustration,
|
|
732
|
-
_replayRecorder
|
|
767
|
+
_replayRecorder,
|
|
768
|
+
_collectors
|
|
733
769
|
} = config;
|
|
734
770
|
debugLog(config, "Initializing", {
|
|
735
771
|
serverUrl: config.serverUrl,
|
|
@@ -746,9 +782,10 @@ function init(config) {
|
|
|
746
782
|
return "";
|
|
747
783
|
}
|
|
748
784
|
})();
|
|
749
|
-
const
|
|
785
|
+
const allBlockedHosts = [...blockedHosts, ...flintHost ? [flintHost] : []];
|
|
786
|
+
const consoleCol = enableConsole ? _collectors?.console?.() ?? createConsoleCollector() : null;
|
|
750
787
|
consoleCol?.start();
|
|
751
|
-
const networkCol = enableNetwork ?
|
|
788
|
+
const networkCol = enableNetwork ? _collectors?.network?.(allBlockedHosts) ?? createNetworkCollector(allBlockedHosts) : null;
|
|
752
789
|
networkCol?.start();
|
|
753
790
|
const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;
|
|
754
791
|
if (formErrorsCol) {
|
|
@@ -789,6 +826,7 @@ function init(config) {
|
|
|
789
826
|
if (instance) instance.stopReplay = stopReplay;
|
|
790
827
|
});
|
|
791
828
|
}
|
|
829
|
+
const getEnvironment = _collectors?.environment ?? collectEnvironment;
|
|
792
830
|
if (frustrationCol && autoReportFrustration) {
|
|
793
831
|
frustrationCol.onFrustration(async (event) => {
|
|
794
832
|
debugLog(config, "Frustration detected, auto-reporting:", event.type, event.details);
|
|
@@ -804,7 +842,7 @@ function init(config) {
|
|
|
804
842
|
source: "auto_capture",
|
|
805
843
|
meta: {
|
|
806
844
|
...config.meta,
|
|
807
|
-
environment:
|
|
845
|
+
environment: getEnvironment(),
|
|
808
846
|
consoleLogs: consoleCol?.getEntries() ?? [],
|
|
809
847
|
networkErrors: networkCol?.getEntries() ?? [],
|
|
810
848
|
formErrors: formErrorsCol?.getEntries() ?? [],
|
|
@@ -816,6 +854,33 @@ function init(config) {
|
|
|
816
854
|
} else if (frustrationCol && onFrustration) {
|
|
817
855
|
frustrationCol.onFrustration(onFrustration);
|
|
818
856
|
}
|
|
857
|
+
if (!config.externalReplayProvider) {
|
|
858
|
+
fetchProjectConfig(config.serverUrl, config.projectKey).then((remote) => {
|
|
859
|
+
if (!instance || !remote?.integrations) return;
|
|
860
|
+
const dd = remote.integrations.datadog;
|
|
861
|
+
if (dd?.enabled && dd.site) {
|
|
862
|
+
Promise.resolve().then(() => (init_datadog(), datadog_exports)).then(({ createDatadogReplayProvider: createDatadogReplayProvider2 }) => {
|
|
863
|
+
if (!instance) return;
|
|
864
|
+
instance.config = {
|
|
865
|
+
...instance.config,
|
|
866
|
+
externalReplayProvider: createDatadogReplayProvider2(dd.site)
|
|
867
|
+
};
|
|
868
|
+
debugLog(config, "Remote Datadog integration loaded:", dd.site);
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}).catch(() => {
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
async function fetchProjectConfig(serverUrl, projectKey) {
|
|
876
|
+
try {
|
|
877
|
+
const url = `${serverUrl.replace(/\/$/, "")}/api/v1/project-config`;
|
|
878
|
+
const res = await fetch(url, { headers: { "x-project-key": projectKey } });
|
|
879
|
+
if (!res.ok) return null;
|
|
880
|
+
return res.json();
|
|
881
|
+
} catch {
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
819
884
|
}
|
|
820
885
|
function shutdown() {
|
|
821
886
|
if (!instance) return;
|
|
@@ -834,9 +899,10 @@ function getInstance() {
|
|
|
834
899
|
return instance;
|
|
835
900
|
}
|
|
836
901
|
function getMeta(extraMeta) {
|
|
902
|
+
const getEnvironment = instance?.config._collectors?.environment ?? collectEnvironment;
|
|
837
903
|
return {
|
|
838
904
|
...extraMeta,
|
|
839
|
-
environment:
|
|
905
|
+
environment: getEnvironment(),
|
|
840
906
|
consoleLogs: instance?.console?.getEntries() ?? [],
|
|
841
907
|
networkErrors: instance?.network?.getEntries() ?? [],
|
|
842
908
|
formErrors: instance?.formErrors?.getEntries() ?? []
|
|
@@ -848,6 +914,12 @@ function getReplayEvents() {
|
|
|
848
914
|
function getConfig() {
|
|
849
915
|
return instance?.config ?? null;
|
|
850
916
|
}
|
|
917
|
+
function getExternalReplayUrl() {
|
|
918
|
+
const storeReplay = getSnapshot().sessionReplay;
|
|
919
|
+
const fromStore = typeof storeReplay === "function" ? storeReplay() : storeReplay;
|
|
920
|
+
if (fromStore) return fromStore;
|
|
921
|
+
return instance?.config.externalReplayProvider?.();
|
|
922
|
+
}
|
|
851
923
|
var Flint = {
|
|
852
924
|
init,
|
|
853
925
|
shutdown,
|
|
@@ -856,6 +928,7 @@ var Flint = {
|
|
|
856
928
|
getMeta,
|
|
857
929
|
getReplayEvents,
|
|
858
930
|
getConfig,
|
|
931
|
+
getExternalReplayUrl,
|
|
859
932
|
setUser: flint.setUser,
|
|
860
933
|
setSessionReplay: flint.setSessionReplay,
|
|
861
934
|
reportFormError: flint.reportFormError
|
|
@@ -901,14 +974,15 @@ function resolveTheme(theme) {
|
|
|
901
974
|
}
|
|
902
975
|
// Annotate the CommonJS export names for ESM import in node:
|
|
903
976
|
0 && (module.exports = {
|
|
977
|
+
DATADOG_BLOCKED_HOSTS,
|
|
904
978
|
Flint,
|
|
905
979
|
_setFormErrorCollector,
|
|
906
980
|
collectEnvironment,
|
|
907
981
|
createConsoleCollector,
|
|
982
|
+
createDatadogReplayProvider,
|
|
908
983
|
createFormErrorCollector,
|
|
909
984
|
createFrustrationCollector,
|
|
910
985
|
createNetworkCollector,
|
|
911
|
-
flint,
|
|
912
986
|
getSnapshot,
|
|
913
987
|
resolveTheme,
|
|
914
988
|
submitReplay,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/formErrors.ts","../src/collectors/frustration.ts","../src/collectors/network.ts","../src/store.ts","../src/singleton.ts","../src/theme.ts"],"sourcesContent":["// Collectors\n\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 { FormErrorCollector } from \"./collectors/formErrors.js\";\nexport { createFormErrorCollector } from \"./collectors/formErrors.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\";\n// Singleton\nexport { Flint } from \"./singleton.js\";\nexport type { FlintState } from \"./store.js\";\n// State\nexport { _setFormErrorCollector, flint, getSnapshot, subscribe } from \"./store.js\";\nexport type { ResolvedTheme } from \"./theme.js\";\n// Theme\nexport { resolveTheme } from \"./theme.js\";\n// Types (re-export)\nexport type {\n CollectedMeta,\n ConsoleEntry,\n EnvironmentInfo,\n FlintConfig,\n FlintExtraFields,\n FlintUser,\n FlintWidgetProps,\n FormErrorEntry,\n Locale,\n NetworkEntry,\n ReportPayload,\n ReportResult,\n Severity,\n Theme,\n ThemeOverride,\n} from \"./types.js\";\n","import { gzipSync } from \"fflate\";\nimport type { ReportPayload, ReportResult } from \"./types.js\";\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\n// Redact values that look like tokens, passwords, secrets, or API keys\nconst SENSITIVE_PATTERNS = [\n /(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|bearer)\\s*[:=]\\s*[\"']?[^\\s\"',]{4,}/gi,\n /\\b(sk-[a-zA-Z0-9_-]{20,})\\b/g, // API keys (e.g. sk-ant-...)\n /\\b(ghp_[a-zA-Z0-9]{36,})\\b/g, // GitHub tokens\n /\\b(xoxb-[a-zA-Z0-9-]+)\\b/g, // Slack tokens\n /\\b(eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,})\\b/g, // JWTs\n];\n\nfunction sanitize(str: string): string {\n let result = str;\n for (const pattern of SENSITIVE_PATTERNS) {\n result = result.replace(pattern, \"[REDACTED]\");\n }\n return result;\n}\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 str = sanitize(str);\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","import type { FormErrorEntry, FormErrorField } from \"../types.js\";\n\nconst MAX_ENTRIES = 30;\n\n/**\n * Time to wait after a submit event before checking the DOM for error\n * indicators. RHF/zod validate asynchronously — 300ms covers most cases\n * while keeping the detection snappy.\n */\nconst POST_SUBMIT_CHECK_MS = 300;\n\n/**\n * After a submit button click, if no submit event fires within this window\n * it means the form library swallowed the submit entirely (e.g. RHF's\n * handleSubmit called preventDefault before the native event propagated,\n * or the button isn't even inside a <form>). We treat this as a potential\n * silent failure and scan the page for error indicators.\n */\nconst SILENT_SUBMIT_WINDOW_MS = 400;\n\nexport interface FormErrorCollector {\n start(): void;\n stop(): void;\n getEntries(): FormErrorEntry[];\n /**\n * Manually report a form validation failure. Call this from your form\n * library's error handler to capture exact field-level errors.\n *\n * @example\n * // React Hook Form + Zod\n * <form onSubmit={handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * })}>\n */\n report(formId: string, errors: Record<string, { message?: string } | undefined>): void;\n}\n\n// ── DOM helpers ────────────────────────────────────────────────────────────────\n\nfunction getFormId(form: HTMLFormElement): string {\n if (form.id) return `#${form.id}`;\n if (form.getAttribute(\"name\")) return `form[name=\"${form.getAttribute(\"name\")}\"]`;\n if (form.action && form.action !== location.href) return form.action;\n return `form:${Array.from(document.forms).indexOf(form)}`;\n}\n\nfunction getFieldLabel(field: HTMLElement, root: HTMLElement): string {\n const ariaLabel = field.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n const id = field.id;\n if (id) {\n const label = root.querySelector<HTMLLabelElement>(`label[for=\"${id}\"]`);\n if (label?.textContent) return label.textContent.trim();\n }\n\n const parentLabel = field.closest(\"label\");\n if (parentLabel?.textContent) {\n const text = parentLabel.textContent.trim();\n if (text.length < 60) return text;\n }\n\n return (field as HTMLInputElement).name || (field as HTMLInputElement).placeholder || field.tagName.toLowerCase();\n}\n\nfunction getErrorMessage(field: HTMLElement): string | undefined {\n // aria-errormessage\n const errId = field.getAttribute(\"aria-errormessage\");\n if (errId) {\n const el = document.getElementById(errId);\n if (el?.textContent) return el.textContent.trim();\n }\n\n // aria-describedby (RHF wires error messages here)\n const descId = field.getAttribute(\"aria-describedby\");\n if (descId) {\n for (const id of descId.split(/\\s+/)) {\n const el = document.getElementById(id);\n if (el?.textContent?.trim()) return el.textContent.trim();\n }\n }\n\n // native validationMessage\n if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {\n if (field.validationMessage) return field.validationMessage;\n }\n\n // adjacent role=\"alert\"\n const next = field.nextElementSibling;\n if (next?.getAttribute(\"role\") === \"alert\" && next.textContent) {\n return next.textContent.trim();\n }\n\n return undefined;\n}\n\n/**\n * Collect fields with `aria-invalid=\"true\"` or native `:invalid` inside a\n * root element (a <form> or the whole document).\n */\nfunction collectInvalidFields(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const invalidEls = root.querySelectorAll<HTMLElement>('[aria-invalid=\"true\"], :invalid');\n\n for (const el of invalidEls) {\n if (el instanceof HTMLFormElement) continue;\n if (el.tagName === \"FIELDSET\") continue;\n\n fields.push({\n name: getFieldLabel(el, root),\n message: getErrorMessage(el),\n });\n }\n\n return fields;\n}\n\n/**\n * Scan the page for visible error indicators that appeared after a submit\n * attempt. This catches errors even when:\n * - The errored field is in another tab/step of a multi-step form\n * - The form doesn't use aria-invalid at all\n * - Errors are rendered as standalone alert divs\n */\nfunction collectVisibleErrors(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const seen = new Set<string>();\n\n // 1. role=\"alert\" elements that appeared (error toasts, inline messages)\n const alerts = root.querySelectorAll<HTMLElement>('[role=\"alert\"]');\n for (const el of alerts) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"alert\", message: text });\n }\n\n // 2. Common error class patterns (data-error, .error, .field-error, etc.)\n const errorEls = root.querySelectorAll<HTMLElement>(\n \"[data-error], [data-field-error], .error-message, .field-error, .form-error\",\n );\n for (const el of errorEls) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"error\", message: text });\n }\n\n return fields;\n}\n\nfunction isSubmitButton(el: HTMLElement): el is HTMLButtonElement {\n if (el.tagName === \"BUTTON\") {\n const type = el.getAttribute(\"type\");\n // Buttons default to type=\"submit\" inside forms when no type is set\n return type === \"submit\" || type === null || type === \"\";\n }\n if (el.tagName === \"INPUT\") {\n return (el as HTMLInputElement).type === \"submit\";\n }\n return false;\n}\n\nfunction findClosestForm(el: HTMLElement): HTMLFormElement | null {\n // Direct form association\n if (\"form\" in el && (el as HTMLButtonElement).form) {\n return (el as HTMLButtonElement).form;\n }\n return el.closest(\"form\");\n}\n\n// ── Collector ──────────────────────────────────────────────────────────────────\n\nexport function createFormErrorCollector(): FormErrorCollector {\n const entries: FormErrorEntry[] = [];\n const attemptCounts = new Map<string, number>();\n let active = false;\n\n // Handlers we need to clean up\n let submitHandler: ((e: Event) => void) | null = null;\n let invalidHandler: ((e: Event) => void) | null = null;\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n\n // Track whether a real submit event followed a button click\n let lastSubmitTime = 0;\n\n // Dedup: avoid recording the same form twice within a short window\n const recentRecords = new Map<string, number>();\n const DEDUP_MS = 500;\n\n function push(entry: FormErrorEntry) {\n // Dedup check\n const _key = `${entry.formId}:${entry.timestamp}`;\n const last = recentRecords.get(entry.formId);\n if (last && entry.timestamp - last < DEDUP_MS) return;\n recentRecords.set(entry.formId, entry.timestamp);\n\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n function recordFormErrors(form: HTMLFormElement, type: FormErrorEntry[\"type\"]) {\n const formId = getFormId(form);\n\n // Strategy 1: aria-invalid / :invalid fields\n let fields = collectInvalidFields(form);\n\n // Strategy 2: if no aria-invalid fields found, look for visible error messages\n if (fields.length === 0) {\n fields = collectVisibleErrors(form);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type,\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Handle clicks on submit buttons. If no submit event follows within\n * SILENT_SUBMIT_WINDOW_MS, it means the form library blocked the submit\n * entirely — scan for errors.\n */\n function handleSubmitButtonClick(button: HTMLElement) {\n const form = findClosestForm(button);\n\n setTimeout(() => {\n if (!active) return;\n // A real submit event fired — the submitHandler already took care of it\n if (Date.now() - lastSubmitTime < SILENT_SUBMIT_WINDOW_MS) return;\n\n // No submit event → silent failure\n const root = form ?? (document.body as HTMLElement);\n const formId = form ? getFormId(form) : `page:${location.pathname}`;\n\n let fields = form ? collectInvalidFields(form) : [];\n if (fields.length === 0) {\n fields = collectVisibleErrors(root);\n }\n\n // Also scan full page — the error might be in a toast or outside the form\n if (fields.length === 0 && form) {\n fields = collectVisibleErrors(document.body);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"silent_submit\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }, SILENT_SUBMIT_WINDOW_MS);\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // 1. Capture submit events (fires even with e.preventDefault())\n submitHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const form = e.target as HTMLFormElement;\n if (!(form instanceof HTMLFormElement)) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"submit\", submitHandler, true);\n\n // 2. Capture native invalid events (HTML5 constraint validation)\n invalidHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const field = e.target as HTMLElement;\n const form = field.closest(\"form\");\n if (!form) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"invalid\", invalidHandler, true);\n\n // 3. Capture clicks on submit buttons — catches silent failures\n // where the form lib eats the submit and shows nothing\n clickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n if (!target) return;\n\n // Walk up to find the actual button (click might be on an icon/span inside)\n const button = target.closest(\"button, input[type=submit]\") as HTMLElement | null;\n if (!button) return;\n if (!isSubmitButton(button)) return;\n\n handleSubmitButtonClick(button);\n };\n document.addEventListener(\"click\", clickHandler, true);\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (submitHandler) document.removeEventListener(\"submit\", submitHandler, true);\n if (invalidHandler) document.removeEventListener(\"invalid\", invalidHandler, true);\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n attemptCounts.clear();\n recentRecords.clear();\n },\n\n getEntries() {\n return [...entries];\n },\n\n report(formId: string, errors: Record<string, { message?: string } | undefined>) {\n const fields: FormErrorField[] = [];\n for (const [name, err] of Object.entries(errors)) {\n if (!err) continue;\n fields.push({ name, message: err.message });\n }\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"validation_failed\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\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 {\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 (!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 (!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 { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FlintUser } from \"./types.js\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\n// Holds a reference to the active form error collector so flint.reportFormError\n// can forward calls without the consumer needing to manage the collector.\nlet formErrorCollectorRef: FormErrorCollector | null = null;\n\nexport function _setFormErrorCollector(collector: FormErrorCollector | null) {\n formErrorCollectorRef = collector;\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 /**\n * Report a form validation failure with exact field-level errors.\n * Call this from your form library's error callback.\n *\n * @example\n * // React Hook Form + Zod\n * const onSubmit = handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * });\n */\n reportFormError(formId: string, errors: Record<string, { message?: string } | undefined>) {\n formErrorCollectorRef?.report(formId, errors);\n },\n};\n","// Flint singleton — manages global collector lifecycle\nimport { submitReport } from \"./api.js\";\nimport type { ConsoleCollector } from \"./collectors/console.js\";\nimport { createConsoleCollector } from \"./collectors/console.js\";\nimport { collectEnvironment } from \"./collectors/environment.js\";\nimport type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport { createFormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FrustrationCollector } from \"./collectors/frustration.js\";\nimport { createFrustrationCollector } from \"./collectors/frustration.js\";\nimport type { NetworkCollector } from \"./collectors/network.js\";\nimport { createNetworkCollector } from \"./collectors/network.js\";\nimport { _setFormErrorCollector, flint, getSnapshot } from \"./store.js\";\nimport type { CollectedMeta, FlintConfig } from \"./types.js\";\n\nconst DEFAULT_REPLAY_BUFFER_MS = 60_000;\n\ninterface FlintInstance {\n config: Readonly<FlintConfig>;\n console: ConsoleCollector | null;\n network: NetworkCollector | null;\n formErrors: FormErrorCollector | null;\n frustration: FrustrationCollector | null;\n replayEvents: unknown[];\n stopReplay: (() => void) | null;\n}\n\nlet instance: FlintInstance | null = null;\n\nfunction debugLog(config: FlintConfig, ...args: unknown[]) {\n if (config.debug) console.log(\"[Flint]\", ...args);\n}\n\nfunction init(config: FlintConfig): void {\n if (instance) {\n console.warn(\"[Flint] Already initialized. Call Flint.shutdown() first to re-initialize.\");\n return;\n }\n\n const {\n enableConsole = true,\n enableNetwork = true,\n enableFormErrors = true,\n enableFrustration = false,\n autoReportFrustration = false,\n enableReplay = false,\n replayBufferMs = DEFAULT_REPLAY_BUFFER_MS,\n blockedHosts = [],\n frustration: frustrationOpts,\n onFrustration,\n _replayRecorder,\n } = config;\n\n debugLog(config, \"Initializing\", {\n serverUrl: config.serverUrl,\n enableConsole,\n enableNetwork,\n enableFormErrors,\n enableFrustration,\n enableReplay,\n });\n\n // Determine Flint server host to block from network capture\n const flintHost = (() => {\n try {\n return new URL(config.serverUrl).hostname;\n } catch {\n return \"\";\n }\n })();\n\n // Create and start collectors\n const consoleCol = enableConsole ? createConsoleCollector() : null;\n consoleCol?.start();\n\n const networkCol = enableNetwork\n ? createNetworkCollector([...blockedHosts, ...(flintHost ? [flintHost] : [])])\n : null;\n networkCol?.start();\n\n const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;\n if (formErrorsCol) {\n formErrorsCol.start();\n _setFormErrorCollector(formErrorsCol);\n }\n\n const frustrationCol = enableFrustration ? createFrustrationCollector(frustrationOpts) : null;\n frustrationCol?.start();\n\n // Set user if provided\n if (config.user) {\n flint.setUser(config.user);\n }\n\n const replayEvents: unknown[] = [];\n let stopReplay: (() => void) | null = null;\n\n debugLog(config, \"Collectors started\", {\n console: !!consoleCol,\n network: !!networkCol,\n formErrors: !!formErrorsCol,\n frustration: !!frustrationCol,\n });\n\n instance = {\n config,\n console: consoleCol,\n network: networkCol,\n formErrors: formErrorsCol,\n frustration: frustrationCol,\n replayEvents,\n stopReplay: null,\n };\n\n // Start replay recording (async, via injected recorder)\n if (enableReplay && _replayRecorder) {\n _replayRecorder((event: unknown) => {\n replayEvents.push(event);\n const cutoff = Date.now() - replayBufferMs;\n while (replayEvents.length > 0 && (replayEvents[0] as { timestamp: number }).timestamp < cutoff) {\n replayEvents.shift();\n }\n }).then((stop) => {\n stopReplay = stop ?? null;\n if (instance) instance.stopReplay = stopReplay;\n });\n }\n\n // Wire frustration auto-report\n if (frustrationCol && autoReportFrustration) {\n frustrationCol.onFrustration(async (event) => {\n debugLog(config, \"Frustration detected, auto-reporting:\", event.type, event.details);\n onFrustration?.(event);\n const user = getSnapshot().user ?? config.user;\n await submitReport(config.serverUrl, config.projectKey, {\n reporterId: user?.id ?? \"anonymous\",\n reporterName: user?.name ?? \"Anonymous\",\n reporterEmail: user?.email,\n description: `[Auto-detected] ${event.type.replace(/_/g, \" \")}: ${event.details}`,\n severity: event.type === \"error_loop\" ? \"P1\" : event.type === \"rage_click\" ? \"P2\" : \"P3\",\n url: event.url,\n source: \"auto_capture\",\n meta: {\n ...config.meta,\n environment: collectEnvironment(),\n consoleLogs: consoleCol?.getEntries() ?? [],\n networkErrors: networkCol?.getEntries() ?? [],\n formErrors: formErrorsCol?.getEntries() ?? [],\n frustrationEvent: event,\n },\n }).catch(() => {});\n });\n } else if (frustrationCol && onFrustration) {\n frustrationCol.onFrustration(onFrustration);\n }\n}\n\nfunction shutdown(): void {\n if (!instance) return;\n\n instance.console?.stop();\n instance.network?.stop();\n instance.formErrors?.stop();\n _setFormErrorCollector(null);\n instance.frustration?.stop();\n instance.stopReplay?.();\n instance = null;\n}\n\nfunction isInitialized(): boolean {\n return instance !== null;\n}\n\nfunction getInstance(): FlintInstance | null {\n return instance;\n}\n\nfunction getMeta(extraMeta?: Record<string, unknown>): CollectedMeta {\n return {\n ...extraMeta,\n environment: collectEnvironment(),\n consoleLogs: instance?.console?.getEntries() ?? [],\n networkErrors: instance?.network?.getEntries() ?? [],\n formErrors: instance?.formErrors?.getEntries() ?? [],\n };\n}\n\nfunction getReplayEvents(): unknown[] {\n return instance ? [...instance.replayEvents] : [];\n}\n\nfunction getConfig(): Readonly<FlintConfig> | null {\n return instance?.config ?? null;\n}\n\nexport const Flint = {\n init,\n shutdown,\n isInitialized,\n getInstance,\n getMeta,\n getReplayEvents,\n getConfig,\n setUser: flint.setUser,\n setSessionReplay: flint.setSessionReplay,\n reportFormError: flint.reportFormError,\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":["init","MAX_ENTRIES","window","listeners","emit","MAX_ENTRIES","init"]}
|
|
1
|
+
{"version":3,"sources":["../src/integrations/datadog.ts","../src/index.ts","../src/api.ts","../src/collectors/console.ts","../src/collectors/environment.ts","../src/collectors/formErrors.ts","../src/collectors/frustration.ts","../src/collectors/network.ts","../src/store.ts","../src/singleton.ts","../src/theme.ts"],"sourcesContent":["/**\n * Datadog RUM integration — opt-in helper.\n *\n * Usage:\n * import { Flint } from \"@diegotsi/flint-core\";\n * import { createDatadogReplayProvider, DATADOG_BLOCKED_HOSTS } from \"@diegotsi/flint-core\";\n *\n * Flint.init({\n * projectKey: \"...\",\n * serverUrl: \"...\",\n * externalReplayProvider: createDatadogReplayProvider(\"app.datadoghq.com\"),\n * blockedHosts: DATADOG_BLOCKED_HOSTS,\n * });\n */\n\n/** Datadog intake hosts to exclude from network capture. */\nexport const DATADOG_BLOCKED_HOSTS = [\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\n/**\n * Creates an `externalReplayProvider` that reads the current Datadog RUM\n * session and returns a deep link to the Session Replay viewer.\n */\nexport function createDatadogReplayProvider(site: string): () => string | undefined {\n return () => {\n try {\n const ddRum = (window as unknown as Record<string, unknown>).DD_RUM as\n | { getInternalContext?: () => { session_id?: string } | undefined }\n | undefined;\n const ctx = ddRum?.getInternalContext?.();\n if (ctx?.session_id) {\n const ts = Date.now();\n const fromTs = ts - 30_000;\n const toTs = ts + 5_000;\n return `https://${site}/rum/replay/sessions/${ctx.session_id}?from_ts=${fromTs}&to_ts=${toTs}&tab=replay&live=false`;\n }\n } catch {\n // DD_RUM not available — silently skip\n }\n return undefined;\n };\n}\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 { FormErrorCollector } from \"./collectors/formErrors.js\";\nexport { createFormErrorCollector } from \"./collectors/formErrors.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\";\n// Integrations\nexport { createDatadogReplayProvider, DATADOG_BLOCKED_HOSTS } from \"./integrations/datadog.js\";\n// Singleton\nexport { Flint } from \"./singleton.js\";\nexport type { FlintState } from \"./store.js\";\n// State (flint lowercase removed from public API — use Flint.setUser() etc.)\nexport { _setFormErrorCollector, getSnapshot, subscribe } from \"./store.js\";\nexport type { ResolvedTheme } from \"./theme.js\";\n// Theme\nexport { resolveTheme } from \"./theme.js\";\n// Types (re-export)\nexport type {\n CollectedMeta,\n ConsoleEntry,\n EnvironmentInfo,\n FlintConfig,\n FlintUser,\n FlintWidgetProps,\n FormErrorEntry,\n Locale,\n NetworkEntry,\n ReportPayload,\n ReportResult,\n Severity,\n Theme,\n ThemeOverride,\n} from \"./types.js\";\n","import { gzipSync } from \"fflate\";\nimport type { ReportPayload, ReportResult } from \"./types.js\";\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\n// Redact values that look like tokens, passwords, secrets, or API keys\nconst SENSITIVE_PATTERNS = [\n /(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|bearer)\\s*[:=]\\s*[\"']?[^\\s\"',]{4,}/gi,\n /\\b(sk-[a-zA-Z0-9_-]{20,})\\b/g, // API keys (e.g. sk-ant-...)\n /\\b(ghp_[a-zA-Z0-9]{36,})\\b/g, // GitHub tokens\n /\\b(xoxb-[a-zA-Z0-9-]+)\\b/g, // Slack tokens\n /\\b(eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,})\\b/g, // JWTs\n];\n\nfunction sanitize(str: string): string {\n let result = str;\n for (const pattern of SENSITIVE_PATTERNS) {\n result = result.replace(pattern, \"[REDACTED]\");\n }\n return result;\n}\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 str = sanitize(str);\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","import type { FormErrorEntry, FormErrorField } from \"../types.js\";\n\nconst MAX_ENTRIES = 30;\n\n/**\n * Time to wait after a submit event before checking the DOM for error\n * indicators. RHF/zod validate asynchronously — 300ms covers most cases\n * while keeping the detection snappy.\n */\nconst POST_SUBMIT_CHECK_MS = 300;\n\n/**\n * After a submit button click, if no submit event fires within this window\n * it means the form library swallowed the submit entirely (e.g. RHF's\n * handleSubmit called preventDefault before the native event propagated,\n * or the button isn't even inside a <form>). We treat this as a potential\n * silent failure and scan the page for error indicators.\n */\nconst SILENT_SUBMIT_WINDOW_MS = 400;\n\nexport interface FormErrorCollector {\n start(): void;\n stop(): void;\n getEntries(): FormErrorEntry[];\n /**\n * Manually report a form validation failure. Call this from your form\n * library's error handler to capture exact field-level errors.\n *\n * @example\n * // React Hook Form + Zod\n * <form onSubmit={handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * })}>\n */\n report(formId: string, errors: Record<string, { message?: string } | undefined>): void;\n}\n\n// ── DOM helpers ────────────────────────────────────────────────────────────────\n\nfunction getFormId(form: HTMLFormElement): string {\n if (form.id) return `#${form.id}`;\n if (form.getAttribute(\"name\")) return `form[name=\"${form.getAttribute(\"name\")}\"]`;\n if (form.action && form.action !== location.href) return form.action;\n return `form:${Array.from(document.forms).indexOf(form)}`;\n}\n\nfunction getFieldLabel(field: HTMLElement, root: HTMLElement): string {\n const ariaLabel = field.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel;\n\n const id = field.id;\n if (id) {\n const label = root.querySelector<HTMLLabelElement>(`label[for=\"${id}\"]`);\n if (label?.textContent) return label.textContent.trim();\n }\n\n const parentLabel = field.closest(\"label\");\n if (parentLabel?.textContent) {\n const text = parentLabel.textContent.trim();\n if (text.length < 60) return text;\n }\n\n return (field as HTMLInputElement).name || (field as HTMLInputElement).placeholder || field.tagName.toLowerCase();\n}\n\nfunction getErrorMessage(field: HTMLElement): string | undefined {\n // aria-errormessage\n const errId = field.getAttribute(\"aria-errormessage\");\n if (errId) {\n const el = document.getElementById(errId);\n if (el?.textContent) return el.textContent.trim();\n }\n\n // aria-describedby (RHF wires error messages here)\n const descId = field.getAttribute(\"aria-describedby\");\n if (descId) {\n for (const id of descId.split(/\\s+/)) {\n const el = document.getElementById(id);\n if (el?.textContent?.trim()) return el.textContent.trim();\n }\n }\n\n // native validationMessage\n if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {\n if (field.validationMessage) return field.validationMessage;\n }\n\n // adjacent role=\"alert\"\n const next = field.nextElementSibling;\n if (next?.getAttribute(\"role\") === \"alert\" && next.textContent) {\n return next.textContent.trim();\n }\n\n return undefined;\n}\n\n/**\n * Collect fields with `aria-invalid=\"true\"` or native `:invalid` inside a\n * root element (a <form> or the whole document).\n */\nfunction collectInvalidFields(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const invalidEls = root.querySelectorAll<HTMLElement>('[aria-invalid=\"true\"], :invalid');\n\n for (const el of invalidEls) {\n if (el instanceof HTMLFormElement) continue;\n if (el.tagName === \"FIELDSET\") continue;\n\n fields.push({\n name: getFieldLabel(el, root),\n message: getErrorMessage(el),\n });\n }\n\n return fields;\n}\n\n/**\n * Scan the page for visible error indicators that appeared after a submit\n * attempt. This catches errors even when:\n * - The errored field is in another tab/step of a multi-step form\n * - The form doesn't use aria-invalid at all\n * - Errors are rendered as standalone alert divs\n */\nfunction collectVisibleErrors(root: HTMLElement): FormErrorField[] {\n const fields: FormErrorField[] = [];\n const seen = new Set<string>();\n\n // 1. role=\"alert\" elements that appeared (error toasts, inline messages)\n const alerts = root.querySelectorAll<HTMLElement>('[role=\"alert\"]');\n for (const el of alerts) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"alert\", message: text });\n }\n\n // 2. Common error class patterns (data-error, .error, .field-error, etc.)\n const errorEls = root.querySelectorAll<HTMLElement>(\n \"[data-error], [data-field-error], .error-message, .field-error, .form-error\",\n );\n for (const el of errorEls) {\n const text = el.textContent?.trim();\n if (!text || text.length > 200) continue;\n if (seen.has(text)) continue;\n seen.add(text);\n fields.push({ name: \"error\", message: text });\n }\n\n return fields;\n}\n\nfunction isSubmitButton(el: HTMLElement): el is HTMLButtonElement {\n if (el.tagName === \"BUTTON\") {\n const type = el.getAttribute(\"type\");\n // Buttons default to type=\"submit\" inside forms when no type is set\n return type === \"submit\" || type === null || type === \"\";\n }\n if (el.tagName === \"INPUT\") {\n return (el as HTMLInputElement).type === \"submit\";\n }\n return false;\n}\n\nfunction findClosestForm(el: HTMLElement): HTMLFormElement | null {\n // Direct form association\n if (\"form\" in el && (el as HTMLButtonElement).form) {\n return (el as HTMLButtonElement).form;\n }\n return el.closest(\"form\");\n}\n\n// ── Collector ──────────────────────────────────────────────────────────────────\n\nexport function createFormErrorCollector(): FormErrorCollector {\n const entries: FormErrorEntry[] = [];\n const attemptCounts = new Map<string, number>();\n let active = false;\n\n // Handlers we need to clean up\n let submitHandler: ((e: Event) => void) | null = null;\n let invalidHandler: ((e: Event) => void) | null = null;\n let clickHandler: ((e: MouseEvent) => void) | null = null;\n\n // Track whether a real submit event followed a button click\n let lastSubmitTime = 0;\n\n // Dedup: avoid recording the same form twice within a short window\n const recentRecords = new Map<string, number>();\n const DEDUP_MS = 500;\n\n function push(entry: FormErrorEntry) {\n // Dedup check\n const _key = `${entry.formId}:${entry.timestamp}`;\n const last = recentRecords.get(entry.formId);\n if (last && entry.timestamp - last < DEDUP_MS) return;\n recentRecords.set(entry.formId, entry.timestamp);\n\n entries.push(entry);\n if (entries.length > MAX_ENTRIES) entries.shift();\n }\n\n function recordFormErrors(form: HTMLFormElement, type: FormErrorEntry[\"type\"]) {\n const formId = getFormId(form);\n\n // Strategy 1: aria-invalid / :invalid fields\n let fields = collectInvalidFields(form);\n\n // Strategy 2: if no aria-invalid fields found, look for visible error messages\n if (fields.length === 0) {\n fields = collectVisibleErrors(form);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type,\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Handle clicks on submit buttons. If no submit event follows within\n * SILENT_SUBMIT_WINDOW_MS, it means the form library blocked the submit\n * entirely — scan for errors.\n */\n function handleSubmitButtonClick(button: HTMLElement) {\n const form = findClosestForm(button);\n\n setTimeout(() => {\n if (!active) return;\n // A real submit event fired — the submitHandler already took care of it\n if (Date.now() - lastSubmitTime < SILENT_SUBMIT_WINDOW_MS) return;\n\n // No submit event → silent failure\n const root = form ?? (document.body as HTMLElement);\n const formId = form ? getFormId(form) : `page:${location.pathname}`;\n\n let fields = form ? collectInvalidFields(form) : [];\n if (fields.length === 0) {\n fields = collectVisibleErrors(root);\n }\n\n // Also scan full page — the error might be in a toast or outside the form\n if (fields.length === 0 && form) {\n fields = collectVisibleErrors(document.body);\n }\n\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"silent_submit\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\n }, SILENT_SUBMIT_WINDOW_MS);\n }\n\n return {\n start() {\n if (active) return;\n active = true;\n\n // 1. Capture submit events (fires even with e.preventDefault())\n submitHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const form = e.target as HTMLFormElement;\n if (!(form instanceof HTMLFormElement)) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"submit\", submitHandler, true);\n\n // 2. Capture native invalid events (HTML5 constraint validation)\n invalidHandler = (e: Event) => {\n lastSubmitTime = Date.now();\n const field = e.target as HTMLElement;\n const form = field.closest(\"form\");\n if (!form) return;\n\n setTimeout(() => {\n if (!active) return;\n recordFormErrors(form, \"validation_failed\");\n }, POST_SUBMIT_CHECK_MS);\n };\n document.addEventListener(\"invalid\", invalidHandler, true);\n\n // 3. Capture clicks on submit buttons — catches silent failures\n // where the form lib eats the submit and shows nothing\n clickHandler = (e: MouseEvent) => {\n const target = e.target as HTMLElement;\n if (!target) return;\n\n // Walk up to find the actual button (click might be on an icon/span inside)\n const button = target.closest(\"button, input[type=submit]\") as HTMLElement | null;\n if (!button) return;\n if (!isSubmitButton(button)) return;\n\n handleSubmitButtonClick(button);\n };\n document.addEventListener(\"click\", clickHandler, true);\n },\n\n stop() {\n if (!active) return;\n active = false;\n if (submitHandler) document.removeEventListener(\"submit\", submitHandler, true);\n if (invalidHandler) document.removeEventListener(\"invalid\", invalidHandler, true);\n if (clickHandler) document.removeEventListener(\"click\", clickHandler, true);\n attemptCounts.clear();\n recentRecords.clear();\n },\n\n getEntries() {\n return [...entries];\n },\n\n report(formId: string, errors: Record<string, { message?: string } | undefined>) {\n const fields: FormErrorField[] = [];\n for (const [name, err] of Object.entries(errors)) {\n if (!err) continue;\n fields.push({ name, message: err.message });\n }\n if (fields.length === 0) return;\n\n const count = (attemptCounts.get(formId) ?? 0) + 1;\n attemptCounts.set(formId, count);\n\n push({\n type: \"validation_failed\",\n formId,\n fields,\n attemptNumber: count,\n url: location.href,\n timestamp: Date.now(),\n });\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 {\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\nfunction isBlockedUrl(url: string, blocked: Set<string>): boolean {\n try {\n const host = new URL(url, location.href).hostname;\n return [...blocked].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 (!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 (!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 { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FlintUser } from \"./types.js\";\n\nexport interface FlintState {\n user: FlintUser | undefined;\n sessionReplay: string | (() => string) | undefined;\n}\n\n// Holds a reference to the active form error collector so flint.reportFormError\n// can forward calls without the consumer needing to manage the collector.\nlet formErrorCollectorRef: FormErrorCollector | null = null;\n\nexport function _setFormErrorCollector(collector: FormErrorCollector | null) {\n formErrorCollectorRef = collector;\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\n/** @internal Use Flint.setUser() / Flint.setSessionReplay() / Flint.reportFormError() instead */\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 /**\n * Report a form validation failure with exact field-level errors.\n * Call this from your form library's error callback.\n *\n * @example\n * // React Hook Form + Zod\n * const onSubmit = handleSubmit(onValid, (errors) => {\n * flint.reportFormError('checkout-form', errors);\n * });\n */\n reportFormError(formId: string, errors: Record<string, { message?: string } | undefined>) {\n formErrorCollectorRef?.report(formId, errors);\n },\n};\n","// Flint singleton — manages global collector lifecycle\nimport { submitReport } from \"./api.js\";\nimport type { ConsoleCollector } from \"./collectors/console.js\";\nimport { createConsoleCollector } from \"./collectors/console.js\";\nimport { collectEnvironment } from \"./collectors/environment.js\";\nimport type { FormErrorCollector } from \"./collectors/formErrors.js\";\nimport { createFormErrorCollector } from \"./collectors/formErrors.js\";\nimport type { FrustrationCollector } from \"./collectors/frustration.js\";\nimport { createFrustrationCollector } from \"./collectors/frustration.js\";\nimport type { NetworkCollector } from \"./collectors/network.js\";\nimport { createNetworkCollector } from \"./collectors/network.js\";\nimport { _setFormErrorCollector, flint, getSnapshot } from \"./store.js\";\nimport type { CollectedMeta, FlintConfig } from \"./types.js\";\n\nconst DEFAULT_REPLAY_BUFFER_MS = 60_000;\n\ninterface FlintInstance {\n config: FlintConfig;\n console: ConsoleCollector | null;\n network: NetworkCollector | null;\n formErrors: FormErrorCollector | null;\n frustration: FrustrationCollector | null;\n replayEvents: unknown[];\n stopReplay: (() => void) | null;\n}\n\nlet instance: FlintInstance | null = null;\n\nfunction debugLog(config: FlintConfig, ...args: unknown[]) {\n if (config.debug) console.log(\"[Flint]\", ...args);\n}\n\nfunction init(config: FlintConfig): void {\n if (instance) {\n console.warn(\"[Flint] Already initialized. Call Flint.shutdown() first to re-initialize.\");\n return;\n }\n\n const {\n enableConsole = true,\n enableNetwork = true,\n enableFormErrors = true,\n enableFrustration = false,\n autoReportFrustration = false,\n enableReplay = false,\n replayBufferMs = DEFAULT_REPLAY_BUFFER_MS,\n blockedHosts = [],\n frustration: frustrationOpts,\n onFrustration,\n _replayRecorder,\n _collectors,\n } = config;\n\n debugLog(config, \"Initializing\", {\n serverUrl: config.serverUrl,\n enableConsole,\n enableNetwork,\n enableFormErrors,\n enableFrustration,\n enableReplay,\n });\n\n // Determine Flint server host to block from network capture\n const flintHost = (() => {\n try {\n return new URL(config.serverUrl).hostname;\n } catch {\n return \"\";\n }\n })();\n\n const allBlockedHosts = [...blockedHosts, ...(flintHost ? [flintHost] : [])];\n\n // Create and start collectors (use injected factories if provided, e.g. React Native)\n const consoleCol = enableConsole ? (_collectors?.console?.() ?? createConsoleCollector()) : null;\n consoleCol?.start();\n\n const networkCol = enableNetwork\n ? (_collectors?.network?.(allBlockedHosts) ?? createNetworkCollector(allBlockedHosts))\n : null;\n networkCol?.start();\n\n const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;\n if (formErrorsCol) {\n formErrorsCol.start();\n _setFormErrorCollector(formErrorsCol);\n }\n\n const frustrationCol = enableFrustration ? createFrustrationCollector(frustrationOpts) : null;\n frustrationCol?.start();\n\n // Set user if provided\n if (config.user) {\n flint.setUser(config.user);\n }\n\n const replayEvents: unknown[] = [];\n let stopReplay: (() => void) | null = null;\n\n debugLog(config, \"Collectors started\", {\n console: !!consoleCol,\n network: !!networkCol,\n formErrors: !!formErrorsCol,\n frustration: !!frustrationCol,\n });\n\n instance = {\n config,\n console: consoleCol,\n network: networkCol,\n formErrors: formErrorsCol,\n frustration: frustrationCol,\n replayEvents,\n stopReplay: null,\n };\n\n // Start replay recording (async, via injected recorder)\n if (enableReplay && _replayRecorder) {\n _replayRecorder((event: unknown) => {\n replayEvents.push(event);\n const cutoff = Date.now() - replayBufferMs;\n while (replayEvents.length > 0 && (replayEvents[0] as { timestamp: number }).timestamp < cutoff) {\n replayEvents.shift();\n }\n }).then((stop) => {\n stopReplay = stop ?? null;\n if (instance) instance.stopReplay = stopReplay;\n });\n }\n\n // Wire frustration auto-report\n const getEnvironment = _collectors?.environment ?? collectEnvironment;\n if (frustrationCol && autoReportFrustration) {\n frustrationCol.onFrustration(async (event) => {\n debugLog(config, \"Frustration detected, auto-reporting:\", event.type, event.details);\n onFrustration?.(event);\n const user = getSnapshot().user ?? config.user;\n await submitReport(config.serverUrl, config.projectKey, {\n reporterId: user?.id ?? \"anonymous\",\n reporterName: user?.name ?? \"Anonymous\",\n reporterEmail: user?.email,\n description: `[Auto-detected] ${event.type.replace(/_/g, \" \")}: ${event.details}`,\n severity: event.type === \"error_loop\" ? \"P1\" : event.type === \"rage_click\" ? \"P2\" : \"P3\",\n url: event.url,\n source: \"auto_capture\",\n meta: {\n ...config.meta,\n environment: getEnvironment(),\n consoleLogs: consoleCol?.getEntries() ?? [],\n networkErrors: networkCol?.getEntries() ?? [],\n formErrors: formErrorsCol?.getEntries() ?? [],\n frustrationEvent: event,\n },\n }).catch(() => {});\n });\n } else if (frustrationCol && onFrustration) {\n frustrationCol.onFrustration(onFrustration);\n }\n\n // Auto-fetch remote integrations config if no externalReplayProvider was set\n if (!config.externalReplayProvider) {\n fetchProjectConfig(config.serverUrl, config.projectKey)\n .then((remote) => {\n if (!instance || !remote?.integrations) return;\n const dd = remote.integrations.datadog;\n if (dd?.enabled && dd.site) {\n // Lazy-import to avoid bundling datadog helper when not needed\n import(\"./integrations/datadog.js\").then(({ createDatadogReplayProvider }) => {\n if (!instance) return;\n instance.config = {\n ...instance.config,\n externalReplayProvider: createDatadogReplayProvider(dd.site as string),\n };\n debugLog(config, \"Remote Datadog integration loaded:\", dd.site);\n });\n }\n })\n .catch(() => {\n // Fail silently — remote config is optional\n });\n }\n}\n\n/** Fetch project integrations config from the server. */\nasync function fetchProjectConfig(\n serverUrl: string,\n projectKey: string,\n): Promise<{ integrations: Record<string, { enabled: boolean; [k: string]: unknown }> } | null> {\n try {\n const url = `${serverUrl.replace(/\\/$/, \"\")}/api/v1/project-config`;\n const res = await fetch(url, { headers: { \"x-project-key\": projectKey } });\n if (!res.ok) return null;\n return res.json();\n } catch {\n return null;\n }\n}\n\nfunction shutdown(): void {\n if (!instance) return;\n\n instance.console?.stop();\n instance.network?.stop();\n instance.formErrors?.stop();\n _setFormErrorCollector(null);\n instance.frustration?.stop();\n instance.stopReplay?.();\n instance = null;\n}\n\nfunction isInitialized(): boolean {\n return instance !== null;\n}\n\nfunction getInstance(): FlintInstance | null {\n return instance;\n}\n\nfunction getMeta(extraMeta?: Record<string, unknown>): CollectedMeta {\n const getEnvironment = instance?.config._collectors?.environment ?? collectEnvironment;\n return {\n ...extraMeta,\n environment: getEnvironment(),\n consoleLogs: instance?.console?.getEntries() ?? [],\n networkErrors: instance?.network?.getEntries() ?? [],\n formErrors: instance?.formErrors?.getEntries() ?? [],\n };\n}\n\nfunction getReplayEvents(): unknown[] {\n return instance ? [...instance.replayEvents] : [];\n}\n\nfunction getConfig(): FlintConfig | null {\n return instance?.config ?? null;\n}\n\nfunction getExternalReplayUrl(): string | undefined {\n // 1. Store value (set via Flint.setSessionReplay)\n const storeReplay = getSnapshot().sessionReplay;\n const fromStore = typeof storeReplay === \"function\" ? storeReplay() : storeReplay;\n if (fromStore) return fromStore;\n // 2. Config provider (e.g. createDatadogReplayProvider)\n return instance?.config.externalReplayProvider?.();\n}\n\nexport const Flint = {\n init,\n shutdown,\n isInitialized,\n getInstance,\n getMeta,\n getReplayEvents,\n getConfig,\n getExternalReplayUrl,\n setUser: flint.setUser,\n setSessionReplay: flint.setSessionReplay,\n reportFormError: flint.reportFormError,\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":["init","MAX_ENTRIES","window","listeners","emit","MAX_ENTRIES","init","createDatadogReplayProvider"]}
|