@affiliateo/web 1.5.0 → 1.5.2

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.
Files changed (2) hide show
  1. package/dist/provider.js +41 -57
  2. package/package.json +1 -1
package/dist/provider.js CHANGED
@@ -14,7 +14,8 @@ function setCookie(name, value, days) {
14
14
  return;
15
15
  const dt = new Date();
16
16
  dt.setTime(dt.getTime() + days * 864e5);
17
- document.cookie = `${name}=${value};expires=${dt.toUTCString()};path=/;SameSite=Lax`;
17
+ const secure = window.location.protocol === 'https:' ? ';Secure' : '';
18
+ document.cookie = `${name}=${value};expires=${dt.toUTCString()};path=/;SameSite=Lax${secure}`;
18
19
  }
19
20
  // Generate UUID
20
21
  function uuid() {
@@ -43,8 +44,6 @@ export function AffiliateoProvider({ campaignId, apiUrl, children, }) {
43
44
  });
44
45
  const apiBase = (apiUrl || DEFAULT_API_URL).replace(/\/$/, '');
45
46
  const campaignIdRef = useRef(campaignId);
46
- const visitorIdRef = useRef(null);
47
- const refRef = useRef(null);
48
47
  const sentRef = useRef(false);
49
48
  useEffect(() => {
50
49
  if (typeof window === 'undefined')
@@ -54,28 +53,32 @@ export function AffiliateoProvider({ campaignId, apiUrl, children, }) {
54
53
  if (!vid) {
55
54
  vid = uuid();
56
55
  }
57
- setCookie('affiliateo_visitor_id', vid, 365);
58
- setCookie('affiliateo_campaign_id', campaignIdRef.current, 365);
59
- visitorIdRef.current = vid;
56
+ setCookie('affiliateo_visitor_id', vid, 90);
57
+ setCookie('affiliateo_campaign_id', campaignIdRef.current, 90);
60
58
  // Read ref from URL params
61
59
  const params = new URLSearchParams(window.location.search);
62
60
  const ref = params.get('ref') || params.get('via') || params.get('source') || getCookie('affiliateo_ref') || null;
63
61
  if (ref) {
64
- setCookie('affiliateo_ref', ref, 365);
62
+ setCookie('affiliateo_ref', ref, 90);
65
63
  }
66
- refRef.current = ref;
67
64
  setState({ refCode: ref, visitorId: vid });
68
- // Send event helper
69
- function send(type) {
65
+ // Throttle: max 1 pageview per second
66
+ let lastSendTime = 0;
67
+ // Send pageview
68
+ function sendPageview() {
69
+ const now = Date.now();
70
+ if (now - lastSendTime < 1000)
71
+ return;
72
+ lastSendTime = now;
70
73
  const payload = {
71
- type,
74
+ type: 'pageview',
72
75
  campaign_id: campaignIdRef.current,
73
- visitor_id: visitorIdRef.current,
76
+ visitor_id: vid,
74
77
  url: window.location.href,
75
78
  path: window.location.pathname,
76
79
  hostname: window.location.hostname,
77
80
  referrer: document.referrer || '',
78
- ref: refRef.current || '',
81
+ ref: ref || getCookie('affiliateo_ref') || '',
79
82
  utm_source: params.get('utm_source') || '',
80
83
  utm_medium: params.get('utm_medium') || '',
81
84
  utm_campaign: params.get('utm_campaign') || '',
@@ -94,55 +97,36 @@ export function AffiliateoProvider({ campaignId, apiUrl, children, }) {
94
97
  }).catch(() => { });
95
98
  }
96
99
  }
97
- // Session cookie — 30 min expiry. Only send session_start if no active session.
98
- const SESSION_COOKIE = 'affiliateo_session';
99
- const hasActiveSession = !!getCookie(SESSION_COOKIE);
100
- if (!hasActiveSession && !sentRef.current) {
100
+ // Send pageview on mount (first load or page navigation)
101
+ if (!sentRef.current) {
101
102
  sentRef.current = true;
102
- send('session_start');
103
- }
104
- // Refresh session cookie (30 min sliding window)
105
- setCookie(SESSION_COOKIE, '1', 1 / 48); // 30 minutes = 1/48 of a day
106
- // session_end only fires when actually leaving the site (tab close, navigate away)
107
- // NOT on SPA page navigations or tab switching
108
- let ended = false;
109
- function onLeave() {
110
- if (ended)
111
- return;
112
- ended = true;
113
- send('session_end');
103
+ sendPageview();
114
104
  }
115
- window.addEventListener('beforeunload', onLeave);
116
- // On visibility change: refresh session cookie when visible, send session_end after 30 min hidden
117
- let hiddenTimer = null;
118
- function onVisibilityChange() {
119
- if (document.visibilityState === 'hidden') {
120
- // Start a 30-min timer — if user doesn't come back, session is over
121
- hiddenTimer = setTimeout(() => {
122
- send('session_end');
123
- ended = true;
124
- }, 30 * 60 * 1000);
125
- }
126
- else if (document.visibilityState === 'visible') {
127
- // User came back — cancel the timer, refresh session cookie
128
- if (hiddenTimer) {
129
- clearTimeout(hiddenTimer);
130
- hiddenTimer = null;
131
- }
132
- // If session expired while away (30+ min), start a new one
133
- if (!getCookie(SESSION_COOKIE)) {
134
- ended = false;
135
- send('session_start');
136
- }
137
- setCookie(SESSION_COOKIE, '1', 1 / 48);
105
+ // For SPA: listen for URL changes (popstate + pushState/replaceState)
106
+ let lastPath = window.location.pathname;
107
+ function onUrlChange() {
108
+ if (window.location.pathname !== lastPath) {
109
+ lastPath = window.location.pathname;
110
+ sendPageview();
138
111
  }
139
112
  }
140
- document.addEventListener('visibilitychange', onVisibilityChange);
113
+ // Listen for browser back/forward
114
+ window.addEventListener('popstate', onUrlChange);
115
+ // Patch pushState/replaceState to detect SPA navigations
116
+ const origPushState = history.pushState;
117
+ const origReplaceState = history.replaceState;
118
+ history.pushState = function (...args) {
119
+ origPushState.apply(this, args);
120
+ onUrlChange();
121
+ };
122
+ history.replaceState = function (...args) {
123
+ origReplaceState.apply(this, args);
124
+ onUrlChange();
125
+ };
141
126
  return () => {
142
- window.removeEventListener('beforeunload', onLeave);
143
- document.removeEventListener('visibilitychange', onVisibilityChange);
144
- if (hiddenTimer)
145
- clearTimeout(hiddenTimer);
127
+ window.removeEventListener('popstate', onUrlChange);
128
+ history.pushState = origPushState;
129
+ history.replaceState = origReplaceState;
146
130
  };
147
131
  }, [campaignId, apiBase]);
148
132
  return (_jsx(AffiliateoContext.Provider, { value: state, children: children }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@affiliateo/web",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Affiliateo web SDK — affiliate attribution and session tracking for web apps",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",