@outlit/browser 0.0.0-canary-202512180440-754f4ab-20251218044038 → 0.0.0-canary-202601161920-3feb1db-20260116192046

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,9 +1,9 @@
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 { c as OutlitOptions, O as Outlit } from '../tracker-DFcTv3EM.mjs';
4
+ import { j as OutlitOptions, U as UserIdentity, O as Outlit } from '../tracker-DK-2gYCi.mjs';
5
5
  import { BrowserTrackOptions, BrowserIdentifyOptions } from '@outlit/core';
6
- export { BrowserIdentifyOptions, BrowserTrackOptions, TrackerConfig } from '@outlit/core';
6
+ export { BrowserIdentifyOptions, BrowserTrackOptions, ExplicitJourneyStage, TrackerConfig } from '@outlit/core';
7
7
 
8
8
  interface OutlitContextValue {
9
9
  outlit: Outlit | null;
@@ -26,6 +26,29 @@ interface OutlitProviderProps extends Omit<OutlitOptions, "trackPageviews"> {
26
26
  * @default true
27
27
  */
28
28
  autoTrack?: boolean;
29
+ /**
30
+ * Current user identity.
31
+ * When provided with email or userId, calls setUser() to identify the user.
32
+ * When null, undefined, or missing identity fields, calls clearUser().
33
+ *
34
+ * This is the recommended way to handle user identity in server-rendered apps:
35
+ * pass the user from your auth system as a prop.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * // Server component (layout.tsx)
40
+ * const session = await auth()
41
+ * return (
42
+ * <OutlitProvider
43
+ * publicKey="pk_xxx"
44
+ * user={session?.user ? { email: session.user.email, userId: session.user.id } : null}
45
+ * >
46
+ * {children}
47
+ * </OutlitProvider>
48
+ * )
49
+ * ```
50
+ */
51
+ user?: UserIdentity | null;
29
52
  }
30
53
  /**
31
54
  * Outlit Provider component.
@@ -67,7 +90,7 @@ interface OutlitProviderProps extends Omit<OutlitOptions, "trackPageviews"> {
67
90
  * }
68
91
  * ```
69
92
  */
70
- declare function OutlitProvider({ children, publicKey, apiHost, trackPageviews, trackForms, formFieldDenylist, flushInterval, autoTrack, autoIdentify, }: OutlitProviderProps): react_jsx_runtime.JSX.Element;
93
+ declare function OutlitProvider({ children, publicKey, apiHost, trackPageviews, trackForms, formFieldDenylist, flushInterval, autoTrack, autoIdentify, user, }: OutlitProviderProps): react_jsx_runtime.JSX.Element;
71
94
 
72
95
  interface UseOutlitReturn {
73
96
  /**
@@ -84,6 +107,35 @@ interface UseOutlitReturn {
84
107
  * Returns null if tracking is not enabled.
85
108
  */
86
109
  getVisitorId: () => string | null;
110
+ /**
111
+ * Set the current user identity.
112
+ * Use this for persistent identity after login.
113
+ */
114
+ setUser: (identity: UserIdentity) => void;
115
+ /**
116
+ * Clear the current user identity (on logout).
117
+ */
118
+ clearUser: () => void;
119
+ /**
120
+ * Mark the current user as activated.
121
+ * Call after a user completes onboarding or activation milestone.
122
+ */
123
+ activate: (properties?: Record<string, string | number | boolean | null>) => void;
124
+ /**
125
+ * Mark the current user as engaged.
126
+ * Call when user reaches a usage milestone.
127
+ */
128
+ engaged: (properties?: Record<string, string | number | boolean | null>) => void;
129
+ /**
130
+ * Mark the current user as paid.
131
+ * Call after successful payment/subscription.
132
+ */
133
+ paid: (properties?: Record<string, string | number | boolean | null>) => void;
134
+ /**
135
+ * Mark the current user as churned.
136
+ * Call when subscription is cancelled.
137
+ */
138
+ churned: (properties?: Record<string, string | number | boolean | null>) => void;
87
139
  /**
88
140
  * Whether Outlit is initialized.
89
141
  */
@@ -167,4 +219,4 @@ declare function useTrack(): (eventName: string, properties?: BrowserTrackOption
167
219
  */
168
220
  declare function useIdentify(): (options: BrowserIdentifyOptions) => void;
169
221
 
170
- export { OutlitContext, type OutlitContextValue, OutlitProvider, type OutlitProviderProps, type UseOutlitReturn, useIdentify, useOutlit, useTrack };
222
+ export { OutlitContext, type OutlitContextValue, OutlitProvider, type OutlitProviderProps, type UseOutlitReturn, UserIdentity, useIdentify, useOutlit, useTrack };
@@ -1,9 +1,9 @@
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 { c as OutlitOptions, O as Outlit } from '../tracker-DFcTv3EM.js';
4
+ import { j as OutlitOptions, U as UserIdentity, O as Outlit } from '../tracker-DK-2gYCi.js';
5
5
  import { BrowserTrackOptions, BrowserIdentifyOptions } from '@outlit/core';
6
- export { BrowserIdentifyOptions, BrowserTrackOptions, TrackerConfig } from '@outlit/core';
6
+ export { BrowserIdentifyOptions, BrowserTrackOptions, ExplicitJourneyStage, TrackerConfig } from '@outlit/core';
7
7
 
8
8
  interface OutlitContextValue {
9
9
  outlit: Outlit | null;
@@ -26,6 +26,29 @@ interface OutlitProviderProps extends Omit<OutlitOptions, "trackPageviews"> {
26
26
  * @default true
27
27
  */
28
28
  autoTrack?: boolean;
29
+ /**
30
+ * Current user identity.
31
+ * When provided with email or userId, calls setUser() to identify the user.
32
+ * When null, undefined, or missing identity fields, calls clearUser().
33
+ *
34
+ * This is the recommended way to handle user identity in server-rendered apps:
35
+ * pass the user from your auth system as a prop.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * // Server component (layout.tsx)
40
+ * const session = await auth()
41
+ * return (
42
+ * <OutlitProvider
43
+ * publicKey="pk_xxx"
44
+ * user={session?.user ? { email: session.user.email, userId: session.user.id } : null}
45
+ * >
46
+ * {children}
47
+ * </OutlitProvider>
48
+ * )
49
+ * ```
50
+ */
51
+ user?: UserIdentity | null;
29
52
  }
30
53
  /**
31
54
  * Outlit Provider component.
@@ -67,7 +90,7 @@ interface OutlitProviderProps extends Omit<OutlitOptions, "trackPageviews"> {
67
90
  * }
68
91
  * ```
69
92
  */
70
- declare function OutlitProvider({ children, publicKey, apiHost, trackPageviews, trackForms, formFieldDenylist, flushInterval, autoTrack, autoIdentify, }: OutlitProviderProps): react_jsx_runtime.JSX.Element;
93
+ declare function OutlitProvider({ children, publicKey, apiHost, trackPageviews, trackForms, formFieldDenylist, flushInterval, autoTrack, autoIdentify, user, }: OutlitProviderProps): react_jsx_runtime.JSX.Element;
71
94
 
72
95
  interface UseOutlitReturn {
73
96
  /**
@@ -84,6 +107,35 @@ interface UseOutlitReturn {
84
107
  * Returns null if tracking is not enabled.
85
108
  */
86
109
  getVisitorId: () => string | null;
110
+ /**
111
+ * Set the current user identity.
112
+ * Use this for persistent identity after login.
113
+ */
114
+ setUser: (identity: UserIdentity) => void;
115
+ /**
116
+ * Clear the current user identity (on logout).
117
+ */
118
+ clearUser: () => void;
119
+ /**
120
+ * Mark the current user as activated.
121
+ * Call after a user completes onboarding or activation milestone.
122
+ */
123
+ activate: (properties?: Record<string, string | number | boolean | null>) => void;
124
+ /**
125
+ * Mark the current user as engaged.
126
+ * Call when user reaches a usage milestone.
127
+ */
128
+ engaged: (properties?: Record<string, string | number | boolean | null>) => void;
129
+ /**
130
+ * Mark the current user as paid.
131
+ * Call after successful payment/subscription.
132
+ */
133
+ paid: (properties?: Record<string, string | number | boolean | null>) => void;
134
+ /**
135
+ * Mark the current user as churned.
136
+ * Call when subscription is cancelled.
137
+ */
138
+ churned: (properties?: Record<string, string | number | boolean | null>) => void;
87
139
  /**
88
140
  * Whether Outlit is initialized.
89
141
  */
@@ -167,4 +219,4 @@ declare function useTrack(): (eventName: string, properties?: BrowserTrackOption
167
219
  */
168
220
  declare function useIdentify(): (options: BrowserIdentifyOptions) => void;
169
221
 
170
- export { OutlitContext, type OutlitContextValue, OutlitProvider, type OutlitProviderProps, type UseOutlitReturn, useIdentify, useOutlit, useTrack };
222
+ export { OutlitContext, type OutlitContextValue, OutlitProvider, type OutlitProviderProps, type UseOutlitReturn, UserIdentity, useIdentify, useOutlit, useTrack };
@@ -251,6 +251,7 @@ var DEFAULT_IDLE_TIMEOUT = 3e4;
251
251
  var SESSION_TIMEOUT = 30 * 60 * 1e3;
252
252
  var TIME_UPDATE_INTERVAL = 1e3;
253
253
  var MIN_SPURIOUS_THRESHOLD = 50;
254
+ var MIN_PAGE_TIME_FOR_ENGAGEMENT = 500;
254
255
  var SESSION_ID_KEY = "outlit_session_id";
255
256
  var SESSION_LAST_ACTIVITY_KEY = "outlit_session_last_activity";
256
257
  var SessionTracker = class {
@@ -295,7 +296,8 @@ var SessionTracker = class {
295
296
  this.updateActiveTime();
296
297
  const totalTimeMs = Date.now() - this.state.pageEntryTime;
297
298
  const isSpuriousEvent = this.state.activeTimeMs < MIN_SPURIOUS_THRESHOLD && totalTimeMs < MIN_SPURIOUS_THRESHOLD;
298
- if (!isSpuriousEvent) {
299
+ const isTooSoonAfterNavigation = totalTimeMs < MIN_PAGE_TIME_FOR_ENGAGEMENT;
300
+ if (!isSpuriousEvent && !isTooSoonAfterNavigation) {
299
301
  const event = (0, import_core2.buildEngagementEvent)({
300
302
  url: this.state.currentUrl,
301
303
  referrer: document.referrer,
@@ -481,6 +483,7 @@ var SessionTracker = class {
481
483
  this.checkSessionExpiry();
482
484
  this.state.lastActiveTime = Date.now();
483
485
  this.state.hasEmittedEngagement = false;
486
+ this.updateSessionActivity();
484
487
  }
485
488
  }
486
489
  /**
@@ -636,6 +639,9 @@ var Outlit = class {
636
639
  options;
637
640
  hasHandledExit = false;
638
641
  sessionTracker = null;
642
+ // User identity state for stage events
643
+ currentUser = null;
644
+ pendingUser = null;
639
645
  constructor(options) {
640
646
  this.publicKey = options.publicKey;
641
647
  this.apiHost = options.apiHost ?? import_core3.DEFAULT_API_HOST;
@@ -681,9 +687,7 @@ var Outlit = class {
681
687
  }
682
688
  this.visitorId = getOrCreateVisitorId();
683
689
  this.startFlushTimer();
684
- if (this.options.trackEngagement !== false) {
685
- this.initSessionTracking();
686
- }
690
+ this.initSessionTracking();
687
691
  if (this.options.trackPageviews !== false) {
688
692
  this.initPageviewTracking();
689
693
  }
@@ -694,6 +698,10 @@ var Outlit = class {
694
698
  this.initCalendarTracking();
695
699
  }
696
700
  this.isTrackingEnabled = true;
701
+ if (this.pendingUser) {
702
+ this.applyUser(this.pendingUser);
703
+ this.pendingUser = null;
704
+ }
697
705
  }
698
706
  /**
699
707
  * Check if tracking is currently enabled.
@@ -720,12 +728,21 @@ var Outlit = class {
720
728
  /**
721
729
  * Identify the current visitor.
722
730
  * Links the anonymous visitor to a known user.
731
+ *
732
+ * When email or userId is provided, also sets the current user identity
733
+ * for stage events (activate, engaged, paid).
723
734
  */
724
735
  identify(options) {
725
736
  if (!this.isTrackingEnabled) {
726
737
  console.warn("[Outlit] Tracking not enabled. Call enableTracking() first.");
727
738
  return;
728
739
  }
740
+ if (options.email || options.userId) {
741
+ this.currentUser = {
742
+ email: options.email,
743
+ userId: options.userId
744
+ };
745
+ }
729
746
  const event = (0, import_core3.buildIdentifyEvent)({
730
747
  url: window.location.href,
731
748
  referrer: document.referrer,
@@ -735,6 +752,98 @@ var Outlit = class {
735
752
  });
736
753
  this.enqueue(event);
737
754
  }
755
+ /**
756
+ * Set the current user identity.
757
+ * This is useful for SPA applications where you know the user's identity
758
+ * after authentication. Calls identify() under the hood.
759
+ *
760
+ * If called before tracking is enabled, the identity is stored as pending
761
+ * and applied automatically when enableTracking() is called.
762
+ *
763
+ * Note: Both setUser() and identify() enable stage events. The difference is
764
+ * setUser() can be called before tracking is enabled (identity is queued),
765
+ * while identify() requires tracking to be enabled first.
766
+ */
767
+ setUser(identity) {
768
+ if (!identity.email && !identity.userId) {
769
+ console.warn("[Outlit] setUser requires at least email or userId");
770
+ return;
771
+ }
772
+ if (!this.isTrackingEnabled) {
773
+ this.pendingUser = identity;
774
+ return;
775
+ }
776
+ this.applyUser(identity);
777
+ }
778
+ /**
779
+ * Clear the current user identity.
780
+ * Call this when the user logs out.
781
+ */
782
+ clearUser() {
783
+ this.currentUser = null;
784
+ this.pendingUser = null;
785
+ }
786
+ /**
787
+ * Apply user identity and send identify event.
788
+ */
789
+ applyUser(identity) {
790
+ this.currentUser = identity;
791
+ this.identify({ email: identity.email, userId: identity.userId, traits: identity.traits });
792
+ }
793
+ /**
794
+ * Mark the current user as activated.
795
+ * This is typically called after a user completes onboarding or a key activation milestone.
796
+ * Requires the user to be identified (via setUser or identify with userId).
797
+ */
798
+ activate(properties) {
799
+ this.sendStageEvent("activated", properties);
800
+ }
801
+ /**
802
+ * Mark the current user as engaged.
803
+ * This is typically called when a user reaches a usage milestone.
804
+ * Can also be computed automatically by the engagement cron.
805
+ */
806
+ engaged(properties) {
807
+ this.sendStageEvent("engaged", properties);
808
+ }
809
+ /**
810
+ * Mark the current user as paid.
811
+ * This is typically called after a successful payment/subscription.
812
+ * Can also be triggered by Stripe integration.
813
+ */
814
+ paid(properties) {
815
+ this.sendStageEvent("paid", properties);
816
+ }
817
+ /**
818
+ * Mark the current user as churned.
819
+ * This is typically called when a subscription is cancelled.
820
+ * Can also be triggered by Stripe integration.
821
+ */
822
+ churned(properties) {
823
+ this.sendStageEvent("churned", properties);
824
+ }
825
+ /**
826
+ * Internal method to send a stage event.
827
+ */
828
+ sendStageEvent(stage, properties) {
829
+ if (!this.isTrackingEnabled) {
830
+ console.warn("[Outlit] Tracking not enabled. Call enableTracking() first.");
831
+ return;
832
+ }
833
+ if (!this.currentUser) {
834
+ console.warn(
835
+ `[Outlit] Cannot call ${stage}() without setting user identity. Call setUser() or identify() first.`
836
+ );
837
+ return;
838
+ }
839
+ const event = (0, import_core3.buildStageEvent)({
840
+ url: window.location.href,
841
+ referrer: document.referrer,
842
+ stage,
843
+ properties
844
+ });
845
+ this.enqueue(event);
846
+ }
738
847
  /**
739
848
  * Get the current visitor ID.
740
849
  * Returns null if tracking is not enabled.
@@ -770,8 +879,8 @@ var Outlit = class {
770
879
  // ============================================
771
880
  initSessionTracking() {
772
881
  this.sessionTracker = initSessionTracking({
773
- onEngagement: (event) => {
774
- this.enqueue(event);
882
+ // Only emit engagement events when trackEngagement is enabled (default: true)
883
+ onEngagement: this.options.trackEngagement !== false ? (event) => this.enqueue(event) : () => {
775
884
  },
776
885
  idleTimeout: this.options.idleTimeout
777
886
  });
@@ -842,7 +951,9 @@ var Outlit = class {
842
951
  async sendEvents(events) {
843
952
  if (events.length === 0) return;
844
953
  if (!this.visitorId) return;
845
- const payload = (0, import_core3.buildIngestPayload)(this.visitorId, "client", events);
954
+ const userIdentity = this.currentUser ?? void 0;
955
+ const sessionId = this.sessionTracker?.getSessionId();
956
+ const payload = (0, import_core3.buildIngestPayload)(this.visitorId, "client", events, userIdentity, sessionId);
846
957
  const url = `${this.apiHost}/api/i/v1/${this.publicKey}/events`;
847
958
  try {
848
959
  if (typeof navigator !== "undefined" && navigator.sendBeacon) {
@@ -882,7 +993,8 @@ function OutlitProvider({
882
993
  formFieldDenylist,
883
994
  flushInterval,
884
995
  autoTrack = true,
885
- autoIdentify = true
996
+ autoIdentify = true,
997
+ user
886
998
  }) {
887
999
  const outlitRef = (0, import_react.useRef)(null);
888
1000
  const initializedRef = (0, import_react.useRef)(false);
@@ -914,6 +1026,14 @@ function OutlitProvider({
914
1026
  autoTrack,
915
1027
  autoIdentify
916
1028
  ]);
1029
+ (0, import_react.useEffect)(() => {
1030
+ if (!outlitRef.current) return;
1031
+ if (user && (user.email || user.userId)) {
1032
+ outlitRef.current.setUser(user);
1033
+ } else {
1034
+ outlitRef.current.clearUser();
1035
+ }
1036
+ }, [user]);
917
1037
  const enableTracking = (0, import_react.useCallback)(() => {
918
1038
  if (outlitRef.current) {
919
1039
  outlitRef.current.enableTracking();
@@ -962,10 +1082,73 @@ function useOutlit() {
962
1082
  if (!outlit) return null;
963
1083
  return outlit.getVisitorId();
964
1084
  }, [outlit]);
1085
+ const setUser = (0, import_react2.useCallback)(
1086
+ (identity) => {
1087
+ if (!outlit) {
1088
+ console.warn("[Outlit] Not initialized. Make sure OutlitProvider is mounted.");
1089
+ return;
1090
+ }
1091
+ outlit.setUser(identity);
1092
+ },
1093
+ [outlit]
1094
+ );
1095
+ const clearUser = (0, import_react2.useCallback)(() => {
1096
+ if (!outlit) {
1097
+ console.warn("[Outlit] Not initialized. Make sure OutlitProvider is mounted.");
1098
+ return;
1099
+ }
1100
+ outlit.clearUser();
1101
+ }, [outlit]);
1102
+ const activate = (0, import_react2.useCallback)(
1103
+ (properties) => {
1104
+ if (!outlit) {
1105
+ console.warn("[Outlit] Not initialized. Make sure OutlitProvider is mounted.");
1106
+ return;
1107
+ }
1108
+ outlit.activate(properties);
1109
+ },
1110
+ [outlit]
1111
+ );
1112
+ const engaged = (0, import_react2.useCallback)(
1113
+ (properties) => {
1114
+ if (!outlit) {
1115
+ console.warn("[Outlit] Not initialized. Make sure OutlitProvider is mounted.");
1116
+ return;
1117
+ }
1118
+ outlit.engaged(properties);
1119
+ },
1120
+ [outlit]
1121
+ );
1122
+ const paid = (0, import_react2.useCallback)(
1123
+ (properties) => {
1124
+ if (!outlit) {
1125
+ console.warn("[Outlit] Not initialized. Make sure OutlitProvider is mounted.");
1126
+ return;
1127
+ }
1128
+ outlit.paid(properties);
1129
+ },
1130
+ [outlit]
1131
+ );
1132
+ const churned = (0, import_react2.useCallback)(
1133
+ (properties) => {
1134
+ if (!outlit) {
1135
+ console.warn("[Outlit] Not initialized. Make sure OutlitProvider is mounted.");
1136
+ return;
1137
+ }
1138
+ outlit.churned(properties);
1139
+ },
1140
+ [outlit]
1141
+ );
965
1142
  return {
966
1143
  track,
967
1144
  identify,
968
1145
  getVisitorId,
1146
+ setUser,
1147
+ clearUser,
1148
+ activate,
1149
+ engaged,
1150
+ paid,
1151
+ churned,
969
1152
  isInitialized,
970
1153
  isTrackingEnabled,
971
1154
  enableTracking