@cedarai/session-replay-sdk 0.2.0 → 0.4.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/dist/index.js +117 -5
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ function isCompressionSupported() {
|
|
|
24
24
|
|
|
25
25
|
// src/transport.ts
|
|
26
26
|
var BEACON_MAX_SIZE = 64 * 1024;
|
|
27
|
+
var MAX_QUEUE_LENGTH = 1e3;
|
|
27
28
|
var Transport = class {
|
|
28
29
|
config;
|
|
29
30
|
queue = [];
|
|
@@ -65,6 +66,9 @@ var Transport = class {
|
|
|
65
66
|
enqueue(event) {
|
|
66
67
|
if (!this.started) return;
|
|
67
68
|
this.queue.push(event);
|
|
69
|
+
if (this.queue.length > MAX_QUEUE_LENGTH) {
|
|
70
|
+
this.queue.splice(0, this.queue.length - MAX_QUEUE_LENGTH);
|
|
71
|
+
}
|
|
68
72
|
const maxSize = this.config.batchMaxSize ?? 1024 * 512;
|
|
69
73
|
const estimatedSize = this.estimateQueueSize();
|
|
70
74
|
if (estimatedSize >= maxSize) {
|
|
@@ -294,6 +298,7 @@ var ErrorCapture = class {
|
|
|
294
298
|
|
|
295
299
|
// src/network.ts
|
|
296
300
|
var nextId = 0;
|
|
301
|
+
var RESPONSE_BODY_MAX_SIZE = 100 * 1024;
|
|
297
302
|
var NetworkCapture = class {
|
|
298
303
|
onEvent;
|
|
299
304
|
serverUrl;
|
|
@@ -330,10 +335,16 @@ var NetworkCapture = class {
|
|
|
330
335
|
const response = await this.originalFetch(input, init);
|
|
331
336
|
const endTime = performance.now();
|
|
332
337
|
let responseBody;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
338
|
+
const contentLength = parseInt(response.headers.get("content-length") ?? "0", 10);
|
|
339
|
+
if (contentLength === 0 || contentLength <= RESPONSE_BODY_MAX_SIZE) {
|
|
340
|
+
try {
|
|
341
|
+
const cloned = response.clone();
|
|
342
|
+
const text = await cloned.text();
|
|
343
|
+
if (text.length <= RESPONSE_BODY_MAX_SIZE) {
|
|
344
|
+
responseBody = text;
|
|
345
|
+
}
|
|
346
|
+
} catch {
|
|
347
|
+
}
|
|
337
348
|
}
|
|
338
349
|
this.onEvent({
|
|
339
350
|
type: "network",
|
|
@@ -385,6 +396,16 @@ var NetworkCapture = class {
|
|
|
385
396
|
|
|
386
397
|
// src/recorder.ts
|
|
387
398
|
import { record } from "rrweb";
|
|
399
|
+
var DEFAULT_SAMPLING = {
|
|
400
|
+
mousemove: 50,
|
|
401
|
+
// throttle mousemove to one event per 50ms (default is every frame)
|
|
402
|
+
mouseInteraction: true,
|
|
403
|
+
// keep mouse clicks/interactions
|
|
404
|
+
scroll: 150,
|
|
405
|
+
// throttle scroll to one event per 150ms
|
|
406
|
+
input: "last"
|
|
407
|
+
// only capture the final input value, not every keystroke
|
|
408
|
+
};
|
|
388
409
|
var RecorderCapture = class {
|
|
389
410
|
onEvent;
|
|
390
411
|
config;
|
|
@@ -398,6 +419,7 @@ var RecorderCapture = class {
|
|
|
398
419
|
if (this.started) return;
|
|
399
420
|
this.started = true;
|
|
400
421
|
const { checkoutEveryNms, blockSelector, maskAllInputs, inlineStylesheet, sampling } = this.config;
|
|
422
|
+
const mergedSampling = { ...DEFAULT_SAMPLING, ...sampling };
|
|
401
423
|
const result = record({
|
|
402
424
|
emit: (event) => {
|
|
403
425
|
if (!this.started) return;
|
|
@@ -410,7 +432,7 @@ var RecorderCapture = class {
|
|
|
410
432
|
...blockSelector !== void 0 && { blockSelector },
|
|
411
433
|
...maskAllInputs !== void 0 && { maskAllInputs },
|
|
412
434
|
...inlineStylesheet !== void 0 && { inlineStylesheet },
|
|
413
|
-
|
|
435
|
+
sampling: mergedSampling
|
|
414
436
|
});
|
|
415
437
|
this.stopFn = result ?? null;
|
|
416
438
|
}
|
|
@@ -422,6 +444,91 @@ var RecorderCapture = class {
|
|
|
422
444
|
}
|
|
423
445
|
};
|
|
424
446
|
|
|
447
|
+
// src/performance.ts
|
|
448
|
+
import { onLCP, onCLS, onINP } from "web-vitals";
|
|
449
|
+
var MEMORY_INTERVAL_MS = 3e4;
|
|
450
|
+
var PerformanceCapture = class {
|
|
451
|
+
onEvent;
|
|
452
|
+
started = false;
|
|
453
|
+
longTaskObserver = null;
|
|
454
|
+
memoryTimer = null;
|
|
455
|
+
constructor(onEvent2) {
|
|
456
|
+
this.onEvent = onEvent2;
|
|
457
|
+
}
|
|
458
|
+
start() {
|
|
459
|
+
if (this.started) return;
|
|
460
|
+
this.started = true;
|
|
461
|
+
this.startWebVitals();
|
|
462
|
+
this.startLongTaskObserver();
|
|
463
|
+
this.startMemorySnapshots();
|
|
464
|
+
}
|
|
465
|
+
stop() {
|
|
466
|
+
if (!this.started) return;
|
|
467
|
+
this.started = false;
|
|
468
|
+
this.longTaskObserver?.disconnect();
|
|
469
|
+
this.longTaskObserver = null;
|
|
470
|
+
if (this.memoryTimer) {
|
|
471
|
+
clearInterval(this.memoryTimer);
|
|
472
|
+
this.memoryTimer = null;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
emit(data) {
|
|
476
|
+
if (!this.started) return;
|
|
477
|
+
this.onEvent({
|
|
478
|
+
type: "performance",
|
|
479
|
+
timestamp: Date.now(),
|
|
480
|
+
data
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
startWebVitals() {
|
|
484
|
+
const handler = (metric) => {
|
|
485
|
+
this.emit({
|
|
486
|
+
metric: "web-vital",
|
|
487
|
+
name: metric.name,
|
|
488
|
+
value: metric.value,
|
|
489
|
+
rating: metric.rating
|
|
490
|
+
});
|
|
491
|
+
};
|
|
492
|
+
onLCP(handler);
|
|
493
|
+
onCLS(handler, { reportAllChanges: true });
|
|
494
|
+
onINP(handler, { reportAllChanges: true });
|
|
495
|
+
}
|
|
496
|
+
startLongTaskObserver() {
|
|
497
|
+
if (typeof globalThis.PerformanceObserver === "undefined") return;
|
|
498
|
+
try {
|
|
499
|
+
this.longTaskObserver = new globalThis.PerformanceObserver((list) => {
|
|
500
|
+
for (const entry of list.getEntries()) {
|
|
501
|
+
const attribution = entry.attribution?.[0]?.containerSrc;
|
|
502
|
+
this.emit({
|
|
503
|
+
metric: "long-task",
|
|
504
|
+
duration: entry.duration,
|
|
505
|
+
attribution
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
this.longTaskObserver.observe({ entryTypes: ["longtask"] });
|
|
510
|
+
} catch {
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
startMemorySnapshots() {
|
|
514
|
+
const memory = performance.memory;
|
|
515
|
+
if (!memory) return;
|
|
516
|
+
this.emitMemory(memory);
|
|
517
|
+
this.memoryTimer = setInterval(() => {
|
|
518
|
+
const mem = performance.memory;
|
|
519
|
+
if (mem) this.emitMemory(mem);
|
|
520
|
+
}, MEMORY_INTERVAL_MS);
|
|
521
|
+
}
|
|
522
|
+
emitMemory(memory) {
|
|
523
|
+
this.emit({
|
|
524
|
+
metric: "memory",
|
|
525
|
+
usedJSHeapSize: memory.usedJSHeapSize,
|
|
526
|
+
totalJSHeapSize: memory.totalJSHeapSize,
|
|
527
|
+
jsHeapSizeLimit: memory.jsHeapSizeLimit
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
|
|
425
532
|
// src/index.ts
|
|
426
533
|
var config = null;
|
|
427
534
|
var transport = null;
|
|
@@ -429,6 +536,7 @@ var consoleCapture = null;
|
|
|
429
536
|
var errorCapture = null;
|
|
430
537
|
var networkCapture = null;
|
|
431
538
|
var recorderCapture = null;
|
|
539
|
+
var performanceCapture = null;
|
|
432
540
|
var unloadHandler = null;
|
|
433
541
|
function onEvent(event) {
|
|
434
542
|
transport?.enqueue(event);
|
|
@@ -458,6 +566,8 @@ var CedarReplay = {
|
|
|
458
566
|
networkCapture.start();
|
|
459
567
|
recorderCapture = new RecorderCapture(onEvent, config.recorder);
|
|
460
568
|
recorderCapture.start();
|
|
569
|
+
performanceCapture = new PerformanceCapture(onEvent);
|
|
570
|
+
performanceCapture.start();
|
|
461
571
|
unloadHandler = () => transport?.flushOnUnload();
|
|
462
572
|
window.addEventListener("beforeunload", unloadHandler);
|
|
463
573
|
},
|
|
@@ -466,6 +576,8 @@ var CedarReplay = {
|
|
|
466
576
|
window.removeEventListener("beforeunload", unloadHandler);
|
|
467
577
|
unloadHandler = null;
|
|
468
578
|
}
|
|
579
|
+
performanceCapture?.stop();
|
|
580
|
+
performanceCapture = null;
|
|
469
581
|
recorderCapture?.stop();
|
|
470
582
|
recorderCapture = null;
|
|
471
583
|
networkCapture?.stop();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cedarai/session-replay-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"test:watch": "vitest"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"rrweb": "^2.0.0-alpha.4"
|
|
22
|
+
"rrweb": "^2.0.0-alpha.4",
|
|
23
|
+
"web-vitals": "^4.2.4"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@vitest/browser": "^4.0.18",
|