@openreplay/tracker 17.1.5 → 17.1.6
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/cjs/entry.js +119 -44
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +119 -44
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/canvas.d.ts +9 -0
- package/dist/lib/entry.js +119 -44
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +119 -44
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/canvas.d.ts +9 -0
- package/dist/types/main/app/canvas.d.ts +9 -0
- package/package.json +1 -1
package/dist/cjs/entry.js
CHANGED
|
@@ -1135,7 +1135,7 @@ const stars = 'repeat' in String.prototype
|
|
|
1135
1135
|
? (str) => '*'.repeat(str.length)
|
|
1136
1136
|
: (str) => str.replace(/./g, '*');
|
|
1137
1137
|
function normSpaces(str) {
|
|
1138
|
-
return str.trim().replace(/\s+/g, ' ');
|
|
1138
|
+
return str ? str.trim().replace(/\s+/g, ' ') : '';
|
|
1139
1139
|
}
|
|
1140
1140
|
// isAbsoluteUrl regexp: /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url)
|
|
1141
1141
|
function isURL(s) {
|
|
@@ -2332,7 +2332,13 @@ class CanvasRecorder {
|
|
|
2332
2332
|
this.app = app;
|
|
2333
2333
|
this.options = options;
|
|
2334
2334
|
this.snapshots = {};
|
|
2335
|
-
this.intervals =
|
|
2335
|
+
this.intervals = new Map();
|
|
2336
|
+
this.observers = new Map();
|
|
2337
|
+
this.uploadQueue = 0;
|
|
2338
|
+
this.MAX_CONCURRENT_UPLOADS = 2;
|
|
2339
|
+
this.MAX_QUEUE_SIZE = 50; // ~500 images max (50 batches × 10 images)
|
|
2340
|
+
this.pendingBatches = [];
|
|
2341
|
+
this.isProcessingQueue = false;
|
|
2336
2342
|
this.restartTracking = () => {
|
|
2337
2343
|
this.clear();
|
|
2338
2344
|
this.app.nodes.scanTree(this.captureCanvas);
|
|
@@ -2343,34 +2349,33 @@ class CanvasRecorder {
|
|
|
2343
2349
|
return;
|
|
2344
2350
|
}
|
|
2345
2351
|
const isIgnored = this.app.sanitizer.isObscured(id) || this.app.sanitizer.isHidden(id);
|
|
2346
|
-
if (isIgnored ||
|
|
2352
|
+
if (isIgnored || this.snapshots[id]) {
|
|
2347
2353
|
return;
|
|
2348
2354
|
}
|
|
2349
2355
|
const observer = new IntersectionObserver((entries) => {
|
|
2350
2356
|
entries.forEach((entry) => {
|
|
2351
2357
|
if (entry.isIntersecting) {
|
|
2352
|
-
if (
|
|
2353
|
-
|
|
2354
|
-
this.snapshots[id].paused = false;
|
|
2355
|
-
}
|
|
2356
|
-
else {
|
|
2357
|
-
this.recordCanvas(entry.target, id);
|
|
2358
|
-
}
|
|
2359
|
-
/**
|
|
2360
|
-
* We can switch this to start observing when element is in the view
|
|
2361
|
-
* but otherwise right now we're just pausing when it's not
|
|
2362
|
-
* just to save some bandwidth and space on backend
|
|
2363
|
-
* */
|
|
2364
|
-
// observer.unobserve(entry.target)
|
|
2358
|
+
if (this.snapshots[id] && this.snapshots[id].createdAt) {
|
|
2359
|
+
this.snapshots[id].paused = false;
|
|
2365
2360
|
}
|
|
2366
2361
|
else {
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2362
|
+
this.recordCanvas(entry.target, id);
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* We can switch this to start observing when element is in the view
|
|
2366
|
+
* but otherwise right now we're just pausing when it's not
|
|
2367
|
+
* just to save some bandwidth and space on backend
|
|
2368
|
+
* */
|
|
2369
|
+
// observer.unobserve(entry.target)
|
|
2370
|
+
}
|
|
2371
|
+
else {
|
|
2372
|
+
if (this.snapshots[id]) {
|
|
2373
|
+
this.snapshots[id].paused = true;
|
|
2370
2374
|
}
|
|
2371
2375
|
}
|
|
2372
2376
|
});
|
|
2373
2377
|
});
|
|
2378
|
+
this.observers.set(id, observer);
|
|
2374
2379
|
observer.observe(node);
|
|
2375
2380
|
};
|
|
2376
2381
|
this.recordCanvas = (node, id) => {
|
|
@@ -2380,15 +2385,23 @@ class CanvasRecorder {
|
|
|
2380
2385
|
createdAt: ts,
|
|
2381
2386
|
paused: false,
|
|
2382
2387
|
dummy: document.createElement('canvas'),
|
|
2388
|
+
isCapturing: false,
|
|
2389
|
+
isStopped: false,
|
|
2383
2390
|
};
|
|
2384
2391
|
const canvasMsg = CanvasNode(id.toString(), ts);
|
|
2385
2392
|
this.app.send(canvasMsg);
|
|
2393
|
+
const cachedCanvas = node;
|
|
2386
2394
|
const captureFn = (canvas) => {
|
|
2395
|
+
if (!this.snapshots[id] || this.snapshots[id].isCapturing || this.snapshots[id].isStopped) {
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
this.snapshots[id].isCapturing = true;
|
|
2387
2399
|
captureSnapshot(canvas, this.options.quality, this.snapshots[id].dummy, this.options.fixedScaling, this.fileExt, (blob) => {
|
|
2388
|
-
if (
|
|
2400
|
+
if (this.snapshots[id]) {
|
|
2401
|
+
this.snapshots[id].isCapturing = false;
|
|
2402
|
+
}
|
|
2403
|
+
if (!blob || !this.snapshots[id] || this.snapshots[id].isStopped) {
|
|
2389
2404
|
return;
|
|
2390
|
-
if (!this.snapshots[id]) {
|
|
2391
|
-
return this.app.debug.warn('Canvas not present in snapshots after capture:', this.snapshots, id);
|
|
2392
2405
|
}
|
|
2393
2406
|
this.snapshots[id].images.push({ id: this.app.timestamp(), data: blob });
|
|
2394
2407
|
if (this.snapshots[id].images.length > 9) {
|
|
@@ -2398,32 +2411,29 @@ class CanvasRecorder {
|
|
|
2398
2411
|
});
|
|
2399
2412
|
};
|
|
2400
2413
|
const int = setInterval(() => {
|
|
2401
|
-
const
|
|
2402
|
-
|
|
2403
|
-
if (!this.snapshots[id]) {
|
|
2414
|
+
const snapshot = this.snapshots[id];
|
|
2415
|
+
if (!snapshot || snapshot.isStopped) {
|
|
2404
2416
|
this.app.debug.log('Canvas is not present in {snapshots}');
|
|
2405
|
-
|
|
2417
|
+
this.cleanupCanvas(id);
|
|
2406
2418
|
return;
|
|
2407
2419
|
}
|
|
2408
|
-
if (!
|
|
2409
|
-
this.app.debug.log('Canvas element not in sync',
|
|
2410
|
-
|
|
2420
|
+
if (!document.contains(cachedCanvas)) {
|
|
2421
|
+
this.app.debug.log('Canvas element not in sync', cachedCanvas, node);
|
|
2422
|
+
this.cleanupCanvas(id);
|
|
2411
2423
|
return;
|
|
2412
2424
|
}
|
|
2413
|
-
|
|
2414
|
-
if (
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
captureFn(canvas);
|
|
2422
|
-
}
|
|
2425
|
+
if (!snapshot.paused) {
|
|
2426
|
+
if (this.options.useAnimationFrame) {
|
|
2427
|
+
requestAnimationFrame(() => {
|
|
2428
|
+
captureFn(cachedCanvas);
|
|
2429
|
+
});
|
|
2430
|
+
}
|
|
2431
|
+
else {
|
|
2432
|
+
captureFn(cachedCanvas);
|
|
2423
2433
|
}
|
|
2424
2434
|
}
|
|
2425
2435
|
}, this.interval);
|
|
2426
|
-
this.intervals.
|
|
2436
|
+
this.intervals.set(id, int);
|
|
2427
2437
|
};
|
|
2428
2438
|
this.fileExt = options.fileExt ?? 'webp';
|
|
2429
2439
|
this.interval = 1000 / options.fps;
|
|
@@ -2438,6 +2448,30 @@ class CanvasRecorder {
|
|
|
2438
2448
|
if (Object.keys(this.snapshots).length === 0) {
|
|
2439
2449
|
return;
|
|
2440
2450
|
}
|
|
2451
|
+
if (this.pendingBatches.length >= this.MAX_QUEUE_SIZE) {
|
|
2452
|
+
this.app.debug.warn('Upload queue full, dropping canvas batch');
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2455
|
+
this.pendingBatches.push({ images, canvasId, createdAt });
|
|
2456
|
+
if (!this.isProcessingQueue) {
|
|
2457
|
+
this.processUploadQueue();
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
async processUploadQueue() {
|
|
2461
|
+
this.isProcessingQueue = true;
|
|
2462
|
+
while (this.pendingBatches.length > 0) {
|
|
2463
|
+
if (this.uploadQueue >= this.MAX_CONCURRENT_UPLOADS) {
|
|
2464
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2465
|
+
continue;
|
|
2466
|
+
}
|
|
2467
|
+
const batch = this.pendingBatches.shift();
|
|
2468
|
+
if (!batch)
|
|
2469
|
+
break;
|
|
2470
|
+
this.uploadBatch(batch.images, batch.canvasId, batch.createdAt);
|
|
2471
|
+
}
|
|
2472
|
+
this.isProcessingQueue = false;
|
|
2473
|
+
}
|
|
2474
|
+
uploadBatch(images, canvasId, createdAt) {
|
|
2441
2475
|
const formData = new FormData();
|
|
2442
2476
|
images.forEach((snapshot) => {
|
|
2443
2477
|
const blob = snapshot.data;
|
|
@@ -2455,6 +2489,7 @@ class CanvasRecorder {
|
|
|
2455
2489
|
void this.app.start({}, true);
|
|
2456
2490
|
}, 250);
|
|
2457
2491
|
};
|
|
2492
|
+
this.uploadQueue++;
|
|
2458
2493
|
fetch(this.app.options.ingestPoint + '/v1/web/images', {
|
|
2459
2494
|
method: 'POST',
|
|
2460
2495
|
headers: {
|
|
@@ -2470,10 +2505,50 @@ class CanvasRecorder {
|
|
|
2470
2505
|
})
|
|
2471
2506
|
.catch((e) => {
|
|
2472
2507
|
this.app.debug.error('error saving canvas', e);
|
|
2508
|
+
})
|
|
2509
|
+
.finally(() => {
|
|
2510
|
+
this.uploadQueue--;
|
|
2473
2511
|
});
|
|
2474
2512
|
}
|
|
2513
|
+
cleanupCanvas(id) {
|
|
2514
|
+
if (this.snapshots[id]) {
|
|
2515
|
+
this.snapshots[id].isStopped = true;
|
|
2516
|
+
}
|
|
2517
|
+
const interval = this.intervals.get(id);
|
|
2518
|
+
if (interval) {
|
|
2519
|
+
clearInterval(interval);
|
|
2520
|
+
this.intervals.delete(id);
|
|
2521
|
+
}
|
|
2522
|
+
const observer = this.observers.get(id);
|
|
2523
|
+
if (observer) {
|
|
2524
|
+
observer.disconnect();
|
|
2525
|
+
this.observers.delete(id);
|
|
2526
|
+
}
|
|
2527
|
+
if (this.snapshots[id]?.dummy) {
|
|
2528
|
+
const dummy = this.snapshots[id].dummy;
|
|
2529
|
+
dummy.width = 0;
|
|
2530
|
+
dummy.height = 0;
|
|
2531
|
+
}
|
|
2532
|
+
delete this.snapshots[id];
|
|
2533
|
+
}
|
|
2475
2534
|
clear() {
|
|
2476
|
-
|
|
2535
|
+
// Flush remaining images before cleanup
|
|
2536
|
+
Object.keys(this.snapshots).forEach((idStr) => {
|
|
2537
|
+
const id = parseInt(idStr, 10);
|
|
2538
|
+
const snapshot = this.snapshots[id];
|
|
2539
|
+
if (snapshot && snapshot.images.length > 0) {
|
|
2540
|
+
this.sendSnaps(snapshot.images, id, snapshot.createdAt);
|
|
2541
|
+
snapshot.images = [];
|
|
2542
|
+
}
|
|
2543
|
+
});
|
|
2544
|
+
Object.keys(this.snapshots).forEach((idStr) => {
|
|
2545
|
+
const id = parseInt(idStr, 10);
|
|
2546
|
+
this.cleanupCanvas(id);
|
|
2547
|
+
});
|
|
2548
|
+
// don't clear pendingBatches or stop queue processing
|
|
2549
|
+
// to allow flushed images to finish uploading in the background
|
|
2550
|
+
this.intervals.clear();
|
|
2551
|
+
this.observers.clear();
|
|
2477
2552
|
this.snapshots = {};
|
|
2478
2553
|
}
|
|
2479
2554
|
}
|
|
@@ -4349,7 +4424,7 @@ class App {
|
|
|
4349
4424
|
this.stopCallbacks = [];
|
|
4350
4425
|
this.commitCallbacks = [];
|
|
4351
4426
|
this.activityState = ActivityState.NotActive;
|
|
4352
|
-
this.version = '17.1.
|
|
4427
|
+
this.version = '17.1.6'; // TODO: version compatability check inside each plugin.
|
|
4353
4428
|
this.socketMode = false;
|
|
4354
4429
|
this.compressionThreshold = 24 * 1000;
|
|
4355
4430
|
this.bc = null;
|
|
@@ -9186,7 +9261,7 @@ class ConstantProperties {
|
|
|
9186
9261
|
user_id: this.user_id,
|
|
9187
9262
|
distinct_id: this.deviceId,
|
|
9188
9263
|
sdk_edition: 'web',
|
|
9189
|
-
sdk_version: '17.1.
|
|
9264
|
+
sdk_version: '17.1.6',
|
|
9190
9265
|
timezone: getUTCOffsetString(),
|
|
9191
9266
|
search_engine: this.searchEngine,
|
|
9192
9267
|
};
|
|
@@ -9830,7 +9905,7 @@ class API {
|
|
|
9830
9905
|
this.signalStartIssue = (reason, missingApi) => {
|
|
9831
9906
|
const doNotTrack = this.checkDoNotTrack();
|
|
9832
9907
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
9833
|
-
trackerVersion: '17.1.
|
|
9908
|
+
trackerVersion: '17.1.6',
|
|
9834
9909
|
projectKey: this.options.projectKey,
|
|
9835
9910
|
doNotTrack,
|
|
9836
9911
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|