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