@faststats/web 0.10.2 → 0.10.3
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-I5UnTXGR.js → identifiers-BgGtAQdp.js} +1 -1
- package/dist/chunks/{send-data-Dplz7m4c.js → send-data-DAVUdfNm.js} +5 -9
- package/dist/error.js +3 -3
- package/dist/index.js +2 -2
- package/dist/replay.d.ts +1 -0
- package/dist/replay.js +43 -54
- package/dist/web-vitals.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as removeStorageItem, l as setStorageItem, n as createId, s as getStorageItem } from "./send-data-
|
|
1
|
+
import { c as removeStorageItem, l as setStorageItem, n as createId, s as getStorageItem } from "./send-data-DAVUdfNm.js";
|
|
2
2
|
//#region src/utils/identifiers.ts
|
|
3
3
|
const ANONYMOUS_ID_KEY = "faststats_anon_id_";
|
|
4
4
|
function getAnonymousId(siteKey, cookieless = false) {
|
|
@@ -150,7 +150,7 @@ function onSessionRotated(siteKey, cookieless, listener) {
|
|
|
150
150
|
//#endregion
|
|
151
151
|
//#region src/utils/send-data.ts
|
|
152
152
|
async function sendData(options) {
|
|
153
|
-
const { url, data, contentType = "application/json", headers = {}, debug = false, debugPrefix = "[Analytics]", useBeacon = true, keepalive = true, signal } = options;
|
|
153
|
+
const { url, data, contentType = "application/json", headers = {}, debug = false, debugPrefix = "[Analytics]", useBeacon = true, keepalive = true, signal, settleRejected } = options;
|
|
154
154
|
if (signal?.aborted) return false;
|
|
155
155
|
if (useBeacon && typeof globalThis.navigator?.sendBeacon === "function") try {
|
|
156
156
|
const beaconBody = data instanceof Blob || typeof Blob === "undefined" ? data : new Blob([data], { type: contentType });
|
|
@@ -160,7 +160,7 @@ async function sendData(options) {
|
|
|
160
160
|
}
|
|
161
161
|
} catch {}
|
|
162
162
|
try {
|
|
163
|
-
const
|
|
163
|
+
const { ok, status } = await fetch(url, {
|
|
164
164
|
method: "POST",
|
|
165
165
|
body: data,
|
|
166
166
|
headers: {
|
|
@@ -171,14 +171,10 @@ async function sendData(options) {
|
|
|
171
171
|
credentials: "omit",
|
|
172
172
|
signal
|
|
173
173
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const message = success ? `${debugPrefix} Sent via fetch` : `${debugPrefix} Failed: ${response.status}`;
|
|
177
|
-
console[success ? "log" : "warn"](message);
|
|
178
|
-
}
|
|
179
|
-
return success;
|
|
174
|
+
if (debug) console.log(debugPrefix, ok ? "Sent via fetch" : status);
|
|
175
|
+
return ok || !!settleRejected && status < 500 && status !== 429;
|
|
180
176
|
} catch {
|
|
181
|
-
if (debug) console.
|
|
177
|
+
if (debug) console.log(debugPrefix, "Failed to send");
|
|
182
178
|
return false;
|
|
183
179
|
}
|
|
184
180
|
}
|
package/dist/error.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { i as resolveBaseUrl, n as URLS, r as getPageContext, t as ANALYTICS_BASE } from "./chunks/api-urls-DWk43fu6.js";
|
|
2
|
-
import { r as getSessionContext, t as sendData } from "./chunks/send-data-
|
|
3
|
-
import { t as getAnonymousId } from "./chunks/identifiers-
|
|
2
|
+
import { r as getSessionContext, t as sendData } from "./chunks/send-data-DAVUdfNm.js";
|
|
3
|
+
import { t as getAnonymousId } from "./chunks/identifiers-BgGtAQdp.js";
|
|
4
4
|
//#region src/error.ts
|
|
5
5
|
const SDK_NAME = "@faststats/web";
|
|
6
|
-
const SDK_VERSION = "0.10.
|
|
6
|
+
const SDK_VERSION = "0.10.3";
|
|
7
7
|
function errorTracking(options = {}) {
|
|
8
8
|
return {
|
|
9
9
|
name: "error-tracking",
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as resolveBaseUrl, n as URLS, r as getPageContext, t as ANALYTICS_BASE } from "./chunks/api-urls-DWk43fu6.js";
|
|
2
|
-
import { a as resetSession, n as createId, o as touchActivity, r as getSessionContext, t as sendData } from "./chunks/send-data-
|
|
3
|
-
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-
|
|
2
|
+
import { a as resetSession, n as createId, o as touchActivity, r as getSessionContext, t as sendData } from "./chunks/send-data-DAVUdfNm.js";
|
|
3
|
+
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-BgGtAQdp.js";
|
|
4
4
|
//#region src/extensions.ts
|
|
5
5
|
var ExtensionHost = class {
|
|
6
6
|
runtime;
|
package/dist/replay.d.ts
CHANGED
package/dist/replay.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as sanitizeUrl, i as resolveBaseUrl, n as URLS, t as ANALYTICS_BASE } from "./chunks/api-urls-DWk43fu6.js";
|
|
2
|
-
import { c as removeStorageItem, 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-
|
|
3
|
-
import { t as getAnonymousId } from "./chunks/identifiers-
|
|
2
|
+
import { c as removeStorageItem, 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-DAVUdfNm.js";
|
|
3
|
+
import { t as getAnonymousId } from "./chunks/identifiers-BgGtAQdp.js";
|
|
4
4
|
import { record } from "@rrweb/record";
|
|
5
5
|
import { getRecordConsolePlugin } from "@rrweb/rrweb-plugin-console-record";
|
|
6
6
|
import { getRecordNetworkPlugin } from "@rrweb/rrweb-plugin-network-record";
|
|
@@ -63,8 +63,14 @@ function readCounter(value) {
|
|
|
63
63
|
if (parsed && typeof parsed.sessionId === "string" && typeof parsed.windowId === "string" && typeof parsed.next === "number" && Number.isSafeInteger(parsed.next) && parsed.next >= 0 && (parsed.version === 0 || parsed.version === 1)) return parsed;
|
|
64
64
|
} catch {}
|
|
65
65
|
}
|
|
66
|
+
const sequenceKey = (siteKey) => `faststats_replay_sequence_v1_${siteKey}`;
|
|
67
|
+
/** Whether this recording already sent batches, e.g. from an earlier page load. */
|
|
68
|
+
function hasReplaySequence(siteKey, context, cookieless) {
|
|
69
|
+
const raw = cookieless ? null : getStorageItem("sessionStorage", sequenceKey(siteKey));
|
|
70
|
+
return [counters.get(siteKey), raw === null ? void 0 : readCounter(raw)].some((counter) => sameRecording(counter, context) && counter.next > 0);
|
|
71
|
+
}
|
|
66
72
|
function nextReplaySequence(siteKey, context, cookieless) {
|
|
67
|
-
const key =
|
|
73
|
+
const key = sequenceKey(siteKey);
|
|
68
74
|
const cached = counters.get(siteKey);
|
|
69
75
|
const raw = cookieless ? null : getStorageItem("sessionStorage", key);
|
|
70
76
|
const stored = raw === null ? void 0 : readCounter(raw);
|
|
@@ -170,6 +176,7 @@ var ReplayTracker = class {
|
|
|
170
176
|
lastCookielessMode;
|
|
171
177
|
needsFinalization = false;
|
|
172
178
|
initialFlushPending = true;
|
|
179
|
+
resumed = false;
|
|
173
180
|
overflowed = false;
|
|
174
181
|
replayEventSequence = 0;
|
|
175
182
|
mutationThrottler;
|
|
@@ -218,8 +225,7 @@ var ReplayTracker = class {
|
|
|
218
225
|
this.clearFlushTimers();
|
|
219
226
|
const context = getSessionContext(this.options.siteKey, this.lastCookielessMode);
|
|
220
227
|
this.activeSessionId = context.sessionId;
|
|
221
|
-
this.
|
|
222
|
-
this.chunkStartedAt = Date.now();
|
|
228
|
+
this.resetSessionTiming(context);
|
|
223
229
|
this.beginReplayStream();
|
|
224
230
|
}
|
|
225
231
|
start() {
|
|
@@ -233,8 +239,7 @@ var ReplayTracker = class {
|
|
|
233
239
|
this.viewUrl = sanitizeUrl(window.location.href, this.options.trackHash ?? false);
|
|
234
240
|
const context = getSessionContext(this.options.siteKey, this.lastCookielessMode);
|
|
235
241
|
this.activeSessionId = context.sessionId;
|
|
236
|
-
this.
|
|
237
|
-
this.chunkStartedAt = Date.now();
|
|
242
|
+
this.resetSessionTiming(context);
|
|
238
243
|
this.subscribeToSessionRotation();
|
|
239
244
|
this.beginRecording();
|
|
240
245
|
this.intervalId = setInterval(() => this.requestFlush("interval"), this.flushInterval);
|
|
@@ -377,10 +382,7 @@ var ReplayTracker = class {
|
|
|
377
382
|
}
|
|
378
383
|
applySessionRotation(prev, next) {
|
|
379
384
|
if (this.activeSessionId === next.sessionId) return;
|
|
380
|
-
if (this.hasReachedMinLength()) this.enqueueEventsIfReady(prev,
|
|
381
|
-
isFinal: true,
|
|
382
|
-
flushReason: "sessionRotate"
|
|
383
|
-
});
|
|
385
|
+
if (this.hasReachedMinLength()) this.enqueueEventsIfReady(prev, "sessionRotate");
|
|
384
386
|
else {
|
|
385
387
|
this.events.length = 0;
|
|
386
388
|
this.queuedEventsSizeBytes = 0;
|
|
@@ -411,9 +413,10 @@ var ReplayTracker = class {
|
|
|
411
413
|
}
|
|
412
414
|
}
|
|
413
415
|
}
|
|
414
|
-
resetSessionTiming(
|
|
416
|
+
resetSessionTiming(next) {
|
|
415
417
|
if (this.minLengthFlushTask) clearTimeout(this.minLengthFlushTask);
|
|
416
418
|
this.minLengthFlushTask = null;
|
|
419
|
+
this.resumed = hasReplaySequence(this.options.siteKey, next, this.lastCookielessMode);
|
|
417
420
|
this.startTime = Date.now();
|
|
418
421
|
this.chunkStartedAt = Date.now();
|
|
419
422
|
this.initialFlushPending = true;
|
|
@@ -467,7 +470,7 @@ var ReplayTracker = class {
|
|
|
467
470
|
this.flush(true, true, reason);
|
|
468
471
|
}
|
|
469
472
|
hasReachedMinLength() {
|
|
470
|
-
return this.minReplayLengthMs <= 0 || Date.now() - this.startTime >= this.minReplayLengthMs;
|
|
473
|
+
return this.resumed || this.minReplayLengthMs <= 0 || Date.now() - this.startTime >= this.minReplayLengthMs;
|
|
471
474
|
}
|
|
472
475
|
requestFlush = (reason = "manual") => {
|
|
473
476
|
if (this.flushTask || this.events.length === 0) return;
|
|
@@ -495,17 +498,20 @@ var ReplayTracker = class {
|
|
|
495
498
|
this.requestFlush("minLength");
|
|
496
499
|
}, Math.min(remainingMs, MAX_TIMEOUT_MS));
|
|
497
500
|
}
|
|
498
|
-
enqueueEventsIfReady(context,
|
|
499
|
-
const { isFinal = false, flushReason = "manual", maxBatchSizeBytes = this.maxBatchSizeBytes } = options;
|
|
501
|
+
enqueueEventsIfReady(context, flushReason = "manual", maxBatchSizeBytes = this.maxBatchSizeBytes) {
|
|
500
502
|
if (this.events.length === 0) return;
|
|
501
|
-
if (!
|
|
503
|
+
if (!this.hasReachedMinLength()) {
|
|
502
504
|
this.scheduleMinLengthFlush();
|
|
503
505
|
return;
|
|
504
506
|
}
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
507
|
+
for (const events of this.takeEventBatches(maxBatchSizeBytes)) {
|
|
508
|
+
if (this.pending.length >= this.maxPendingBatches) {
|
|
509
|
+
this.stopCapture("Replay queue limit reached");
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
const batch = this.createBatch(events, context, false, flushReason);
|
|
513
|
+
if (!batch) break;
|
|
514
|
+
this.enqueueBatch(batch);
|
|
509
515
|
}
|
|
510
516
|
this.queuedEventsSizeBytes = 0;
|
|
511
517
|
}
|
|
@@ -578,45 +584,27 @@ var ReplayTracker = class {
|
|
|
578
584
|
this.stopRecording = void 0;
|
|
579
585
|
}
|
|
580
586
|
enqueueBatch(batch) {
|
|
581
|
-
const size = this.batchSizeBytes(batch);
|
|
582
|
-
if (size > this.maxQueueSizeBytes || this.pending.length >= this.maxPendingBatches || this.pendingSizeBytes + size > this.maxQueueSizeBytes) {
|
|
583
|
-
this.stopCapture(`Replay queue limit reached at batch ${batch.sequence}`);
|
|
584
|
-
return false;
|
|
585
|
-
}
|
|
586
587
|
this.needsFinalization = !batch.isFinal;
|
|
587
588
|
this.pending.push(batch);
|
|
588
|
-
this.pendingSizeBytes +=
|
|
589
|
-
return true;
|
|
589
|
+
this.pendingSizeBytes += this.batchSizeBytes(batch);
|
|
590
590
|
}
|
|
591
591
|
async flush(lowLatency, isFinal = false, flushReason = "manual") {
|
|
592
592
|
if (this.events.length === 0 && !isFinal) {
|
|
593
|
-
await this.sendPending(lowLatency
|
|
593
|
+
await this.sendPending(lowLatency);
|
|
594
594
|
return;
|
|
595
595
|
}
|
|
596
596
|
const rotation = touchActivity(this.options.siteKey, this.lastCookielessMode);
|
|
597
597
|
if (rotation) this.applySessionRotation(rotation.prev, rotation.next);
|
|
598
598
|
const context = getSessionContext(this.options.siteKey, this.lastCookielessMode);
|
|
599
|
-
this.enqueueEventsIfReady(context,
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (this.
|
|
606
|
-
await this.sendPending(lowLatency, lowLatency && isFinal);
|
|
607
|
-
}
|
|
608
|
-
async sendPending(lowLatency, bypassRetryDelay) {
|
|
609
|
-
if (this.retryTask) {
|
|
610
|
-
if (!bypassRetryDelay) return;
|
|
611
|
-
clearTimeout(this.retryTask);
|
|
612
|
-
this.retryTask = null;
|
|
613
|
-
}
|
|
599
|
+
this.enqueueEventsIfReady(context, flushReason, lowLatency ? this.maxLowLatencyBatchSizeBytes : this.maxBatchSizeBytes);
|
|
600
|
+
if (!isFinal) return this.sendPending(lowLatency);
|
|
601
|
+
this.finalizeSession(context, flushReason);
|
|
602
|
+
for (const batch of [...this.pending].reverse()) this.sendBatch(batch, true).then((sent) => sent && this.removePending(batch));
|
|
603
|
+
}
|
|
604
|
+
async sendPending(lowLatency) {
|
|
605
|
+
if (this.retryTask) return;
|
|
614
606
|
if (this.sending) {
|
|
615
|
-
|
|
616
|
-
this.queuedFlush = {
|
|
617
|
-
lowLatency: (queued?.lowLatency ?? false) || lowLatency,
|
|
618
|
-
bypassRetryDelay: (queued?.bypassRetryDelay ?? false) || bypassRetryDelay
|
|
619
|
-
};
|
|
607
|
+
this.queuedFlush = !!this.queuedFlush || lowLatency;
|
|
620
608
|
return;
|
|
621
609
|
}
|
|
622
610
|
this.sending = true;
|
|
@@ -630,14 +618,14 @@ var ReplayTracker = class {
|
|
|
630
618
|
}
|
|
631
619
|
this.retryAttempt = 0;
|
|
632
620
|
this.removePending(batch);
|
|
633
|
-
if (this.queuedFlush
|
|
621
|
+
if (this.queuedFlush) break;
|
|
634
622
|
}
|
|
635
623
|
} finally {
|
|
636
624
|
this.sending = false;
|
|
637
|
-
if (this.queuedFlush) {
|
|
638
|
-
const
|
|
625
|
+
if (this.queuedFlush !== null) {
|
|
626
|
+
const lowLatency = this.queuedFlush;
|
|
639
627
|
this.queuedFlush = null;
|
|
640
|
-
this.sendPending(
|
|
628
|
+
this.sendPending(lowLatency);
|
|
641
629
|
}
|
|
642
630
|
}
|
|
643
631
|
}
|
|
@@ -649,7 +637,7 @@ var ReplayTracker = class {
|
|
|
649
637
|
this.retryTask = setTimeout(() => {
|
|
650
638
|
this.retryTask = null;
|
|
651
639
|
if (this.retryAttempt === RETRY_MAX_ATTEMPTS) this.retryAttempt = 0;
|
|
652
|
-
this.sendPending(false
|
|
640
|
+
this.sendPending(false);
|
|
653
641
|
}, delay);
|
|
654
642
|
}
|
|
655
643
|
retryDelay(attempt) {
|
|
@@ -664,7 +652,8 @@ var ReplayTracker = class {
|
|
|
664
652
|
debug: this.options.debug,
|
|
665
653
|
debugPrefix: "[Replay]",
|
|
666
654
|
useBeacon: lowLatencySafe,
|
|
667
|
-
keepalive: lowLatencySafe
|
|
655
|
+
keepalive: lowLatencySafe,
|
|
656
|
+
settleRejected: true
|
|
668
657
|
};
|
|
669
658
|
if (!lowLatency && (this.options.compress ?? true) && this.compressionSupported) try {
|
|
670
659
|
const stream = new Blob([json]).stream().pipeThrough(new CompressionStream("gzip"));
|
package/dist/web-vitals.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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-
|
|
2
|
+
import { r as getSessionContext, t as sendData } from "./chunks/send-data-DAVUdfNm.js";
|
|
3
3
|
import { onCLS, onFCP, onINP, onLCP, onTTFB } from "web-vitals/attribution";
|
|
4
4
|
//#region src/utils/web-vitals-collector.ts
|
|
5
5
|
const collectors = /* @__PURE__ */ new WeakMap();
|