@faststats/web 0.13.0 → 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/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 -4
- package/dist/index.js +51 -121
- 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-C76HbZ8d.js +0 -96
- package/dist/chunks/session-manager-DUpF3mi3.js +0 -149
package/dist/index.js
CHANGED
|
@@ -1,62 +1,6 @@
|
|
|
1
|
-
import { a as resolveBaseUrl, i as getPageContext, n as URLS, o as sanitizeUrl, r as getClientContext, t as sendData } from "./chunks/send-data-
|
|
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,38 +32,30 @@ 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
|
}
|
|
@@ -190,9 +126,8 @@ var WebAnalytics = class {
|
|
|
190
126
|
context,
|
|
191
127
|
duration: 0,
|
|
192
128
|
startedAt: null,
|
|
193
|
-
checkedAt: Date.now(),
|
|
194
129
|
activeAt: Date.now(),
|
|
195
|
-
reportedDuration:
|
|
130
|
+
reportedDuration: 0,
|
|
196
131
|
beats: 0
|
|
197
132
|
};
|
|
198
133
|
this.send("pageview", {
|
|
@@ -257,21 +192,19 @@ var WebAnalytics = class {
|
|
|
257
192
|
setConsentMode(mode) {
|
|
258
193
|
if (mode === this.consentMode) return;
|
|
259
194
|
const wasTracking = this.canTrack();
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
const willBeCookieless = this.options.cookieless === true || mode === "anonymous";
|
|
263
|
-
if (wasTracking && willTrack && wasCookieless !== willBeCookieless) this.leavePage();
|
|
195
|
+
const identityChanges = this.isCookieless() !== this.isCookieless(mode);
|
|
196
|
+
if (mode !== "denied" && identityChanges) this.leavePage();
|
|
264
197
|
this.consentMode = mode;
|
|
265
|
-
if (
|
|
198
|
+
if (mode === "denied") {
|
|
266
199
|
this.leavePage();
|
|
267
200
|
this.pendingPageview = null;
|
|
268
201
|
this.stopExtensions(true);
|
|
269
|
-
|
|
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" });
|
|
270
207
|
}
|
|
271
|
-
if (wasTracking) {
|
|
272
|
-
this.extensions?.emit("consentChange", { cookieless: willBeCookieless });
|
|
273
|
-
if (wasCookieless !== willBeCookieless) this.pageview({ trigger: "consent" });
|
|
274
|
-
} else if (this.state === "started") this.activate("consent");
|
|
275
208
|
}
|
|
276
209
|
getAnonymousId() {
|
|
277
210
|
return this.canTrack() ? getAnonymousId(this.options.siteKey, this.isCookieless()) : "";
|
|
@@ -283,8 +216,8 @@ var WebAnalytics = class {
|
|
|
283
216
|
canTrack() {
|
|
284
217
|
return this.consentMode !== "denied";
|
|
285
218
|
}
|
|
286
|
-
isCookieless() {
|
|
287
|
-
return this.options.cookieless === true ||
|
|
219
|
+
isCookieless(mode = this.consentMode) {
|
|
220
|
+
return this.options.cookieless === true || mode === "anonymous";
|
|
288
221
|
}
|
|
289
222
|
isActive() {
|
|
290
223
|
return this.state === "started" && this.canTrack();
|
|
@@ -328,7 +261,7 @@ var WebAnalytics = class {
|
|
|
328
261
|
this.bind(window, "pagehide", this.onPageHide);
|
|
329
262
|
this.bind(window, "pageshow", this.onPageShow);
|
|
330
263
|
this.bind(window, "focus", this.onResume);
|
|
331
|
-
this.bind(window, "blur",
|
|
264
|
+
this.bind(window, "blur", this.onBlur);
|
|
332
265
|
this.bind(window, "popstate", this.onNavigate);
|
|
333
266
|
for (const type of [
|
|
334
267
|
"pointerdown",
|
|
@@ -345,7 +278,6 @@ var WebAnalytics = class {
|
|
|
345
278
|
}
|
|
346
279
|
onNavigate = () => this.pageview({ trigger: "navigation" });
|
|
347
280
|
onVisibilityChange = () => {
|
|
348
|
-
if (!this.canTrack()) return;
|
|
349
281
|
if (document.visibilityState === "hidden") {
|
|
350
282
|
this.pauseVisit();
|
|
351
283
|
this.extensions?.emit("pageHide", {
|
|
@@ -357,7 +289,6 @@ var WebAnalytics = class {
|
|
|
357
289
|
this.onResume();
|
|
358
290
|
};
|
|
359
291
|
onPageHide = (event) => {
|
|
360
|
-
if (!this.canTrack()) return;
|
|
361
292
|
const persisted = event.persisted;
|
|
362
293
|
if (persisted) this.pauseVisit();
|
|
363
294
|
else this.leavePage();
|
|
@@ -367,7 +298,6 @@ var WebAnalytics = class {
|
|
|
367
298
|
});
|
|
368
299
|
};
|
|
369
300
|
onPageShow = (event) => {
|
|
370
|
-
if (!this.canTrack()) return;
|
|
371
301
|
this.onResume();
|
|
372
302
|
this.extensions?.emit("pageShow", { persisted: event.persisted });
|
|
373
303
|
};
|
|
@@ -375,6 +305,13 @@ var WebAnalytics = class {
|
|
|
375
305
|
this.pageview(this.pendingPageview ?? { trigger: "session" });
|
|
376
306
|
this.resumeVisit();
|
|
377
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
|
+
};
|
|
378
315
|
onActivity = () => {
|
|
379
316
|
this.lastInputAt = performance.now();
|
|
380
317
|
if (this.visit?.startedAt === null && document.hasFocus()) this.onResume();
|
|
@@ -382,30 +319,24 @@ var WebAnalytics = class {
|
|
|
382
319
|
isIdle() {
|
|
383
320
|
const now = performance.now();
|
|
384
321
|
if (now - this.lastInputAt < IDLE_MS) return false;
|
|
385
|
-
const
|
|
386
|
-
if (
|
|
387
|
-
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;
|
|
388
325
|
}
|
|
389
326
|
leavePage() {
|
|
390
327
|
this.pauseVisit(true);
|
|
391
328
|
this.visit = null;
|
|
392
329
|
}
|
|
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) {
|
|
330
|
+
checkpoint(leaving = false, periodic = false, now = performance.now()) {
|
|
398
331
|
const visit = this.visit;
|
|
399
332
|
if (!visit || !this.isActive()) return;
|
|
400
333
|
if (visit.startedAt !== null) {
|
|
401
|
-
const
|
|
402
|
-
if (now - visit.startedAt < IDLE_MS) {
|
|
403
|
-
|
|
404
|
-
visit.
|
|
405
|
-
visit.activeAt = visit.checkedAt + 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);
|
|
406
338
|
}
|
|
407
339
|
visit.startedAt = now;
|
|
408
|
-
visit.checkedAt = Date.now();
|
|
409
340
|
}
|
|
410
341
|
if (periodic && ++visit.beats !== 1 && visit.beats % 4 !== 3) return;
|
|
411
342
|
const duration = Math.round(visit.duration);
|
|
@@ -417,16 +348,15 @@ var WebAnalytics = class {
|
|
|
417
348
|
session_duration: Math.max(0, visit.activeAt - visit.session.sessionStart)
|
|
418
349
|
}, visit.context);
|
|
419
350
|
}
|
|
420
|
-
pauseVisit(leaving = false) {
|
|
351
|
+
pauseVisit(leaving = false, at) {
|
|
421
352
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
422
353
|
this.heartbeatTimer = null;
|
|
423
|
-
this.checkpoint(leaving);
|
|
354
|
+
this.checkpoint(leaving, false, at);
|
|
424
355
|
if (this.visit) this.visit.startedAt = null;
|
|
425
356
|
}
|
|
426
357
|
resumeVisit() {
|
|
427
358
|
if (!this.isActive() || !this.visit || this.visit.startedAt !== null || document.visibilityState !== "visible" || !document.hasFocus()) return;
|
|
428
359
|
this.visit.startedAt = this.lastInputAt = performance.now();
|
|
429
|
-
this.visit.checkedAt = Date.now();
|
|
430
360
|
this.heartbeatTimer = setInterval(() => {
|
|
431
361
|
if (document.visibilityState !== "visible" || !document.hasFocus() || this.isIdle()) {
|
|
432
362
|
this.pauseVisit();
|
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 {};
|