@diegotsi/flint-core 1.2.0 → 1.3.1

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.
@@ -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,9 @@
1
+ import {
2
+ DATADOG_BLOCKED_HOSTS,
3
+ createDatadogReplayProvider
4
+ } from "./chunk-HVSD45YR.js";
5
+ export {
6
+ DATADOG_BLOCKED_HOSTS,
7
+ createDatadogReplayProvider
8
+ };
9
+ //# sourceMappingURL=datadog-I3QKI6Q3.js.map
@@ -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,21 +620,85 @@ function createFrustrationCollector(opts) {
581
620
 
582
621
  // src/collectors/network.ts
583
622
  var MAX_ENTRIES3 = 50;
584
- var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
623
+ var DEFAULT_BLOCKED_HOSTS = [
585
624
  "browser-intake-datadoghq.com",
586
- "rum.browser-intake-datadoghq.com",
587
- "logs.browser-intake-datadoghq.com",
588
- "session-replay.browser-intake-datadoghq.com"
625
+ "datadoghq.com",
626
+ "datadoghq.eu",
627
+ "google.com",
628
+ "googleapis.com",
629
+ "google-analytics.com",
630
+ "googletagmanager.com",
631
+ "gstatic.com",
632
+ "doubleclick.net",
633
+ "facebook.com",
634
+ "facebook.net",
635
+ "hotjar.com",
636
+ "hotjar.io",
637
+ "intercom.io",
638
+ "intercomcdn.com",
639
+ "segment.io",
640
+ "segment.com",
641
+ "mixpanel.com",
642
+ "amplitude.com",
643
+ "sentry.io",
644
+ "fullstory.com",
645
+ "clarity.ms",
646
+ "newrelic.com",
647
+ "nr-data.net",
648
+ "cloudflareinsights.com",
649
+ "heapanalytics.com",
650
+ "posthog.com",
651
+ "logrocket.io",
652
+ "lr-in-prod.com"
653
+ ];
654
+ var ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
655
+ ".woff",
656
+ ".woff2",
657
+ ".ttf",
658
+ ".otf",
659
+ ".eot",
660
+ // fonts
661
+ ".png",
662
+ ".jpg",
663
+ ".jpeg",
664
+ ".gif",
665
+ ".svg",
666
+ ".webp",
667
+ ".ico",
668
+ ".avif",
669
+ // images
670
+ ".css",
671
+ ".map",
672
+ // styles & source maps
673
+ ".js",
674
+ ".mjs",
675
+ // scripts (usually loaded by browser, not app logic)
676
+ ".mp3",
677
+ ".mp4",
678
+ ".webm",
679
+ ".ogg"
680
+ // media
589
681
  ]);
590
- function isBlockedUrl(url, extra) {
682
+ function isAssetUrl(url) {
683
+ try {
684
+ const pathname = new URL(url, location.href).pathname;
685
+ const ext = pathname.slice(pathname.lastIndexOf(".")).toLowerCase();
686
+ return ASSET_EXTENSIONS.has(ext);
687
+ } catch {
688
+ return false;
689
+ }
690
+ }
691
+ function isBlockedUrl(url, blocked) {
591
692
  try {
592
693
  const host = new URL(url, location.href).hostname;
593
- const all = [...BLOCKED_HOSTS, ...extra];
594
- return all.some((b) => host === b || host.endsWith("." + b));
694
+ return [...blocked].some((b) => host === b || host.endsWith("." + b));
595
695
  } catch {
596
696
  return false;
597
697
  }
598
698
  }
699
+ function shouldIgnore(url, blocked) {
700
+ return isBlockedUrl(url, blocked) || isAssetUrl(url);
701
+ }
599
702
  function truncateUrl(url) {
600
703
  try {
601
704
  const u = new URL(url, location.href);
@@ -607,7 +710,7 @@ function truncateUrl(url) {
607
710
  }
608
711
  function createNetworkCollector(extraBlockedHosts = []) {
609
712
  const entries = [];
610
- const blocked = new Set(extraBlockedHosts);
713
+ const blocked = /* @__PURE__ */ new Set([...DEFAULT_BLOCKED_HOSTS, ...extraBlockedHosts]);
611
714
  let origFetch = null;
612
715
  let origXHROpen = null;
613
716
  let active = false;
@@ -625,7 +728,7 @@ function createNetworkCollector(extraBlockedHosts = []) {
625
728
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
626
729
  const startTime = Date.now();
627
730
  const res = await origFetch.call(window, input, init2);
628
- if (!isBlockedUrl(url, blocked)) {
731
+ if (!shouldIgnore(url, blocked)) {
629
732
  push({
630
733
  method,
631
734
  url: truncateUrl(url),
@@ -641,7 +744,7 @@ function createNetworkCollector(extraBlockedHosts = []) {
641
744
  const startTime = Date.now();
642
745
  const urlStr = typeof url === "string" ? url : url.href;
643
746
  this.addEventListener("load", () => {
644
- if (!isBlockedUrl(urlStr, blocked)) {
747
+ if (!shouldIgnore(urlStr, blocked)) {
645
748
  push({
646
749
  method: method.toUpperCase(),
647
750
  url: truncateUrl(urlStr),
@@ -666,6 +769,9 @@ function createNetworkCollector(extraBlockedHosts = []) {
666
769
  };
667
770
  }
668
771
 
772
+ // src/index.ts
773
+ init_datadog();
774
+
669
775
  // src/store.ts
670
776
  var formErrorCollectorRef = null;
671
777
  function _setFormErrorCollector(collector) {
@@ -729,7 +835,8 @@ function init(config) {
729
835
  blockedHosts = [],
730
836
  frustration: frustrationOpts,
731
837
  onFrustration,
732
- _replayRecorder
838
+ _replayRecorder,
839
+ _collectors
733
840
  } = config;
734
841
  debugLog(config, "Initializing", {
735
842
  serverUrl: config.serverUrl,
@@ -746,9 +853,10 @@ function init(config) {
746
853
  return "";
747
854
  }
748
855
  })();
749
- const consoleCol = enableConsole ? createConsoleCollector() : null;
856
+ const allBlockedHosts = [...blockedHosts, ...flintHost ? [flintHost] : []];
857
+ const consoleCol = enableConsole ? _collectors?.console?.() ?? createConsoleCollector() : null;
750
858
  consoleCol?.start();
751
- const networkCol = enableNetwork ? createNetworkCollector([...blockedHosts, ...flintHost ? [flintHost] : []]) : null;
859
+ const networkCol = enableNetwork ? _collectors?.network?.(allBlockedHosts) ?? createNetworkCollector(allBlockedHosts) : null;
752
860
  networkCol?.start();
753
861
  const formErrorsCol = enableFormErrors ? createFormErrorCollector() : null;
754
862
  if (formErrorsCol) {
@@ -789,6 +897,7 @@ function init(config) {
789
897
  if (instance) instance.stopReplay = stopReplay;
790
898
  });
791
899
  }
900
+ const getEnvironment = _collectors?.environment ?? collectEnvironment;
792
901
  if (frustrationCol && autoReportFrustration) {
793
902
  frustrationCol.onFrustration(async (event) => {
794
903
  debugLog(config, "Frustration detected, auto-reporting:", event.type, event.details);
@@ -804,7 +913,7 @@ function init(config) {
804
913
  source: "auto_capture",
805
914
  meta: {
806
915
  ...config.meta,
807
- environment: collectEnvironment(),
916
+ environment: getEnvironment(),
808
917
  consoleLogs: consoleCol?.getEntries() ?? [],
809
918
  networkErrors: networkCol?.getEntries() ?? [],
810
919
  formErrors: formErrorsCol?.getEntries() ?? [],
@@ -816,6 +925,33 @@ function init(config) {
816
925
  } else if (frustrationCol && onFrustration) {
817
926
  frustrationCol.onFrustration(onFrustration);
818
927
  }
928
+ if (!config.externalReplayProvider) {
929
+ fetchProjectConfig(config.serverUrl, config.projectKey).then((remote) => {
930
+ if (!instance || !remote?.integrations) return;
931
+ const dd = remote.integrations.datadog;
932
+ if (dd?.enabled && dd.site) {
933
+ Promise.resolve().then(() => (init_datadog(), datadog_exports)).then(({ createDatadogReplayProvider: createDatadogReplayProvider2 }) => {
934
+ if (!instance) return;
935
+ instance.config = {
936
+ ...instance.config,
937
+ externalReplayProvider: createDatadogReplayProvider2(dd.site)
938
+ };
939
+ debugLog(config, "Remote Datadog integration loaded:", dd.site);
940
+ });
941
+ }
942
+ }).catch(() => {
943
+ });
944
+ }
945
+ }
946
+ async function fetchProjectConfig(serverUrl, projectKey) {
947
+ try {
948
+ const url = `${serverUrl.replace(/\/$/, "")}/api/v1/project-config`;
949
+ const res = await fetch(url, { headers: { "x-project-key": projectKey } });
950
+ if (!res.ok) return null;
951
+ return res.json();
952
+ } catch {
953
+ return null;
954
+ }
819
955
  }
820
956
  function shutdown() {
821
957
  if (!instance) return;
@@ -834,9 +970,10 @@ function getInstance() {
834
970
  return instance;
835
971
  }
836
972
  function getMeta(extraMeta) {
973
+ const getEnvironment = instance?.config._collectors?.environment ?? collectEnvironment;
837
974
  return {
838
975
  ...extraMeta,
839
- environment: collectEnvironment(),
976
+ environment: getEnvironment(),
840
977
  consoleLogs: instance?.console?.getEntries() ?? [],
841
978
  networkErrors: instance?.network?.getEntries() ?? [],
842
979
  formErrors: instance?.formErrors?.getEntries() ?? []
@@ -848,6 +985,12 @@ function getReplayEvents() {
848
985
  function getConfig() {
849
986
  return instance?.config ?? null;
850
987
  }
988
+ function getExternalReplayUrl() {
989
+ const storeReplay = getSnapshot().sessionReplay;
990
+ const fromStore = typeof storeReplay === "function" ? storeReplay() : storeReplay;
991
+ if (fromStore) return fromStore;
992
+ return instance?.config.externalReplayProvider?.();
993
+ }
851
994
  var Flint = {
852
995
  init,
853
996
  shutdown,
@@ -856,6 +999,7 @@ var Flint = {
856
999
  getMeta,
857
1000
  getReplayEvents,
858
1001
  getConfig,
1002
+ getExternalReplayUrl,
859
1003
  setUser: flint.setUser,
860
1004
  setSessionReplay: flint.setSessionReplay,
861
1005
  reportFormError: flint.reportFormError
@@ -901,14 +1045,15 @@ function resolveTheme(theme) {
901
1045
  }
902
1046
  // Annotate the CommonJS export names for ESM import in node:
903
1047
  0 && (module.exports = {
1048
+ DATADOG_BLOCKED_HOSTS,
904
1049
  Flint,
905
1050
  _setFormErrorCollector,
906
1051
  collectEnvironment,
907
1052
  createConsoleCollector,
1053
+ createDatadogReplayProvider,
908
1054
  createFormErrorCollector,
909
1055
  createFrustrationCollector,
910
1056
  createNetworkCollector,
911
- flint,
912
1057
  getSnapshot,
913
1058
  resolveTheme,
914
1059
  submitReplay,