@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.
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
- import { f as OutlitOptions, U as UserIdentity, O as Outlit, B as BillingOptions } from '../tracker-BTkkFb0f.mjs';
4
+ import { U as UserIdentity, h as OutlitOptions, O as Outlit, B as BillingOptions } from '../tracker-OMgVDwlV.mjs';
5
5
  import { BrowserTrackOptions, BrowserIdentifyOptions } from '@outlit/core';
6
6
  export { BrowserIdentifyOptions, BrowserTrackOptions, CustomerIdentifier, ExplicitJourneyStage, TrackerConfig } from '@outlit/core';
7
7
 
@@ -10,22 +10,11 @@ interface OutlitContextValue {
10
10
  isInitialized: boolean;
11
11
  isTrackingEnabled: boolean;
12
12
  enableTracking: () => void;
13
+ disableTracking: () => void;
13
14
  }
14
15
  declare const OutlitContext: react.Context<OutlitContextValue>;
15
- interface OutlitProviderProps extends Omit<OutlitOptions, "trackPageviews"> {
16
+ interface OutlitProviderBaseProps {
16
17
  children: ReactNode;
17
- /**
18
- * Whether to automatically track pageviews.
19
- * When true (default), tracks pageviews on route changes.
20
- */
21
- trackPageviews?: boolean;
22
- /**
23
- * Whether to start tracking automatically on mount.
24
- * Set to false if you need to wait for user consent.
25
- * Call enableTracking() (from useOutlit hook) after consent is obtained.
26
- * @default true
27
- */
28
- autoTrack?: boolean;
29
18
  /**
30
19
  * Current user identity.
31
20
  * When provided with email or userId, calls setUser() to identify the user.
@@ -51,46 +40,83 @@ interface OutlitProviderProps extends Omit<OutlitOptions, "trackPageviews"> {
51
40
  user?: UserIdentity | null;
52
41
  }
53
42
  /**
54
- * Outlit Provider component.
55
- * Initializes the client and provides it to child components via context.
43
+ * Props for using a pre-existing Outlit instance.
44
+ * The provider will use this instance directly without creating a new one.
45
+ * The caller owns the instance lifecycle — shutdown() will NOT be called on unmount.
56
46
  *
57
47
  * @example
58
48
  * ```tsx
59
- * // layout.tsx - Auto tracking (default)
49
+ * import { Outlit } from '@outlit/browser'
60
50
  * import { OutlitProvider } from '@outlit/browser/react'
61
51
  *
62
- * export default function RootLayout({ children }) {
52
+ * const outlit = new Outlit({ publicKey: 'pk_xxx', trackPageviews: false })
53
+ *
54
+ * function App() {
55
+ * const user = useAuth()
63
56
  * return (
64
- * <OutlitProvider publicKey="pk_xxx" trackPageviews>
57
+ * <OutlitProvider client={outlit} user={user ? { email: user.email } : null}>
65
58
  * {children}
66
59
  * </OutlitProvider>
67
60
  * )
68
61
  * }
69
62
  * ```
63
+ */
64
+ type NeverOutlitOptions = {
65
+ [K in keyof OutlitOptions]?: never;
66
+ };
67
+ interface OutlitProviderClientProps extends OutlitProviderBaseProps, NeverOutlitOptions {
68
+ /** An existing Outlit instance to use. Config props are ignored when this is provided. */
69
+ client: Outlit;
70
+ }
71
+ /**
72
+ * Props for creating a new Outlit instance internally.
73
+ * This is the default behavior — the provider creates and owns the instance.
74
+ */
75
+ interface OutlitProviderConfigProps extends OutlitProviderBaseProps, Omit<OutlitOptions, "trackPageviews"> {
76
+ client?: never;
77
+ /**
78
+ * Whether to automatically track pageviews.
79
+ * When true (default), tracks pageviews on route changes.
80
+ */
81
+ trackPageviews?: boolean;
82
+ /**
83
+ * Whether to start tracking automatically on mount.
84
+ * Set to false if you need to wait for user consent.
85
+ * Call enableTracking() (from useOutlit hook) after consent is obtained.
86
+ * @default true
87
+ */
88
+ autoTrack?: boolean;
89
+ }
90
+ type OutlitProviderProps = OutlitProviderClientProps | OutlitProviderConfigProps;
91
+ /**
92
+ * Outlit Provider component.
93
+ * Initializes the client and provides it to child components via context.
94
+ *
95
+ * Can be used in two ways:
96
+ *
97
+ * 1. **Config mode** (default): Pass `publicKey` and config options to create a new instance.
98
+ * 2. **Client mode**: Pass an existing `client` instance for shared imperative + React usage.
70
99
  *
71
100
  * @example
72
101
  * ```tsx
73
- * // layout.tsx - With consent management
74
- * import { OutlitProvider } from '@outlit/browser/react'
75
- *
76
- * export default function RootLayout({ children }) {
77
- * return (
78
- * <OutlitProvider publicKey="pk_xxx" autoTrack={false}>
79
- * {children}
80
- * </OutlitProvider>
81
- * )
82
- * }
102
+ * // Config mode provider creates and owns the instance
103
+ * <OutlitProvider publicKey="pk_xxx" trackPageviews>
104
+ * {children}
105
+ * </OutlitProvider>
106
+ * ```
83
107
  *
84
- * // ConsentBanner.tsx
85
- * import { useOutlit } from '@outlit/browser/react'
108
+ * @example
109
+ * ```tsx
110
+ * // Client mode — use an existing instance
111
+ * const outlit = new Outlit({ publicKey: 'pk_xxx' })
112
+ * outlit.track('pageview') // imperative usage
86
113
  *
87
- * function ConsentBanner() {
88
- * const { enableTracking } = useOutlit()
89
- * return <button onClick={enableTracking}>Accept Cookies</button>
90
- * }
114
+ * <OutlitProvider client={outlit} user={user}>
115
+ * {children}
116
+ * </OutlitProvider>
91
117
  * ```
92
118
  */
93
- declare function OutlitProvider({ children, publicKey, apiHost, trackPageviews, trackForms, formFieldDenylist, flushInterval, autoTrack, autoIdentify, user, }: OutlitProviderProps): react_jsx_runtime.JSX.Element;
119
+ declare function OutlitProvider(props: OutlitProviderProps): react_jsx_runtime.JSX.Element;
94
120
 
95
121
  interface UseOutlitReturn {
96
122
  /**
@@ -147,6 +173,11 @@ interface UseOutlitReturn {
147
173
  * Only needed if you initialized with autoTrack: false.
148
174
  */
149
175
  enableTracking: () => void;
176
+ /**
177
+ * Disable tracking and persist the opt-out decision.
178
+ * Call this when a user revokes consent.
179
+ */
180
+ disableTracking: () => void;
150
181
  }
151
182
  /**
152
183
  * Hook to access the Outlit client.
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
- import { f as OutlitOptions, U as UserIdentity, O as Outlit, B as BillingOptions } from '../tracker-BTkkFb0f.js';
4
+ import { U as UserIdentity, h as OutlitOptions, O as Outlit, B as BillingOptions } from '../tracker-OMgVDwlV.js';
5
5
  import { BrowserTrackOptions, BrowserIdentifyOptions } from '@outlit/core';
6
6
  export { BrowserIdentifyOptions, BrowserTrackOptions, CustomerIdentifier, ExplicitJourneyStage, TrackerConfig } from '@outlit/core';
7
7
 
@@ -10,22 +10,11 @@ interface OutlitContextValue {
10
10
  isInitialized: boolean;
11
11
  isTrackingEnabled: boolean;
12
12
  enableTracking: () => void;
13
+ disableTracking: () => void;
13
14
  }
14
15
  declare const OutlitContext: react.Context<OutlitContextValue>;
15
- interface OutlitProviderProps extends Omit<OutlitOptions, "trackPageviews"> {
16
+ interface OutlitProviderBaseProps {
16
17
  children: ReactNode;
17
- /**
18
- * Whether to automatically track pageviews.
19
- * When true (default), tracks pageviews on route changes.
20
- */
21
- trackPageviews?: boolean;
22
- /**
23
- * Whether to start tracking automatically on mount.
24
- * Set to false if you need to wait for user consent.
25
- * Call enableTracking() (from useOutlit hook) after consent is obtained.
26
- * @default true
27
- */
28
- autoTrack?: boolean;
29
18
  /**
30
19
  * Current user identity.
31
20
  * When provided with email or userId, calls setUser() to identify the user.
@@ -51,46 +40,83 @@ interface OutlitProviderProps extends Omit<OutlitOptions, "trackPageviews"> {
51
40
  user?: UserIdentity | null;
52
41
  }
53
42
  /**
54
- * Outlit Provider component.
55
- * Initializes the client and provides it to child components via context.
43
+ * Props for using a pre-existing Outlit instance.
44
+ * The provider will use this instance directly without creating a new one.
45
+ * The caller owns the instance lifecycle — shutdown() will NOT be called on unmount.
56
46
  *
57
47
  * @example
58
48
  * ```tsx
59
- * // layout.tsx - Auto tracking (default)
49
+ * import { Outlit } from '@outlit/browser'
60
50
  * import { OutlitProvider } from '@outlit/browser/react'
61
51
  *
62
- * export default function RootLayout({ children }) {
52
+ * const outlit = new Outlit({ publicKey: 'pk_xxx', trackPageviews: false })
53
+ *
54
+ * function App() {
55
+ * const user = useAuth()
63
56
  * return (
64
- * <OutlitProvider publicKey="pk_xxx" trackPageviews>
57
+ * <OutlitProvider client={outlit} user={user ? { email: user.email } : null}>
65
58
  * {children}
66
59
  * </OutlitProvider>
67
60
  * )
68
61
  * }
69
62
  * ```
63
+ */
64
+ type NeverOutlitOptions = {
65
+ [K in keyof OutlitOptions]?: never;
66
+ };
67
+ interface OutlitProviderClientProps extends OutlitProviderBaseProps, NeverOutlitOptions {
68
+ /** An existing Outlit instance to use. Config props are ignored when this is provided. */
69
+ client: Outlit;
70
+ }
71
+ /**
72
+ * Props for creating a new Outlit instance internally.
73
+ * This is the default behavior — the provider creates and owns the instance.
74
+ */
75
+ interface OutlitProviderConfigProps extends OutlitProviderBaseProps, Omit<OutlitOptions, "trackPageviews"> {
76
+ client?: never;
77
+ /**
78
+ * Whether to automatically track pageviews.
79
+ * When true (default), tracks pageviews on route changes.
80
+ */
81
+ trackPageviews?: boolean;
82
+ /**
83
+ * Whether to start tracking automatically on mount.
84
+ * Set to false if you need to wait for user consent.
85
+ * Call enableTracking() (from useOutlit hook) after consent is obtained.
86
+ * @default true
87
+ */
88
+ autoTrack?: boolean;
89
+ }
90
+ type OutlitProviderProps = OutlitProviderClientProps | OutlitProviderConfigProps;
91
+ /**
92
+ * Outlit Provider component.
93
+ * Initializes the client and provides it to child components via context.
94
+ *
95
+ * Can be used in two ways:
96
+ *
97
+ * 1. **Config mode** (default): Pass `publicKey` and config options to create a new instance.
98
+ * 2. **Client mode**: Pass an existing `client` instance for shared imperative + React usage.
70
99
  *
71
100
  * @example
72
101
  * ```tsx
73
- * // layout.tsx - With consent management
74
- * import { OutlitProvider } from '@outlit/browser/react'
75
- *
76
- * export default function RootLayout({ children }) {
77
- * return (
78
- * <OutlitProvider publicKey="pk_xxx" autoTrack={false}>
79
- * {children}
80
- * </OutlitProvider>
81
- * )
82
- * }
102
+ * // Config mode provider creates and owns the instance
103
+ * <OutlitProvider publicKey="pk_xxx" trackPageviews>
104
+ * {children}
105
+ * </OutlitProvider>
106
+ * ```
83
107
  *
84
- * // ConsentBanner.tsx
85
- * import { useOutlit } from '@outlit/browser/react'
108
+ * @example
109
+ * ```tsx
110
+ * // Client mode — use an existing instance
111
+ * const outlit = new Outlit({ publicKey: 'pk_xxx' })
112
+ * outlit.track('pageview') // imperative usage
86
113
  *
87
- * function ConsentBanner() {
88
- * const { enableTracking } = useOutlit()
89
- * return <button onClick={enableTracking}>Accept Cookies</button>
90
- * }
114
+ * <OutlitProvider client={outlit} user={user}>
115
+ * {children}
116
+ * </OutlitProvider>
91
117
  * ```
92
118
  */
93
- declare function OutlitProvider({ children, publicKey, apiHost, trackPageviews, trackForms, formFieldDenylist, flushInterval, autoTrack, autoIdentify, user, }: OutlitProviderProps): react_jsx_runtime.JSX.Element;
119
+ declare function OutlitProvider(props: OutlitProviderProps): react_jsx_runtime.JSX.Element;
94
120
 
95
121
  interface UseOutlitReturn {
96
122
  /**
@@ -147,6 +173,11 @@ interface UseOutlitReturn {
147
173
  * Only needed if you initialized with autoTrack: false.
148
174
  */
149
175
  enableTracking: () => void;
176
+ /**
177
+ * Disable tracking and persist the opt-out decision.
178
+ * Call this when a user revokes consent.
179
+ */
180
+ disableTracking: () => void;
150
181
  }
151
182
  /**
152
183
  * Hook to access the Outlit client.
@@ -625,6 +625,27 @@ function setCookie(name, value, days) {
625
625
  }
626
626
  document.cookie = cookie;
627
627
  }
628
+ var CONSENT_KEY = "outlit_consent";
629
+ function getConsentState() {
630
+ try {
631
+ const stored = localStorage.getItem(CONSENT_KEY);
632
+ if (stored === "1") return true;
633
+ if (stored === "0") return false;
634
+ } catch {
635
+ }
636
+ const cookieValue = getCookie(CONSENT_KEY);
637
+ if (cookieValue === "1") return true;
638
+ if (cookieValue === "0") return false;
639
+ return null;
640
+ }
641
+ function setConsentState(granted) {
642
+ const value = granted ? "1" : "0";
643
+ try {
644
+ localStorage.setItem(CONSENT_KEY, value);
645
+ } catch {
646
+ }
647
+ setCookie(CONSENT_KEY, value, 365);
648
+ }
628
649
 
629
650
  // src/tracker.ts
630
651
  var MAX_PENDING_STAGE_EVENTS = 10;
@@ -667,7 +688,8 @@ var Outlit = class {
667
688
  window.addEventListener("beforeunload", handleExit);
668
689
  }
669
690
  this.isInitialized = true;
670
- if (options.autoTrack !== false) {
691
+ const consent = getConsentState();
692
+ if (consent === true || consent === null && options.autoTrack !== false) {
671
693
  this.enableTracking();
672
694
  }
673
695
  }
@@ -700,11 +722,38 @@ var Outlit = class {
700
722
  this.initCalendarTracking();
701
723
  }
702
724
  this.isTrackingEnabled = true;
725
+ setConsentState(true);
703
726
  if (this.pendingUser) {
704
727
  this.applyUser(this.pendingUser);
705
728
  this.pendingUser = null;
706
729
  }
707
730
  }
731
+ /**
732
+ * Disable tracking. Call this when a user revokes consent.
733
+ * This will:
734
+ * - Flush any pending events (captured while user had consent)
735
+ * - Stop the flush timer, pageview tracking, form tracking, and session tracking
736
+ * - Persist the opt-out decision so it's remembered across sessions
737
+ *
738
+ * The SDK instance remains usable — enableTracking() can be called again to re-enable.
739
+ */
740
+ async disableTracking() {
741
+ if (!this.isTrackingEnabled) {
742
+ setConsentState(false);
743
+ return;
744
+ }
745
+ if (this.flushTimer) {
746
+ clearInterval(this.flushTimer);
747
+ this.flushTimer = null;
748
+ }
749
+ stopAutocapture();
750
+ stopCalendarTracking();
751
+ stopSessionTracking();
752
+ await this.flush();
753
+ this.sessionTracker = null;
754
+ this.isTrackingEnabled = false;
755
+ setConsentState(false);
756
+ }
708
757
  /**
709
758
  * Check if tracking is currently enabled.
710
759
  */
@@ -1020,50 +1069,82 @@ var OutlitContext = (0, import_react.createContext)({
1020
1069
  isInitialized: false,
1021
1070
  isTrackingEnabled: false,
1022
1071
  enableTracking: () => {
1072
+ },
1073
+ disableTracking: () => {
1023
1074
  }
1024
1075
  });
1025
- function OutlitProvider({
1026
- children,
1027
- publicKey,
1028
- apiHost,
1029
- trackPageviews = true,
1030
- trackForms = true,
1031
- formFieldDenylist,
1032
- flushInterval,
1033
- autoTrack = true,
1034
- autoIdentify = true,
1035
- user
1036
- }) {
1076
+ function OutlitProvider(props) {
1077
+ const { children, user } = props;
1037
1078
  const outlitRef = (0, import_react.useRef)(null);
1038
1079
  const initializedRef = (0, import_react.useRef)(false);
1080
+ const isExternalClientRef = (0, import_react.useRef)(false);
1081
+ const [isInitialized, setIsInitialized] = (0, import_react.useState)(false);
1039
1082
  const [isTrackingEnabled, setIsTrackingEnabled] = (0, import_react.useState)(false);
1040
1083
  (0, import_react.useEffect)(() => {
1041
1084
  if (initializedRef.current) return;
1042
- outlitRef.current = new Outlit({
1043
- publicKey,
1044
- apiHost,
1045
- trackPageviews,
1046
- trackForms,
1047
- formFieldDenylist,
1048
- flushInterval,
1049
- autoTrack,
1050
- autoIdentify
1051
- });
1085
+ if (props.client) {
1086
+ if (process.env.NODE_ENV !== "production") {
1087
+ const configKeys = [
1088
+ "publicKey",
1089
+ "apiHost",
1090
+ "trackPageviews",
1091
+ "trackForms",
1092
+ "formFieldDenylist",
1093
+ "flushInterval",
1094
+ "autoTrack",
1095
+ "autoIdentify",
1096
+ "trackCalendarEmbeds",
1097
+ "trackEngagement",
1098
+ "idleTimeout"
1099
+ ];
1100
+ const conflicting = configKeys.filter(
1101
+ (k) => k in props && props[k] !== void 0
1102
+ );
1103
+ if (conflicting.length > 0) {
1104
+ console.warn(
1105
+ `[Outlit] Both \`client\` and config props (${conflicting.join(", ")}) were provided to OutlitProvider. The \`client\` instance will be used and config props will be ignored.`
1106
+ );
1107
+ }
1108
+ }
1109
+ outlitRef.current = props.client;
1110
+ isExternalClientRef.current = true;
1111
+ } else {
1112
+ const {
1113
+ publicKey,
1114
+ apiHost,
1115
+ trackPageviews = true,
1116
+ trackForms = true,
1117
+ formFieldDenylist,
1118
+ flushInterval,
1119
+ autoTrack = true,
1120
+ autoIdentify = true,
1121
+ trackCalendarEmbeds,
1122
+ trackEngagement,
1123
+ idleTimeout
1124
+ } = props;
1125
+ outlitRef.current = new Outlit({
1126
+ publicKey,
1127
+ apiHost,
1128
+ trackPageviews,
1129
+ trackForms,
1130
+ formFieldDenylist,
1131
+ flushInterval,
1132
+ autoTrack,
1133
+ autoIdentify,
1134
+ trackCalendarEmbeds,
1135
+ trackEngagement,
1136
+ idleTimeout
1137
+ });
1138
+ }
1052
1139
  initializedRef.current = true;
1140
+ setIsInitialized(true);
1053
1141
  setIsTrackingEnabled(outlitRef.current.isEnabled());
1054
1142
  return () => {
1055
- outlitRef.current?.shutdown();
1143
+ if (!isExternalClientRef.current) {
1144
+ outlitRef.current?.shutdown();
1145
+ }
1056
1146
  };
1057
- }, [
1058
- publicKey,
1059
- apiHost,
1060
- trackPageviews,
1061
- trackForms,
1062
- formFieldDenylist,
1063
- flushInterval,
1064
- autoTrack,
1065
- autoIdentify
1066
- ]);
1147
+ }, []);
1067
1148
  (0, import_react.useEffect)(() => {
1068
1149
  if (!outlitRef.current) return;
1069
1150
  if (user && (user.email || user.userId)) {
@@ -1078,14 +1159,21 @@ function OutlitProvider({
1078
1159
  setIsTrackingEnabled(true);
1079
1160
  }
1080
1161
  }, []);
1162
+ const disableTracking = (0, import_react.useCallback)(() => {
1163
+ if (outlitRef.current) {
1164
+ outlitRef.current.disableTracking();
1165
+ setIsTrackingEnabled(false);
1166
+ }
1167
+ }, []);
1081
1168
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1082
1169
  OutlitContext.Provider,
1083
1170
  {
1084
1171
  value: {
1085
1172
  outlit: outlitRef.current,
1086
- isInitialized: initializedRef.current,
1173
+ isInitialized,
1087
1174
  isTrackingEnabled,
1088
- enableTracking
1175
+ enableTracking,
1176
+ disableTracking
1089
1177
  },
1090
1178
  children
1091
1179
  }
@@ -1095,7 +1183,7 @@ function OutlitProvider({
1095
1183
  // src/react/hooks.ts
1096
1184
  var import_react2 = require("react");
1097
1185
  function useOutlit() {
1098
- const { outlit, isInitialized, isTrackingEnabled, enableTracking } = (0, import_react2.useContext)(OutlitContext);
1186
+ const { outlit, isInitialized, isTrackingEnabled, enableTracking, disableTracking } = (0, import_react2.useContext)(OutlitContext);
1099
1187
  const track = (0, import_react2.useCallback)(
1100
1188
  (eventName, properties) => {
1101
1189
  if (!outlit) {
@@ -1226,7 +1314,8 @@ function useOutlit() {
1226
1314
  },
1227
1315
  isInitialized,
1228
1316
  isTrackingEnabled,
1229
- enableTracking
1317
+ enableTracking,
1318
+ disableTracking
1230
1319
  };
1231
1320
  }
1232
1321
  function useTrack() {