@arthurreira/analytics 0.17.0 → 0.19.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.
- package/dist/af-analytics.umd.js +139 -11
- package/dist/client.js +133 -3
- package/dist/index.d.ts +10 -1
- package/dist/index.js +26 -0
- package/package.json +1 -1
package/dist/af-analytics.umd.js
CHANGED
|
@@ -38,10 +38,39 @@ var AfAnalytics = (() => {
|
|
|
38
38
|
// src/vanilla.ts
|
|
39
39
|
var vanilla_exports = {};
|
|
40
40
|
__export(vanilla_exports, {
|
|
41
|
-
|
|
41
|
+
consent: () => consent,
|
|
42
|
+
init: () => init,
|
|
43
|
+
isOptedOut: () => isOptedOut,
|
|
44
|
+
optIn: () => optIn,
|
|
45
|
+
optOut: () => optOut
|
|
42
46
|
});
|
|
47
|
+
|
|
48
|
+
// src/lib/consent.ts
|
|
49
|
+
var CONSENT_KEY = "af_analytics_opted_out";
|
|
50
|
+
function optOut() {
|
|
51
|
+
if (typeof localStorage === "undefined") return;
|
|
52
|
+
localStorage.setItem(CONSENT_KEY, "1");
|
|
53
|
+
}
|
|
54
|
+
function optIn() {
|
|
55
|
+
if (typeof localStorage === "undefined") return;
|
|
56
|
+
localStorage.removeItem(CONSENT_KEY);
|
|
57
|
+
}
|
|
58
|
+
function isOptedOut() {
|
|
59
|
+
if (typeof localStorage !== "undefined" && localStorage.getItem(CONSENT_KEY) === "1") return true;
|
|
60
|
+
if (typeof navigator !== "undefined" && navigator.doNotTrack === "1") return true;
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
function consent(enabled) {
|
|
64
|
+
if (enabled) optIn();
|
|
65
|
+
else optOut();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/vanilla.ts
|
|
43
69
|
var _currentScript = document.currentScript;
|
|
44
70
|
var SESSION_EXPIRY_MS = 30 * 60 * 1e3;
|
|
71
|
+
var IDLE_TOUCH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
72
|
+
var BC_CHANNEL = "af_analytics_session";
|
|
73
|
+
var BC_ADOPT_TIMEOUT_MS = 50;
|
|
45
74
|
function readConfig() {
|
|
46
75
|
var _a, _b, _c, _d;
|
|
47
76
|
const script = _currentScript != null ? _currentScript : document.querySelector("script[data-api-key]");
|
|
@@ -94,6 +123,7 @@ var AfAnalytics = (() => {
|
|
|
94
123
|
}
|
|
95
124
|
}
|
|
96
125
|
function send(apiUrl, apiKey, payload) {
|
|
126
|
+
if (isOptedOut()) return;
|
|
97
127
|
fetch(`${apiUrl}/events`, {
|
|
98
128
|
method: "POST",
|
|
99
129
|
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
@@ -110,6 +140,49 @@ var AfAnalytics = (() => {
|
|
|
110
140
|
page_url: `${window.location.origin}${window.location.pathname}`
|
|
111
141
|
};
|
|
112
142
|
}
|
|
143
|
+
function requestSessionFromPeer() {
|
|
144
|
+
if (typeof BroadcastChannel === "undefined") return Promise.resolve(null);
|
|
145
|
+
return new Promise((resolve) => {
|
|
146
|
+
let channel;
|
|
147
|
+
try {
|
|
148
|
+
channel = new BroadcastChannel(BC_CHANNEL);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
resolve(null);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const timer = setTimeout(() => {
|
|
154
|
+
channel.close();
|
|
155
|
+
resolve(null);
|
|
156
|
+
}, BC_ADOPT_TIMEOUT_MS);
|
|
157
|
+
channel.onmessage = (e) => {
|
|
158
|
+
var _a;
|
|
159
|
+
if (((_a = e.data) == null ? void 0 : _a.type) === "session_adopt" && typeof e.data.session_id === "string") {
|
|
160
|
+
clearTimeout(timer);
|
|
161
|
+
channel.close();
|
|
162
|
+
resolve(e.data.session_id);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
channel.postMessage({ type: "session_claim" });
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function openSessionResponder(sessionId) {
|
|
169
|
+
if (typeof BroadcastChannel === "undefined") return () => {
|
|
170
|
+
};
|
|
171
|
+
let channel;
|
|
172
|
+
try {
|
|
173
|
+
channel = new BroadcastChannel(BC_CHANNEL);
|
|
174
|
+
} catch (e) {
|
|
175
|
+
return () => {
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
channel.onmessage = (e) => {
|
|
179
|
+
var _a;
|
|
180
|
+
if (((_a = e.data) == null ? void 0 : _a.type) === "session_claim") {
|
|
181
|
+
channel.postMessage({ type: "session_adopt", session_id: sessionId });
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
return () => channel.close();
|
|
185
|
+
}
|
|
113
186
|
async function createRemoteSession(apiUrl, apiKey, visitorId) {
|
|
114
187
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
115
188
|
const nav = navigator;
|
|
@@ -151,9 +224,17 @@ var AfAnalytics = (() => {
|
|
|
151
224
|
touchSession();
|
|
152
225
|
return stored;
|
|
153
226
|
}
|
|
227
|
+
const adopted = await requestSessionFromPeer();
|
|
228
|
+
if (adopted) {
|
|
229
|
+
localStorage.setItem("af_session_id", adopted);
|
|
230
|
+
localStorage.setItem("af_session_last_activity", String(Date.now()));
|
|
231
|
+
return adopted;
|
|
232
|
+
}
|
|
154
233
|
const id = await createRemoteSession(apiUrl, apiKey, getVisitorId());
|
|
155
|
-
|
|
156
|
-
|
|
234
|
+
if (id) {
|
|
235
|
+
localStorage.setItem("af_session_id", id);
|
|
236
|
+
localStorage.setItem("af_session_last_activity", String(Date.now()));
|
|
237
|
+
}
|
|
157
238
|
return id;
|
|
158
239
|
}
|
|
159
240
|
function trackPageview(apiUrl, apiKey, sessionId) {
|
|
@@ -181,6 +262,7 @@ var AfAnalytics = (() => {
|
|
|
181
262
|
send(apiUrl, apiKey, __spreadValues(__spreadValues({}, baseFields(sessionId, "js_exception")), fields));
|
|
182
263
|
}
|
|
183
264
|
function init() {
|
|
265
|
+
if (isOptedOut()) return;
|
|
184
266
|
const config = readConfig();
|
|
185
267
|
if (!config) return;
|
|
186
268
|
const { apiKey, apiUrl } = config;
|
|
@@ -188,6 +270,9 @@ var AfAnalytics = (() => {
|
|
|
188
270
|
const pending = [];
|
|
189
271
|
let sessionEnded = false;
|
|
190
272
|
let lastScrollDepth = 0;
|
|
273
|
+
let closeResponder = () => {
|
|
274
|
+
};
|
|
275
|
+
let idleTimer = null;
|
|
191
276
|
function enqueue(fn) {
|
|
192
277
|
if (sessionId) {
|
|
193
278
|
touchSession();
|
|
@@ -196,9 +281,43 @@ var AfAnalytics = (() => {
|
|
|
196
281
|
pending.push(fn);
|
|
197
282
|
}
|
|
198
283
|
}
|
|
199
|
-
|
|
284
|
+
function startSession(id) {
|
|
200
285
|
sessionId = id;
|
|
286
|
+
closeResponder = openSessionResponder(id);
|
|
287
|
+
idleTimer = setInterval(touchSession, IDLE_TOUCH_INTERVAL_MS);
|
|
201
288
|
pending.splice(0).forEach((fn) => fn(id));
|
|
289
|
+
}
|
|
290
|
+
function endSession() {
|
|
291
|
+
if (!sessionId || sessionEnded) return;
|
|
292
|
+
sessionEnded = true;
|
|
293
|
+
if (idleTimer !== null) {
|
|
294
|
+
clearInterval(idleTimer);
|
|
295
|
+
idleTimer = null;
|
|
296
|
+
}
|
|
297
|
+
closeResponder();
|
|
298
|
+
navigator.sendBeacon(`${apiUrl}/sessions/${sessionId}/end`);
|
|
299
|
+
clearSession();
|
|
300
|
+
}
|
|
301
|
+
function restartSession() {
|
|
302
|
+
sessionEnded = false;
|
|
303
|
+
sessionId = null;
|
|
304
|
+
if (idleTimer !== null) {
|
|
305
|
+
clearInterval(idleTimer);
|
|
306
|
+
idleTimer = null;
|
|
307
|
+
}
|
|
308
|
+
closeResponder();
|
|
309
|
+
closeResponder = () => {
|
|
310
|
+
};
|
|
311
|
+
clearSession();
|
|
312
|
+
getOrCreateSession(apiUrl, apiKey).then((id) => {
|
|
313
|
+
if (!id) return;
|
|
314
|
+
startSession(id);
|
|
315
|
+
trackPageview(apiUrl, apiKey, id);
|
|
316
|
+
}).catch(() => {
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
getOrCreateSession(apiUrl, apiKey).then((id) => {
|
|
320
|
+
if (id) startSession(id);
|
|
202
321
|
}).catch(() => {
|
|
203
322
|
});
|
|
204
323
|
enqueue((id) => trackPageview(apiUrl, apiKey, id));
|
|
@@ -263,12 +382,11 @@ var AfAnalytics = (() => {
|
|
|
263
382
|
});
|
|
264
383
|
});
|
|
265
384
|
});
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
};
|
|
385
|
+
window.addEventListener("storage", (e) => {
|
|
386
|
+
if (e.key === "af_session_id" && e.newValue === null && e.oldValue !== null) {
|
|
387
|
+
restartSession();
|
|
388
|
+
}
|
|
389
|
+
});
|
|
272
390
|
let visHideTimer = null;
|
|
273
391
|
document.addEventListener("visibilitychange", () => {
|
|
274
392
|
if (document.visibilityState === "hidden") {
|
|
@@ -280,7 +398,17 @@ var AfAnalytics = (() => {
|
|
|
280
398
|
}
|
|
281
399
|
}
|
|
282
400
|
});
|
|
283
|
-
window.addEventListener("pagehide",
|
|
401
|
+
window.addEventListener("pagehide", (e) => {
|
|
402
|
+
if (!e.persisted) endSession();
|
|
403
|
+
});
|
|
404
|
+
window.addEventListener("pageshow", (e) => {
|
|
405
|
+
if (!e.persisted) return;
|
|
406
|
+
if (sessionEnded || !sessionId) {
|
|
407
|
+
restartSession();
|
|
408
|
+
} else {
|
|
409
|
+
trackPageview(apiUrl, apiKey, sessionId);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
284
412
|
window.addEventListener("beforeunload", endSession);
|
|
285
413
|
}
|
|
286
414
|
if (document.readyState === "loading") {
|
package/dist/client.js
CHANGED
|
@@ -8,6 +8,14 @@ import { usePathname } from "next/navigation";
|
|
|
8
8
|
// src/hooks/useAnalytics.ts
|
|
9
9
|
import { useEffect, useRef } from "react";
|
|
10
10
|
|
|
11
|
+
// src/lib/consent.ts
|
|
12
|
+
var CONSENT_KEY = "af_analytics_opted_out";
|
|
13
|
+
function isOptedOut() {
|
|
14
|
+
if (typeof localStorage !== "undefined" && localStorage.getItem(CONSENT_KEY) === "1") return true;
|
|
15
|
+
if (typeof navigator !== "undefined" && navigator.doNotTrack === "1") return true;
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
// src/lib/api.ts
|
|
12
20
|
var BASE_FIELDS = (sessionId, eventType, path) => ({
|
|
13
21
|
session_id: sessionId,
|
|
@@ -28,6 +36,7 @@ function getGpu() {
|
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
async function createSession(apiUrl, apiKey, visitorId) {
|
|
39
|
+
if (isOptedOut()) return null;
|
|
31
40
|
const sessionData = { visitor_id: visitorId };
|
|
32
41
|
if (typeof window !== "undefined") {
|
|
33
42
|
const nav = navigator;
|
|
@@ -77,6 +86,7 @@ async function createSession(apiUrl, apiKey, visitorId) {
|
|
|
77
86
|
}
|
|
78
87
|
}
|
|
79
88
|
async function sendEvent(apiUrl, apiKey, payload) {
|
|
89
|
+
if (isOptedOut()) return;
|
|
80
90
|
await fetch(`${apiUrl}/events`, {
|
|
81
91
|
method: "POST",
|
|
82
92
|
headers: {
|
|
@@ -216,7 +226,51 @@ function connectPresence(apiKey, sessionId, wsUrl = DEFAULT_WS_URL) {
|
|
|
216
226
|
|
|
217
227
|
// src/hooks/useAnalytics.ts
|
|
218
228
|
var SESSION_EXPIRY_MINUTES = 30;
|
|
229
|
+
var IDLE_TOUCH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
230
|
+
var BC_CHANNEL = "af_analytics_session";
|
|
231
|
+
var BC_ADOPT_TIMEOUT_MS = 50;
|
|
219
232
|
var _sessionFlight = null;
|
|
233
|
+
function requestSessionFromPeer() {
|
|
234
|
+
if (typeof BroadcastChannel === "undefined") return Promise.resolve(null);
|
|
235
|
+
return new Promise((resolve) => {
|
|
236
|
+
let channel;
|
|
237
|
+
try {
|
|
238
|
+
channel = new BroadcastChannel(BC_CHANNEL);
|
|
239
|
+
} catch {
|
|
240
|
+
resolve(null);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const timer = setTimeout(() => {
|
|
244
|
+
channel.close();
|
|
245
|
+
resolve(null);
|
|
246
|
+
}, BC_ADOPT_TIMEOUT_MS);
|
|
247
|
+
channel.onmessage = (e) => {
|
|
248
|
+
if (e.data?.type === "session_adopt" && typeof e.data.session_id === "string") {
|
|
249
|
+
clearTimeout(timer);
|
|
250
|
+
channel.close();
|
|
251
|
+
resolve(e.data.session_id);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
channel.postMessage({ type: "session_claim" });
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function openSessionResponder(sessionId) {
|
|
258
|
+
if (typeof BroadcastChannel === "undefined") return () => {
|
|
259
|
+
};
|
|
260
|
+
let channel;
|
|
261
|
+
try {
|
|
262
|
+
channel = new BroadcastChannel(BC_CHANNEL);
|
|
263
|
+
} catch {
|
|
264
|
+
return () => {
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
channel.onmessage = (e) => {
|
|
268
|
+
if (e.data?.type === "session_claim") {
|
|
269
|
+
channel.postMessage({ type: "session_adopt", session_id: sessionId });
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
return () => channel.close();
|
|
273
|
+
}
|
|
220
274
|
async function getOrCreateSession(apiUrl, apiKey, visitorId) {
|
|
221
275
|
const storedSessionId = localStorage.getItem("af_session_id");
|
|
222
276
|
const lastActivity = localStorage.getItem("af_session_last_activity");
|
|
@@ -231,11 +285,20 @@ async function getOrCreateSession(apiUrl, apiKey, visitorId) {
|
|
|
231
285
|
localStorage.removeItem("af_session_last_activity");
|
|
232
286
|
}
|
|
233
287
|
if (_sessionFlight) return _sessionFlight;
|
|
234
|
-
_sessionFlight =
|
|
288
|
+
_sessionFlight = (async () => {
|
|
289
|
+
const adopted = await requestSessionFromPeer();
|
|
290
|
+
if (adopted) {
|
|
291
|
+
localStorage.setItem("af_session_id", adopted);
|
|
292
|
+
localStorage.setItem("af_session_last_activity", String(Date.now()));
|
|
293
|
+
return adopted;
|
|
294
|
+
}
|
|
295
|
+
const id = await createSession(apiUrl, apiKey, visitorId);
|
|
235
296
|
if (id) {
|
|
236
297
|
localStorage.setItem("af_session_id", id);
|
|
237
298
|
localStorage.setItem("af_session_last_activity", String(Date.now()));
|
|
238
299
|
}
|
|
300
|
+
return id;
|
|
301
|
+
})().then((id) => {
|
|
239
302
|
_sessionFlight = null;
|
|
240
303
|
return id;
|
|
241
304
|
}).catch((err) => {
|
|
@@ -249,6 +312,13 @@ function useAnalytics(apiUrl, apiKey, currentPath, wsUrl) {
|
|
|
249
312
|
const pendingEvents = useRef([]);
|
|
250
313
|
const hasSentEnd = useRef(false);
|
|
251
314
|
const presenceRef = useRef(null);
|
|
315
|
+
const closeResponderRef = useRef(() => {
|
|
316
|
+
});
|
|
317
|
+
const idleTimerRef = useRef(null);
|
|
318
|
+
const currentPathRef = useRef(currentPath);
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
currentPathRef.current = currentPath;
|
|
321
|
+
}, [currentPath]);
|
|
252
322
|
useEffect(() => {
|
|
253
323
|
if (typeof window === "undefined") return;
|
|
254
324
|
const visitor_id = localStorage.getItem("af_analytics_visitor_id") || crypto.randomUUID();
|
|
@@ -257,6 +327,12 @@ function useAnalytics(apiUrl, apiKey, currentPath, wsUrl) {
|
|
|
257
327
|
getOrCreateSession(apiUrl, apiKey, visitor_id).then((id) => {
|
|
258
328
|
if (cancelled || !id) return;
|
|
259
329
|
sessionId.current = id;
|
|
330
|
+
closeResponderRef.current = openSessionResponder(id);
|
|
331
|
+
idleTimerRef.current = setInterval(() => {
|
|
332
|
+
if (!hasSentEnd.current) {
|
|
333
|
+
localStorage.setItem("af_session_last_activity", String(Date.now()));
|
|
334
|
+
}
|
|
335
|
+
}, IDLE_TOUCH_INTERVAL_MS);
|
|
260
336
|
if (wsUrl) {
|
|
261
337
|
presenceRef.current = connectPresence(apiKey, id, wsUrl);
|
|
262
338
|
}
|
|
@@ -272,11 +348,61 @@ function useAnalytics(apiUrl, apiKey, currentPath, wsUrl) {
|
|
|
272
348
|
const sendEnd = () => {
|
|
273
349
|
if (!sessionId.current || hasSentEnd.current) return;
|
|
274
350
|
hasSentEnd.current = true;
|
|
351
|
+
if (idleTimerRef.current !== null) {
|
|
352
|
+
clearInterval(idleTimerRef.current);
|
|
353
|
+
idleTimerRef.current = null;
|
|
354
|
+
}
|
|
355
|
+
closeResponderRef.current();
|
|
275
356
|
presenceRef.current?.disconnect();
|
|
357
|
+
presenceRef.current = null;
|
|
276
358
|
navigator.sendBeacon(`${apiUrl}/sessions/${sessionId.current}/end`);
|
|
277
359
|
localStorage.removeItem("af_session_id");
|
|
278
360
|
localStorage.removeItem("af_session_last_activity");
|
|
279
361
|
};
|
|
362
|
+
const restartSession = () => {
|
|
363
|
+
sessionId.current = null;
|
|
364
|
+
hasSentEnd.current = false;
|
|
365
|
+
if (idleTimerRef.current !== null) {
|
|
366
|
+
clearInterval(idleTimerRef.current);
|
|
367
|
+
idleTimerRef.current = null;
|
|
368
|
+
}
|
|
369
|
+
closeResponderRef.current();
|
|
370
|
+
closeResponderRef.current = () => {
|
|
371
|
+
};
|
|
372
|
+
presenceRef.current?.disconnect();
|
|
373
|
+
presenceRef.current = null;
|
|
374
|
+
const visitor_id = localStorage.getItem("af_analytics_visitor_id") || crypto.randomUUID();
|
|
375
|
+
getOrCreateSession(apiUrl, apiKey, visitor_id).then((id) => {
|
|
376
|
+
if (!id) return;
|
|
377
|
+
sessionId.current = id;
|
|
378
|
+
closeResponderRef.current = openSessionResponder(id);
|
|
379
|
+
idleTimerRef.current = setInterval(() => {
|
|
380
|
+
if (!hasSentEnd.current) {
|
|
381
|
+
localStorage.setItem("af_session_last_activity", String(Date.now()));
|
|
382
|
+
}
|
|
383
|
+
}, IDLE_TOUCH_INTERVAL_MS);
|
|
384
|
+
if (wsUrl) {
|
|
385
|
+
presenceRef.current = connectPresence(apiKey, id, wsUrl);
|
|
386
|
+
}
|
|
387
|
+
trackPageview(apiUrl, apiKey, id, currentPathRef.current);
|
|
388
|
+
});
|
|
389
|
+
};
|
|
390
|
+
const handleStorage = (e) => {
|
|
391
|
+
if (e.key === "af_session_id" && e.newValue === null && e.oldValue !== null) {
|
|
392
|
+
restartSession();
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const handlePageHide = (e) => {
|
|
396
|
+
if (!e.persisted) sendEnd();
|
|
397
|
+
};
|
|
398
|
+
const handlePageShow = (e) => {
|
|
399
|
+
if (!e.persisted) return;
|
|
400
|
+
if (hasSentEnd.current || !sessionId.current) {
|
|
401
|
+
restartSession();
|
|
402
|
+
} else {
|
|
403
|
+
trackPageview(apiUrl, apiKey, sessionId.current, currentPathRef.current);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
280
406
|
let visHideTimer = null;
|
|
281
407
|
const handleVisibility = () => {
|
|
282
408
|
if (document.visibilityState === "hidden") {
|
|
@@ -288,13 +414,17 @@ function useAnalytics(apiUrl, apiKey, currentPath, wsUrl) {
|
|
|
288
414
|
}
|
|
289
415
|
}
|
|
290
416
|
};
|
|
417
|
+
window.addEventListener("storage", handleStorage);
|
|
291
418
|
document.addEventListener("visibilitychange", handleVisibility);
|
|
292
|
-
window.addEventListener("pagehide",
|
|
419
|
+
window.addEventListener("pagehide", handlePageHide);
|
|
420
|
+
window.addEventListener("pageshow", handlePageShow);
|
|
293
421
|
window.addEventListener("beforeunload", sendEnd);
|
|
294
422
|
return () => {
|
|
295
423
|
if (visHideTimer !== null) clearTimeout(visHideTimer);
|
|
424
|
+
window.removeEventListener("storage", handleStorage);
|
|
296
425
|
document.removeEventListener("visibilitychange", handleVisibility);
|
|
297
|
-
window.removeEventListener("pagehide",
|
|
426
|
+
window.removeEventListener("pagehide", handlePageHide);
|
|
427
|
+
window.removeEventListener("pageshow", handlePageShow);
|
|
298
428
|
window.removeEventListener("beforeunload", sendEnd);
|
|
299
429
|
};
|
|
300
430
|
}, [apiUrl]);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
declare function optOut(): void;
|
|
2
|
+
declare function optIn(): void;
|
|
3
|
+
/**
|
|
4
|
+
* Returns true when the user has opted out via localStorage flag OR
|
|
5
|
+
* the browser-level DNT header is set to "1".
|
|
6
|
+
*/
|
|
7
|
+
declare function isOptedOut(): boolean;
|
|
8
|
+
declare function consent(enabled: boolean): void;
|
|
9
|
+
|
|
1
10
|
declare function createSession(apiUrl: string, apiKey: string, visitorId?: string): Promise<string | null>;
|
|
2
11
|
declare function trackPageview(apiUrl: string, apiKey: string, sessionId: string, path: string): Promise<void>;
|
|
3
12
|
declare function trackClick(apiUrl: string, apiKey: string, sessionId: string, path: string, e: MouseEvent, element: HTMLElement): Promise<void>;
|
|
@@ -19,4 +28,4 @@ interface PresenceConnection {
|
|
|
19
28
|
}
|
|
20
29
|
declare function connectPresence(apiKey: string, sessionId: string, wsUrl?: string): PresenceConnection;
|
|
21
30
|
|
|
22
|
-
export { DEFAULT_WS_URL, type ExceptionFields, type PresenceConnection, connectPresence, createSession, trackCTA, trackClick, trackCopy, trackError, trackException, trackPageview, trackScroll, trackSearch };
|
|
31
|
+
export { DEFAULT_WS_URL, type ExceptionFields, type PresenceConnection, connectPresence, consent, createSession, isOptedOut, optIn, optOut, trackCTA, trackClick, trackCopy, trackError, trackException, trackPageview, trackScroll, trackSearch };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
// src/lib/consent.ts
|
|
2
|
+
var CONSENT_KEY = "af_analytics_opted_out";
|
|
3
|
+
function optOut() {
|
|
4
|
+
if (typeof localStorage === "undefined") return;
|
|
5
|
+
localStorage.setItem(CONSENT_KEY, "1");
|
|
6
|
+
}
|
|
7
|
+
function optIn() {
|
|
8
|
+
if (typeof localStorage === "undefined") return;
|
|
9
|
+
localStorage.removeItem(CONSENT_KEY);
|
|
10
|
+
}
|
|
11
|
+
function isOptedOut() {
|
|
12
|
+
if (typeof localStorage !== "undefined" && localStorage.getItem(CONSENT_KEY) === "1") return true;
|
|
13
|
+
if (typeof navigator !== "undefined" && navigator.doNotTrack === "1") return true;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
function consent(enabled) {
|
|
17
|
+
if (enabled) optIn();
|
|
18
|
+
else optOut();
|
|
19
|
+
}
|
|
20
|
+
|
|
1
21
|
// src/lib/api.ts
|
|
2
22
|
var BASE_FIELDS = (sessionId, eventType, path) => ({
|
|
3
23
|
session_id: sessionId,
|
|
@@ -18,6 +38,7 @@ function getGpu() {
|
|
|
18
38
|
}
|
|
19
39
|
}
|
|
20
40
|
async function createSession(apiUrl, apiKey, visitorId) {
|
|
41
|
+
if (isOptedOut()) return null;
|
|
21
42
|
const sessionData = { visitor_id: visitorId };
|
|
22
43
|
if (typeof window !== "undefined") {
|
|
23
44
|
const nav = navigator;
|
|
@@ -67,6 +88,7 @@ async function createSession(apiUrl, apiKey, visitorId) {
|
|
|
67
88
|
}
|
|
68
89
|
}
|
|
69
90
|
async function sendEvent(apiUrl, apiKey, payload) {
|
|
91
|
+
if (isOptedOut()) return;
|
|
70
92
|
await fetch(`${apiUrl}/events`, {
|
|
71
93
|
method: "POST",
|
|
72
94
|
headers: {
|
|
@@ -206,7 +228,11 @@ function connectPresence(apiKey, sessionId, wsUrl = DEFAULT_WS_URL) {
|
|
|
206
228
|
export {
|
|
207
229
|
DEFAULT_WS_URL,
|
|
208
230
|
connectPresence,
|
|
231
|
+
consent,
|
|
209
232
|
createSession,
|
|
233
|
+
isOptedOut,
|
|
234
|
+
optIn,
|
|
235
|
+
optOut,
|
|
210
236
|
trackCTA,
|
|
211
237
|
trackClick,
|
|
212
238
|
trackCopy,
|