@faststats/web 0.7.0 → 0.8.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 +67 -0
- package/dist/chunks/{api-urls-Dp2XNjRF.js → api-urls-DWk43fu6.js} +12 -5
- package/dist/chunks/extensions-DW7tJY0T.d.ts +51 -0
- package/dist/chunks/{identifiers-BvRNbiwA.js → identifiers-KgQDX74Q.js} +3 -3
- package/dist/chunks/{send-data-DR1JUXka.js → send-data-DFZnsaL2.js} +63 -45
- package/dist/error.d.ts +7 -2
- package/dist/error.js +36 -14
- package/dist/index.d.ts +54 -72
- package/dist/index.js +341 -363
- package/dist/outbound-links.d.ts +10 -0
- package/dist/outbound-links.js +62 -0
- package/dist/replay.d.ts +128 -2
- package/dist/replay.js +51 -32
- package/dist/web-vitals.d.ts +7 -1
- package/dist/web-vitals.js +39 -12
- package/package.json +11 -12
- package/dist/chunks/replay-BeC2XA4N.d.ts +0 -124
- package/dist/feature-flags.d.ts +0 -18
- package/dist/feature-flags.js +0 -32
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { t as AnalyticsExtension } from "./chunks/extensions-DW7tJY0T.js";
|
|
2
|
+
//#region src/outbound-links.d.ts
|
|
3
|
+
declare function outboundLinks(): AnalyticsExtension;
|
|
4
|
+
declare class OutboundLinkTracker {
|
|
5
|
+
private lastActivation;
|
|
6
|
+
getHref(event: Event): string | null;
|
|
7
|
+
private isDuplicate;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { OutboundLinkTracker, outboundLinks };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { a as sanitizeUrl } from "./chunks/api-urls-DWk43fu6.js";
|
|
2
|
+
//#region src/outbound-links.ts
|
|
3
|
+
function outboundLinks() {
|
|
4
|
+
return {
|
|
5
|
+
name: "outbound-links",
|
|
6
|
+
setup(context) {
|
|
7
|
+
const tracker = new OutboundLinkTracker();
|
|
8
|
+
const onClick = (event) => {
|
|
9
|
+
const href = tracker.getHref(event);
|
|
10
|
+
if (href) context.capture("outbound_link", { outbound_link: sanitizeUrl(href) });
|
|
11
|
+
};
|
|
12
|
+
document.addEventListener("click", onClick, true);
|
|
13
|
+
document.addEventListener("auxclick", onClick, true);
|
|
14
|
+
return () => {
|
|
15
|
+
document.removeEventListener("click", onClick, true);
|
|
16
|
+
document.removeEventListener("auxclick", onClick, true);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const DEDUPE_MS = 500;
|
|
22
|
+
var OutboundLinkTracker = class {
|
|
23
|
+
lastActivation = null;
|
|
24
|
+
getHref(event) {
|
|
25
|
+
if (!(event instanceof MouseEvent) || !isEligibleActivation(event)) return null;
|
|
26
|
+
const anchor = findOutboundAnchor(event);
|
|
27
|
+
if (!anchor || this.isDuplicate(event, anchor.href)) return null;
|
|
28
|
+
return anchor.href;
|
|
29
|
+
}
|
|
30
|
+
isDuplicate(event, href) {
|
|
31
|
+
const activation = {
|
|
32
|
+
button: event.button,
|
|
33
|
+
href,
|
|
34
|
+
clientX: event.clientX,
|
|
35
|
+
clientY: event.clientY,
|
|
36
|
+
at: Date.now()
|
|
37
|
+
};
|
|
38
|
+
const previous = this.lastActivation;
|
|
39
|
+
this.lastActivation = activation;
|
|
40
|
+
return !!(previous && previous.href === activation.href && previous.button === activation.button && previous.clientX === activation.clientX && previous.clientY === activation.clientY && activation.at - previous.at <= DEDUPE_MS);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function isEligibleActivation(event) {
|
|
44
|
+
if (event.defaultPrevented) return false;
|
|
45
|
+
if (event.type === "click") return event.button === 0;
|
|
46
|
+
if (event.type === "auxclick") return event.button === 1;
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function findOutboundAnchor(event) {
|
|
50
|
+
for (const node of event.composedPath()) if (isOutboundAnchor(node)) return node;
|
|
51
|
+
let node = event.target;
|
|
52
|
+
while (node) {
|
|
53
|
+
if (isOutboundAnchor(node)) return node;
|
|
54
|
+
node = node.parentNode;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function isOutboundAnchor(node) {
|
|
59
|
+
return node instanceof HTMLAnchorElement && (node.protocol === "http:" || node.protocol === "https:") && node.host !== location.host;
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { OutboundLinkTracker, outboundLinks };
|
package/dist/replay.d.ts
CHANGED
|
@@ -1,2 +1,128 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { t as AnalyticsExtension } from "./chunks/extensions-DW7tJY0T.js";
|
|
2
|
+
import { record } from "@rrweb/record";
|
|
3
|
+
import { LogLevel } from "@rrweb/rrweb-plugin-console-record";
|
|
4
|
+
//#region ../../node_modules/.bun/@rrweb+types@2.1.1/node_modules/@rrweb/types/dist/index.d.ts
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
FontFace: typeof FontFace;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/replay-mutation-throttler.d.ts
|
|
12
|
+
type ReplayMutationThrottleOptions = {
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
bucketSize?: number;
|
|
15
|
+
refillRate?: number;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/replay.d.ts
|
|
19
|
+
type RecordOptions = NonNullable<Parameters<typeof record>[0]>;
|
|
20
|
+
interface ReplayTrackerOptions {
|
|
21
|
+
siteKey: string;
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
debug?: boolean;
|
|
24
|
+
trackHash?: boolean;
|
|
25
|
+
cookieless?: boolean;
|
|
26
|
+
compress?: boolean;
|
|
27
|
+
flushInterval?: number;
|
|
28
|
+
maxEvents?: number;
|
|
29
|
+
maxBatchSizeBytes?: number;
|
|
30
|
+
maxLowLatencyBatchSizeBytes?: number;
|
|
31
|
+
maxPendingBatches?: number;
|
|
32
|
+
maxQueueSizeBytes?: number;
|
|
33
|
+
minReplayLengthMs?: number;
|
|
34
|
+
sampling?: RecordOptions["sampling"];
|
|
35
|
+
slimDOMOptions?: RecordOptions["slimDOMOptions"];
|
|
36
|
+
maskAllInputs?: boolean;
|
|
37
|
+
maskInputOptions?: RecordOptions["maskInputOptions"];
|
|
38
|
+
blockClass?: string;
|
|
39
|
+
blockSelector?: string;
|
|
40
|
+
maskTextClass?: string;
|
|
41
|
+
maskTextSelector?: string;
|
|
42
|
+
checkoutEveryNms?: number;
|
|
43
|
+
checkoutEveryNth?: number;
|
|
44
|
+
recordConsole?: boolean;
|
|
45
|
+
consoleLevel?: LogLevel[];
|
|
46
|
+
consoleLengthThreshold?: number;
|
|
47
|
+
consoleStringLengthLimit?: number;
|
|
48
|
+
mutationThrottle?: ReplayMutationThrottleOptions;
|
|
49
|
+
}
|
|
50
|
+
type SessionReplayExtensionOptions = Omit<ReplayTrackerOptions, "siteKey" | "baseUrl" | "debug" | "trackHash" | "cookieless">;
|
|
51
|
+
declare function sessionReplay(options?: SessionReplayExtensionOptions): AnalyticsExtension;
|
|
52
|
+
declare class ReplayTracker {
|
|
53
|
+
private readonly options;
|
|
54
|
+
private readonly endpoint;
|
|
55
|
+
private readonly compressionSupported;
|
|
56
|
+
private readonly flushInterval;
|
|
57
|
+
private readonly maxEvents;
|
|
58
|
+
private readonly maxBatchSizeBytes;
|
|
59
|
+
private readonly maxLowLatencyBatchSizeBytes;
|
|
60
|
+
private readonly maxPendingBatches;
|
|
61
|
+
private readonly maxQueueSizeBytes;
|
|
62
|
+
private readonly minReplayLengthMs;
|
|
63
|
+
private readonly events;
|
|
64
|
+
private readonly pending;
|
|
65
|
+
private readonly minLengthBlockedBatches;
|
|
66
|
+
private queuedEventsSizeBytes;
|
|
67
|
+
private pendingSizeBytes;
|
|
68
|
+
private viewId;
|
|
69
|
+
private viewUrl;
|
|
70
|
+
private chunkStartedAt;
|
|
71
|
+
private started;
|
|
72
|
+
private disposed;
|
|
73
|
+
private startTime;
|
|
74
|
+
private sequence;
|
|
75
|
+
private intervalId;
|
|
76
|
+
private flushTask;
|
|
77
|
+
private retryTask;
|
|
78
|
+
private retryAttempt;
|
|
79
|
+
private minLengthFlushTask;
|
|
80
|
+
private stopRecording?;
|
|
81
|
+
private sending;
|
|
82
|
+
private inFlightBatch;
|
|
83
|
+
private queuedFlush;
|
|
84
|
+
private unsubscribeRotation?;
|
|
85
|
+
private lastCookielessMode;
|
|
86
|
+
private terminalFlushRequested;
|
|
87
|
+
private overflowed;
|
|
88
|
+
private replayEventSequence;
|
|
89
|
+
private readonly mutationThrottler;
|
|
90
|
+
constructor(options: ReplayTrackerOptions);
|
|
91
|
+
private log;
|
|
92
|
+
private clearFlushTimers;
|
|
93
|
+
private clearQueues;
|
|
94
|
+
private dropMinLengthBlockedBatches;
|
|
95
|
+
private unblockSessionBatches;
|
|
96
|
+
private finalizeSessionPending;
|
|
97
|
+
private nextBatchSequence;
|
|
98
|
+
setCookielessMode(cookieless: boolean): void;
|
|
99
|
+
start(): void;
|
|
100
|
+
trackPageChange(url?: string): void;
|
|
101
|
+
onPageHidden(persisted?: boolean, terminal?: boolean): void;
|
|
102
|
+
onPageShow(persisted?: boolean): void;
|
|
103
|
+
private beginRecording;
|
|
104
|
+
stop(discard?: boolean): void;
|
|
105
|
+
private onSessionRotated;
|
|
106
|
+
private subscribeToSessionRotation;
|
|
107
|
+
private applySessionRotation;
|
|
108
|
+
private resetSessionTiming;
|
|
109
|
+
private onEvent;
|
|
110
|
+
private onUnload;
|
|
111
|
+
private requestTerminalFlush;
|
|
112
|
+
private hasReachedMinLength;
|
|
113
|
+
private requestFlush;
|
|
114
|
+
private scheduleMinLengthFlush;
|
|
115
|
+
private enqueueEventsIfReady;
|
|
116
|
+
private takeEventBatches;
|
|
117
|
+
private createBatch;
|
|
118
|
+
private eventSizeBytes;
|
|
119
|
+
private batchSizeBytes;
|
|
120
|
+
private removePending;
|
|
121
|
+
private enqueueBatch;
|
|
122
|
+
private flush;
|
|
123
|
+
private scheduleRetry;
|
|
124
|
+
private retryDelay;
|
|
125
|
+
private sendBatch;
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
export { ReplayTrackerOptions, SessionReplayExtensionOptions, ReplayTracker as default, sessionReplay };
|
package/dist/replay.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import {
|
|
3
|
-
import { t as getAnonymousId } from "./chunks/identifiers-
|
|
1
|
+
import { a as sanitizeUrl, i as resolveBaseUrl, n as URLS, t as ANALYTICS_BASE } from "./chunks/api-urls-DWk43fu6.js";
|
|
2
|
+
import { i as onSessionRotated, l as setStorageItem, n as createId, o as touchActivity, r as getSessionContext, s as getStorageItem, t as sendData } from "./chunks/send-data-DFZnsaL2.js";
|
|
3
|
+
import { t as getAnonymousId } from "./chunks/identifiers-KgQDX74Q.js";
|
|
4
4
|
import { record } from "@rrweb/record";
|
|
5
|
+
import { getRecordConsolePlugin } from "@rrweb/rrweb-plugin-console-record";
|
|
5
6
|
//#region src/replay-mutation-throttler.ts
|
|
6
7
|
const INCREMENTAL_SNAPSHOT_EVENT_TYPE = 3;
|
|
7
8
|
const MUTATION_SOURCE_TYPE = 0;
|
|
@@ -52,18 +53,39 @@ var ReplayMutationThrottler = class {
|
|
|
52
53
|
//#endregion
|
|
53
54
|
//#region src/replay.ts
|
|
54
55
|
const MAX_TIMEOUT_MS = 2147483647;
|
|
55
|
-
const DEFAULT_MAX_QUEUE_SIZE_BYTES =
|
|
56
|
-
const DEFAULT_MAX_BATCH_SIZE_BYTES =
|
|
57
|
-
const DEFAULT_MAX_LOW_LATENCY_BATCH_SIZE_BYTES =
|
|
56
|
+
const DEFAULT_MAX_QUEUE_SIZE_BYTES = 2097152;
|
|
57
|
+
const DEFAULT_MAX_BATCH_SIZE_BYTES = 921600;
|
|
58
|
+
const DEFAULT_MAX_LOW_LATENCY_BATCH_SIZE_BYTES = 61440;
|
|
58
59
|
const RETRY_BASE_DELAY_MS = 2e3;
|
|
59
60
|
const RETRY_MAX_ATTEMPTS = 5;
|
|
60
|
-
const RETRY_COOLDOWN_MS =
|
|
61
|
+
const RETRY_COOLDOWN_MS = 3e5;
|
|
61
62
|
const VIEW_META_EVENT_TYPE = 5;
|
|
62
63
|
const RRWEB_EVENT_FULL_SNAPSHOT = 2;
|
|
63
64
|
const RRWEB_EVENT_META = 4;
|
|
64
65
|
const REPLAY_SEQUENTIAL_ID_KEY = "_faststatsSeqId";
|
|
65
66
|
const textEncoder = new TextEncoder();
|
|
66
67
|
const batchSizeCache = /* @__PURE__ */ new WeakMap();
|
|
68
|
+
function sessionReplay(options = {}) {
|
|
69
|
+
return {
|
|
70
|
+
name: "session-replay",
|
|
71
|
+
setup(context) {
|
|
72
|
+
const tracker = new ReplayTracker({
|
|
73
|
+
...options,
|
|
74
|
+
siteKey: context.siteKey,
|
|
75
|
+
baseUrl: context.baseUrl,
|
|
76
|
+
debug: context.debug,
|
|
77
|
+
trackHash: context.trackHash,
|
|
78
|
+
cookieless: context.isCookieless()
|
|
79
|
+
});
|
|
80
|
+
context.on("pageChange", ({ url }) => tracker.trackPageChange(url));
|
|
81
|
+
context.on("pageHide", ({ persisted, terminal }) => tracker.onPageHidden(persisted, terminal));
|
|
82
|
+
context.on("pageShow", ({ persisted }) => tracker.onPageShow(persisted));
|
|
83
|
+
context.on("consentChange", ({ cookieless }) => tracker.setCookielessMode(cookieless));
|
|
84
|
+
tracker.start();
|
|
85
|
+
return ({ discard }) => tracker.stop(discard);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
67
89
|
var ReplayTracker = class {
|
|
68
90
|
options;
|
|
69
91
|
endpoint;
|
|
@@ -85,7 +107,6 @@ var ReplayTracker = class {
|
|
|
85
107
|
chunkStartedAt = 0;
|
|
86
108
|
started = false;
|
|
87
109
|
disposed = false;
|
|
88
|
-
startGeneration = 0;
|
|
89
110
|
startTime = 0;
|
|
90
111
|
sequence = 0;
|
|
91
112
|
intervalId = null;
|
|
@@ -98,12 +119,14 @@ var ReplayTracker = class {
|
|
|
98
119
|
inFlightBatch = null;
|
|
99
120
|
queuedFlush = null;
|
|
100
121
|
unsubscribeRotation;
|
|
101
|
-
lastCookielessMode
|
|
122
|
+
lastCookielessMode;
|
|
102
123
|
terminalFlushRequested = false;
|
|
103
124
|
overflowed = false;
|
|
125
|
+
replayEventSequence = 0;
|
|
104
126
|
mutationThrottler;
|
|
105
127
|
constructor(options) {
|
|
106
128
|
this.options = options;
|
|
129
|
+
this.lastCookielessMode = options.cookieless ?? false;
|
|
107
130
|
this.endpoint = `${resolveBaseUrl(options.baseUrl, ANALYTICS_BASE)}${URLS.replay}`;
|
|
108
131
|
this.flushInterval = options.flushInterval ?? 15e3;
|
|
109
132
|
this.maxEvents = options.maxEvents ?? 1e3;
|
|
@@ -166,9 +189,10 @@ var ReplayTracker = class {
|
|
|
166
189
|
setCookielessMode(cookieless) {
|
|
167
190
|
if (this.lastCookielessMode === cookieless) return;
|
|
168
191
|
this.lastCookielessMode = cookieless;
|
|
192
|
+
if (this.started) this.subscribeToSessionRotation();
|
|
169
193
|
this.clearQueues();
|
|
170
194
|
this.clearFlushTimers();
|
|
171
|
-
this.startTime = getSessionContext(this.options.siteKey).sessionStart;
|
|
195
|
+
this.startTime = getSessionContext(this.options.siteKey, this.lastCookielessMode).sessionStart;
|
|
172
196
|
this.chunkStartedAt = Date.now();
|
|
173
197
|
}
|
|
174
198
|
start() {
|
|
@@ -179,13 +203,10 @@ var ReplayTracker = class {
|
|
|
179
203
|
this.overflowed = false;
|
|
180
204
|
this.viewId = createId();
|
|
181
205
|
this.viewUrl = sanitizeUrl(window.location.href, this.options.trackHash ?? false);
|
|
182
|
-
this.startTime = getSessionContext(this.options.siteKey).sessionStart;
|
|
206
|
+
this.startTime = getSessionContext(this.options.siteKey, this.lastCookielessMode).sessionStart;
|
|
183
207
|
this.chunkStartedAt = Date.now();
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
});
|
|
187
|
-
const generation = ++this.startGeneration;
|
|
188
|
-
this.beginRecording(generation);
|
|
208
|
+
this.subscribeToSessionRotation();
|
|
209
|
+
this.beginRecording();
|
|
189
210
|
this.intervalId = setInterval(() => this.requestFlush("interval"), this.flushInterval);
|
|
190
211
|
window.addEventListener("beforeunload", this.onUnload);
|
|
191
212
|
this.log("Recording started");
|
|
@@ -219,11 +240,9 @@ var ReplayTracker = class {
|
|
|
219
240
|
if (!persisted || !this.started) return;
|
|
220
241
|
if (this.pending.length > 0) this.flush(false, false, "pageShow");
|
|
221
242
|
}
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
if (
|
|
225
|
-
const plugins = [sequentialIdPluginModule.getRecordSequentialIdPlugin({ key: REPLAY_SEQUENTIAL_ID_KEY })];
|
|
226
|
-
if (consolePluginModule) plugins.push(consolePluginModule.getRecordConsolePlugin({
|
|
243
|
+
beginRecording() {
|
|
244
|
+
const plugins = [];
|
|
245
|
+
if (this.options.recordConsole === true) plugins.push(getRecordConsolePlugin({
|
|
227
246
|
level: this.options.consoleLevel ?? [
|
|
228
247
|
"error",
|
|
229
248
|
"warn",
|
|
@@ -267,21 +286,16 @@ var ReplayTracker = class {
|
|
|
267
286
|
blockSelector: this.options.blockSelector,
|
|
268
287
|
maskTextClass: this.options.maskTextClass,
|
|
269
288
|
maskTextSelector: this.options.maskTextSelector,
|
|
270
|
-
checkoutEveryNms: this.options.checkoutEveryNms ??
|
|
289
|
+
checkoutEveryNms: this.options.checkoutEveryNms ?? 3e5,
|
|
271
290
|
checkoutEveryNth: this.options.checkoutEveryNth,
|
|
272
291
|
plugins
|
|
273
292
|
});
|
|
274
|
-
if (generation !== this.startGeneration) {
|
|
275
|
-
stop?.();
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
293
|
this.stopRecording = stop;
|
|
279
294
|
}
|
|
280
295
|
stop(discard = false) {
|
|
281
296
|
if (!this.started) return;
|
|
282
297
|
this.started = false;
|
|
283
298
|
this.disposed = true;
|
|
284
|
-
this.startGeneration++;
|
|
285
299
|
this.unsubscribeRotation?.();
|
|
286
300
|
this.unsubscribeRotation = void 0;
|
|
287
301
|
this.stopRecording?.();
|
|
@@ -309,6 +323,10 @@ var ReplayTracker = class {
|
|
|
309
323
|
this.applySessionRotation(prev, next);
|
|
310
324
|
if (this.pending.length > 0) queueMicrotask(() => void this.flush(false, false, "sessionRotate"));
|
|
311
325
|
}
|
|
326
|
+
subscribeToSessionRotation() {
|
|
327
|
+
this.unsubscribeRotation?.();
|
|
328
|
+
this.unsubscribeRotation = onSessionRotated(this.options.siteKey, this.lastCookielessMode, (prev, next) => this.onSessionRotated(prev, next));
|
|
329
|
+
}
|
|
312
330
|
applySessionRotation(prev, next) {
|
|
313
331
|
this.enqueueEventsIfReady(prev, {
|
|
314
332
|
isFinal: true,
|
|
@@ -343,6 +361,7 @@ var ReplayTracker = class {
|
|
|
343
361
|
if (this.mutationThrottler && !throttled) return;
|
|
344
362
|
if (throttled) capturedEvent = throttled;
|
|
345
363
|
}
|
|
364
|
+
capturedEvent = Object.assign(capturedEvent, { [REPLAY_SEQUENTIAL_ID_KEY]: ++this.replayEventSequence });
|
|
346
365
|
this.events.push(capturedEvent);
|
|
347
366
|
this.queuedEventsSizeBytes += this.eventSizeBytes(capturedEvent);
|
|
348
367
|
let reason;
|
|
@@ -421,7 +440,7 @@ var ReplayTracker = class {
|
|
|
421
440
|
return batches;
|
|
422
441
|
}
|
|
423
442
|
createBatch(events, context, isFinal, flushReason = "manual") {
|
|
424
|
-
const identifier = getAnonymousId(this.options.siteKey);
|
|
443
|
+
const identifier = getAnonymousId(this.options.siteKey, this.lastCookielessMode);
|
|
425
444
|
const sequence = this.nextBatchSequence(context);
|
|
426
445
|
const chunkStartedAt = this.chunkStartedAt;
|
|
427
446
|
const now = Date.now();
|
|
@@ -475,9 +494,9 @@ var ReplayTracker = class {
|
|
|
475
494
|
this.pendingSizeBytes += size;
|
|
476
495
|
}
|
|
477
496
|
async flush(lowLatency, isFinal = false, flushReason = "manual") {
|
|
478
|
-
const rotation = touchActivity(this.options.siteKey);
|
|
497
|
+
const rotation = touchActivity(this.options.siteKey, this.lastCookielessMode);
|
|
479
498
|
if (rotation) this.applySessionRotation(rotation.prev, rotation.next);
|
|
480
|
-
const context = getSessionContext(this.options.siteKey);
|
|
499
|
+
const context = getSessionContext(this.options.siteKey, this.lastCookielessMode);
|
|
481
500
|
this.enqueueEventsIfReady(context, {
|
|
482
501
|
isFinal,
|
|
483
502
|
flushReason,
|
|
@@ -549,7 +568,7 @@ var ReplayTracker = class {
|
|
|
549
568
|
}
|
|
550
569
|
async sendBatch(batch, lowLatency) {
|
|
551
570
|
const json = JSON.stringify(batch);
|
|
552
|
-
const lowLatencySafe = lowLatency && textEncoder.encode(json).byteLength <=
|
|
571
|
+
const lowLatencySafe = lowLatency && textEncoder.encode(json).byteLength <= 61440;
|
|
553
572
|
const sendOptions = {
|
|
554
573
|
useBeacon: lowLatencySafe,
|
|
555
574
|
keepalive: lowLatencySafe
|
|
@@ -576,4 +595,4 @@ var ReplayTracker = class {
|
|
|
576
595
|
}
|
|
577
596
|
};
|
|
578
597
|
//#endregion
|
|
579
|
-
export { ReplayTracker as default };
|
|
598
|
+
export { ReplayTracker as default, sessionReplay };
|
package/dist/web-vitals.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as AnalyticsExtension } from "./chunks/extensions-DW7tJY0T.js";
|
|
1
2
|
//#region src/web-vitals.d.ts
|
|
2
3
|
interface WebVitalsOptions {
|
|
3
4
|
siteKey: string;
|
|
@@ -5,7 +6,10 @@ interface WebVitalsOptions {
|
|
|
5
6
|
debug?: boolean;
|
|
6
7
|
attribution?: boolean;
|
|
7
8
|
trackHash?: boolean;
|
|
9
|
+
cookieless?: boolean;
|
|
8
10
|
}
|
|
11
|
+
type WebVitalsExtensionOptions = Pick<WebVitalsOptions, "attribution">;
|
|
12
|
+
declare function webVitals(options?: WebVitalsExtensionOptions): AnalyticsExtension;
|
|
9
13
|
declare class WebVitalsTracker {
|
|
10
14
|
private readonly options;
|
|
11
15
|
private readonly endpoint;
|
|
@@ -14,7 +18,9 @@ declare class WebVitalsTracker {
|
|
|
14
18
|
private flushing;
|
|
15
19
|
private initialUrl;
|
|
16
20
|
private session;
|
|
21
|
+
private cookieless;
|
|
17
22
|
constructor(options: WebVitalsOptions);
|
|
23
|
+
setCookielessMode(cookieless: boolean): void;
|
|
18
24
|
start(): Promise<void>;
|
|
19
25
|
stop(discard?: boolean): void;
|
|
20
26
|
onPageHidden(persisted?: boolean): void;
|
|
@@ -22,4 +28,4 @@ declare class WebVitalsTracker {
|
|
|
22
28
|
private flush;
|
|
23
29
|
}
|
|
24
30
|
//#endregion
|
|
25
|
-
export { WebVitalsOptions, WebVitalsTracker as default };
|
|
31
|
+
export { WebVitalsExtensionOptions, WebVitalsOptions, WebVitalsTracker as default, webVitals };
|
package/dist/web-vitals.js
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { r as getSessionContext, t as sendData } from "./chunks/send-data-
|
|
1
|
+
import { a as sanitizeUrl, i as resolveBaseUrl, n as URLS, t as ANALYTICS_BASE } from "./chunks/api-urls-DWk43fu6.js";
|
|
2
|
+
import { r as getSessionContext, t as sendData } from "./chunks/send-data-DFZnsaL2.js";
|
|
3
3
|
//#region src/web-vitals.ts
|
|
4
|
+
function webVitals(options = {}) {
|
|
5
|
+
return {
|
|
6
|
+
name: "web-vitals",
|
|
7
|
+
async setup(context) {
|
|
8
|
+
const tracker = new WebVitalsTracker({
|
|
9
|
+
...options,
|
|
10
|
+
siteKey: context.siteKey,
|
|
11
|
+
baseUrl: context.baseUrl,
|
|
12
|
+
debug: context.debug,
|
|
13
|
+
trackHash: context.trackHash,
|
|
14
|
+
cookieless: context.isCookieless()
|
|
15
|
+
});
|
|
16
|
+
context.on("pageHide", ({ persisted }) => tracker.onPageHidden(persisted));
|
|
17
|
+
context.on("consentChange", ({ cookieless }) => tracker.setCookielessMode(cookieless));
|
|
18
|
+
await tracker.start();
|
|
19
|
+
return ({ discard }) => tracker.stop(discard);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
4
23
|
const METRIC_NAMES = /* @__PURE__ */ new Set([
|
|
5
24
|
"CLS",
|
|
6
25
|
"INP",
|
|
@@ -16,23 +35,31 @@ var WebVitalsTracker = class {
|
|
|
16
35
|
flushing = false;
|
|
17
36
|
initialUrl = "";
|
|
18
37
|
session = null;
|
|
38
|
+
cookieless;
|
|
19
39
|
constructor(options) {
|
|
20
40
|
this.options = options;
|
|
21
41
|
this.endpoint = `${resolveBaseUrl(options.baseUrl, ANALYTICS_BASE)}${URLS.vitals}`;
|
|
42
|
+
this.cookieless = options.cookieless ?? false;
|
|
43
|
+
}
|
|
44
|
+
setCookielessMode(cookieless) {
|
|
45
|
+
if (this.cookieless === cookieless) return;
|
|
46
|
+
this.cookieless = cookieless;
|
|
47
|
+
this.metrics.clear();
|
|
48
|
+
this.session = this.started ? getSessionContext(this.options.siteKey, cookieless) : null;
|
|
22
49
|
}
|
|
23
50
|
async start() {
|
|
24
51
|
if (this.started || typeof window === "undefined") return;
|
|
25
52
|
this.started = true;
|
|
26
53
|
this.initialUrl = sanitizeUrl(window.location.href, this.options.trackHash ?? false);
|
|
27
|
-
this.session = getSessionContext(this.options.siteKey);
|
|
28
|
-
const
|
|
54
|
+
this.session = getSessionContext(this.options.siteKey, this.cookieless);
|
|
55
|
+
const vitals = this.options.attribution ? await import("web-vitals/attribution") : await import("web-vitals");
|
|
29
56
|
if (!this.started) return;
|
|
30
57
|
const changes = { reportAllChanges: true };
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
58
|
+
vitals.onCLS(this.onMetric, changes);
|
|
59
|
+
vitals.onINP(this.onMetric, changes);
|
|
60
|
+
vitals.onLCP(this.onMetric, changes);
|
|
61
|
+
vitals.onFCP(this.onMetric);
|
|
62
|
+
vitals.onTTFB(this.onMetric);
|
|
36
63
|
}
|
|
37
64
|
stop(discard = false) {
|
|
38
65
|
if (!this.started) return;
|
|
@@ -49,7 +76,7 @@ var WebVitalsTracker = class {
|
|
|
49
76
|
const name = metric.name;
|
|
50
77
|
if (!METRIC_NAMES.has(name) || !Number.isFinite(metric.value) || metric.value < 0) return;
|
|
51
78
|
const { id, rating, delta, navigationType } = metric;
|
|
52
|
-
const attribution = "attribution" in metric && metric.attribution ? metric.attribution : void 0;
|
|
79
|
+
const attribution = this.options.attribution && "attribution" in metric && metric.attribution ? metric.attribution : void 0;
|
|
53
80
|
this.metrics.set(name, {
|
|
54
81
|
metric: name,
|
|
55
82
|
value: metric.value,
|
|
@@ -67,7 +94,7 @@ var WebVitalsTracker = class {
|
|
|
67
94
|
this.flushing = true;
|
|
68
95
|
const batch = this.metrics;
|
|
69
96
|
this.metrics = /* @__PURE__ */ new Map();
|
|
70
|
-
const session = this.session ?? getSessionContext(this.options.siteKey);
|
|
97
|
+
const session = this.session ?? getSessionContext(this.options.siteKey, this.cookieless);
|
|
71
98
|
let sent = false;
|
|
72
99
|
try {
|
|
73
100
|
sent = await sendData({
|
|
@@ -91,4 +118,4 @@ var WebVitalsTracker = class {
|
|
|
91
118
|
}
|
|
92
119
|
};
|
|
93
120
|
//#endregion
|
|
94
|
-
export { WebVitalsTracker as default };
|
|
121
|
+
export { WebVitalsTracker as default, webVitals };
|
package/package.json
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"import": "./dist/index.js",
|
|
14
14
|
"default": "./dist/index.js"
|
|
15
15
|
},
|
|
16
|
-
"./
|
|
17
|
-
"types": "./dist/
|
|
18
|
-
"import": "./dist/
|
|
19
|
-
"default": "./dist/
|
|
16
|
+
"./outbound-links": {
|
|
17
|
+
"types": "./dist/outbound-links.d.ts",
|
|
18
|
+
"import": "./dist/outbound-links.js",
|
|
19
|
+
"default": "./dist/outbound-links.js"
|
|
20
20
|
},
|
|
21
21
|
"./replay": {
|
|
22
22
|
"types": "./dist/replay.d.ts",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
|
-
"version": "0.
|
|
45
|
+
"version": "0.8.0",
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsdown && bun run check-size",
|
|
48
48
|
"dev": "bun run build",
|
|
@@ -51,15 +51,14 @@
|
|
|
51
51
|
"check-size": "node scripts/check-bundle-size.mjs"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@rrweb/types": "^2.
|
|
55
|
-
"@types/bun": "
|
|
56
|
-
"tsdown": "^0.22.
|
|
54
|
+
"@rrweb/types": "^2.1.1",
|
|
55
|
+
"@types/bun": "^1.4.0",
|
|
56
|
+
"tsdown": "^0.22.14",
|
|
57
57
|
"typescript": "^6.0.3"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@rrweb/record": "^2.1.
|
|
61
|
-
"@rrweb/rrweb-plugin-console-record": "^2.1.
|
|
62
|
-
"
|
|
63
|
-
"web-vitals": "^6.0.0"
|
|
60
|
+
"@rrweb/record": "^2.1.1",
|
|
61
|
+
"@rrweb/rrweb-plugin-console-record": "^2.1.1",
|
|
62
|
+
"web-vitals": "^6.2.1"
|
|
64
63
|
}
|
|
65
64
|
}
|