@faststats/web 0.12.1 → 0.13.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/README.md +21 -8
- package/dist/chunks/identifiers-3X8XI1pD.js +16 -0
- package/dist/chunks/send-data-B7P3f5PT.js +177 -0
- package/dist/chunks/session-manager-VM5RZj9H.js +116 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.js +47 -59
- package/dist/flags.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +67 -126
- package/dist/replay.d.ts +37 -64
- package/dist/replay.js +423 -439
- package/dist/web-vitals.js +3 -18
- package/package.json +3 -3
- package/dist/chunks/identifiers-DOtve3E5.js +0 -18
- package/dist/chunks/send-data-gBIF6DTb.js +0 -83
- package/dist/chunks/session-manager-DUpF3mi3.js +0 -149
package/dist/index.js
CHANGED
|
@@ -1,62 +1,6 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as touchActivity, i as resetSession, n as getSessionContext, t as createId } from "./chunks/session-manager-
|
|
3
|
-
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-
|
|
4
|
-
//#region src/extensions.ts
|
|
5
|
-
var ExtensionHost = class {
|
|
6
|
-
context;
|
|
7
|
-
log;
|
|
8
|
-
handlers = /* @__PURE__ */ new Map();
|
|
9
|
-
cleanups = [];
|
|
10
|
-
stopped = false;
|
|
11
|
-
constructor(extensions, context, log) {
|
|
12
|
-
this.context = context;
|
|
13
|
-
this.log = log;
|
|
14
|
-
for (const extension of extensions) this.setup(extension);
|
|
15
|
-
}
|
|
16
|
-
emit(hook, event) {
|
|
17
|
-
if (this.stopped) return;
|
|
18
|
-
for (const handler of this.handlers.get(hook) ?? []) try {
|
|
19
|
-
handler(event)?.catch((error) => this.log(`${hook} failed`, error));
|
|
20
|
-
} catch (error) {
|
|
21
|
-
this.log(`${hook} failed`, error);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
stop(discard = false) {
|
|
25
|
-
if (this.stopped) return;
|
|
26
|
-
this.stopped = true;
|
|
27
|
-
for (const cleanup of this.cleanups.reverse()) try {
|
|
28
|
-
cleanup({ discard });
|
|
29
|
-
} catch (error) {
|
|
30
|
-
this.log("cleanup failed", error);
|
|
31
|
-
}
|
|
32
|
-
this.cleanups.length = 0;
|
|
33
|
-
this.handlers.clear();
|
|
34
|
-
}
|
|
35
|
-
async setup(extension) {
|
|
36
|
-
if (this.stopped) return;
|
|
37
|
-
const subscriptions = [];
|
|
38
|
-
try {
|
|
39
|
-
const cleanup = await extension.setup({
|
|
40
|
-
...this.context,
|
|
41
|
-
on: (hook, handler) => {
|
|
42
|
-
if (this.stopped) return () => {};
|
|
43
|
-
const handlers = this.handlers.get(hook) ?? /* @__PURE__ */ new Set();
|
|
44
|
-
this.handlers.set(hook, handlers);
|
|
45
|
-
handlers.add(handler);
|
|
46
|
-
const off = () => handlers.delete(handler);
|
|
47
|
-
subscriptions.push(off);
|
|
48
|
-
return off;
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
if (this.stopped) cleanup?.({ discard: true });
|
|
52
|
-
else if (cleanup) this.cleanups.push(cleanup);
|
|
53
|
-
} catch (error) {
|
|
54
|
-
for (const off of subscriptions) off();
|
|
55
|
-
this.log(`${extension.name} setup failed`, error);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
//#endregion
|
|
1
|
+
import { a as resolveBaseUrl, i as getPageContext, n as URLS, o as sanitizeUrl, r as getClientContext, s as ExtensionHost, t as sendData } from "./chunks/send-data-B7P3f5PT.js";
|
|
2
|
+
import { a as touchActivity, i as resetSession, n as getSessionContext, t as createId } from "./chunks/session-manager-VM5RZj9H.js";
|
|
3
|
+
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-3X8XI1pD.js";
|
|
60
4
|
//#region src/outbound-links.ts
|
|
61
5
|
const DEDUPE_MS = 500;
|
|
62
6
|
function outboundHref(event) {
|
|
@@ -88,43 +32,36 @@ function trackOutboundLinks(report) {
|
|
|
88
32
|
//#endregion
|
|
89
33
|
//#region src/analytics.ts
|
|
90
34
|
const navigationListeners = /* @__PURE__ */ new Set();
|
|
91
|
-
const historyPatches =
|
|
92
|
-
function patchHistory(method) {
|
|
93
|
-
if (historyPatches[method]?.owner === history) return;
|
|
94
|
-
const original = history[method];
|
|
95
|
-
const wrapped = function(data, unused, url) {
|
|
96
|
-
Reflect.apply(original, this, [
|
|
97
|
-
data,
|
|
98
|
-
unused,
|
|
99
|
-
url
|
|
100
|
-
]);
|
|
101
|
-
for (const listener of navigationListeners) listener();
|
|
102
|
-
};
|
|
103
|
-
history[method] = wrapped;
|
|
104
|
-
historyPatches[method] = {
|
|
105
|
-
owner: history,
|
|
106
|
-
original,
|
|
107
|
-
wrapped
|
|
108
|
-
};
|
|
109
|
-
}
|
|
35
|
+
const historyPatches = /* @__PURE__ */ new Map();
|
|
110
36
|
function observeHistory(listener) {
|
|
111
37
|
navigationListeners.add(listener);
|
|
112
|
-
|
|
113
|
-
|
|
38
|
+
for (const method of ["pushState", "replaceState"]) {
|
|
39
|
+
if (historyPatches.get(method)?.target === history) continue;
|
|
40
|
+
const original = history[method];
|
|
41
|
+
const wrapped = function(...args) {
|
|
42
|
+
Reflect.apply(original, this, args);
|
|
43
|
+
for (const listener of navigationListeners) listener();
|
|
44
|
+
};
|
|
45
|
+
history[method] = wrapped;
|
|
46
|
+
historyPatches.set(method, {
|
|
47
|
+
target: history,
|
|
48
|
+
original,
|
|
49
|
+
wrapped
|
|
50
|
+
});
|
|
51
|
+
}
|
|
114
52
|
return () => {
|
|
115
53
|
navigationListeners.delete(listener);
|
|
116
54
|
if (navigationListeners.size) return;
|
|
117
|
-
for (const method of
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
delete historyPatches[method];
|
|
122
|
-
}
|
|
55
|
+
for (const [method, patch] of historyPatches) {
|
|
56
|
+
if (history[method] !== patch.wrapped) continue;
|
|
57
|
+
history[method] = patch.original;
|
|
58
|
+
historyPatches.delete(method);
|
|
123
59
|
}
|
|
124
60
|
};
|
|
125
61
|
}
|
|
126
62
|
const RESERVED_EVENTS = /* @__PURE__ */ new Set([
|
|
127
63
|
"pageview",
|
|
64
|
+
"page_ping",
|
|
128
65
|
"page_leave",
|
|
129
66
|
"error",
|
|
130
67
|
"outbound_link"
|
|
@@ -189,8 +126,9 @@ var WebAnalytics = class {
|
|
|
189
126
|
context,
|
|
190
127
|
duration: 0,
|
|
191
128
|
startedAt: null,
|
|
192
|
-
|
|
193
|
-
reportedDuration:
|
|
129
|
+
activeAt: Date.now(),
|
|
130
|
+
reportedDuration: 0,
|
|
131
|
+
beats: 0
|
|
194
132
|
};
|
|
195
133
|
this.send("pageview", {
|
|
196
134
|
...properties,
|
|
@@ -254,21 +192,19 @@ var WebAnalytics = class {
|
|
|
254
192
|
setConsentMode(mode) {
|
|
255
193
|
if (mode === this.consentMode) return;
|
|
256
194
|
const wasTracking = this.canTrack();
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
const willBeCookieless = this.options.cookieless === true || mode === "anonymous";
|
|
260
|
-
if (wasTracking && willTrack && wasCookieless !== willBeCookieless) this.leavePage();
|
|
195
|
+
const identityChanges = this.isCookieless() !== this.isCookieless(mode);
|
|
196
|
+
if (mode !== "denied" && identityChanges) this.leavePage();
|
|
261
197
|
this.consentMode = mode;
|
|
262
|
-
if (
|
|
198
|
+
if (mode === "denied") {
|
|
263
199
|
this.leavePage();
|
|
264
200
|
this.pendingPageview = null;
|
|
265
201
|
this.stopExtensions(true);
|
|
266
|
-
|
|
202
|
+
} else if (!wasTracking) {
|
|
203
|
+
if (this.state === "started") this.activate("consent");
|
|
204
|
+
} else {
|
|
205
|
+
this.extensions?.emit("consentChange", { cookieless: this.isCookieless() });
|
|
206
|
+
if (identityChanges) this.pageview({ trigger: "consent" });
|
|
267
207
|
}
|
|
268
|
-
if (wasTracking) {
|
|
269
|
-
this.extensions?.emit("consentChange", { cookieless: willBeCookieless });
|
|
270
|
-
if (wasCookieless !== willBeCookieless) this.pageview({ trigger: "consent" });
|
|
271
|
-
} else if (this.state === "started") this.activate("consent");
|
|
272
208
|
}
|
|
273
209
|
getAnonymousId() {
|
|
274
210
|
return this.canTrack() ? getAnonymousId(this.options.siteKey, this.isCookieless()) : "";
|
|
@@ -280,8 +216,8 @@ var WebAnalytics = class {
|
|
|
280
216
|
canTrack() {
|
|
281
217
|
return this.consentMode !== "denied";
|
|
282
218
|
}
|
|
283
|
-
isCookieless() {
|
|
284
|
-
return this.options.cookieless === true ||
|
|
219
|
+
isCookieless(mode = this.consentMode) {
|
|
220
|
+
return this.options.cookieless === true || mode === "anonymous";
|
|
285
221
|
}
|
|
286
222
|
isActive() {
|
|
287
223
|
return this.state === "started" && this.canTrack();
|
|
@@ -291,6 +227,7 @@ var WebAnalytics = class {
|
|
|
291
227
|
}
|
|
292
228
|
context(session = this.session()) {
|
|
293
229
|
return {
|
|
230
|
+
...getClientContext(),
|
|
294
231
|
...getPageContext(this.options.trackHash),
|
|
295
232
|
userId: this.getAnonymousId() || void 0,
|
|
296
233
|
sessionId: session.sessionId,
|
|
@@ -324,7 +261,7 @@ var WebAnalytics = class {
|
|
|
324
261
|
this.bind(window, "pagehide", this.onPageHide);
|
|
325
262
|
this.bind(window, "pageshow", this.onPageShow);
|
|
326
263
|
this.bind(window, "focus", this.onResume);
|
|
327
|
-
this.bind(window, "blur",
|
|
264
|
+
this.bind(window, "blur", this.onBlur);
|
|
328
265
|
this.bind(window, "popstate", this.onNavigate);
|
|
329
266
|
for (const type of [
|
|
330
267
|
"pointerdown",
|
|
@@ -341,7 +278,6 @@ var WebAnalytics = class {
|
|
|
341
278
|
}
|
|
342
279
|
onNavigate = () => this.pageview({ trigger: "navigation" });
|
|
343
280
|
onVisibilityChange = () => {
|
|
344
|
-
if (!this.canTrack()) return;
|
|
345
281
|
if (document.visibilityState === "hidden") {
|
|
346
282
|
this.pauseVisit();
|
|
347
283
|
this.extensions?.emit("pageHide", {
|
|
@@ -353,17 +289,15 @@ var WebAnalytics = class {
|
|
|
353
289
|
this.onResume();
|
|
354
290
|
};
|
|
355
291
|
onPageHide = (event) => {
|
|
356
|
-
if (!this.canTrack()) return;
|
|
357
292
|
const persisted = event.persisted;
|
|
358
|
-
this.pauseVisit();
|
|
293
|
+
if (persisted) this.pauseVisit();
|
|
294
|
+
else this.leavePage();
|
|
359
295
|
this.extensions?.emit("pageHide", {
|
|
360
296
|
persisted,
|
|
361
297
|
terminal: !persisted
|
|
362
298
|
});
|
|
363
|
-
if (!persisted) this.leavePage();
|
|
364
299
|
};
|
|
365
300
|
onPageShow = (event) => {
|
|
366
|
-
if (!this.canTrack()) return;
|
|
367
301
|
this.onResume();
|
|
368
302
|
this.extensions?.emit("pageShow", { persisted: event.persisted });
|
|
369
303
|
};
|
|
@@ -371,6 +305,13 @@ var WebAnalytics = class {
|
|
|
371
305
|
this.pageview(this.pendingPageview ?? { trigger: "session" });
|
|
372
306
|
this.resumeVisit();
|
|
373
307
|
};
|
|
308
|
+
onBlur = () => {
|
|
309
|
+
const visit = this.visit;
|
|
310
|
+
const blurredAt = performance.now();
|
|
311
|
+
setTimeout(() => {
|
|
312
|
+
if (this.visit === visit && !document.hasFocus()) this.pauseVisit(false, blurredAt);
|
|
313
|
+
});
|
|
314
|
+
};
|
|
374
315
|
onActivity = () => {
|
|
375
316
|
this.lastInputAt = performance.now();
|
|
376
317
|
if (this.visit?.startedAt === null && document.hasFocus()) this.onResume();
|
|
@@ -378,47 +319,44 @@ var WebAnalytics = class {
|
|
|
378
319
|
isIdle() {
|
|
379
320
|
const now = performance.now();
|
|
380
321
|
if (now - this.lastInputAt < IDLE_MS) return false;
|
|
381
|
-
const
|
|
382
|
-
if (
|
|
383
|
-
return !
|
|
322
|
+
const engaged = document.activeElement?.tagName === "IFRAME" || [...document.querySelectorAll("video,audio")].some((media) => !media.paused && !media.ended);
|
|
323
|
+
if (engaged) this.lastInputAt = now;
|
|
324
|
+
return !engaged;
|
|
384
325
|
}
|
|
385
326
|
leavePage() {
|
|
386
|
-
this.pauseVisit();
|
|
327
|
+
this.pauseVisit(true);
|
|
387
328
|
this.visit = null;
|
|
388
329
|
}
|
|
389
|
-
checkpoint() {
|
|
330
|
+
checkpoint(leaving = false, periodic = false, now = performance.now()) {
|
|
390
331
|
const visit = this.visit;
|
|
391
332
|
if (!visit || !this.isActive()) return;
|
|
392
|
-
let activeAt = visit.checkedAt;
|
|
393
333
|
if (visit.startedAt !== null) {
|
|
394
|
-
const
|
|
395
|
-
if (now - visit.startedAt < IDLE_MS) {
|
|
396
|
-
|
|
397
|
-
visit.
|
|
398
|
-
activeAt += active;
|
|
334
|
+
const activeUntil = Math.min(now, this.lastInputAt + IDLE_MS);
|
|
335
|
+
if (now - visit.startedAt < IDLE_MS && activeUntil > visit.startedAt) {
|
|
336
|
+
visit.duration += activeUntil - visit.startedAt;
|
|
337
|
+
visit.activeAt = Date.now() - (performance.now() - activeUntil);
|
|
399
338
|
}
|
|
400
339
|
visit.startedAt = now;
|
|
401
|
-
visit.checkedAt = Date.now();
|
|
402
340
|
}
|
|
341
|
+
if (periodic && ++visit.beats !== 1 && visit.beats % 4 !== 3) return;
|
|
403
342
|
const duration = Math.round(visit.duration);
|
|
404
|
-
if (duration === visit.reportedDuration) return;
|
|
343
|
+
if (!leaving && duration === visit.reportedDuration) return;
|
|
405
344
|
visit.reportedDuration = duration;
|
|
406
|
-
this.send("page_leave", {
|
|
345
|
+
this.send(leaving ? "page_leave" : "page_ping", {
|
|
407
346
|
visit_id: visit.id,
|
|
408
347
|
time_on_page: duration,
|
|
409
|
-
session_duration: Math.max(0, activeAt - visit.session.sessionStart)
|
|
348
|
+
session_duration: Math.max(0, visit.activeAt - visit.session.sessionStart)
|
|
410
349
|
}, visit.context);
|
|
411
350
|
}
|
|
412
|
-
pauseVisit() {
|
|
351
|
+
pauseVisit(leaving = false, at) {
|
|
413
352
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
414
353
|
this.heartbeatTimer = null;
|
|
415
|
-
this.checkpoint();
|
|
354
|
+
this.checkpoint(leaving, false, at);
|
|
416
355
|
if (this.visit) this.visit.startedAt = null;
|
|
417
356
|
}
|
|
418
357
|
resumeVisit() {
|
|
419
358
|
if (!this.isActive() || !this.visit || this.visit.startedAt !== null || document.visibilityState !== "visible" || !document.hasFocus()) return;
|
|
420
359
|
this.visit.startedAt = this.lastInputAt = performance.now();
|
|
421
|
-
this.visit.checkedAt = Date.now();
|
|
422
360
|
this.heartbeatTimer = setInterval(() => {
|
|
423
361
|
if (document.visibilityState !== "visible" || !document.hasFocus() || this.isIdle()) {
|
|
424
362
|
this.pauseVisit();
|
|
@@ -426,12 +364,15 @@ var WebAnalytics = class {
|
|
|
426
364
|
}
|
|
427
365
|
const visit = this.visit;
|
|
428
366
|
this.pageview({ trigger: "session" });
|
|
429
|
-
if (this.visit === visit) this.checkpoint();
|
|
367
|
+
if (this.visit === visit) this.checkpoint(false, true);
|
|
430
368
|
}, 15e3);
|
|
431
369
|
}
|
|
432
370
|
send(event, properties = {}, context) {
|
|
433
371
|
if (!this.isActive()) return;
|
|
434
|
-
context ??=
|
|
372
|
+
context ??= {
|
|
373
|
+
...this.context(),
|
|
374
|
+
visit_id: this.visit?.id
|
|
375
|
+
};
|
|
435
376
|
let data;
|
|
436
377
|
try {
|
|
437
378
|
data = JSON.stringify({
|
package/dist/replay.d.ts
CHANGED
|
@@ -57,81 +57,54 @@ export type SessionReplayExtensionOptions = Omit<ReplayTrackerOptions, "siteKey"
|
|
|
57
57
|
export declare function sessionReplay(options?: SessionReplayExtensionOptions): AnalyticsExtension;
|
|
58
58
|
export default class ReplayTracker {
|
|
59
59
|
private readonly options;
|
|
60
|
-
private readonly
|
|
61
|
-
private readonly
|
|
62
|
-
private readonly
|
|
63
|
-
private
|
|
64
|
-
private readonly maxBatchSizeBytes;
|
|
65
|
-
private readonly maxLowLatencyBatchSizeBytes;
|
|
66
|
-
private readonly maxPendingBatches;
|
|
67
|
-
private readonly maxQueueSizeBytes;
|
|
68
|
-
private readonly minReplayLengthMs;
|
|
69
|
-
private readonly events;
|
|
70
|
-
private readonly pending;
|
|
71
|
-
private queuedEventsSizeBytes;
|
|
72
|
-
private pendingSizeBytes;
|
|
73
|
-
private viewId;
|
|
74
|
-
private viewUrl;
|
|
75
|
-
private chunkStartedAt;
|
|
60
|
+
private readonly limits;
|
|
61
|
+
private readonly uploader;
|
|
62
|
+
private readonly throttler;
|
|
63
|
+
private cookieless;
|
|
76
64
|
private started;
|
|
77
|
-
private
|
|
78
|
-
private startTime;
|
|
79
|
-
private intervalId;
|
|
80
|
-
private flushTask;
|
|
81
|
-
private retryTask;
|
|
82
|
-
private retryAttempt;
|
|
83
|
-
private minLengthFlushTask;
|
|
65
|
+
private overflowed;
|
|
84
66
|
private stopRecording?;
|
|
85
|
-
private
|
|
86
|
-
private
|
|
87
|
-
private
|
|
88
|
-
private
|
|
89
|
-
private
|
|
90
|
-
private
|
|
91
|
-
private
|
|
67
|
+
private stopFollowing?;
|
|
68
|
+
private intervalTimer?;
|
|
69
|
+
private flushTimer?;
|
|
70
|
+
private minLengthTimer?;
|
|
71
|
+
private viewId;
|
|
72
|
+
private viewUrl;
|
|
73
|
+
private session;
|
|
74
|
+
private streamStart;
|
|
75
|
+
private chunkStart;
|
|
76
|
+
private sequence;
|
|
77
|
+
private events;
|
|
78
|
+
private eventBytes;
|
|
92
79
|
private resumed;
|
|
93
|
-
private
|
|
94
|
-
private replayEventSequence;
|
|
95
|
-
private readonly mutationThrottler;
|
|
80
|
+
private unfinalized;
|
|
96
81
|
constructor(options: ReplayTrackerOptions);
|
|
97
|
-
private log;
|
|
98
|
-
private sessionContext;
|
|
99
|
-
private sanitize;
|
|
100
|
-
private clearFlushTimers;
|
|
101
|
-
private dropEvents;
|
|
102
|
-
private clearQueues;
|
|
103
|
-
private finalizeSession;
|
|
104
|
-
setCookielessMode(cookieless: boolean): void;
|
|
105
82
|
start(): void;
|
|
83
|
+
stop(discard?: boolean): void;
|
|
84
|
+
/** Nothing captured under the old identity may be sent under the new one. */
|
|
85
|
+
setCookielessMode(cookieless: boolean): void;
|
|
106
86
|
trackPageChange(url?: string): void;
|
|
107
87
|
onPageHidden(persisted?: boolean, terminal?: boolean): void;
|
|
108
88
|
onPageShow(persisted?: boolean): void;
|
|
109
|
-
private
|
|
89
|
+
private log;
|
|
90
|
+
private sanitize;
|
|
91
|
+
private currentSession;
|
|
92
|
+
private followSession;
|
|
110
93
|
private beginRecording;
|
|
111
|
-
|
|
112
|
-
private
|
|
113
|
-
private subscribeToSessionRotation;
|
|
114
|
-
private applySessionRotation;
|
|
115
|
-
private beginReplayStream;
|
|
116
|
-
private resetSessionTiming;
|
|
117
|
-
private onEvent;
|
|
118
|
-
private requestTerminalFlush;
|
|
119
|
-
private hasReachedMinLength;
|
|
120
|
-
private requestFlush;
|
|
121
|
-
private scheduleMinLengthFlush;
|
|
122
|
-
private enqueueEventsIfReady;
|
|
123
|
-
private takeEventBatches;
|
|
124
|
-
private createBatch;
|
|
125
|
-
private eventSizeBytes;
|
|
126
|
-
private batchSizeBytes;
|
|
127
|
-
private removePending;
|
|
94
|
+
private recordingPlugins;
|
|
95
|
+
private takeFullSnapshot;
|
|
128
96
|
private stopCapture;
|
|
129
|
-
private
|
|
97
|
+
private readonly onEvent;
|
|
98
|
+
private beginStream;
|
|
99
|
+
private rotate;
|
|
100
|
+
private reachedMinLength;
|
|
101
|
+
private dropEvents;
|
|
102
|
+
private requestFlush;
|
|
130
103
|
private flush;
|
|
131
|
-
private
|
|
132
|
-
private
|
|
133
|
-
private
|
|
134
|
-
private
|
|
104
|
+
private finish;
|
|
105
|
+
private batchEvents;
|
|
106
|
+
private endStream;
|
|
107
|
+
private enqueue;
|
|
135
108
|
}
|
|
136
109
|
//#endregion
|
|
137
110
|
export {};
|