@faststats/web 0.12.1 → 0.13.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/README.md +21 -8
- package/dist/chunks/{send-data-gBIF6DTb.js → send-data-C76HbZ8d.js} +14 -1
- package/dist/error.js +2 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.js +26 -15
- package/dist/replay.js +1 -1
- package/dist/web-vitals.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,8 +48,8 @@ Consent remains client-local: use `"anonymous"` for cookieless collection,
|
|
|
48
48
|
|
|
49
49
|
### Visit duration
|
|
50
50
|
|
|
51
|
-
`
|
|
52
|
-
that the page was visible and its document had focus. It pauses when the tab is
|
|
51
|
+
`properties.time_on_page` on `page_ping` and `page_leave` is the accumulated
|
|
52
|
+
time in milliseconds that the page was visible and its document had focus. It pauses when the tab is
|
|
53
53
|
hidden, the window loses focus, or the page enters the back-forward cache, and
|
|
54
54
|
resets on navigation, logout, or session rotation. A monotonic clock keeps
|
|
55
55
|
system-clock changes from affecting this duration. It also pauses after 60
|
|
@@ -60,11 +60,24 @@ the device spent asleep or the tab frozen is not counted.
|
|
|
60
60
|
Pageviews wait until the page becomes visible. Pages replaced or closed before
|
|
61
61
|
being shown do not emit pageviews or page-leave events.
|
|
62
62
|
|
|
63
|
-
The SDK sends cumulative `
|
|
64
|
-
and
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
session
|
|
63
|
+
The SDK sends a cumulative `page_ping` when activity pauses and periodically
|
|
64
|
+
while active: after 15 and 45 seconds, then every 60 seconds. A ping that adds no
|
|
65
|
+
time is suppressed. A visit ends with
|
|
66
|
+
exactly one `page_leave`, on navigation, a terminal `pagehide`, logout, a consent
|
|
67
|
+
change, or session rotation. A page the browser discards without notice never
|
|
68
|
+
sends one, so its last ping holds its duration. A pageview, its pings, and its
|
|
69
|
+
leave share `properties.visit_id`; consumers should use the maximum
|
|
70
|
+
`time_on_page` per visit across both events, never sum them.
|
|
71
|
+
|
|
72
|
+
Before 0.13 the pings were sent as `page_leave` every 15 seconds. The collector
|
|
73
|
+
and dashboards read both shapes; deploy them before releasing this SDK.
|
|
74
|
+
|
|
75
|
+
### Default context
|
|
76
|
+
|
|
77
|
+
Every event carries `timezone` (IANA name), `language`, and `screen`
|
|
78
|
+
(`1920x1080`, CSS pixels) beside the page context, separate from its properties.
|
|
79
|
+
Custom events and outbound links also carry the `visit_id` of the page view they
|
|
80
|
+
happened during.
|
|
68
81
|
|
|
69
82
|
Each visit retains its original identity, session, and page metadata. A session
|
|
70
83
|
or identity change starts a new visit, even on the same route. Both persistent
|
|
@@ -76,7 +89,7 @@ session, so it is separate from active time on the page. An idle visitor stops
|
|
|
76
89
|
extending the session, which then expires after 30 minutes like any other.
|
|
77
90
|
|
|
78
91
|
Delivery is best effort: a sudden process kill or network failure can lose time
|
|
79
|
-
since the last delivered
|
|
92
|
+
since the last delivered ping. The collector must accept successive events
|
|
80
93
|
for the same visitor and URL; deploy the removal of its five-second debounce
|
|
81
94
|
before releasing this SDK.
|
|
82
95
|
|
|
@@ -31,6 +31,19 @@ function getReferrer() {
|
|
|
31
31
|
} catch {}
|
|
32
32
|
return sanitizeUrl(raw);
|
|
33
33
|
}
|
|
34
|
+
/** Visitor environment sent with every event; unavailable values are omitted. */
|
|
35
|
+
function getClientContext() {
|
|
36
|
+
let timezone;
|
|
37
|
+
try {
|
|
38
|
+
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
39
|
+
} catch {}
|
|
40
|
+
const { width, height } = globalThis.screen ?? {};
|
|
41
|
+
return {
|
|
42
|
+
timezone,
|
|
43
|
+
language: globalThis.navigator?.language,
|
|
44
|
+
screen: width && height ? `${width}x${height}` : void 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
34
47
|
function getPageContext(includeHash = false) {
|
|
35
48
|
const hash = includeHash ? location.hash : "";
|
|
36
49
|
const ctx = {
|
|
@@ -80,4 +93,4 @@ async function sendData(options) {
|
|
|
80
93
|
}
|
|
81
94
|
}
|
|
82
95
|
//#endregion
|
|
83
|
-
export {
|
|
96
|
+
export { resolveBaseUrl as a, getPageContext as i, URLS as n, sanitizeUrl as o, getClientContext as r, sendData as t };
|
package/dist/error.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as resolveBaseUrl, i as getPageContext, n as URLS, t as sendData } from "./chunks/send-data-C76HbZ8d.js";
|
|
2
2
|
import { n as getSessionContext } from "./chunks/session-manager-DUpF3mi3.js";
|
|
3
3
|
import { t as getAnonymousId } from "./chunks/identifiers-DOtve3E5.js";
|
|
4
4
|
//#region src/error.ts
|
|
5
5
|
const SDK_NAME = "@faststats/web";
|
|
6
|
-
const SDK_VERSION = "0.
|
|
6
|
+
const SDK_VERSION = "0.13.0";
|
|
7
7
|
function errorTracking(options = {}) {
|
|
8
8
|
return {
|
|
9
9
|
name: "error-tracking",
|
package/dist/index.d.ts
CHANGED
|
@@ -69,6 +69,10 @@ export declare class WebAnalytics {
|
|
|
69
69
|
private readonly onActivity;
|
|
70
70
|
private isIdle;
|
|
71
71
|
private leavePage;
|
|
72
|
+
/**
|
|
73
|
+
* Reports the visit's running totals: as a `page_ping` while it may still
|
|
74
|
+
* continue, and as its single `page_leave` once it has ended.
|
|
75
|
+
*/
|
|
72
76
|
private checkpoint;
|
|
73
77
|
private pauseVisit;
|
|
74
78
|
private resumeVisit;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as resolveBaseUrl, i as getPageContext, n as URLS, o as sanitizeUrl, r as getClientContext, t as sendData } from "./chunks/send-data-C76HbZ8d.js";
|
|
2
2
|
import { a as touchActivity, i as resetSession, n as getSessionContext, t as createId } from "./chunks/session-manager-DUpF3mi3.js";
|
|
3
3
|
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-DOtve3E5.js";
|
|
4
4
|
//#region src/extensions.ts
|
|
@@ -125,6 +125,7 @@ function observeHistory(listener) {
|
|
|
125
125
|
}
|
|
126
126
|
const RESERVED_EVENTS = /* @__PURE__ */ new Set([
|
|
127
127
|
"pageview",
|
|
128
|
+
"page_ping",
|
|
128
129
|
"page_leave",
|
|
129
130
|
"error",
|
|
130
131
|
"outbound_link"
|
|
@@ -190,7 +191,9 @@ var WebAnalytics = class {
|
|
|
190
191
|
duration: 0,
|
|
191
192
|
startedAt: null,
|
|
192
193
|
checkedAt: Date.now(),
|
|
193
|
-
|
|
194
|
+
activeAt: Date.now(),
|
|
195
|
+
reportedDuration: -1,
|
|
196
|
+
beats: 0
|
|
194
197
|
};
|
|
195
198
|
this.send("pageview", {
|
|
196
199
|
...properties,
|
|
@@ -291,6 +294,7 @@ var WebAnalytics = class {
|
|
|
291
294
|
}
|
|
292
295
|
context(session = this.session()) {
|
|
293
296
|
return {
|
|
297
|
+
...getClientContext(),
|
|
294
298
|
...getPageContext(this.options.trackHash),
|
|
295
299
|
userId: this.getAnonymousId() || void 0,
|
|
296
300
|
sessionId: session.sessionId,
|
|
@@ -355,12 +359,12 @@ var WebAnalytics = class {
|
|
|
355
359
|
onPageHide = (event) => {
|
|
356
360
|
if (!this.canTrack()) return;
|
|
357
361
|
const persisted = event.persisted;
|
|
358
|
-
this.pauseVisit();
|
|
362
|
+
if (persisted) this.pauseVisit();
|
|
363
|
+
else this.leavePage();
|
|
359
364
|
this.extensions?.emit("pageHide", {
|
|
360
365
|
persisted,
|
|
361
366
|
terminal: !persisted
|
|
362
367
|
});
|
|
363
|
-
if (!persisted) this.leavePage();
|
|
364
368
|
};
|
|
365
369
|
onPageShow = (event) => {
|
|
366
370
|
if (!this.canTrack()) return;
|
|
@@ -383,36 +387,40 @@ var WebAnalytics = class {
|
|
|
383
387
|
return !playing;
|
|
384
388
|
}
|
|
385
389
|
leavePage() {
|
|
386
|
-
this.pauseVisit();
|
|
390
|
+
this.pauseVisit(true);
|
|
387
391
|
this.visit = null;
|
|
388
392
|
}
|
|
389
|
-
|
|
393
|
+
/**
|
|
394
|
+
* Reports the visit's running totals: as a `page_ping` while it may still
|
|
395
|
+
* continue, and as its single `page_leave` once it has ended.
|
|
396
|
+
*/
|
|
397
|
+
checkpoint(leaving = false, periodic = false) {
|
|
390
398
|
const visit = this.visit;
|
|
391
399
|
if (!visit || !this.isActive()) return;
|
|
392
|
-
let activeAt = visit.checkedAt;
|
|
393
400
|
if (visit.startedAt !== null) {
|
|
394
401
|
const now = performance.now();
|
|
395
402
|
if (now - visit.startedAt < IDLE_MS) {
|
|
396
403
|
const active = Math.min(now, this.lastInputAt + IDLE_MS) - visit.startedAt;
|
|
397
404
|
visit.duration += active;
|
|
398
|
-
activeAt
|
|
405
|
+
visit.activeAt = visit.checkedAt + active;
|
|
399
406
|
}
|
|
400
407
|
visit.startedAt = now;
|
|
401
408
|
visit.checkedAt = Date.now();
|
|
402
409
|
}
|
|
410
|
+
if (periodic && ++visit.beats !== 1 && visit.beats % 4 !== 3) return;
|
|
403
411
|
const duration = Math.round(visit.duration);
|
|
404
|
-
if (duration === visit.reportedDuration) return;
|
|
412
|
+
if (!leaving && duration === visit.reportedDuration) return;
|
|
405
413
|
visit.reportedDuration = duration;
|
|
406
|
-
this.send("page_leave", {
|
|
414
|
+
this.send(leaving ? "page_leave" : "page_ping", {
|
|
407
415
|
visit_id: visit.id,
|
|
408
416
|
time_on_page: duration,
|
|
409
|
-
session_duration: Math.max(0, activeAt - visit.session.sessionStart)
|
|
417
|
+
session_duration: Math.max(0, visit.activeAt - visit.session.sessionStart)
|
|
410
418
|
}, visit.context);
|
|
411
419
|
}
|
|
412
|
-
pauseVisit() {
|
|
420
|
+
pauseVisit(leaving = false) {
|
|
413
421
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
414
422
|
this.heartbeatTimer = null;
|
|
415
|
-
this.checkpoint();
|
|
423
|
+
this.checkpoint(leaving);
|
|
416
424
|
if (this.visit) this.visit.startedAt = null;
|
|
417
425
|
}
|
|
418
426
|
resumeVisit() {
|
|
@@ -426,12 +434,15 @@ var WebAnalytics = class {
|
|
|
426
434
|
}
|
|
427
435
|
const visit = this.visit;
|
|
428
436
|
this.pageview({ trigger: "session" });
|
|
429
|
-
if (this.visit === visit) this.checkpoint();
|
|
437
|
+
if (this.visit === visit) this.checkpoint(false, true);
|
|
430
438
|
}, 15e3);
|
|
431
439
|
}
|
|
432
440
|
send(event, properties = {}, context) {
|
|
433
441
|
if (!this.isActive()) return;
|
|
434
|
-
context ??=
|
|
442
|
+
context ??= {
|
|
443
|
+
...this.context(),
|
|
444
|
+
visit_id: this.visit?.id
|
|
445
|
+
};
|
|
435
446
|
let data;
|
|
436
447
|
try {
|
|
437
448
|
data = JSON.stringify({
|
package/dist/replay.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as resolveBaseUrl, n as URLS, o as sanitizeUrl, t as sendData } from "./chunks/send-data-C76HbZ8d.js";
|
|
2
2
|
import { a as touchActivity, c as setStorageItem, n as getSessionContext, o as getStorageItem, r as onSessionRotated, t as createId } from "./chunks/session-manager-DUpF3mi3.js";
|
|
3
3
|
import { t as getAnonymousId } from "./chunks/identifiers-DOtve3E5.js";
|
|
4
4
|
import { record } from "@rrweb/record";
|
package/dist/web-vitals.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as resolveBaseUrl, n as URLS, o as sanitizeUrl, t as sendData } from "./chunks/send-data-C76HbZ8d.js";
|
|
2
2
|
import { n as getSessionContext, r as onSessionRotated } from "./chunks/session-manager-DUpF3mi3.js";
|
|
3
3
|
import { onCLS, onFCP, onINP, onLCP, onTTFB } from "web-vitals/attribution";
|
|
4
4
|
//#region src/utils/web-vitals-collector.ts
|