@faststats/web 0.10.3 → 0.12.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 +54 -6
- package/dist/chunks/{extensions-DW7tJY0T.d.ts → extensions-BfubKIzk.d.ts} +4 -11
- package/dist/chunks/{identifiers-BgGtAQdp.js → identifiers-CpmDmpDv.js} +1 -1
- package/dist/chunks/send-data-DYZZjeM-.js +84 -0
- package/dist/chunks/{send-data-DAVUdfNm.js → session-manager-CFQEWbgo.js} +5 -35
- package/dist/error.d.ts +5 -2
- package/dist/error.js +47 -32
- package/dist/flags.d.ts +79 -0
- package/dist/flags.js +145 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +84 -50
- package/dist/replay.d.ts +6 -4
- package/dist/replay.js +86 -97
- package/dist/web-vitals.d.ts +1 -1
- package/dist/web-vitals.js +5 -7
- package/package.json +11 -11
- package/dist/chunks/api-urls-DWk43fu6.js +0 -53
- package/dist/outbound-links.d.ts +0 -9
- package/dist/outbound-links.js +0 -62
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ExtensionHook, i as ExtensionHandler, n as AnalyticsExtensionContext, o as ExtensionHooks, r as ExtensionCleanup, t as AnalyticsExtension } from "./chunks/extensions-
|
|
1
|
+
import { a as ExtensionHook, i as ExtensionHandler, n as AnalyticsExtensionContext, o as ExtensionHooks, r as ExtensionCleanup, t as AnalyticsExtension } from "./chunks/extensions-BfubKIzk.js";
|
|
2
2
|
//#region src/analytics.d.ts
|
|
3
3
|
type ConsentMode = "anonymous" | "granted" | "denied";
|
|
4
4
|
interface IdentifyOptions {
|
|
@@ -32,7 +32,7 @@ export declare class WebAnalytics {
|
|
|
32
32
|
private readonly baseUrl;
|
|
33
33
|
private readonly debug;
|
|
34
34
|
private readonly cleanup;
|
|
35
|
-
private
|
|
35
|
+
private state;
|
|
36
36
|
private heartbeatTimer;
|
|
37
37
|
private extensions;
|
|
38
38
|
private pageKey;
|
|
@@ -53,6 +53,8 @@ export declare class WebAnalytics {
|
|
|
53
53
|
private canTrack;
|
|
54
54
|
private isCookieless;
|
|
55
55
|
private isActive;
|
|
56
|
+
private session;
|
|
57
|
+
private context;
|
|
56
58
|
private activate;
|
|
57
59
|
private startExtensions;
|
|
58
60
|
private stopExtensions;
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
|
-
import { i as resolveBaseUrl, n as URLS, r as getPageContext, t as
|
|
2
|
-
import { a as
|
|
3
|
-
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-
|
|
1
|
+
import { a as sanitizeUrl, i as resolveBaseUrl, n as URLS, r as getPageContext, t as sendData } from "./chunks/send-data-DYZZjeM-.js";
|
|
2
|
+
import { a as touchActivity, i as resetSession, n as getSessionContext, t as createId } from "./chunks/session-manager-CFQEWbgo.js";
|
|
3
|
+
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-CpmDmpDv.js";
|
|
4
4
|
//#region src/extensions.ts
|
|
5
5
|
var ExtensionHost = class {
|
|
6
|
-
|
|
6
|
+
context;
|
|
7
|
+
log;
|
|
7
8
|
handlers = /* @__PURE__ */ new Map();
|
|
8
9
|
cleanups = [];
|
|
9
10
|
stopped = false;
|
|
10
|
-
constructor(extensions,
|
|
11
|
-
this.
|
|
11
|
+
constructor(extensions, context, log) {
|
|
12
|
+
this.context = context;
|
|
13
|
+
this.log = log;
|
|
12
14
|
for (const extension of extensions) this.setup(extension);
|
|
13
15
|
}
|
|
14
16
|
emit(hook, event) {
|
|
15
17
|
if (this.stopped) return;
|
|
16
18
|
for (const handler of this.handlers.get(hook) ?? []) try {
|
|
17
|
-
handler(event)?.catch((error) => this.
|
|
19
|
+
handler(event)?.catch((error) => this.log(`${hook} failed`, error));
|
|
18
20
|
} catch (error) {
|
|
19
|
-
this.
|
|
21
|
+
this.log(`${hook} failed`, error);
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
stop(discard = false) {
|
|
@@ -25,7 +27,7 @@ var ExtensionHost = class {
|
|
|
25
27
|
for (const cleanup of this.cleanups.reverse()) try {
|
|
26
28
|
cleanup({ discard });
|
|
27
29
|
} catch (error) {
|
|
28
|
-
this.
|
|
30
|
+
this.log("cleanup failed", error);
|
|
29
31
|
}
|
|
30
32
|
this.cleanups.length = 0;
|
|
31
33
|
this.handlers.clear();
|
|
@@ -35,7 +37,7 @@ var ExtensionHost = class {
|
|
|
35
37
|
const subscriptions = [];
|
|
36
38
|
try {
|
|
37
39
|
const cleanup = await extension.setup({
|
|
38
|
-
...this.
|
|
40
|
+
...this.context,
|
|
39
41
|
on: (hook, handler) => {
|
|
40
42
|
if (this.stopped) return () => {};
|
|
41
43
|
let handlers = this.handlers.get(hook);
|
|
@@ -53,11 +55,40 @@ var ExtensionHost = class {
|
|
|
53
55
|
else if (cleanup) this.cleanups.push(cleanup);
|
|
54
56
|
} catch (error) {
|
|
55
57
|
for (const off of subscriptions) off();
|
|
56
|
-
this.
|
|
58
|
+
this.log(`${extension.name} setup failed`, error);
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
};
|
|
60
62
|
//#endregion
|
|
63
|
+
//#region src/outbound-links.ts
|
|
64
|
+
const DEDUPE_MS = 500;
|
|
65
|
+
function outboundHref(event) {
|
|
66
|
+
for (const node of event.composedPath()) if (node instanceof HTMLAnchorElement && (node.protocol === "http:" || node.protocol === "https:") && node.host !== location.host) return node.href;
|
|
67
|
+
}
|
|
68
|
+
function trackOutboundLinks(report) {
|
|
69
|
+
let last;
|
|
70
|
+
const onClick = (event) => {
|
|
71
|
+
if (!(event instanceof MouseEvent) || event.defaultPrevented) return;
|
|
72
|
+
if (event.button !== (event.type === "click" ? 0 : 1)) return;
|
|
73
|
+
const href = outboundHref(event);
|
|
74
|
+
if (!href) return;
|
|
75
|
+
const key = `${event.button}:${event.clientX}:${event.clientY}:${href}`;
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
const duplicate = last?.key === key && now - last.at <= DEDUPE_MS;
|
|
78
|
+
last = {
|
|
79
|
+
key,
|
|
80
|
+
at: now
|
|
81
|
+
};
|
|
82
|
+
if (!duplicate) report(sanitizeUrl(href));
|
|
83
|
+
};
|
|
84
|
+
document.addEventListener("click", onClick, true);
|
|
85
|
+
document.addEventListener("auxclick", onClick, true);
|
|
86
|
+
return () => {
|
|
87
|
+
document.removeEventListener("click", onClick, true);
|
|
88
|
+
document.removeEventListener("auxclick", onClick, true);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
61
92
|
//#region src/analytics.ts
|
|
62
93
|
const navigationListeners = /* @__PURE__ */ new Set();
|
|
63
94
|
const historyPatches = {};
|
|
@@ -95,12 +126,18 @@ function observeHistory(listener) {
|
|
|
95
126
|
}
|
|
96
127
|
};
|
|
97
128
|
}
|
|
129
|
+
const RESERVED_EVENTS = /* @__PURE__ */ new Set([
|
|
130
|
+
"pageview",
|
|
131
|
+
"page_leave",
|
|
132
|
+
"error",
|
|
133
|
+
"outbound_link"
|
|
134
|
+
]);
|
|
98
135
|
var WebAnalytics = class {
|
|
99
136
|
options;
|
|
100
137
|
baseUrl;
|
|
101
138
|
debug;
|
|
102
139
|
cleanup = [];
|
|
103
|
-
|
|
140
|
+
state = "idle";
|
|
104
141
|
heartbeatTimer = null;
|
|
105
142
|
extensions = null;
|
|
106
143
|
pageKey = "";
|
|
@@ -110,20 +147,20 @@ var WebAnalytics = class {
|
|
|
110
147
|
constructor(options) {
|
|
111
148
|
this.options = options;
|
|
112
149
|
if (!options.siteKey.trim()) throw new Error("siteKey is required");
|
|
113
|
-
this.baseUrl = resolveBaseUrl(options.baseUrl
|
|
150
|
+
this.baseUrl = resolveBaseUrl(options.baseUrl);
|
|
114
151
|
this.debug = options.debug ?? false;
|
|
115
152
|
this.consentMode = options.consent ?? "granted";
|
|
116
153
|
}
|
|
117
154
|
start() {
|
|
118
|
-
if (this.
|
|
119
|
-
this.
|
|
155
|
+
if (this.state !== "idle" || typeof window === "undefined") return;
|
|
156
|
+
this.state = "started";
|
|
120
157
|
this.installBrowserLifecycle();
|
|
121
158
|
if (this.canTrack()) this.activate("load");
|
|
122
159
|
}
|
|
123
160
|
destroy() {
|
|
124
|
-
if (this.
|
|
161
|
+
if (this.state === "destroyed") return;
|
|
125
162
|
this.leavePage();
|
|
126
|
-
this.
|
|
163
|
+
this.state = "destroyed";
|
|
127
164
|
this.pendingPageview = null;
|
|
128
165
|
for (const cleanup of this.cleanup.splice(0).reverse()) cleanup();
|
|
129
166
|
this.stopExtensions(false);
|
|
@@ -135,26 +172,22 @@ var WebAnalytics = class {
|
|
|
135
172
|
this.leavePage();
|
|
136
173
|
if (this.pageKey) this.extensions?.emit("pageChange", { url: getPageContext(this.options.trackHash).url ?? "" });
|
|
137
174
|
this.pageKey = key;
|
|
175
|
+
this.pendingPageview = null;
|
|
138
176
|
}
|
|
139
177
|
if (document.visibilityState !== "visible") {
|
|
140
|
-
this.pendingPageview
|
|
178
|
+
this.pendingPageview ??= properties;
|
|
141
179
|
return;
|
|
142
180
|
}
|
|
143
181
|
this.pendingPageview = null;
|
|
144
182
|
if (document.hasFocus()) touchActivity(this.options.siteKey, this.isCookieless());
|
|
145
|
-
const session =
|
|
146
|
-
const
|
|
147
|
-
if (this.visit?.session.sessionId === session.sessionId && this.visit.context.userId ===
|
|
183
|
+
const session = this.session();
|
|
184
|
+
const context = this.context(session);
|
|
185
|
+
if (this.visit?.session.sessionId === session.sessionId && this.visit.context.userId === context.userId) return;
|
|
148
186
|
this.leavePage();
|
|
149
187
|
this.visit = {
|
|
150
188
|
id: createId(),
|
|
151
189
|
session,
|
|
152
|
-
context
|
|
153
|
-
...getPageContext(this.options.trackHash),
|
|
154
|
-
userId: userId || void 0,
|
|
155
|
-
sessionId: session.sessionId,
|
|
156
|
-
windowId: session.windowId
|
|
157
|
-
},
|
|
190
|
+
context,
|
|
158
191
|
duration: 0,
|
|
159
192
|
startedAt: null,
|
|
160
193
|
reportedDuration: -1
|
|
@@ -168,7 +201,7 @@ var WebAnalytics = class {
|
|
|
168
201
|
track(name, properties = {}) {
|
|
169
202
|
if (!this.isActive()) return;
|
|
170
203
|
const eventName = name.trim();
|
|
171
|
-
if (!eventName || eventName
|
|
204
|
+
if (!eventName || RESERVED_EVENTS.has(eventName)) return;
|
|
172
205
|
this.pageview({ trigger: "session" });
|
|
173
206
|
this.send(eventName, properties);
|
|
174
207
|
}
|
|
@@ -181,6 +214,7 @@ var WebAnalytics = class {
|
|
|
181
214
|
} : userOrExternalId;
|
|
182
215
|
const externalId = user.id.trim();
|
|
183
216
|
if (!externalId) return false;
|
|
217
|
+
this.extensions?.emit("identityChange", { userId: externalId });
|
|
184
218
|
const clearFields = [
|
|
185
219
|
"email",
|
|
186
220
|
"name",
|
|
@@ -214,6 +248,7 @@ var WebAnalytics = class {
|
|
|
214
248
|
this.leavePage();
|
|
215
249
|
if (resetAnonymousIdentity) resetAnonymousId(this.options.siteKey, this.isCookieless());
|
|
216
250
|
resetSession(this.options.siteKey, this.isCookieless());
|
|
251
|
+
this.extensions?.emit("identityChange", { userId: void 0 });
|
|
217
252
|
this.pageview({ trigger: "logout" });
|
|
218
253
|
}
|
|
219
254
|
setConsentMode(mode) {
|
|
@@ -233,7 +268,7 @@ var WebAnalytics = class {
|
|
|
233
268
|
if (wasTracking) {
|
|
234
269
|
this.extensions?.emit("consentChange", { cookieless: willBeCookieless });
|
|
235
270
|
if (wasCookieless !== willBeCookieless) this.pageview({ trigger: "consent" });
|
|
236
|
-
} else if (this.started) this.activate("consent");
|
|
271
|
+
} else if (this.state === "started") this.activate("consent");
|
|
237
272
|
}
|
|
238
273
|
getAnonymousId() {
|
|
239
274
|
return this.canTrack() ? getAnonymousId(this.options.siteKey, this.isCookieless()) : "";
|
|
@@ -249,7 +284,18 @@ var WebAnalytics = class {
|
|
|
249
284
|
return this.options.cookieless === true || this.consentMode === "anonymous";
|
|
250
285
|
}
|
|
251
286
|
isActive() {
|
|
252
|
-
return this.
|
|
287
|
+
return this.state === "started" && this.canTrack();
|
|
288
|
+
}
|
|
289
|
+
session() {
|
|
290
|
+
return getSessionContext(this.options.siteKey, this.isCookieless());
|
|
291
|
+
}
|
|
292
|
+
context(session = this.session()) {
|
|
293
|
+
return {
|
|
294
|
+
...getPageContext(this.options.trackHash),
|
|
295
|
+
userId: this.getAnonymousId() || void 0,
|
|
296
|
+
sessionId: session.sessionId,
|
|
297
|
+
windowId: session.windowId
|
|
298
|
+
};
|
|
253
299
|
}
|
|
254
300
|
activate(trigger) {
|
|
255
301
|
this.startExtensions();
|
|
@@ -257,22 +303,17 @@ var WebAnalytics = class {
|
|
|
257
303
|
}
|
|
258
304
|
startExtensions() {
|
|
259
305
|
if (this.extensions || !this.options.extensions?.length) return;
|
|
260
|
-
|
|
306
|
+
this.extensions = new ExtensionHost(this.options.extensions, {
|
|
261
307
|
siteKey: this.options.siteKey,
|
|
262
308
|
baseUrl: this.baseUrl,
|
|
263
309
|
debug: this.debug,
|
|
264
310
|
trackHash: this.options.trackHash ?? false,
|
|
265
311
|
sdkName: this.options.sdkName,
|
|
266
312
|
sdkVersion: this.options.sdkVersion,
|
|
267
|
-
isCookieless: () => this.isCookieless()
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
capture: (event, properties) => this.send(event, properties),
|
|
271
|
-
log: (...details) => {
|
|
272
|
-
if (this.debug) console.log(...details);
|
|
273
|
-
}
|
|
313
|
+
isCookieless: () => this.isCookieless()
|
|
314
|
+
}, (...details) => {
|
|
315
|
+
if (this.debug) console.log(...details);
|
|
274
316
|
});
|
|
275
|
-
this.extensions = host;
|
|
276
317
|
}
|
|
277
318
|
stopExtensions(discard) {
|
|
278
319
|
this.extensions?.stop(discard);
|
|
@@ -286,7 +327,7 @@ var WebAnalytics = class {
|
|
|
286
327
|
this.bind(window, "blur", () => this.pauseVisit());
|
|
287
328
|
this.bind(window, "popstate", this.onNavigate);
|
|
288
329
|
if (this.options.trackHash) this.bind(window, "hashchange", this.onNavigate);
|
|
289
|
-
this.cleanup.push(observeHistory(this.onNavigate));
|
|
330
|
+
this.cleanup.push(observeHistory(this.onNavigate), trackOutboundLinks((url) => this.send("outbound_link", { outbound_link: url })));
|
|
290
331
|
}
|
|
291
332
|
bind(target, type, listener) {
|
|
292
333
|
target.addEventListener(type, listener);
|
|
@@ -359,21 +400,14 @@ var WebAnalytics = class {
|
|
|
359
400
|
this.pauseVisit();
|
|
360
401
|
return;
|
|
361
402
|
}
|
|
403
|
+
const visit = this.visit;
|
|
362
404
|
this.pageview({ trigger: "session" });
|
|
363
|
-
this.checkpoint();
|
|
405
|
+
if (this.visit === visit) this.checkpoint();
|
|
364
406
|
}, 15e3);
|
|
365
407
|
}
|
|
366
408
|
send(event, properties = {}, context) {
|
|
367
409
|
if (!this.isActive()) return;
|
|
368
|
-
|
|
369
|
-
const session = getSessionContext(this.options.siteKey, this.isCookieless());
|
|
370
|
-
context = {
|
|
371
|
-
...getPageContext(this.options.trackHash),
|
|
372
|
-
userId: this.getAnonymousId() || void 0,
|
|
373
|
-
sessionId: session.sessionId,
|
|
374
|
-
windowId: session.windowId
|
|
375
|
-
};
|
|
376
|
-
}
|
|
410
|
+
context ??= this.context();
|
|
377
411
|
let data;
|
|
378
412
|
try {
|
|
379
413
|
data = JSON.stringify({
|
package/dist/replay.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as AnalyticsExtension } from "./chunks/extensions-
|
|
1
|
+
import { t as AnalyticsExtension } from "./chunks/extensions-BfubKIzk.js";
|
|
2
2
|
import { record } from "@rrweb/record";
|
|
3
3
|
import { LogLevel } from "@rrweb/rrweb-plugin-console-record";
|
|
4
4
|
import { NetworkRecordOptions } from "@rrweb/rrweb-plugin-network-record";
|
|
5
|
-
//#region ../../node_modules/.bun/@rrweb+types@2.1.
|
|
5
|
+
//#region ../../node_modules/.bun/@rrweb+types@2.1.7/node_modules/@rrweb/types/dist/index.d.ts
|
|
6
6
|
declare global {
|
|
7
7
|
interface Window {
|
|
8
8
|
FontFace: typeof FontFace;
|
|
@@ -58,7 +58,7 @@ export declare function sessionReplay(options?: SessionReplayExtensionOptions):
|
|
|
58
58
|
export default class ReplayTracker {
|
|
59
59
|
private readonly options;
|
|
60
60
|
private readonly endpoint;
|
|
61
|
-
private readonly
|
|
61
|
+
private readonly compress;
|
|
62
62
|
private readonly flushInterval;
|
|
63
63
|
private readonly maxEvents;
|
|
64
64
|
private readonly maxBatchSizeBytes;
|
|
@@ -95,7 +95,10 @@ export default class ReplayTracker {
|
|
|
95
95
|
private readonly mutationThrottler;
|
|
96
96
|
constructor(options: ReplayTrackerOptions);
|
|
97
97
|
private log;
|
|
98
|
+
private sessionContext;
|
|
99
|
+
private sanitize;
|
|
98
100
|
private clearFlushTimers;
|
|
101
|
+
private dropEvents;
|
|
99
102
|
private clearQueues;
|
|
100
103
|
private finalizeSession;
|
|
101
104
|
setCookielessMode(cookieless: boolean): void;
|
|
@@ -112,7 +115,6 @@ export default class ReplayTracker {
|
|
|
112
115
|
private beginReplayStream;
|
|
113
116
|
private resetSessionTiming;
|
|
114
117
|
private onEvent;
|
|
115
|
-
private onUnload;
|
|
116
118
|
private requestTerminalFlush;
|
|
117
119
|
private hasReachedMinLength;
|
|
118
120
|
private requestFlush;
|