@arthurreira/analytics 0.15.0 → 0.16.1
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 +8 -0
- package/dist/client.d.ts +2 -5
- package/dist/client.js +33 -74
- package/dist/index.d.ts +1 -1
- package/dist/index.js +23 -10
- package/package.json +38 -32
package/dist/af-analytics.umd.js
CHANGED
|
@@ -136,12 +136,20 @@ var AfAnalytics = (() => {
|
|
|
136
136
|
utm_term: params.get("utm_term"),
|
|
137
137
|
utm_content: params.get("utm_content")
|
|
138
138
|
};
|
|
139
|
+
console.log(`[AF Analytics] Creating session at ${apiUrl}/sessions with API key: ${apiKey.slice(0, 10)}...`);
|
|
139
140
|
const res = await fetch(`${apiUrl}/sessions`, {
|
|
140
141
|
method: "POST",
|
|
141
142
|
headers: { "X-API-Key": apiKey, "Content-Type": "application/json" },
|
|
142
143
|
body: JSON.stringify(body)
|
|
143
144
|
});
|
|
145
|
+
console.log(`[AF Analytics] Session creation response: ${res.status} ${res.statusText}`);
|
|
146
|
+
if (!res.ok) {
|
|
147
|
+
const errText = await res.text();
|
|
148
|
+
console.error(`[AF Analytics] Session creation failed: ${errText}`);
|
|
149
|
+
return "";
|
|
150
|
+
}
|
|
144
151
|
const data = await res.json();
|
|
152
|
+
console.log(`[AF Analytics] Session created: ${data.id}`);
|
|
145
153
|
return data.id;
|
|
146
154
|
}
|
|
147
155
|
async function getOrCreateSession(apiUrl, apiKey) {
|
package/dist/client.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
interface AnalyticsProps {
|
|
2
2
|
apiKey: string;
|
|
3
3
|
apiUrl: string;
|
|
4
|
-
wsUrl?: string;
|
|
5
4
|
}
|
|
6
|
-
declare function Analytics({ apiKey, apiUrl
|
|
5
|
+
declare function Analytics({ apiKey, apiUrl }: AnalyticsProps): null;
|
|
7
6
|
|
|
8
7
|
interface ExceptionFields {
|
|
9
8
|
exception_type: string;
|
|
@@ -11,9 +10,7 @@ interface ExceptionFields {
|
|
|
11
10
|
stack_trace: string | null;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
declare function useAnalytics(apiUrl: string, apiKey: string
|
|
15
|
-
wsUrl?: string;
|
|
16
|
-
}): {
|
|
13
|
+
declare function useAnalytics(apiUrl: string, apiKey: string): {
|
|
17
14
|
trackPageview: (path: string) => void;
|
|
18
15
|
trackClick: (e: MouseEvent, element: HTMLElement) => void;
|
|
19
16
|
trackScroll: (depth: number) => void;
|
package/dist/client.js
CHANGED
|
@@ -51,16 +51,29 @@ async function createSession(apiUrl, apiKey, visitorId) {
|
|
|
51
51
|
sessionData.utm_term = params.get("utm_term");
|
|
52
52
|
sessionData.utm_content = params.get("utm_content");
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
console.log(`[AF Analytics SDK] Creating session at ${apiUrl}/sessions with API key: ${apiKey.slice(0, 10)}...`);
|
|
55
|
+
try {
|
|
56
|
+
const response = await fetch(`${apiUrl}/sessions`, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: {
|
|
59
|
+
"X-API-Key": apiKey,
|
|
60
|
+
"Content-Type": "application/json"
|
|
61
|
+
},
|
|
62
|
+
body: JSON.stringify(sessionData)
|
|
63
|
+
});
|
|
64
|
+
console.log(`[AF Analytics SDK] Session creation response: ${response.status} ${response.statusText}`);
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
const errText = await response.text();
|
|
67
|
+
console.error(`[AF Analytics SDK] Session creation failed: ${errText}`);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const data = await response.json();
|
|
71
|
+
console.log(`[AF Analytics SDK] Session created: ${data.id}`);
|
|
72
|
+
return data.id ?? null;
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error(`[AF Analytics SDK] Session creation error: ${err}`);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
64
77
|
}
|
|
65
78
|
async function sendEvent(apiUrl, apiKey, payload) {
|
|
66
79
|
await fetch(`${apiUrl}/events`, {
|
|
@@ -130,60 +143,6 @@ async function trackCTA(apiUrl, apiKey, sessionId, path, ctaId, ctaVariant) {
|
|
|
130
143
|
});
|
|
131
144
|
}
|
|
132
145
|
|
|
133
|
-
// src/lib/presence.ts
|
|
134
|
-
var DEFAULT_WS_URL = "wss://edge.arthurreira.dev/realtime";
|
|
135
|
-
function connectPresence(apiKey, sessionId, wsUrl = DEFAULT_WS_URL) {
|
|
136
|
-
if (typeof WebSocket === "undefined") {
|
|
137
|
-
return { disconnect: () => {
|
|
138
|
-
} };
|
|
139
|
-
}
|
|
140
|
-
let ws = null;
|
|
141
|
-
let attempts = 0;
|
|
142
|
-
let intentionalClose = false;
|
|
143
|
-
let retryTimer = null;
|
|
144
|
-
let targetUrl;
|
|
145
|
-
try {
|
|
146
|
-
const u = new URL(wsUrl);
|
|
147
|
-
u.searchParams.set("api_key", apiKey);
|
|
148
|
-
u.searchParams.set("session_id", sessionId);
|
|
149
|
-
targetUrl = u.toString();
|
|
150
|
-
} catch {
|
|
151
|
-
return { disconnect: () => {
|
|
152
|
-
} };
|
|
153
|
-
}
|
|
154
|
-
function connect() {
|
|
155
|
-
try {
|
|
156
|
-
ws = new WebSocket(targetUrl);
|
|
157
|
-
} catch {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
ws.onopen = () => {
|
|
161
|
-
attempts = 0;
|
|
162
|
-
ws.send(JSON.stringify({ type: "join", api_key: apiKey, session_id: sessionId }));
|
|
163
|
-
};
|
|
164
|
-
ws.onclose = () => {
|
|
165
|
-
if (intentionalClose || attempts >= 3) return;
|
|
166
|
-
const delay = Math.pow(2, attempts) * 1e3;
|
|
167
|
-
attempts++;
|
|
168
|
-
retryTimer = setTimeout(connect, delay);
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
connect();
|
|
172
|
-
return {
|
|
173
|
-
disconnect: () => {
|
|
174
|
-
intentionalClose = true;
|
|
175
|
-
if (retryTimer !== null) {
|
|
176
|
-
clearTimeout(retryTimer);
|
|
177
|
-
retryTimer = null;
|
|
178
|
-
}
|
|
179
|
-
if (ws) {
|
|
180
|
-
ws.close();
|
|
181
|
-
ws = null;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
146
|
// src/hooks/useAnalytics.ts
|
|
188
147
|
var SESSION_EXPIRY_MINUTES = 30;
|
|
189
148
|
var _sessionFlight = null;
|
|
@@ -197,11 +156,15 @@ async function getOrCreateSession(apiUrl, apiKey, visitorId) {
|
|
|
197
156
|
localStorage.setItem("af_session_last_activity", String(now));
|
|
198
157
|
return storedSessionId;
|
|
199
158
|
}
|
|
159
|
+
localStorage.removeItem("af_session_id");
|
|
160
|
+
localStorage.removeItem("af_session_last_activity");
|
|
200
161
|
}
|
|
201
162
|
if (_sessionFlight) return _sessionFlight;
|
|
202
163
|
_sessionFlight = createSession(apiUrl, apiKey, visitorId).then((id) => {
|
|
203
|
-
|
|
204
|
-
|
|
164
|
+
if (id) {
|
|
165
|
+
localStorage.setItem("af_session_id", id);
|
|
166
|
+
localStorage.setItem("af_session_last_activity", String(Date.now()));
|
|
167
|
+
}
|
|
205
168
|
_sessionFlight = null;
|
|
206
169
|
return id;
|
|
207
170
|
}).catch((err) => {
|
|
@@ -210,11 +173,10 @@ async function getOrCreateSession(apiUrl, apiKey, visitorId) {
|
|
|
210
173
|
});
|
|
211
174
|
return _sessionFlight;
|
|
212
175
|
}
|
|
213
|
-
function useAnalytics(apiUrl, apiKey
|
|
176
|
+
function useAnalytics(apiUrl, apiKey) {
|
|
214
177
|
const sessionId = useRef(null);
|
|
215
178
|
const pendingEvents = useRef([]);
|
|
216
179
|
const pathname = useRef(typeof window !== "undefined" ? window?.location?.pathname ?? "" : "");
|
|
217
|
-
const presence = useRef(null);
|
|
218
180
|
const hasSentEnd = useRef(false);
|
|
219
181
|
useEffect(() => {
|
|
220
182
|
if (typeof window === "undefined") return;
|
|
@@ -222,9 +184,8 @@ function useAnalytics(apiUrl, apiKey, options) {
|
|
|
222
184
|
localStorage.setItem("af_analytics_visitor_id", visitor_id);
|
|
223
185
|
let cancelled = false;
|
|
224
186
|
getOrCreateSession(apiUrl, apiKey, visitor_id).then((id) => {
|
|
225
|
-
if (cancelled) return;
|
|
187
|
+
if (cancelled || !id) return;
|
|
226
188
|
sessionId.current = id;
|
|
227
|
-
presence.current = connectPresence(apiKey, id, options?.wsUrl);
|
|
228
189
|
pendingEvents.current.forEach((fn) => fn());
|
|
229
190
|
pendingEvents.current = [];
|
|
230
191
|
});
|
|
@@ -237,8 +198,6 @@ function useAnalytics(apiUrl, apiKey, options) {
|
|
|
237
198
|
const sendEnd = () => {
|
|
238
199
|
if (!sessionId.current || hasSentEnd.current) return;
|
|
239
200
|
hasSentEnd.current = true;
|
|
240
|
-
presence.current?.disconnect();
|
|
241
|
-
presence.current = null;
|
|
242
201
|
navigator.sendBeacon(`${apiUrl}/sessions/${sessionId.current}/end`);
|
|
243
202
|
localStorage.removeItem("af_session_id");
|
|
244
203
|
localStorage.removeItem("af_session_last_activity");
|
|
@@ -291,8 +250,8 @@ function useAnalytics(apiUrl, apiKey, options) {
|
|
|
291
250
|
}
|
|
292
251
|
|
|
293
252
|
// src/components/Analytics.tsx
|
|
294
|
-
function Analytics({ apiKey, apiUrl
|
|
295
|
-
const { trackPageview: trackPageview2, trackClick: trackClick2, trackScroll: trackScroll2, trackCopy: trackCopy2, trackException: trackException2 } = useAnalytics(apiUrl, apiKey
|
|
253
|
+
function Analytics({ apiKey, apiUrl }) {
|
|
254
|
+
const { trackPageview: trackPageview2, trackClick: trackClick2, trackScroll: trackScroll2, trackCopy: trackCopy2, trackException: trackException2 } = useAnalytics(apiUrl, apiKey);
|
|
296
255
|
const lastTracked = useRef2(null);
|
|
297
256
|
const lastScrollDepth = useRef2(0);
|
|
298
257
|
const pathname = typeof window !== "undefined" ? window.location.pathname : "";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare function createSession(apiUrl: string, apiKey: string, visitorId?: string): Promise<
|
|
1
|
+
declare function createSession(apiUrl: string, apiKey: string, visitorId?: string): Promise<string | null>;
|
|
2
2
|
declare function trackPageview(apiUrl: string, apiKey: string, sessionId: string, path: string): Promise<void>;
|
|
3
3
|
declare function trackClick(apiUrl: string, apiKey: string, sessionId: string, path: string, e: MouseEvent, element: HTMLElement): Promise<void>;
|
|
4
4
|
declare function trackScroll(apiUrl: string, apiKey: string, sessionId: string, path: string, depth: number): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -42,16 +42,29 @@ async function createSession(apiUrl, apiKey, visitorId) {
|
|
|
42
42
|
sessionData.utm_term = params.get("utm_term");
|
|
43
43
|
sessionData.utm_content = params.get("utm_content");
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
console.log(`[AF Analytics SDK] Creating session at ${apiUrl}/sessions with API key: ${apiKey.slice(0, 10)}...`);
|
|
46
|
+
try {
|
|
47
|
+
const response = await fetch(`${apiUrl}/sessions`, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: {
|
|
50
|
+
"X-API-Key": apiKey,
|
|
51
|
+
"Content-Type": "application/json"
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(sessionData)
|
|
54
|
+
});
|
|
55
|
+
console.log(`[AF Analytics SDK] Session creation response: ${response.status} ${response.statusText}`);
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
const errText = await response.text();
|
|
58
|
+
console.error(`[AF Analytics SDK] Session creation failed: ${errText}`);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
console.log(`[AF Analytics SDK] Session created: ${data.id}`);
|
|
63
|
+
return data.id ?? null;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error(`[AF Analytics SDK] Session creation error: ${err}`);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
55
68
|
}
|
|
56
69
|
async function sendEvent(apiUrl, apiKey, payload) {
|
|
57
70
|
await fetch(`${apiUrl}/events`, {
|
package/package.json
CHANGED
|
@@ -1,35 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@arthurreira/analytics",
|
|
3
|
+
"version": "0.16.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsup",
|
|
7
|
+
"prepare": "tsup",
|
|
8
|
+
"dev": "tsup src/index.ts --format esm --dts --watch"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
8
14
|
},
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"import": "./dist/index.js"
|
|
13
|
-
},
|
|
14
|
-
"./client": {
|
|
15
|
-
"types": "./dist/client.d.ts",
|
|
16
|
-
"import": "./dist/client.js"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"sideEffects": false,
|
|
20
|
-
"files": [
|
|
21
|
-
"dist"
|
|
22
|
-
],
|
|
23
|
-
"description": "Analytics SDK for af-analytics (browser client + helpers)",
|
|
24
|
-
"license": "MIT",
|
|
25
|
-
"dependencies": {},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"tsup": "^8.5.1",
|
|
28
|
-
"typescript": "^5.9.2",
|
|
29
|
-
"@types/react": "^19.0.0"
|
|
30
|
-
},
|
|
31
|
-
"peerDependencies": {
|
|
32
|
-
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
|
33
|
-
"react": "^18.0.0 || ^19.0.0"
|
|
15
|
+
"./client": {
|
|
16
|
+
"types": "./dist/client.d.ts",
|
|
17
|
+
"import": "./dist/client.js"
|
|
34
18
|
}
|
|
19
|
+
},
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"description": "Analytics SDK for af-analytics (browser client + helpers)",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
28
|
+
"@testing-library/react": "^16.3.2",
|
|
29
|
+
"@types/react": "^19.0.0",
|
|
30
|
+
"jsdom": "^29.1.1",
|
|
31
|
+
"react": "^19.2.6",
|
|
32
|
+
"react-dom": "^19.2.6",
|
|
33
|
+
"tsup": "^8.5.1",
|
|
34
|
+
"typescript": "^5.9.2",
|
|
35
|
+
"vitest": "^4.1.7"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
|
39
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
40
|
+
}
|
|
35
41
|
}
|