@faststats/web 0.10.0 → 0.10.2
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.
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { c as removeStorageItem, l as setStorageItem, n as createId, s as getStorageItem } from "./send-data-Dplz7m4c.js";
|
|
2
2
|
//#region src/utils/identifiers.ts
|
|
3
3
|
const ANONYMOUS_ID_KEY = "faststats_anon_id_";
|
|
4
|
-
function
|
|
4
|
+
function getAnonymousId(siteKey, cookieless = false) {
|
|
5
|
+
if (cookieless) return "";
|
|
5
6
|
const key = `${ANONYMOUS_ID_KEY}${siteKey}`;
|
|
6
7
|
const existingId = getStorageItem("localStorage", key);
|
|
7
8
|
if (existingId) return existingId;
|
|
8
9
|
const newId = createId();
|
|
9
10
|
return setStorageItem("localStorage", key, newId) ? newId : "";
|
|
10
11
|
}
|
|
11
|
-
function getAnonymousId(siteKey, cookieless = false) {
|
|
12
|
-
if (cookieless) return "";
|
|
13
|
-
return getOrCreateAnonymousId(siteKey);
|
|
14
|
-
}
|
|
15
12
|
function resetAnonymousId(siteKey, cookieless) {
|
|
16
13
|
if (cookieless) return "";
|
|
17
14
|
if (!removeStorageItem("localStorage", `${ANONYMOUS_ID_KEY}${siteKey}`)) return "";
|
|
18
|
-
return
|
|
15
|
+
return getAnonymousId(siteKey);
|
|
19
16
|
}
|
|
20
17
|
//#endregion
|
|
21
18
|
export { resetAnonymousId as n, getAnonymousId as t };
|
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
2
|
import { r as getSessionContext, t as sendData } from "./chunks/send-data-Dplz7m4c.js";
|
|
3
|
-
import { t as getAnonymousId } from "./chunks/identifiers-
|
|
3
|
+
import { t as getAnonymousId } from "./chunks/identifiers-I5UnTXGR.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.2";
|
|
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
2
|
import { a as resetSession, n as createId, o as touchActivity, r as getSessionContext, t as sendData } from "./chunks/send-data-Dplz7m4c.js";
|
|
3
|
-
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-
|
|
3
|
+
import { n as resetAnonymousId, t as getAnonymousId } from "./chunks/identifiers-I5UnTXGR.js";
|
|
4
4
|
//#region src/extensions.ts
|
|
5
5
|
var ExtensionHost = class {
|
|
6
6
|
runtime;
|
package/dist/replay.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface ReplayTrackerOptions {
|
|
|
31
31
|
maxLowLatencyBatchSizeBytes?: number;
|
|
32
32
|
maxPendingBatches?: number;
|
|
33
33
|
maxQueueSizeBytes?: number;
|
|
34
|
+
/** Minimum elapsed time per recording stream before upload. Defaults to 3000. */
|
|
34
35
|
minReplayLengthMs?: number;
|
|
35
36
|
sampling?: RecordOptions["sampling"];
|
|
36
37
|
slimDOMOptions?: RecordOptions["slimDOMOptions"];
|
|
@@ -67,7 +68,6 @@ export default class ReplayTracker {
|
|
|
67
68
|
private readonly minReplayLengthMs;
|
|
68
69
|
private readonly events;
|
|
69
70
|
private readonly pending;
|
|
70
|
-
private readonly minLengthBlockedBatches;
|
|
71
71
|
private queuedEventsSizeBytes;
|
|
72
72
|
private pendingSizeBytes;
|
|
73
73
|
private viewId;
|
|
@@ -76,7 +76,6 @@ export default class ReplayTracker {
|
|
|
76
76
|
private started;
|
|
77
77
|
private disposed;
|
|
78
78
|
private startTime;
|
|
79
|
-
private sequence;
|
|
80
79
|
private intervalId;
|
|
81
80
|
private flushTask;
|
|
82
81
|
private retryTask;
|
|
@@ -97,10 +96,7 @@ export default class ReplayTracker {
|
|
|
97
96
|
private log;
|
|
98
97
|
private clearFlushTimers;
|
|
99
98
|
private clearQueues;
|
|
100
|
-
private dropMinLengthBlockedBatches;
|
|
101
|
-
private unblockSessionBatches;
|
|
102
99
|
private finalizeSession;
|
|
103
|
-
private nextBatchSequence;
|
|
104
100
|
setCookielessMode(cookieless: boolean): void;
|
|
105
101
|
start(): void;
|
|
106
102
|
trackPageChange(url?: string): void;
|
|
@@ -126,6 +122,7 @@ export default class ReplayTracker {
|
|
|
126
122
|
private eventSizeBytes;
|
|
127
123
|
private batchSizeBytes;
|
|
128
124
|
private removePending;
|
|
125
|
+
private stopCapture;
|
|
129
126
|
private enqueueBatch;
|
|
130
127
|
private flush;
|
|
131
128
|
private sendPending;
|
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 { 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-Dplz7m4c.js";
|
|
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-Dplz7m4c.js";
|
|
3
|
+
import { t as getAnonymousId } from "./chunks/identifiers-I5UnTXGR.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";
|
|
@@ -52,6 +52,51 @@ var ReplayMutationThrottler = class {
|
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
54
|
//#endregion
|
|
55
|
+
//#region src/replay-sequence.ts
|
|
56
|
+
const counters = /* @__PURE__ */ new Map();
|
|
57
|
+
function sameRecording(counter, context) {
|
|
58
|
+
return counter?.sessionId === context.sessionId && counter.windowId === context.windowId;
|
|
59
|
+
}
|
|
60
|
+
function readCounter(value) {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(value);
|
|
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
|
+
} catch {}
|
|
65
|
+
}
|
|
66
|
+
function nextReplaySequence(siteKey, context, cookieless) {
|
|
67
|
+
const key = `faststats_replay_sequence_v1_${siteKey}`;
|
|
68
|
+
const cached = counters.get(siteKey);
|
|
69
|
+
const raw = cookieless ? null : getStorageItem("sessionStorage", key);
|
|
70
|
+
const stored = raw === null ? void 0 : readCounter(raw);
|
|
71
|
+
if (raw !== null && !stored && !sameRecording(cached, context)) return;
|
|
72
|
+
let counter = sameRecording(cached, context) ? cached : void 0;
|
|
73
|
+
if (sameRecording(stored, context) && stored && (!counter || stored.next > counter.next)) counter = stored;
|
|
74
|
+
const legacyKey = `faststats_replay_batch_sequence_${siteKey}_${context.windowId}`;
|
|
75
|
+
if (!counter) {
|
|
76
|
+
const legacy = !cookieless && !stored && !sameRecording(cached, context) ? getStorageItem("sessionStorage", legacyKey) : null;
|
|
77
|
+
const next = legacy === null ? 0 : Number(legacy);
|
|
78
|
+
if (!Number.isSafeInteger(next) || next < 0 || legacy !== null && !/^\d+$/.test(legacy)) return;
|
|
79
|
+
counter = {
|
|
80
|
+
sessionId: context.sessionId,
|
|
81
|
+
windowId: context.windowId,
|
|
82
|
+
next,
|
|
83
|
+
version: legacy === null ? 1 : 0
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (!Number.isSafeInteger(counter.next + 1)) return;
|
|
87
|
+
const sequence = counter.next;
|
|
88
|
+
const updated = {
|
|
89
|
+
...counter,
|
|
90
|
+
next: sequence + 1
|
|
91
|
+
};
|
|
92
|
+
counters.set(siteKey, updated);
|
|
93
|
+
if (!cookieless && setStorageItem("sessionStorage", key, JSON.stringify(updated))) removeStorageItem("sessionStorage", legacyKey);
|
|
94
|
+
return {
|
|
95
|
+
sequence,
|
|
96
|
+
...counter.version === 1 ? { sequenceContractVersion: 1 } : {}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
55
100
|
//#region src/replay.ts
|
|
56
101
|
const MAX_TIMEOUT_MS = 2147483647;
|
|
57
102
|
const DEFAULT_MAX_QUEUE_SIZE_BYTES = 2097152;
|
|
@@ -66,6 +111,10 @@ const RRWEB_EVENT_META = 4;
|
|
|
66
111
|
const REPLAY_SEQUENTIAL_ID_KEY = "_faststatsSeqId";
|
|
67
112
|
const textEncoder = new TextEncoder();
|
|
68
113
|
const batchSizeCache = /* @__PURE__ */ new WeakMap();
|
|
114
|
+
function boundedInteger(value, fallback, min, max) {
|
|
115
|
+
if (value === void 0 || !Number.isFinite(value) || value < min) return fallback;
|
|
116
|
+
return Math.min(Math.trunc(value), max);
|
|
117
|
+
}
|
|
69
118
|
function sessionReplay(options = {}) {
|
|
70
119
|
return {
|
|
71
120
|
name: "session-replay",
|
|
@@ -100,7 +149,6 @@ var ReplayTracker = class {
|
|
|
100
149
|
minReplayLengthMs;
|
|
101
150
|
events = [];
|
|
102
151
|
pending = [];
|
|
103
|
-
minLengthBlockedBatches = /* @__PURE__ */ new WeakSet();
|
|
104
152
|
queuedEventsSizeBytes = 0;
|
|
105
153
|
pendingSizeBytes = 0;
|
|
106
154
|
viewId = createId();
|
|
@@ -109,7 +157,6 @@ var ReplayTracker = class {
|
|
|
109
157
|
started = false;
|
|
110
158
|
disposed = false;
|
|
111
159
|
startTime = 0;
|
|
112
|
-
sequence = 0;
|
|
113
160
|
intervalId = null;
|
|
114
161
|
flushTask = null;
|
|
115
162
|
retryTask = null;
|
|
@@ -130,13 +177,13 @@ var ReplayTracker = class {
|
|
|
130
177
|
this.options = options;
|
|
131
178
|
this.lastCookielessMode = options.cookieless ?? false;
|
|
132
179
|
this.endpoint = `${resolveBaseUrl(options.baseUrl, ANALYTICS_BASE)}${URLS.replay}`;
|
|
133
|
-
this.flushInterval = options.flushInterval
|
|
134
|
-
this.maxEvents = options.maxEvents
|
|
135
|
-
this.maxBatchSizeBytes = options.maxBatchSizeBytes
|
|
136
|
-
this.maxLowLatencyBatchSizeBytes = options.maxLowLatencyBatchSizeBytes
|
|
137
|
-
this.maxPendingBatches = options.maxPendingBatches
|
|
138
|
-
this.maxQueueSizeBytes = options.maxQueueSizeBytes
|
|
139
|
-
this.minReplayLengthMs = options.minReplayLengthMs
|
|
180
|
+
this.flushInterval = boundedInteger(options.flushInterval, 15e3, 1, MAX_TIMEOUT_MS);
|
|
181
|
+
this.maxEvents = boundedInteger(options.maxEvents, 1e3, 1, 1e5);
|
|
182
|
+
this.maxBatchSizeBytes = boundedInteger(options.maxBatchSizeBytes, DEFAULT_MAX_BATCH_SIZE_BYTES, 1, 33554432);
|
|
183
|
+
this.maxLowLatencyBatchSizeBytes = boundedInteger(options.maxLowLatencyBatchSizeBytes, DEFAULT_MAX_LOW_LATENCY_BATCH_SIZE_BYTES, 1, DEFAULT_MAX_LOW_LATENCY_BATCH_SIZE_BYTES);
|
|
184
|
+
this.maxPendingBatches = boundedInteger(options.maxPendingBatches, 30, 1, 1e3);
|
|
185
|
+
this.maxQueueSizeBytes = boundedInteger(options.maxQueueSizeBytes, DEFAULT_MAX_QUEUE_SIZE_BYTES, 1, 33554432);
|
|
186
|
+
this.minReplayLengthMs = boundedInteger(options.minReplayLengthMs, 3e3, 0, MAX_TIMEOUT_MS);
|
|
140
187
|
this.mutationThrottler = options.mutationThrottle?.enabled === false ? null : new ReplayMutationThrottler(options.mutationThrottle);
|
|
141
188
|
}
|
|
142
189
|
log(...args) {
|
|
@@ -158,28 +205,10 @@ var ReplayTracker = class {
|
|
|
158
205
|
this.pending.length = 0;
|
|
159
206
|
this.pendingSizeBytes = 0;
|
|
160
207
|
}
|
|
161
|
-
dropMinLengthBlockedBatches() {
|
|
162
|
-
this.needsFinalization = false;
|
|
163
|
-
for (let index = this.pending.length - 1; index >= 0; index--) {
|
|
164
|
-
const batch = this.pending[index];
|
|
165
|
-
if (batch && this.minLengthBlockedBatches.has(batch)) this.removePending(batch);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
unblockSessionBatches(sessionId) {
|
|
169
|
-
for (const batch of this.pending) if (batch.sessionId === sessionId) this.minLengthBlockedBatches.delete(batch);
|
|
170
|
-
}
|
|
171
208
|
finalizeSession(context, flushReason) {
|
|
172
209
|
if (!this.needsFinalization) return;
|
|
173
|
-
this.
|
|
174
|
-
|
|
175
|
-
nextBatchSequence(context) {
|
|
176
|
-
const key = `faststats_replay_batch_sequence_${this.options.siteKey}_${context.windowId}`;
|
|
177
|
-
let sequence = this.sequence;
|
|
178
|
-
const parsed = Number.parseInt(getStorageItem("sessionStorage", key) ?? "", 10);
|
|
179
|
-
if (Number.isFinite(parsed) && parsed >= 0) sequence = parsed;
|
|
180
|
-
setStorageItem("sessionStorage", key, String(sequence + 1));
|
|
181
|
-
this.sequence = sequence + 1;
|
|
182
|
-
return sequence;
|
|
210
|
+
const batch = this.createBatch([], context, true, flushReason);
|
|
211
|
+
if (batch) this.enqueueBatch(batch);
|
|
183
212
|
}
|
|
184
213
|
setCookielessMode(cookieless) {
|
|
185
214
|
if (this.lastCookielessMode === cookieless) return;
|
|
@@ -189,7 +218,7 @@ var ReplayTracker = class {
|
|
|
189
218
|
this.clearFlushTimers();
|
|
190
219
|
const context = getSessionContext(this.options.siteKey, this.lastCookielessMode);
|
|
191
220
|
this.activeSessionId = context.sessionId;
|
|
192
|
-
this.startTime =
|
|
221
|
+
this.startTime = Date.now();
|
|
193
222
|
this.chunkStartedAt = Date.now();
|
|
194
223
|
this.beginReplayStream();
|
|
195
224
|
}
|
|
@@ -204,7 +233,7 @@ var ReplayTracker = class {
|
|
|
204
233
|
this.viewUrl = sanitizeUrl(window.location.href, this.options.trackHash ?? false);
|
|
205
234
|
const context = getSessionContext(this.options.siteKey, this.lastCookielessMode);
|
|
206
235
|
this.activeSessionId = context.sessionId;
|
|
207
|
-
this.startTime =
|
|
236
|
+
this.startTime = Date.now();
|
|
208
237
|
this.chunkStartedAt = Date.now();
|
|
209
238
|
this.subscribeToSessionRotation();
|
|
210
239
|
this.beginRecording();
|
|
@@ -227,7 +256,7 @@ var ReplayTracker = class {
|
|
|
227
256
|
viewId: this.viewId
|
|
228
257
|
}
|
|
229
258
|
}
|
|
230
|
-
}
|
|
259
|
+
});
|
|
231
260
|
}
|
|
232
261
|
onPageHidden(persisted = false, terminal = false) {
|
|
233
262
|
if (persisted) return;
|
|
@@ -276,7 +305,7 @@ var ReplayTracker = class {
|
|
|
276
305
|
return plugins;
|
|
277
306
|
}
|
|
278
307
|
beginRecording() {
|
|
279
|
-
|
|
308
|
+
this.stopRecording = record({
|
|
280
309
|
emit: this.onEvent,
|
|
281
310
|
sampling: this.options.sampling ?? {
|
|
282
311
|
mousemove: 50,
|
|
@@ -310,7 +339,6 @@ var ReplayTracker = class {
|
|
|
310
339
|
checkoutEveryNth: this.options.checkoutEveryNth,
|
|
311
340
|
plugins: this.recordingPlugins()
|
|
312
341
|
});
|
|
313
|
-
this.stopRecording = stop;
|
|
314
342
|
}
|
|
315
343
|
stop(discard = false) {
|
|
316
344
|
if (!this.started) return;
|
|
@@ -332,8 +360,8 @@ var ReplayTracker = class {
|
|
|
332
360
|
if (!this.hasReachedMinLength()) {
|
|
333
361
|
this.events.length = 0;
|
|
334
362
|
this.queuedEventsSizeBytes = 0;
|
|
335
|
-
this.
|
|
336
|
-
this.log(`
|
|
363
|
+
this.needsFinalization = false;
|
|
364
|
+
this.log(`Recording too short (${Date.now() - this.startTime}ms), discarding events`);
|
|
337
365
|
}
|
|
338
366
|
this.flush(true, true, "stop");
|
|
339
367
|
this.log("Recording stopped");
|
|
@@ -349,12 +377,15 @@ var ReplayTracker = class {
|
|
|
349
377
|
}
|
|
350
378
|
applySessionRotation(prev, next) {
|
|
351
379
|
if (this.activeSessionId === next.sessionId) return;
|
|
352
|
-
this.enqueueEventsIfReady(prev, {
|
|
380
|
+
if (this.hasReachedMinLength()) this.enqueueEventsIfReady(prev, {
|
|
353
381
|
isFinal: true,
|
|
354
382
|
flushReason: "sessionRotate"
|
|
355
383
|
});
|
|
384
|
+
else {
|
|
385
|
+
this.events.length = 0;
|
|
386
|
+
this.queuedEventsSizeBytes = 0;
|
|
387
|
+
}
|
|
356
388
|
this.finalizeSession(prev, "sessionRotate");
|
|
357
|
-
this.unblockSessionBatches(prev.sessionId);
|
|
358
389
|
this.needsFinalization = false;
|
|
359
390
|
this.activeSessionId = next.sessionId;
|
|
360
391
|
this.resetSessionTiming(next);
|
|
@@ -380,14 +411,14 @@ var ReplayTracker = class {
|
|
|
380
411
|
}
|
|
381
412
|
}
|
|
382
413
|
}
|
|
383
|
-
resetSessionTiming(
|
|
414
|
+
resetSessionTiming(_next) {
|
|
384
415
|
if (this.minLengthFlushTask) clearTimeout(this.minLengthFlushTask);
|
|
385
416
|
this.minLengthFlushTask = null;
|
|
386
|
-
this.startTime =
|
|
417
|
+
this.startTime = Date.now();
|
|
387
418
|
this.chunkStartedAt = Date.now();
|
|
388
419
|
this.initialFlushPending = true;
|
|
389
420
|
}
|
|
390
|
-
onEvent = (event
|
|
421
|
+
onEvent = (event) => {
|
|
391
422
|
if (this.overflowed) return;
|
|
392
423
|
let capturedEvent = event;
|
|
393
424
|
if (event.type === RRWEB_EVENT_META) {
|
|
@@ -406,9 +437,17 @@ var ReplayTracker = class {
|
|
|
406
437
|
if (this.mutationThrottler && !throttled) return;
|
|
407
438
|
if (throttled) capturedEvent = throttled;
|
|
408
439
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
440
|
+
const sequencedEvent = {
|
|
441
|
+
...capturedEvent,
|
|
442
|
+
[REPLAY_SEQUENTIAL_ID_KEY]: ++this.replayEventSequence
|
|
443
|
+
};
|
|
444
|
+
const eventBytes = this.eventSizeBytes(sequencedEvent);
|
|
445
|
+
if (this.pendingSizeBytes + this.queuedEventsSizeBytes + eventBytes > this.maxQueueSizeBytes) {
|
|
446
|
+
this.stopCapture("Replay buffer limit reached");
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
this.events.push(sequencedEvent);
|
|
450
|
+
this.queuedEventsSizeBytes += eventBytes;
|
|
412
451
|
let reason;
|
|
413
452
|
if (this.events.length >= this.maxEvents) reason = "maxEvents";
|
|
414
453
|
else if (this.queuedEventsSizeBytes >= this.maxBatchSizeBytes) reason = "maxBytes";
|
|
@@ -416,13 +455,13 @@ var ReplayTracker = class {
|
|
|
416
455
|
else this.scheduleMinLengthFlush();
|
|
417
456
|
};
|
|
418
457
|
onUnload = () => {
|
|
419
|
-
this.
|
|
458
|
+
this.flush(true, false, "unload");
|
|
420
459
|
};
|
|
421
460
|
requestTerminalFlush(reason) {
|
|
422
461
|
if (!this.hasReachedMinLength()) {
|
|
423
462
|
this.events.length = 0;
|
|
424
463
|
this.queuedEventsSizeBytes = 0;
|
|
425
|
-
this.
|
|
464
|
+
this.needsFinalization = false;
|
|
426
465
|
return;
|
|
427
466
|
}
|
|
428
467
|
this.flush(true, true, reason);
|
|
@@ -457,18 +496,16 @@ var ReplayTracker = class {
|
|
|
457
496
|
}, Math.min(remainingMs, MAX_TIMEOUT_MS));
|
|
458
497
|
}
|
|
459
498
|
enqueueEventsIfReady(context, options = {}) {
|
|
460
|
-
const { isFinal = false, flushReason = "manual", maxBatchSizeBytes = this.maxBatchSizeBytes
|
|
499
|
+
const { isFinal = false, flushReason = "manual", maxBatchSizeBytes = this.maxBatchSizeBytes } = options;
|
|
461
500
|
if (this.events.length === 0) return;
|
|
462
|
-
if (!isFinal && !
|
|
501
|
+
if (!isFinal && !this.hasReachedMinLength()) {
|
|
463
502
|
this.scheduleMinLengthFlush();
|
|
464
503
|
return;
|
|
465
504
|
}
|
|
466
505
|
const batches = this.takeEventBatches(maxBatchSizeBytes);
|
|
467
|
-
const blockedByMinLength = sealBeforeMinLength && !this.hasReachedMinLength();
|
|
468
506
|
for (const [index, events] of batches.entries()) {
|
|
469
507
|
const batch = this.createBatch(events, context, isFinal && index === batches.length - 1, flushReason);
|
|
470
|
-
if (
|
|
471
|
-
this.enqueueBatch(batch);
|
|
508
|
+
if (!batch || !this.enqueueBatch(batch)) break;
|
|
472
509
|
}
|
|
473
510
|
this.queuedEventsSizeBytes = 0;
|
|
474
511
|
}
|
|
@@ -491,7 +528,11 @@ var ReplayTracker = class {
|
|
|
491
528
|
}
|
|
492
529
|
createBatch(events, context, isFinal, flushReason = "manual") {
|
|
493
530
|
const identifier = getAnonymousId(this.options.siteKey, this.lastCookielessMode);
|
|
494
|
-
const sequence = this.
|
|
531
|
+
const sequence = nextReplaySequence(this.options.siteKey, context, this.lastCookielessMode);
|
|
532
|
+
if (!sequence) {
|
|
533
|
+
this.stopCapture("Replay sequence state is invalid or exhausted");
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
495
536
|
const chunkStartedAt = this.chunkStartedAt;
|
|
496
537
|
const now = Date.now();
|
|
497
538
|
this.chunkStartedAt = now;
|
|
@@ -504,8 +545,8 @@ var ReplayTracker = class {
|
|
|
504
545
|
chunkStartedAt,
|
|
505
546
|
chunkEndedAt: now,
|
|
506
547
|
...identifier ? { identifier } : {},
|
|
507
|
-
batchId: `${context.sessionId}-${sequence}-${chunkStartedAt}`,
|
|
508
|
-
sequence,
|
|
548
|
+
batchId: `${context.sessionId}-${sequence.sequence}-${chunkStartedAt}`,
|
|
549
|
+
...sequence,
|
|
509
550
|
timestamp: now,
|
|
510
551
|
url: this.viewUrl || sanitizeUrl(window.location.href, this.options.trackHash ?? false),
|
|
511
552
|
...isFinal ? { isFinal: true } : {},
|
|
@@ -523,28 +564,35 @@ var ReplayTracker = class {
|
|
|
523
564
|
batchSizeCache.set(batch, size);
|
|
524
565
|
return size;
|
|
525
566
|
}
|
|
526
|
-
removePending(batch
|
|
567
|
+
removePending(batch) {
|
|
527
568
|
const index = this.pending.indexOf(batch);
|
|
528
|
-
if (index < 0) return
|
|
569
|
+
if (index < 0) return;
|
|
529
570
|
this.pending.splice(index, 1);
|
|
530
571
|
this.pendingSizeBytes = Math.max(0, this.pendingSizeBytes - this.batchSizeBytes(batch));
|
|
531
|
-
|
|
532
|
-
|
|
572
|
+
this.log(`Sent replay batch ${batch.sequence}`);
|
|
573
|
+
}
|
|
574
|
+
stopCapture(reason) {
|
|
575
|
+
this.log(reason);
|
|
576
|
+
this.overflowed = true;
|
|
577
|
+
this.stopRecording?.();
|
|
578
|
+
this.stopRecording = void 0;
|
|
533
579
|
}
|
|
534
580
|
enqueueBatch(batch) {
|
|
535
581
|
const size = this.batchSizeBytes(batch);
|
|
536
582
|
if (size > this.maxQueueSizeBytes || this.pending.length >= this.maxPendingBatches || this.pendingSizeBytes + size > this.maxQueueSizeBytes) {
|
|
537
|
-
this.
|
|
538
|
-
|
|
539
|
-
this.stopRecording?.();
|
|
540
|
-
this.stopRecording = void 0;
|
|
541
|
-
return;
|
|
583
|
+
this.stopCapture(`Replay queue limit reached at batch ${batch.sequence}`);
|
|
584
|
+
return false;
|
|
542
585
|
}
|
|
543
586
|
this.needsFinalization = !batch.isFinal;
|
|
544
587
|
this.pending.push(batch);
|
|
545
588
|
this.pendingSizeBytes += size;
|
|
589
|
+
return true;
|
|
546
590
|
}
|
|
547
591
|
async flush(lowLatency, isFinal = false, flushReason = "manual") {
|
|
592
|
+
if (this.events.length === 0 && !isFinal) {
|
|
593
|
+
await this.sendPending(lowLatency, false);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
548
596
|
const rotation = touchActivity(this.options.siteKey, this.lastCookielessMode);
|
|
549
597
|
if (rotation) this.applySessionRotation(rotation.prev, rotation.next);
|
|
550
598
|
const context = getSessionContext(this.options.siteKey, this.lastCookielessMode);
|
|
@@ -555,8 +603,6 @@ var ReplayTracker = class {
|
|
|
555
603
|
});
|
|
556
604
|
if (isFinal) this.finalizeSession(context, flushReason);
|
|
557
605
|
if (this.pending.length === 0) return;
|
|
558
|
-
if (isFinal) for (const batch of this.pending) this.minLengthBlockedBatches.delete(batch);
|
|
559
|
-
else if (!rotation && flushReason !== "sessionRotate" && this.hasReachedMinLength()) this.unblockSessionBatches(context.sessionId);
|
|
560
606
|
await this.sendPending(lowLatency, lowLatency && isFinal);
|
|
561
607
|
}
|
|
562
608
|
async sendPending(lowLatency, bypassRetryDelay) {
|
|
@@ -577,14 +623,13 @@ var ReplayTracker = class {
|
|
|
577
623
|
try {
|
|
578
624
|
while (this.pending.length > 0) {
|
|
579
625
|
const batch = this.pending[0];
|
|
580
|
-
if (this.minLengthBlockedBatches.has(batch)) break;
|
|
581
626
|
if (!await this.sendBatch(batch, lowLatency)) {
|
|
582
627
|
this.log(`Failed to send replay batch ${batch.sequence}`);
|
|
583
628
|
if (!this.disposed) this.scheduleRetry();
|
|
584
629
|
break;
|
|
585
630
|
}
|
|
586
631
|
this.retryAttempt = 0;
|
|
587
|
-
this.removePending(batch
|
|
632
|
+
this.removePending(batch);
|
|
588
633
|
if (this.queuedFlush?.lowLatency) break;
|
|
589
634
|
}
|
|
590
635
|
} finally {
|
|
@@ -604,7 +649,7 @@ var ReplayTracker = class {
|
|
|
604
649
|
this.retryTask = setTimeout(() => {
|
|
605
650
|
this.retryTask = null;
|
|
606
651
|
if (this.retryAttempt === RETRY_MAX_ATTEMPTS) this.retryAttempt = 0;
|
|
607
|
-
this.
|
|
652
|
+
this.sendPending(false, false);
|
|
608
653
|
}, delay);
|
|
609
654
|
}
|
|
610
655
|
retryDelay(attempt) {
|
package/package.json
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
|
-
"version": "0.10.
|
|
45
|
+
"version": "0.10.2",
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsdown && bun run check-size",
|
|
48
48
|
"dev": "bun run build",
|
|
@@ -60,6 +60,6 @@
|
|
|
60
60
|
"@rrweb/record": "^2.1.6",
|
|
61
61
|
"@rrweb/rrweb-plugin-console-record": "^2.1.6",
|
|
62
62
|
"@rrweb/rrweb-plugin-network-record": "^2.1.6",
|
|
63
|
-
"web-vitals": "^6.2.
|
|
63
|
+
"web-vitals": "^6.2.2"
|
|
64
64
|
}
|
|
65
65
|
}
|