@outlit/browser 1.2.0 → 1.4.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.
@@ -605,6 +605,27 @@ function setCookie(name, value, days) {
605
605
  }
606
606
  document.cookie = cookie;
607
607
  }
608
+ var CONSENT_KEY = "outlit_consent";
609
+ function getConsentState() {
610
+ try {
611
+ const stored = localStorage.getItem(CONSENT_KEY);
612
+ if (stored === "1") return true;
613
+ if (stored === "0") return false;
614
+ } catch {
615
+ }
616
+ const cookieValue = getCookie(CONSENT_KEY);
617
+ if (cookieValue === "1") return true;
618
+ if (cookieValue === "0") return false;
619
+ return null;
620
+ }
621
+ function setConsentState(granted) {
622
+ const value = granted ? "1" : "0";
623
+ try {
624
+ localStorage.setItem(CONSENT_KEY, value);
625
+ } catch {
626
+ }
627
+ setCookie(CONSENT_KEY, value, 365);
628
+ }
608
629
 
609
630
  // src/tracker.ts
610
631
  var MAX_PENDING_STAGE_EVENTS = 10;
@@ -647,7 +668,8 @@ var Outlit = class {
647
668
  window.addEventListener("beforeunload", handleExit);
648
669
  }
649
670
  this.isInitialized = true;
650
- if (options.autoTrack !== false) {
671
+ const consent = getConsentState();
672
+ if (consent === true || consent === null && options.autoTrack !== false) {
651
673
  this.enableTracking();
652
674
  }
653
675
  }
@@ -680,11 +702,38 @@ var Outlit = class {
680
702
  this.initCalendarTracking();
681
703
  }
682
704
  this.isTrackingEnabled = true;
705
+ setConsentState(true);
683
706
  if (this.pendingUser) {
684
707
  this.applyUser(this.pendingUser);
685
708
  this.pendingUser = null;
686
709
  }
687
710
  }
711
+ /**
712
+ * Disable tracking. Call this when a user revokes consent.
713
+ * This will:
714
+ * - Flush any pending events (captured while user had consent)
715
+ * - Stop the flush timer, pageview tracking, form tracking, and session tracking
716
+ * - Persist the opt-out decision so it's remembered across sessions
717
+ *
718
+ * The SDK instance remains usable — enableTracking() can be called again to re-enable.
719
+ */
720
+ async disableTracking() {
721
+ if (!this.isTrackingEnabled) {
722
+ setConsentState(false);
723
+ return;
724
+ }
725
+ if (this.flushTimer) {
726
+ clearInterval(this.flushTimer);
727
+ this.flushTimer = null;
728
+ }
729
+ stopAutocapture();
730
+ stopCalendarTracking();
731
+ stopSessionTracking();
732
+ await this.flush();
733
+ this.sessionTracker = null;
734
+ this.isTrackingEnabled = false;
735
+ setConsentState(false);
736
+ }
688
737
  /**
689
738
  * Check if tracking is currently enabled.
690
739
  */
@@ -1000,50 +1049,82 @@ var OutlitContext = createContext({
1000
1049
  isInitialized: false,
1001
1050
  isTrackingEnabled: false,
1002
1051
  enableTracking: () => {
1052
+ },
1053
+ disableTracking: () => {
1003
1054
  }
1004
1055
  });
1005
- function OutlitProvider({
1006
- children,
1007
- publicKey,
1008
- apiHost,
1009
- trackPageviews = true,
1010
- trackForms = true,
1011
- formFieldDenylist,
1012
- flushInterval,
1013
- autoTrack = true,
1014
- autoIdentify = true,
1015
- user
1016
- }) {
1056
+ function OutlitProvider(props) {
1057
+ const { children, user } = props;
1017
1058
  const outlitRef = useRef(null);
1018
1059
  const initializedRef = useRef(false);
1060
+ const isExternalClientRef = useRef(false);
1061
+ const [isInitialized, setIsInitialized] = useState(false);
1019
1062
  const [isTrackingEnabled, setIsTrackingEnabled] = useState(false);
1020
1063
  useEffect(() => {
1021
1064
  if (initializedRef.current) return;
1022
- outlitRef.current = new Outlit({
1023
- publicKey,
1024
- apiHost,
1025
- trackPageviews,
1026
- trackForms,
1027
- formFieldDenylist,
1028
- flushInterval,
1029
- autoTrack,
1030
- autoIdentify
1031
- });
1065
+ if (props.client) {
1066
+ if (process.env.NODE_ENV !== "production") {
1067
+ const configKeys = [
1068
+ "publicKey",
1069
+ "apiHost",
1070
+ "trackPageviews",
1071
+ "trackForms",
1072
+ "formFieldDenylist",
1073
+ "flushInterval",
1074
+ "autoTrack",
1075
+ "autoIdentify",
1076
+ "trackCalendarEmbeds",
1077
+ "trackEngagement",
1078
+ "idleTimeout"
1079
+ ];
1080
+ const conflicting = configKeys.filter(
1081
+ (k) => k in props && props[k] !== void 0
1082
+ );
1083
+ if (conflicting.length > 0) {
1084
+ console.warn(
1085
+ `[Outlit] Both \`client\` and config props (${conflicting.join(", ")}) were provided to OutlitProvider. The \`client\` instance will be used and config props will be ignored.`
1086
+ );
1087
+ }
1088
+ }
1089
+ outlitRef.current = props.client;
1090
+ isExternalClientRef.current = true;
1091
+ } else {
1092
+ const {
1093
+ publicKey,
1094
+ apiHost,
1095
+ trackPageviews = true,
1096
+ trackForms = true,
1097
+ formFieldDenylist,
1098
+ flushInterval,
1099
+ autoTrack = true,
1100
+ autoIdentify = true,
1101
+ trackCalendarEmbeds,
1102
+ trackEngagement,
1103
+ idleTimeout
1104
+ } = props;
1105
+ outlitRef.current = new Outlit({
1106
+ publicKey,
1107
+ apiHost,
1108
+ trackPageviews,
1109
+ trackForms,
1110
+ formFieldDenylist,
1111
+ flushInterval,
1112
+ autoTrack,
1113
+ autoIdentify,
1114
+ trackCalendarEmbeds,
1115
+ trackEngagement,
1116
+ idleTimeout
1117
+ });
1118
+ }
1032
1119
  initializedRef.current = true;
1120
+ setIsInitialized(true);
1033
1121
  setIsTrackingEnabled(outlitRef.current.isEnabled());
1034
1122
  return () => {
1035
- outlitRef.current?.shutdown();
1123
+ if (!isExternalClientRef.current) {
1124
+ outlitRef.current?.shutdown();
1125
+ }
1036
1126
  };
1037
- }, [
1038
- publicKey,
1039
- apiHost,
1040
- trackPageviews,
1041
- trackForms,
1042
- formFieldDenylist,
1043
- flushInterval,
1044
- autoTrack,
1045
- autoIdentify
1046
- ]);
1127
+ }, []);
1047
1128
  useEffect(() => {
1048
1129
  if (!outlitRef.current) return;
1049
1130
  if (user && (user.email || user.userId)) {
@@ -1058,14 +1139,21 @@ function OutlitProvider({
1058
1139
  setIsTrackingEnabled(true);
1059
1140
  }
1060
1141
  }, []);
1142
+ const disableTracking = useCallback(() => {
1143
+ if (outlitRef.current) {
1144
+ outlitRef.current.disableTracking();
1145
+ setIsTrackingEnabled(false);
1146
+ }
1147
+ }, []);
1061
1148
  return /* @__PURE__ */ jsx(
1062
1149
  OutlitContext.Provider,
1063
1150
  {
1064
1151
  value: {
1065
1152
  outlit: outlitRef.current,
1066
- isInitialized: initializedRef.current,
1153
+ isInitialized,
1067
1154
  isTrackingEnabled,
1068
- enableTracking
1155
+ enableTracking,
1156
+ disableTracking
1069
1157
  },
1070
1158
  children
1071
1159
  }
@@ -1075,7 +1163,7 @@ function OutlitProvider({
1075
1163
  // src/react/hooks.ts
1076
1164
  import { useCallback as useCallback2, useContext } from "react";
1077
1165
  function useOutlit() {
1078
- const { outlit, isInitialized, isTrackingEnabled, enableTracking } = useContext(OutlitContext);
1166
+ const { outlit, isInitialized, isTrackingEnabled, enableTracking, disableTracking } = useContext(OutlitContext);
1079
1167
  const track = useCallback2(
1080
1168
  (eventName, properties) => {
1081
1169
  if (!outlit) {
@@ -1206,7 +1294,8 @@ function useOutlit() {
1206
1294
  },
1207
1295
  isInitialized,
1208
1296
  isTrackingEnabled,
1209
- enableTracking
1297
+ enableTracking,
1298
+ disableTracking
1210
1299
  };
1211
1300
  }
1212
1301
  function useTrack() {