@openreplay/tracker 18.0.14-beta.1 → 18.0.14
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/common/interaction.d.ts +1 -4
- package/dist/cjs/entry.js +379 -118
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +316 -101
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/canvas.d.ts +6 -0
- package/dist/cjs/main/app/index.d.ts +6 -1
- package/dist/cjs/main/app/observer/observer.d.ts +10 -0
- package/dist/cjs/main/app/sanitizer.d.ts +5 -3
- package/dist/cjs/main/index.d.ts +17 -2
- package/dist/cjs/main/singleton.d.ts +25 -1
- package/dist/lib/common/interaction.d.ts +1 -4
- package/dist/lib/entry.js +379 -118
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +316 -101
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/canvas.d.ts +6 -0
- package/dist/lib/main/app/index.d.ts +6 -1
- package/dist/lib/main/app/observer/observer.d.ts +10 -0
- package/dist/lib/main/app/sanitizer.d.ts +5 -3
- package/dist/lib/main/index.d.ts +17 -2
- package/dist/lib/main/singleton.d.ts +25 -1
- package/dist/types/common/interaction.d.ts +1 -4
- package/dist/types/main/app/canvas.d.ts +6 -0
- package/dist/types/main/app/index.d.ts +6 -1
- package/dist/types/main/app/observer/observer.d.ts +10 -0
- package/dist/types/main/app/sanitizer.d.ts +5 -3
- package/dist/types/main/index.d.ts +17 -2
- package/dist/types/main/singleton.d.ts +25 -1
- package/package.json +1 -1
package/dist/lib/entry.js
CHANGED
|
@@ -1823,6 +1823,30 @@ class CanvasRecorder {
|
|
|
1823
1823
|
this.MAX_QUEUE_SIZE = 50; // ~500 images max (50 batches × 10 images)
|
|
1824
1824
|
this.pendingBatches = [];
|
|
1825
1825
|
this.isProcessingQueue = false;
|
|
1826
|
+
/**
|
|
1827
|
+
* Reacts to a runtime sanitization change on a canvas: stop capturing if it
|
|
1828
|
+
* just became masked, start if it just became visible. (Already-sent frames
|
|
1829
|
+
* can't be retracted — escalation only stops future capture.)
|
|
1830
|
+
*/
|
|
1831
|
+
this.resanitizeCanvas = (node, id) => {
|
|
1832
|
+
if (!hasTag(node, 'canvas')) {
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
const isIgnored = this.app.sanitizer.isObscured(id) || this.app.sanitizer.isHidden(id);
|
|
1836
|
+
if (isIgnored) {
|
|
1837
|
+
if (this.snapshots[id] || this.observers.has(id)) {
|
|
1838
|
+
const observer = this.observers.get(id);
|
|
1839
|
+
if (observer) {
|
|
1840
|
+
observer.disconnect();
|
|
1841
|
+
this.observers.delete(id);
|
|
1842
|
+
}
|
|
1843
|
+
this.cleanupCanvas(id);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
else if (!this.snapshots[id] && !this.observers.has(id)) {
|
|
1847
|
+
this.captureCanvas(node);
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1826
1850
|
this.restartTracking = () => {
|
|
1827
1851
|
this.clear();
|
|
1828
1852
|
this.app.nodes.scanTree(this.captureCanvas);
|
|
@@ -1926,6 +1950,7 @@ class CanvasRecorder {
|
|
|
1926
1950
|
setTimeout(() => {
|
|
1927
1951
|
this.app.nodes.scanTree(this.captureCanvas);
|
|
1928
1952
|
this.app.nodes.attachNodeCallback(this.captureCanvas);
|
|
1953
|
+
this.app.attachResanitizeCallback(this.resanitizeCanvas);
|
|
1929
1954
|
}, 125);
|
|
1930
1955
|
}
|
|
1931
1956
|
sendSnaps(images, canvasId, createdAt) {
|
|
@@ -2810,6 +2835,120 @@ function ConstructedStyleSheets (app) {
|
|
|
2810
2835
|
});
|
|
2811
2836
|
}
|
|
2812
2837
|
|
|
2838
|
+
var SanitizeLevel;
|
|
2839
|
+
(function (SanitizeLevel) {
|
|
2840
|
+
SanitizeLevel[SanitizeLevel["Plain"] = 0] = "Plain";
|
|
2841
|
+
SanitizeLevel[SanitizeLevel["Obscured"] = 1] = "Obscured";
|
|
2842
|
+
SanitizeLevel[SanitizeLevel["Hidden"] = 2] = "Hidden";
|
|
2843
|
+
})(SanitizeLevel || (SanitizeLevel = {}));
|
|
2844
|
+
const stringWiper = (input) => input
|
|
2845
|
+
.trim()
|
|
2846
|
+
.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
|
|
2847
|
+
class Sanitizer {
|
|
2848
|
+
constructor(params) {
|
|
2849
|
+
// Node id -> level. Plain (0) is never stored; a missing entry means Plain.
|
|
2850
|
+
// A map (not the old grow-only Sets) so levels can be raised and lowered.
|
|
2851
|
+
this.levels = new Map();
|
|
2852
|
+
this.app = params.app;
|
|
2853
|
+
const defaultOptions = {
|
|
2854
|
+
obscureTextEmails: true,
|
|
2855
|
+
obscureTextNumbers: false,
|
|
2856
|
+
privateMode: false,
|
|
2857
|
+
domSanitizer: undefined,
|
|
2858
|
+
};
|
|
2859
|
+
this.privateMode = params.options?.privateMode ?? false;
|
|
2860
|
+
this.options = Object.assign(defaultOptions, params.options);
|
|
2861
|
+
}
|
|
2862
|
+
// Pure recomputation of a node's level from the live DOM + parent level.
|
|
2863
|
+
// Reading current state on every call is what lets resanitize() pick up
|
|
2864
|
+
// runtime attribute/domSanitizer changes.
|
|
2865
|
+
computeLevel(node, parentLevel) {
|
|
2866
|
+
if (this.options.privateMode) {
|
|
2867
|
+
if (isElementNode(node) && !hasOpenreplayAttribute(node, 'unmask')) {
|
|
2868
|
+
return SanitizeLevel.Obscured;
|
|
2869
|
+
}
|
|
2870
|
+
if (isTextNode(node) && !hasOpenreplayAttribute(node.parentNode, 'unmask')) {
|
|
2871
|
+
return SanitizeLevel.Obscured;
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
let level = SanitizeLevel.Plain;
|
|
2875
|
+
if (parentLevel >= SanitizeLevel.Obscured ||
|
|
2876
|
+
(isElementNode(node) &&
|
|
2877
|
+
(hasOpenreplayAttribute(node, 'masked') || hasOpenreplayAttribute(node, 'obscured')))) {
|
|
2878
|
+
level = SanitizeLevel.Obscured;
|
|
2879
|
+
}
|
|
2880
|
+
if (parentLevel === SanitizeLevel.Hidden ||
|
|
2881
|
+
(isElementNode(node) &&
|
|
2882
|
+
(hasOpenreplayAttribute(node, 'htmlmasked') || hasOpenreplayAttribute(node, 'hidden')))) {
|
|
2883
|
+
level = SanitizeLevel.Hidden;
|
|
2884
|
+
}
|
|
2885
|
+
if (this.options.domSanitizer !== undefined && isElementNode(node)) {
|
|
2886
|
+
const sanitizeLevel = this.options.domSanitizer(node);
|
|
2887
|
+
if (sanitizeLevel === SanitizeLevel.Obscured && level < SanitizeLevel.Obscured) {
|
|
2888
|
+
level = SanitizeLevel.Obscured;
|
|
2889
|
+
}
|
|
2890
|
+
if (sanitizeLevel === SanitizeLevel.Hidden) {
|
|
2891
|
+
level = SanitizeLevel.Hidden;
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
return level;
|
|
2895
|
+
}
|
|
2896
|
+
getLevel(id) {
|
|
2897
|
+
return this.levels.get(id) ?? SanitizeLevel.Plain;
|
|
2898
|
+
}
|
|
2899
|
+
// Sets a node's level (either direction) and returns the previous one.
|
|
2900
|
+
setLevel(id, level) {
|
|
2901
|
+
const prev = this.getLevel(id);
|
|
2902
|
+
if (level === SanitizeLevel.Plain) {
|
|
2903
|
+
this.levels.delete(id);
|
|
2904
|
+
}
|
|
2905
|
+
else {
|
|
2906
|
+
this.levels.set(id, level);
|
|
2907
|
+
}
|
|
2908
|
+
return prev;
|
|
2909
|
+
}
|
|
2910
|
+
handleNode(id, parentID, node) {
|
|
2911
|
+
const level = this.computeLevel(node, this.getLevel(parentID));
|
|
2912
|
+
// Escalate-only: commits never lower a level, only resanitize/setLevel do.
|
|
2913
|
+
if (level > this.getLevel(id)) {
|
|
2914
|
+
this.setLevel(id, level);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
sanitize(id, data) {
|
|
2918
|
+
if (this.getLevel(id) >= SanitizeLevel.Obscured) {
|
|
2919
|
+
// TODO: is it the best place to put trim() ? Might trimmed spaces be considered in layout in certain cases?
|
|
2920
|
+
return stringWiper(data);
|
|
2921
|
+
}
|
|
2922
|
+
if (this.options.obscureTextNumbers) {
|
|
2923
|
+
data = data.replace(/\d/g, '0');
|
|
2924
|
+
}
|
|
2925
|
+
if (this.options.obscureTextEmails) {
|
|
2926
|
+
data = data.replace(/^\w+([+.-]\w+)*@\w+([.-]\w+)*\.\w{2,3}$/g, (email) => {
|
|
2927
|
+
const [name, domain] = email.split('@');
|
|
2928
|
+
const [domainName, host] = domain.split('.');
|
|
2929
|
+
return `${stars(name)}@${stars(domainName)}.${stars(host)}`;
|
|
2930
|
+
});
|
|
2931
|
+
}
|
|
2932
|
+
return data;
|
|
2933
|
+
}
|
|
2934
|
+
isObscured(id) {
|
|
2935
|
+
return this.getLevel(id) >= SanitizeLevel.Obscured;
|
|
2936
|
+
}
|
|
2937
|
+
isHidden(id) {
|
|
2938
|
+
return this.getLevel(id) === SanitizeLevel.Hidden;
|
|
2939
|
+
}
|
|
2940
|
+
getInnerTextSecure(el) {
|
|
2941
|
+
const id = this.app.nodes.getID(el);
|
|
2942
|
+
if (!id) {
|
|
2943
|
+
return '';
|
|
2944
|
+
}
|
|
2945
|
+
return this.sanitize(id, el.innerText);
|
|
2946
|
+
}
|
|
2947
|
+
clear() {
|
|
2948
|
+
this.levels.clear();
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2813
2952
|
const iconCache = {};
|
|
2814
2953
|
const svgUrlCache = {};
|
|
2815
2954
|
async function parseUseEl(useElement, mode, domParser) {
|
|
@@ -3399,6 +3538,100 @@ class Observer {
|
|
|
3399
3538
|
beforeCommit(this.app.nodes.getID(node));
|
|
3400
3539
|
this.commitNodes(true);
|
|
3401
3540
|
}
|
|
3541
|
+
/**
|
|
3542
|
+
* Re-evaluates sanitization for every tracked node in `root`'s subtree against
|
|
3543
|
+
* the current DOM and re-emits whatever changed. Pass the highest node you
|
|
3544
|
+
* changed (or the document root) so inherited levels propagate correctly.
|
|
3545
|
+
*/
|
|
3546
|
+
resanitizeSubtree(root) {
|
|
3547
|
+
if (!isObservable(root)) {
|
|
3548
|
+
return;
|
|
3549
|
+
}
|
|
3550
|
+
const parent = root.parentNode;
|
|
3551
|
+
const parentId = parent !== null ? this.app.nodes.getID(parent) : undefined;
|
|
3552
|
+
const parentLevel = parentId !== undefined ? this.app.sanitizer.getLevel(parentId) : SanitizeLevel.Plain;
|
|
3553
|
+
this.resanitizeNode(root, parentLevel);
|
|
3554
|
+
}
|
|
3555
|
+
resanitizeNode(node, parentLevel) {
|
|
3556
|
+
if (isIgnored(node)) {
|
|
3557
|
+
return;
|
|
3558
|
+
}
|
|
3559
|
+
const id = this.app.nodes.getID(node);
|
|
3560
|
+
if (id === undefined) {
|
|
3561
|
+
// Untracked (new, or under a hidden ancestor): the live observer handles it.
|
|
3562
|
+
return;
|
|
3563
|
+
}
|
|
3564
|
+
const newLevel = this.app.sanitizer.computeLevel(node, parentLevel);
|
|
3565
|
+
const prevLevel = this.app.sanitizer.getLevel(id);
|
|
3566
|
+
const wasHidden = prevLevel === SanitizeLevel.Hidden;
|
|
3567
|
+
const willHidden = newLevel === SanitizeLevel.Hidden;
|
|
3568
|
+
// Crossing the hidden boundary changes the rendered structure (placeholder vs
|
|
3569
|
+
// real subtree), so rebuild rather than re-emit.
|
|
3570
|
+
if (wasHidden !== willHidden) {
|
|
3571
|
+
this.recreateSubtree(node);
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
if (willHidden) {
|
|
3575
|
+
return;
|
|
3576
|
+
}
|
|
3577
|
+
// Plain <-> Obscured: same structure, only leaf content changes.
|
|
3578
|
+
if (prevLevel !== newLevel) {
|
|
3579
|
+
this.app.sanitizer.setLevel(id, newLevel);
|
|
3580
|
+
this.reemitNode(id, node);
|
|
3581
|
+
}
|
|
3582
|
+
for (let child = node.firstChild; child !== null; child = child.nextSibling) {
|
|
3583
|
+
this.resanitizeNode(child, newLevel);
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
// Destroys the node player-side and re-emits its subtree from scratch (new ids)
|
|
3587
|
+
// so it materializes at the freshly-computed level.
|
|
3588
|
+
recreateSubtree(node) {
|
|
3589
|
+
const id = this.app.nodes.getID(node);
|
|
3590
|
+
if (id === undefined) {
|
|
3591
|
+
return;
|
|
3592
|
+
}
|
|
3593
|
+
this.app.send(RemoveNode(id));
|
|
3594
|
+
this.clearSubtreeRegistration(node);
|
|
3595
|
+
this.bindTree(node);
|
|
3596
|
+
this.commitNodes();
|
|
3597
|
+
}
|
|
3598
|
+
clearSubtreeRegistration(node) {
|
|
3599
|
+
const clearOne = (n) => {
|
|
3600
|
+
const oldId = this.app.nodes.getID(n);
|
|
3601
|
+
if (oldId !== undefined) {
|
|
3602
|
+
this.app.sanitizer.setLevel(oldId, SanitizeLevel.Plain);
|
|
3603
|
+
}
|
|
3604
|
+
this.app.nodes.unregisterNode(n);
|
|
3605
|
+
};
|
|
3606
|
+
const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, {
|
|
3607
|
+
acceptNode: (n) => isIgnored(n) || this.app.nodes.getID(n) === undefined
|
|
3608
|
+
? NodeFilter.FILTER_REJECT
|
|
3609
|
+
: NodeFilter.FILTER_ACCEPT,
|
|
3610
|
+
},
|
|
3611
|
+
// @ts-ignore
|
|
3612
|
+
false);
|
|
3613
|
+
// Collect first, then clear: unregistering mutates the ids the walker reads.
|
|
3614
|
+
const subtree = [];
|
|
3615
|
+
while (walker.nextNode()) {
|
|
3616
|
+
subtree.push(walker.currentNode);
|
|
3617
|
+
}
|
|
3618
|
+
clearOne(node);
|
|
3619
|
+
subtree.forEach(clearOne);
|
|
3620
|
+
}
|
|
3621
|
+
reemitNode(id, node) {
|
|
3622
|
+
if (isTextNode(node)) {
|
|
3623
|
+
const parent = node.parentNode;
|
|
3624
|
+
if (parent !== null && isElementNode(parent)) {
|
|
3625
|
+
// re-runs sanitize() at the level we just set
|
|
3626
|
+
this.sendNodeData(id, parent, node.data);
|
|
3627
|
+
}
|
|
3628
|
+
return;
|
|
3629
|
+
}
|
|
3630
|
+
if (isElementNode(node)) {
|
|
3631
|
+
// inputs/images/canvas re-emit their own payload via registered callbacks
|
|
3632
|
+
this.app.callResanitizeCallbacks(node, id);
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3402
3635
|
disconnect() {
|
|
3403
3636
|
// THEORY S3: a disconnect may discard MutationRecords still queued by the
|
|
3404
3637
|
// browser. takeRecords() drains them — they would be discarded by
|
|
@@ -3715,9 +3948,19 @@ class TopObserver extends Observer {
|
|
|
3715
3948
|
// it has no node_id here
|
|
3716
3949
|
this.app.nodes.callNodeCallbacks(document, true);
|
|
3717
3950
|
}, window.document.documentElement);
|
|
3718
|
-
//
|
|
3719
|
-
|
|
3720
|
-
|
|
3951
|
+
// DEBUG orload
|
|
3952
|
+
const markCandidates = [
|
|
3953
|
+
document.head,
|
|
3954
|
+
document.body,
|
|
3955
|
+
document.body ? document.body.querySelector('div') : null,
|
|
3956
|
+
];
|
|
3957
|
+
for (const candidate of markCandidates) {
|
|
3958
|
+
const markId = candidate ? this.app.nodes.getID(candidate) : undefined;
|
|
3959
|
+
if (markId !== undefined) {
|
|
3960
|
+
this.app.send(SetNodeAttribute(markId, 'orloaded', 'true'));
|
|
3961
|
+
break;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3721
3964
|
}
|
|
3722
3965
|
crossdomainObserve(rootNodeId, frameOder, frameLevel) {
|
|
3723
3966
|
const observer = this;
|
|
@@ -3748,94 +3991,6 @@ class TopObserver extends Observer {
|
|
|
3748
3991
|
}
|
|
3749
3992
|
}
|
|
3750
3993
|
|
|
3751
|
-
var SanitizeLevel;
|
|
3752
|
-
(function (SanitizeLevel) {
|
|
3753
|
-
SanitizeLevel[SanitizeLevel["Plain"] = 0] = "Plain";
|
|
3754
|
-
SanitizeLevel[SanitizeLevel["Obscured"] = 1] = "Obscured";
|
|
3755
|
-
SanitizeLevel[SanitizeLevel["Hidden"] = 2] = "Hidden";
|
|
3756
|
-
})(SanitizeLevel || (SanitizeLevel = {}));
|
|
3757
|
-
const stringWiper = (input) => input
|
|
3758
|
-
.trim()
|
|
3759
|
-
.replace(/[^\f\n\r\t\v\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\s]/g, '*');
|
|
3760
|
-
class Sanitizer {
|
|
3761
|
-
constructor(params) {
|
|
3762
|
-
this.obscured = new Set();
|
|
3763
|
-
this.hidden = new Set();
|
|
3764
|
-
this.app = params.app;
|
|
3765
|
-
const defaultOptions = {
|
|
3766
|
-
obscureTextEmails: true,
|
|
3767
|
-
obscureTextNumbers: false,
|
|
3768
|
-
privateMode: false,
|
|
3769
|
-
domSanitizer: undefined,
|
|
3770
|
-
};
|
|
3771
|
-
this.privateMode = params.options?.privateMode ?? false;
|
|
3772
|
-
this.options = Object.assign(defaultOptions, params.options);
|
|
3773
|
-
}
|
|
3774
|
-
handleNode(id, parentID, node) {
|
|
3775
|
-
if (this.options.privateMode) {
|
|
3776
|
-
if (isElementNode(node) && !hasOpenreplayAttribute(node, 'unmask')) {
|
|
3777
|
-
return this.obscured.add(id);
|
|
3778
|
-
}
|
|
3779
|
-
if (isTextNode(node) && !hasOpenreplayAttribute(node.parentNode, 'unmask')) {
|
|
3780
|
-
return this.obscured.add(id);
|
|
3781
|
-
}
|
|
3782
|
-
}
|
|
3783
|
-
if (this.obscured.has(parentID) ||
|
|
3784
|
-
(isElementNode(node) &&
|
|
3785
|
-
(hasOpenreplayAttribute(node, 'masked') || hasOpenreplayAttribute(node, 'obscured')))) {
|
|
3786
|
-
this.obscured.add(id);
|
|
3787
|
-
}
|
|
3788
|
-
if (this.hidden.has(parentID) ||
|
|
3789
|
-
(isElementNode(node) &&
|
|
3790
|
-
(hasOpenreplayAttribute(node, 'htmlmasked') || hasOpenreplayAttribute(node, 'hidden')))) {
|
|
3791
|
-
this.hidden.add(id);
|
|
3792
|
-
}
|
|
3793
|
-
if (this.options.domSanitizer !== undefined && isElementNode(node)) {
|
|
3794
|
-
const sanitizeLevel = this.options.domSanitizer(node);
|
|
3795
|
-
if (sanitizeLevel === SanitizeLevel.Obscured) {
|
|
3796
|
-
this.obscured.add(id);
|
|
3797
|
-
}
|
|
3798
|
-
if (sanitizeLevel === SanitizeLevel.Hidden) {
|
|
3799
|
-
this.hidden.add(id);
|
|
3800
|
-
}
|
|
3801
|
-
}
|
|
3802
|
-
}
|
|
3803
|
-
sanitize(id, data) {
|
|
3804
|
-
if (this.obscured.has(id)) {
|
|
3805
|
-
// TODO: is it the best place to put trim() ? Might trimmed spaces be considered in layout in certain cases?
|
|
3806
|
-
return stringWiper(data);
|
|
3807
|
-
}
|
|
3808
|
-
if (this.options.obscureTextNumbers) {
|
|
3809
|
-
data = data.replace(/\d/g, '0');
|
|
3810
|
-
}
|
|
3811
|
-
if (this.options.obscureTextEmails) {
|
|
3812
|
-
data = data.replace(/^\w+([+.-]\w+)*@\w+([.-]\w+)*\.\w{2,3}$/g, (email) => {
|
|
3813
|
-
const [name, domain] = email.split('@');
|
|
3814
|
-
const [domainName, host] = domain.split('.');
|
|
3815
|
-
return `${stars(name)}@${stars(domainName)}.${stars(host)}`;
|
|
3816
|
-
});
|
|
3817
|
-
}
|
|
3818
|
-
return data;
|
|
3819
|
-
}
|
|
3820
|
-
isObscured(id) {
|
|
3821
|
-
return this.obscured.has(id);
|
|
3822
|
-
}
|
|
3823
|
-
isHidden(id) {
|
|
3824
|
-
return this.hidden.has(id);
|
|
3825
|
-
}
|
|
3826
|
-
getInnerTextSecure(el) {
|
|
3827
|
-
const id = this.app.nodes.getID(el);
|
|
3828
|
-
if (!id) {
|
|
3829
|
-
return '';
|
|
3830
|
-
}
|
|
3831
|
-
return this.sanitize(id, el.innerText);
|
|
3832
|
-
}
|
|
3833
|
-
clear() {
|
|
3834
|
-
this.obscured.clear();
|
|
3835
|
-
this.hidden.clear();
|
|
3836
|
-
}
|
|
3837
|
-
}
|
|
3838
|
-
|
|
3839
3994
|
const tokenSeparator = '_$_';
|
|
3840
3995
|
class Session {
|
|
3841
3996
|
constructor(params) {
|
|
@@ -4035,7 +4190,7 @@ class Ticker {
|
|
|
4035
4190
|
* this value is injected during build time via rollup
|
|
4036
4191
|
* */
|
|
4037
4192
|
// @ts-ignore
|
|
4038
|
-
const workerBodyFn = "!function(){\"use strict\";class t{constructor(t,s,i,e=10,n=250,h,r){this.onUnauthorised=s,this.onFailure=i,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.onCompress=h,this.pageNo=r,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.lastBatchNum=0,this.inflightKeepaliveBytes=0,this.ingestURL=t+\"/v1/web/i\",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t,s=\"player\",i){if(this.busy||!this.token)this.queue.push({batch:t,dataType:s,split:i});else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t,s,i);else{const e=++this.lastBatchNum;this.sendBatch(t,!1,e,s,i)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t.batch,t.dataType,t.split);else{const s=++this.lastBatchNum;this.sendBatch(t.batch,!1,s,t.dataType,t.split)}else this.busy=!1}retry(t,s,i,e=\"player\",n){if(this.attemptsCount>=this.MAX_ATTEMPTS_COUNT)return void this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`);this.attemptsCount++;const h=new Uint8Array(t);setTimeout((()=>this.sendBatch(h,s,i,e,n)),this.ATTEMPT_TIMEOUT*this.attemptsCount)}sendBatch(t,s,i,e=\"player\",n){var h,r,a;if(0===t.length)return console.error(\"OpenReplay: refusing to send 0-byte batch.\",{batchNum:i,dataType:e,isCompressed:s,batch:t}),this.attemptsCount=0,void this.sendNext();const u=(null!==(h=null==i?void 0:i.toString())&&void 0!==h?h:\"0\").match(/^([^_]+)(?:_([^_]+))?/),o=null!==(r=null==u?void 0:u[1])&&void 0!==r?r:\"0\",l=(null==u?void 0:u[2])?u[2]:\"\";this.busy=!0;const c={Authorization:`Bearer ${this.token}`,DataType:e};if(s&&(c[\"Content-Encoding\"]=\"gzip\"),null===this.token)return void setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`,e,n)}),500);const d=t.length<65536&&this.inflightKeepaliveBytes+t.length<=65536;d&&(this.inflightKeepaliveBytes+=t.length);const p=()=>{d&&(this.inflightKeepaliveBytes-=t.length)},f=t.byteLength;let g=this.ingestURL;g+=`?batch=${null!==(a=this.pageNo)&&void 0!==a?a:0}`,g+=`_${o}`,g+=`_${f}`,g+=\"_\"+(d?\"kyes\":\"kno\"),l&&(g+=`_${l}`),void 0!==n&&(g+=`&split=${n}`),fetch(g,{body:t,method:\"POST\",headers:c,keepalive:d}).then((h=>{var r;if(p(),null===(r=h.body)||void 0===r||r.cancel().catch((()=>{})),401===h.status)return this.busy=!1,void this.onUnauthorised();h.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${h.status}`,e,n):(this.attemptsCount=0,this.sendNext())})).catch((h=>{p(),console.warn(\"OpenReplay:\",h),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${h.message}`,e,n)}))}sendCompressed(t,s=\"player\",i){const e=++this.lastBatchNum;this.sendBatch(t,!0,e,s,i)}sendUncompressed(t,s=\"player\",i){const e=++this.lastBatchNum;this.sendBatch(t,!1,e,s,i)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=new Set([60,61,71,73]),i=new Set([21,22,40,41,44,45,46,47,48,79,83,84,85,87,89,116,120,121,123]),e=new Set([17,23,24,27,28,29,30,42,63,64,78,112,115,124]),n=\"function\"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let e=-1;for(let n=0,h=0,r=0;r!==s;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===s){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){i[e+=1]=240|n>>>18,i[e+=1]=128|n>>>12&63,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n;continue}}n<=127?i[e+=1]=0|n:n<=2047?(i[e+=1]=192|n>>>6,i[e+=1]=128|63&n):(i[e+=1]=224|n>>>12,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n)}return i.subarray(0,e+1)}};class h{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}getCurrentCheckpoint(){return this.checkpointOffset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,s){this.data.set(t,s)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const s=n.encode(t),i=s.byteLength;return!(!this.uint(i)||this.offset+i>this.size)&&(this.data.set(s,this.offset),this.offset+=i,!0)}reset(){this.offset=0,this.checkpointOffset=0}rewind(t,s){t>this.offset||s>this.checkpointOffset||(this.offset=t,this.checkpointOffset=s)}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class r extends h{encode(t){switch(t[0]){case 0:case 11:case 114:case 115:return this.uint(t[1]);case 4:case 44:case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:case 20:case 65:case 70:case 75:case 76:case 77:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:case 24:case 35:case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 12:case 52:case 61:case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:case 17:case 34:case 36:case 50:case 54:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:case 27:case 30:case 41:case 45:case 46:case 43:case 63:case 64:case 79:case 124:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 28:case 29:case 42:case 117:case 118:return this.string(t[1]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.int(t[5]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 55:return this.boolean(t[1]);case 57:case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:case 120:return this.int(t[1]);case 68:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 83:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 84:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6]);case 85:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10])&&this.uint(t[11])&&this.uint(t[12])&&this.uint(t[13])&&this.uint(t[14])&&this.uint(t[15])&&this.uint(t[16])&&this.uint(t[17]);case 87:return this.string(t[1])&&this.int(t[2])&&this.int(t[3]);case 89:return this.string(t[1])&&this.int(t[2])&&this.int(t[3])&&this.int(t[4])&&this.int(t[5])&&this.string(t[6]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10]);case 119:return this.string(t[1])&&this.uint(t[2]);case 121:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 122:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 123:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])}}}class a{constructor(t,s,i){this.bufferSize=t,this.version=s,this.dataType=i,this.sizeBuffer=new Uint8Array(3),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,this.encoder=new r(t)}push(t,s){const i=this.encoder,e=null===this.snap,n=i.getCurrentOffset(),h=i.getCurrentCheckpoint();if(e){const t={pageNo:s.pageNo,firstIndex:s.index,timestamp:s.timestamp,url:s.url,tabId:s.tabId};if(!this.writeHeader(t))return i.rewind(n,h),!1;this.snap=t,this.lastPushedTs=s.timestamp}return 0===t[0]||s.timestamp===this.lastPushedTs||this.writeMessageWithSize([0,s.timestamp])?this.writeMessageWithSize(t)?(this.lastPushedTs=s.timestamp,0!==t[0]&&(this.hasNonTimestamp=!0),!0):(i.rewind(n,h),e&&(this.snap=null),!1):(i.rewind(n,h),!1)}hasContent(){return null!==this.snap&&this.hasNonTimestamp}size(){return null===this.snap?0:this.encoder.getCurrentOffset()}flush(){if(!this.hasContent())return this.reset(),null;const t=this.encoder.flush();return this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,t}reset(){this.encoder.reset(),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0}writeHeader(t){const s=this.encoder,i=[81,this.version,t.pageNo,t.firstIndex,t.timestamp,t.url];return!!s.uint(i[0])&&(!!s.encode(i)&&(!(s.getCurrentOffset()>this.bufferSize)&&(s.checkpoint(),!!this.writeMessageWithSize([0,t.timestamp])&&!!this.writeMessageWithSize([118,t.tabId]))))}writeMessageWithSize(t){const s=this.encoder;if(!s.uint(t[0])||!s.skip(3))return!1;const i=s.getCurrentOffset();if(!s.encode(t))return!1;const e=s.getCurrentOffset(),n=e-i;return n>16777215?(console.warn(\"OpenReplay: max message size overflow.\"),!1):!(e>this.bufferSize)&&(this.writeSizeAt(n,i-3),s.checkpoint(),!0)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}}class u{constructor(t,s,i,e,n,h,r=!1,u){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=e,this.tabId=n,this.onOfflineEnd=h,this.localDebug=r,this.onLocalSave=u,this.nextIndex=0,this.beaconSize=2e5,this.beaconSizeLimit=1e6,this.protocolVersion=1,this.visualSent=!1,this.messagesWritten=!1,this.heldOther=[],this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\"),this.assetBuilder=new a(this.beaconSize,3,\"assets\"),this.devtoolsBuilder=new a(this.beaconSize,4,\"devtools\"),this.analyticsBuilder=new a(this.beaconSize,5,\"analytics\")}initActive(){return 2===this.protocolVersion&&!this.visualSent}playerVersion(){return 2===this.protocolVersion?2:1}currentCtx(){return{pageNo:this.pageNo,index:this.nextIndex,timestamp:this.timestamp,url:this.url,tabId:this.tabId}}setBeaconSizeLimit(t){this.beaconSizeLimit=t}setProtocolVersion(t){if(this.protocolVersion!==t){if(this.protocolVersion=t,2===t&&!this.messagesWritten)return this.playerBuilder.reset(),this.assetBuilder.reset(),this.playerBuilder=new a(this.beaconSizeLimit,this.playerVersion(),\"player\"),void(this.assetBuilder=new a(this.beaconSizeLimit,3,\"assets\"));2===t&&(this.visualSent=!0),this.playerBuilder.reset(),this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\")}}writeMessage(t){if(-1===t[0])return this.finaliseBatch(),this.onOfflineEnd();if(12===t[0]&&\"orloaded\"===t[2])return void(this.initActive()&&this.finalizeVisual());0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]),this.messagesWritten=!0;const s=this.routeMessage(t);this.pushTo(s,t)}routeMessage(t){if(2===this.protocolVersion){const n=t[0];if(s.has(n))return this.assetBuilder;if(i.has(n))return this.devtoolsBuilder;if(e.has(n))return this.analyticsBuilder}return this.playerBuilder}pushTo(t,s){const i=this.currentCtx();if(this.initActive())return void this.pushDuringInit(t,s,i);if(t.push(s,i))return void this.nextIndex++;if(t===this.assetBuilder&&this.flushBuilder(this.playerBuilder),this.flushBuilder(t),t.push(s,i))return void this.nextIndex++;const e=new a(this.beaconSizeLimit,t.version,t.dataType);if(!e.push(s,i))return void console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",s);this.nextIndex++;const n=e.flush();n&&(t===this.assetBuilder&&this.flushBuilder(this.playerBuilder),this.emitBatch(n,t.dataType,!1))}pushDuringInit(t,s,i){const e=t===this.playerBuilder||t===this.assetBuilder;if(t.push(s,i))return this.nextIndex++,void(e&&this.playerBuilder.size()+this.assetBuilder.size()>=this.beaconSizeLimit&&this.finalizeVisual());if(e)return this.finalizeVisual(),void this.pushTo(this.routeMessage(s),s);if(this.flushBuilderToHeld(t),t.push(s,i))return void this.nextIndex++;const n=new a(this.beaconSizeLimit,t.version,t.dataType);if(!n.push(s,i))return void console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",s);this.nextIndex++;const h=n.flush();h&&this.heldOther.push({batch:h,dataType:t.dataType})}flushBuilderToHeld(t){const s=t.flush();s&&this.heldOther.push({batch:s,dataType:t.dataType})}finalizeVisual(t=!1){const s=this.playerBuilder.flush(),i=this.assetBuilder.flush();if(this.visualSent=!0,this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\"),this.assetBuilder=new a(this.beaconSize,3,\"assets\"),s&&i){const e=new Uint8Array(s.length+i.length);e.set(s,0),e.set(i,s.length),this.emitBatch(e,\"visual\",t,s.length)}else s?this.emitBatch(s,\"visual\",t):i&&this.emitBatch(i,\"assets\",t);for(const s of this.heldOther)this.emitBatch(s.batch,s.dataType,t);this.heldOther.length=0,this.flushBuilder(this.devtoolsBuilder,t),this.flushBuilder(this.analyticsBuilder,t)}flushBuilder(t,s=!1){const i=t.flush();return!!i&&(this.emitBatch(i,t.dataType,s),!0)}emitBatch(t,s,i,e){this.localDebug&&this.onLocalSave&&this.onLocalSave(`${s}-${Date.now()}`,t.slice()),this.onBatch(t,i,s,e)}finaliseBatch(t=!1){this.initActive()?this.finalizeVisual(t):(this.flushBuilder(this.playerBuilder,t),this.flushBuilder(this.assetBuilder,t),this.flushBuilder(this.devtoolsBuilder,t),this.flushBuilder(this.analyticsBuilder,t))}clean(){this.playerBuilder.reset(),this.assetBuilder.reset(),this.devtoolsBuilder.reset(),this.analyticsBuilder.reset(),this.heldOther.length=0,this.visualSent=!1,this.messagesWritten=!1}}var o;!function(t){t[t.NotActive=0]=\"NotActive\",t[t.Starting=1]=\"Starting\",t[t.Stopping=2]=\"Stopping\",t[t.Active=3]=\"Active\",t[t.Stopped=4]=\"Stopped\"}(o||(o={}));let l=null,c=null,d=o.NotActive;function p(t){c&&c.finaliseBatch(t)}function f(){return new Promise((t=>{d=o.Stopping,null!==y&&(clearInterval(y),y=null),c&&(c.clean(),c=null),l&&(l.clean(),setTimeout((()=>{l=null}),20)),setTimeout((()=>{d=o.NotActive,t(null)}),100)}))}function g(){[o.Stopped,o.Stopping].includes(d)||(postMessage(\"a_stop\"),f().then((()=>{postMessage(\"a_start\")})))}let m,y=null;self.onmessage=({data:s})=>{var i;if(\"stop\"===s)return p(),void f().then((()=>{d=o.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!l)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&l.sendCompressed(s.batch,s.dataType,s.split)}if(\"uncompressed\"===s.type){if(!l)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&l.sendUncompressed(s.batch,s.dataType,s.split)}return\"start\"===s.type?(d=o.Starting,l=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),f()}(t)}),s.connAttemptCount,s.connAttemptGap,((t,s,i)=>{postMessage({type:\"compress\",batch:t,dataType:s,split:i},[t.buffer])}),s.pageNo),c=new u(s.pageNo,s.timestamp,s.url,((t,s,i=\"player\",e)=>{l&&(s?l.sendUncompressed(t,i,e):l.push(t,i,e))}),s.tabId,(()=>postMessage({type:\"queue_empty\"})),null!==(i=s.localDebug)&&void 0!==i&&i,((t,s)=>{postMessage({type:\"local_save\",name:t,batch:s},[s.buffer])})),null===y&&(y=setInterval(p,3e4)),d=o.Active):\"auth\"===s.type?l?c?(l.authorise(s.token),s.beaconSizeLimit&&c.setBeaconSizeLimit(s.beaconSizeLimit),void(s.protocolVersion&&c.setProtocolVersion(s.protocolVersion))):(console.debug(\"OR WebWorker: writer not initialised. Received auth.\"),void g()):(console.debug(\"OR WebWorker: sender not initialised. Received auth.\"),void g()):void 0}if(c){const t=c;s.forEach((s=>{55===s[0]&&(s[1]?m=setTimeout((()=>g()),18e5):clearTimeout(m)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else p(!0);else p()}}();\n";
|
|
4193
|
+
const workerBodyFn = "!function(){\"use strict\";class t{constructor(t,s,i,e=10,n=250,h,r){this.onUnauthorised=s,this.onFailure=i,this.MAX_ATTEMPTS_COUNT=e,this.ATTEMPT_TIMEOUT=n,this.onCompress=h,this.pageNo=r,this.attemptsCount=0,this.busy=!1,this.queue=[],this.token=null,this.lastBatchNum=0,this.inflightKeepaliveBytes=0,this.ingestURL=t+\"/v1/web/i\",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t,s=\"player\"){if(this.busy||!this.token)this.queue.push({batch:t,dataType:s});else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t,s);else{const i=++this.lastBatchNum;this.sendBatch(t,!1,i,s)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t.batch,t.dataType);else{const s=++this.lastBatchNum;this.sendBatch(t.batch,!1,s,t.dataType)}else this.busy=!1}retry(t,s,i,e=\"player\"){if(this.attemptsCount>=this.MAX_ATTEMPTS_COUNT)return void this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`);this.attemptsCount++;const n=new Uint8Array(t);setTimeout((()=>this.sendBatch(n,s,i,e)),this.ATTEMPT_TIMEOUT*this.attemptsCount)}sendBatch(t,s,i,e=\"player\"){var n,h,r;if(0===t.length)return console.error(\"OpenReplay: refusing to send 0-byte batch.\",{batchNum:i,dataType:e,isCompressed:s,batch:t}),this.attemptsCount=0,void this.sendNext();const a=(null!==(n=null==i?void 0:i.toString())&&void 0!==n?n:\"0\").match(/^([^_]+)(?:_([^_]+))?/),u=null!==(h=null==a?void 0:a[1])&&void 0!==h?h:\"0\",o=(null==a?void 0:a[2])?a[2]:\"\";this.busy=!0;const c={Authorization:`Bearer ${this.token}`,DataType:e};if(s&&(c[\"Content-Encoding\"]=\"gzip\"),null===this.token)return void setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`,e)}),500);const l=t.length<65536&&this.inflightKeepaliveBytes+t.length<=65536;l&&(this.inflightKeepaliveBytes+=t.length);const d=()=>{l&&(this.inflightKeepaliveBytes-=t.length)},p=t.byteLength;let f=this.ingestURL;f+=`?batch=${null!==(r=this.pageNo)&&void 0!==r?r:0}`,f+=`_${u}`,f+=`_${p}`,f+=\"_\"+(l?\"kyes\":\"kno\"),o&&(f+=`_${o}`),fetch(f,{body:t,method:\"POST\",headers:c,keepalive:l}).then((n=>{var h;if(d(),null===(h=n.body)||void 0===h||h.cancel().catch((()=>{})),401===n.status)return this.busy=!1,void this.onUnauthorised();n.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${n.status}`,e):(this.attemptsCount=0,this.sendNext())})).catch((n=>{d(),console.warn(\"OpenReplay:\",n),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${n.message}`,e)}))}sendCompressed(t,s=\"player\"){const i=++this.lastBatchNum;this.sendBatch(t,!0,i,s)}sendUncompressed(t,s=\"player\"){const i=++this.lastBatchNum;this.sendBatch(t,!1,i,s)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=new Set([60,61,71,73]),i=new Set([21,22,40,41,44,45,46,47,48,79,83,84,85,87,89,116,120,121,123]),e=new Set([17,23,24,27,28,29,30,42,63,64,78,112,115,124]),n=\"function\"==typeof TextEncoder?new TextEncoder:{encode(t){const s=t.length,i=new Uint8Array(3*s);let e=-1;for(let n=0,h=0,r=0;r!==s;){if(n=t.charCodeAt(r),r+=1,n>=55296&&n<=56319){if(r===s){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;break}if(h=t.charCodeAt(r),!(h>=56320&&h<=57343)){i[e+=1]=239,i[e+=1]=191,i[e+=1]=189;continue}if(n=1024*(n-55296)+h-56320+65536,r+=1,n>65535){i[e+=1]=240|n>>>18,i[e+=1]=128|n>>>12&63,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n;continue}}n<=127?i[e+=1]=0|n:n<=2047?(i[e+=1]=192|n>>>6,i[e+=1]=128|63&n):(i[e+=1]=224|n>>>12,i[e+=1]=128|n>>>6&63,i[e+=1]=128|63&n)}return i.subarray(0,e+1)}};class h{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}getCurrentCheckpoint(){return this.checkpointOffset}checkpoint(){this.checkpointOffset=this.offset}get isEmpty(){return 0===this.offset}skip(t){return this.offset+=t,this.offset<=this.size}set(t,s){this.data.set(t,s)}boolean(t){return this.data[this.offset++]=+t,this.offset<=this.size}uint(t){for((t<0||t>Number.MAX_SAFE_INTEGER)&&(t=0);t>=128;)this.data[this.offset++]=t%256|128,t=Math.floor(t/128);return this.data[this.offset++]=t,this.offset<=this.size}int(t){return t=Math.round(t),this.uint(t>=0?2*t:-2*t-1)}string(t){const s=n.encode(t),i=s.byteLength;return!(!this.uint(i)||this.offset+i>this.size)&&(this.data.set(s,this.offset),this.offset+=i,!0)}reset(){this.offset=0,this.checkpointOffset=0}rewind(t,s){t>this.offset||s>this.checkpointOffset||(this.offset=t,this.checkpointOffset=s)}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class r extends h{encode(t){switch(t[0]){case 0:case 11:case 114:case 115:return this.uint(t[1]);case 4:case 44:case 47:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3]);case 5:case 20:case 65:case 70:case 75:case 76:case 77:return this.uint(t[1])&&this.uint(t[2]);case 6:return this.int(t[1])&&this.int(t[2]);case 7:return!0;case 8:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.string(t[4])&&this.boolean(t[5]);case 9:case 10:case 24:case 35:case 51:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3]);case 12:case 52:case 61:case 71:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3]);case 13:case 14:case 17:case 34:case 36:case 50:case 54:return this.uint(t[1])&&this.string(t[2]);case 16:return this.uint(t[1])&&this.int(t[2])&&this.int(t[3]);case 18:return this.uint(t[1])&&this.string(t[2])&&this.int(t[3]);case 19:return this.uint(t[1])&&this.boolean(t[2]);case 21:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8]);case 22:case 27:case 30:case 41:case 45:case 46:case 43:case 63:case 64:case 79:case 124:return this.string(t[1])&&this.string(t[2]);case 23:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 28:case 29:case 42:case 117:case 118:return this.string(t[1]);case 40:return this.string(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 48:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.int(t[5]);case 49:return this.int(t[1])&&this.int(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 53:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8]);case 55:return this.boolean(t[1]);case 57:case 60:return this.uint(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 58:case 120:return this.int(t[1]);case 68:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6]);case 69:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3])&&this.string(t[4]);case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 78:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4]);case 81:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.int(t[4])&&this.string(t[5]);case 83:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.string(t[5])&&this.uint(t[6])&&this.uint(t[7])&&this.uint(t[8])&&this.uint(t[9]);case 84:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6]);case 85:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10])&&this.uint(t[11])&&this.uint(t[12])&&this.uint(t[13])&&this.uint(t[14])&&this.uint(t[15])&&this.uint(t[16])&&this.uint(t[17]);case 87:return this.string(t[1])&&this.int(t[2])&&this.int(t[3]);case 89:return this.string(t[1])&&this.int(t[2])&&this.int(t[3])&&this.int(t[4])&&this.int(t[5])&&this.string(t[6]);case 112:return this.uint(t[1])&&this.string(t[2])&&this.boolean(t[3])&&this.string(t[4])&&this.int(t[5])&&this.int(t[6]);case 113:return this.uint(t[1])&&this.uint(t[2])&&this.string(t[3]);case 116:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.string(t[7])&&this.string(t[8])&&this.uint(t[9])&&this.boolean(t[10]);case 119:return this.string(t[1])&&this.uint(t[2]);case 121:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.uint(t[4]);case 122:return this.string(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);case 123:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])}}}class a{constructor(t,s,i){this.bufferSize=t,this.version=s,this.dataType=i,this.sizeBuffer=new Uint8Array(3),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,this.encoder=new r(t)}push(t,s){const i=this.encoder,e=null===this.snap,n=i.getCurrentOffset(),h=i.getCurrentCheckpoint();if(e){const t={pageNo:s.pageNo,firstIndex:s.index,timestamp:s.timestamp,url:s.url,tabId:s.tabId};if(!this.writeHeader(t))return i.rewind(n,h),!1;this.snap=t,this.lastPushedTs=s.timestamp}return 0===t[0]||s.timestamp===this.lastPushedTs||this.writeMessageWithSize([0,s.timestamp])?this.writeMessageWithSize(t)?(this.lastPushedTs=s.timestamp,0!==t[0]&&(this.hasNonTimestamp=!0),!0):(i.rewind(n,h),e&&(this.snap=null),!1):(i.rewind(n,h),!1)}hasContent(){return null!==this.snap&&this.hasNonTimestamp}flush(){if(!this.hasContent())return this.reset(),null;const t=this.encoder.flush();return this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0,t}reset(){this.encoder.reset(),this.snap=null,this.hasNonTimestamp=!1,this.lastPushedTs=0}writeHeader(t){const s=this.encoder,i=[81,this.version,t.pageNo,t.firstIndex,t.timestamp,t.url];return!!s.uint(i[0])&&(!!s.encode(i)&&(!(s.getCurrentOffset()>this.bufferSize)&&(s.checkpoint(),!!this.writeMessageWithSize([0,t.timestamp])&&!!this.writeMessageWithSize([118,t.tabId]))))}writeMessageWithSize(t){const s=this.encoder;if(!s.uint(t[0])||!s.skip(3))return!1;const i=s.getCurrentOffset();if(!s.encode(t))return!1;const e=s.getCurrentOffset(),n=e-i;return n>16777215?(console.warn(\"OpenReplay: max message size overflow.\"),!1):!(e>this.bufferSize)&&(this.writeSizeAt(n,i-3),s.checkpoint(),!0)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}}class u{constructor(t,s,i,e,n,h,r=!1,u){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=e,this.tabId=n,this.onOfflineEnd=h,this.localDebug=r,this.onLocalSave=u,this.nextIndex=0,this.beaconSize=2e5,this.beaconSizeLimit=1e6,this.protocolVersion=1,this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\"),this.assetBuilder=new a(this.beaconSize,3,\"assets\"),this.devtoolsBuilder=new a(this.beaconSize,4,\"devtools\"),this.analyticsBuilder=new a(this.beaconSize,5,\"analytics\")}playerVersion(){return 2===this.protocolVersion?2:1}currentCtx(){return{pageNo:this.pageNo,index:this.nextIndex,timestamp:this.timestamp,url:this.url,tabId:this.tabId}}setBeaconSizeLimit(t){this.beaconSizeLimit=t}setProtocolVersion(t){this.protocolVersion!==t&&(this.protocolVersion=t,this.playerBuilder.reset(),this.playerBuilder=new a(this.beaconSize,this.playerVersion(),\"player\"))}writeMessage(t){if(-1===t[0])return this.finaliseBatch(),this.onOfflineEnd();0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]);const s=this.routeMessage(t);this.pushTo(s,t)}routeMessage(t){if(2===this.protocolVersion){const n=t[0];if(s.has(n))return this.assetBuilder;if(i.has(n))return this.devtoolsBuilder;if(e.has(n))return this.analyticsBuilder}return this.playerBuilder}pushTo(t,s){const i=this.currentCtx();if(t.push(s,i))return void this.nextIndex++;if(t===this.assetBuilder&&this.flushBuilder(this.playerBuilder),this.flushBuilder(t),t.push(s,i))return void this.nextIndex++;const e=new a(this.beaconSizeLimit,t.version,t.dataType);if(!e.push(s,i))return void console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",s);this.nextIndex++;const n=e.flush();n&&(t===this.assetBuilder&&this.flushBuilder(this.playerBuilder),this.emitBatch(n,t.dataType,!1))}flushBuilder(t,s=!1){const i=t.flush();return!!i&&(this.emitBatch(i,t.dataType,s),!0)}emitBatch(t,s,i){this.localDebug&&this.onLocalSave&&this.onLocalSave(`${s}-${Date.now()}`,t.slice()),this.onBatch(t,i,s)}finaliseBatch(t=!1){this.flushBuilder(this.playerBuilder,t),this.flushBuilder(this.assetBuilder,t),this.flushBuilder(this.devtoolsBuilder,t),this.flushBuilder(this.analyticsBuilder,t)}clean(){this.playerBuilder.reset(),this.assetBuilder.reset(),this.devtoolsBuilder.reset(),this.analyticsBuilder.reset()}}var o;!function(t){t[t.NotActive=0]=\"NotActive\",t[t.Starting=1]=\"Starting\",t[t.Stopping=2]=\"Stopping\",t[t.Active=3]=\"Active\",t[t.Stopped=4]=\"Stopped\"}(o||(o={}));let c=null,l=null,d=o.NotActive;function p(t){l&&l.finaliseBatch(t)}function f(){return new Promise((t=>{d=o.Stopping,null!==y&&(clearInterval(y),y=null),l&&(l.clean(),l=null),c&&(c.clean(),setTimeout((()=>{c=null}),20)),setTimeout((()=>{d=o.NotActive,t(null)}),100)}))}function g(){[o.Stopped,o.Stopping].includes(d)||(postMessage(\"a_stop\"),f().then((()=>{postMessage(\"a_start\")})))}let m,y=null;self.onmessage=({data:s})=>{var i;if(\"stop\"===s)return p(),void f().then((()=>{d=o.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!c)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&c.sendCompressed(s.batch,s.dataType)}if(\"uncompressed\"===s.type){if(!c)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&c.sendUncompressed(s.batch,s.dataType)}return\"start\"===s.type?(d=o.Starting,c=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),f()}(t)}),s.connAttemptCount,s.connAttemptGap,((t,s)=>{postMessage({type:\"compress\",batch:t,dataType:s},[t.buffer])}),s.pageNo),l=new u(s.pageNo,s.timestamp,s.url,((t,s,i=\"player\")=>{c&&(s?c.sendUncompressed(t,i):c.push(t,i))}),s.tabId,(()=>postMessage({type:\"queue_empty\"})),null!==(i=s.localDebug)&&void 0!==i&&i,((t,s)=>{postMessage({type:\"local_save\",name:t,batch:s},[s.buffer])})),null===y&&(y=setInterval(p,3e4)),d=o.Active):\"auth\"===s.type?c?l?(c.authorise(s.token),s.beaconSizeLimit&&l.setBeaconSizeLimit(s.beaconSizeLimit),void(s.protocolVersion&&l.setProtocolVersion(s.protocolVersion))):(console.debug(\"OR WebWorker: writer not initialised. Received auth.\"),void g()):(console.debug(\"OR WebWorker: sender not initialised. Received auth.\"),void g()):void 0}if(l){const t=l;s.forEach((s=>{55===s[0]&&(s[1]?m=setTimeout((()=>g()),18e5):clearTimeout(m)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else p(!0);else p()}}();\n";
|
|
4039
4194
|
const CANCELED = 'canceled';
|
|
4040
4195
|
const bufferStorageKey = 'or_buffer_1';
|
|
4041
4196
|
const UnsuccessfulStart = (reason) => ({ reason, success: false });
|
|
@@ -4087,6 +4242,8 @@ class App {
|
|
|
4087
4242
|
constructor(projectKey, sessionToken, options, signalError, insideIframe) {
|
|
4088
4243
|
this.signalError = signalError;
|
|
4089
4244
|
this.insideIframe = insideIframe;
|
|
4245
|
+
// Registered by input/img/canvas to re-emit a node when its level changes.
|
|
4246
|
+
this.resanitizeCallbacks = [];
|
|
4090
4247
|
this.messages = [];
|
|
4091
4248
|
/**
|
|
4092
4249
|
* we need 2 buffers, so we don't lose anything
|
|
@@ -4098,7 +4255,7 @@ class App {
|
|
|
4098
4255
|
this.stopCallbacks = [];
|
|
4099
4256
|
this.commitCallbacks = [];
|
|
4100
4257
|
this.activityState = ActivityState.NotActive;
|
|
4101
|
-
this.version = '18.0.14
|
|
4258
|
+
this.version = '18.0.14'; // TODO: version compatability check inside each plugin.
|
|
4102
4259
|
this.socketMode = false;
|
|
4103
4260
|
this.compressionThreshold = 24 * 1000;
|
|
4104
4261
|
this.bc = null;
|
|
@@ -4629,6 +4786,26 @@ class App {
|
|
|
4629
4786
|
this.restartCanvasTracking = () => {
|
|
4630
4787
|
this.canvasRecorder?.restartTracking();
|
|
4631
4788
|
};
|
|
4789
|
+
this.attachResanitizeCallback = (cb) => {
|
|
4790
|
+
this.resanitizeCallbacks.push(cb);
|
|
4791
|
+
};
|
|
4792
|
+
this.callResanitizeCallbacks = (node, id) => {
|
|
4793
|
+
this.resanitizeCallbacks.forEach((cb) => cb(node, id));
|
|
4794
|
+
};
|
|
4795
|
+
this.resanitize = (el) => {
|
|
4796
|
+
const root = el ?? (IN_BROWSER ? document.documentElement : undefined);
|
|
4797
|
+
if (!root) {
|
|
4798
|
+
return;
|
|
4799
|
+
}
|
|
4800
|
+
this.observer.resanitizeSubtree(root);
|
|
4801
|
+
};
|
|
4802
|
+
this.checkSanitization = (el) => {
|
|
4803
|
+
const id = this.nodes.getID(el);
|
|
4804
|
+
if (id === undefined) {
|
|
4805
|
+
return undefined;
|
|
4806
|
+
}
|
|
4807
|
+
return this.sanitizer.getLevel(id);
|
|
4808
|
+
};
|
|
4632
4809
|
this.flushBuffer = async (buffer) => {
|
|
4633
4810
|
return new Promise((res, reject) => {
|
|
4634
4811
|
if (buffer.length === 0) {
|
|
@@ -5031,8 +5208,6 @@ class App {
|
|
|
5031
5208
|
else if (data.type === 'compress') {
|
|
5032
5209
|
const batch = data.batch;
|
|
5033
5210
|
const dataType = data.dataType;
|
|
5034
|
-
// split is a decompressed-byte offset, so it survives gzip unchanged.
|
|
5035
|
-
const split = data.split;
|
|
5036
5211
|
const batchSize = batch.byteLength;
|
|
5037
5212
|
const hasCompressionAPI = 'CompressionStream' in globalThis;
|
|
5038
5213
|
if (batchSize > this.compressionThreshold && hasCompressionAPI) {
|
|
@@ -5045,16 +5220,15 @@ class App {
|
|
|
5045
5220
|
type: 'compressed',
|
|
5046
5221
|
batch: new Uint8Array(compressedBuffer),
|
|
5047
5222
|
dataType,
|
|
5048
|
-
split,
|
|
5049
5223
|
});
|
|
5050
5224
|
})
|
|
5051
5225
|
.catch((err) => {
|
|
5052
5226
|
this.debug.error('Openreplay compression error:', err);
|
|
5053
|
-
this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType
|
|
5227
|
+
this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType });
|
|
5054
5228
|
});
|
|
5055
5229
|
}
|
|
5056
5230
|
else {
|
|
5057
|
-
this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType
|
|
5231
|
+
this.worker?.postMessage({ type: 'uncompressed', batch: batch, dataType });
|
|
5058
5232
|
}
|
|
5059
5233
|
}
|
|
5060
5234
|
else if (data.type === 'local_save') {
|
|
@@ -6324,6 +6498,12 @@ function Img (app) {
|
|
|
6324
6498
|
sendImgAttrs(node);
|
|
6325
6499
|
observer.observe(node, { attributes: true, attributeFilter: ['src', 'srcset'] });
|
|
6326
6500
|
});
|
|
6501
|
+
// On a runtime level change, re-evaluate placeholder vs real src for this image.
|
|
6502
|
+
app.attachResanitizeCallback((node) => {
|
|
6503
|
+
if (hasTag(node, 'img')) {
|
|
6504
|
+
sendImgAttrs(node);
|
|
6505
|
+
}
|
|
6506
|
+
});
|
|
6327
6507
|
}
|
|
6328
6508
|
|
|
6329
6509
|
const INPUT_TYPES = [
|
|
@@ -6510,6 +6690,13 @@ function Input (app, opts) {
|
|
|
6510
6690
|
}
|
|
6511
6691
|
app.send(InputChange(id, value, mask !== 0, label, hesitationTime, inputTime));
|
|
6512
6692
|
}
|
|
6693
|
+
// Re-emit a field's value when its sanitization level changes at runtime.
|
|
6694
|
+
// getInputValue() reads the current level, so re-sending applies the new mask.
|
|
6695
|
+
app.attachResanitizeCallback((node, id) => {
|
|
6696
|
+
if (isTextFieldElement(node) || hasTag(node, 'select')) {
|
|
6697
|
+
sendInputValue(id, node);
|
|
6698
|
+
}
|
|
6699
|
+
});
|
|
6513
6700
|
app.nodes.attachNodeCallback(app.safe((node) => {
|
|
6514
6701
|
const id = app.nodes.getID(node);
|
|
6515
6702
|
if (id === undefined) {
|
|
@@ -9400,7 +9587,7 @@ class ConstantProperties {
|
|
|
9400
9587
|
user_id: this.user_id,
|
|
9401
9588
|
distinct_id: this.deviceId,
|
|
9402
9589
|
sdk_edition: 'web',
|
|
9403
|
-
sdk_version: '18.0.14
|
|
9590
|
+
sdk_version: '18.0.14',
|
|
9404
9591
|
timezone: getUTCOffsetString(),
|
|
9405
9592
|
search_engine: this.searchEngine,
|
|
9406
9593
|
};
|
|
@@ -10102,7 +10289,7 @@ class API {
|
|
|
10102
10289
|
this.signalStartIssue = (reason, missingApi) => {
|
|
10103
10290
|
const doNotTrack = this.checkDoNotTrack();
|
|
10104
10291
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
10105
|
-
trackerVersion: '18.0.14
|
|
10292
|
+
trackerVersion: '18.0.14',
|
|
10106
10293
|
projectKey: this.options.projectKey,
|
|
10107
10294
|
doNotTrack,
|
|
10108
10295
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|
|
@@ -10114,6 +10301,31 @@ class API {
|
|
|
10114
10301
|
}
|
|
10115
10302
|
this.app.restartCanvasTracking();
|
|
10116
10303
|
};
|
|
10304
|
+
/**
|
|
10305
|
+
* Re-evaluates sanitization against the current DOM and re-emits whatever
|
|
10306
|
+
* changed, updating already-recorded nodes mid-session. Call after toggling
|
|
10307
|
+
* `data-openreplay-*` attributes or after changing whatever your `domSanitizer`
|
|
10308
|
+
* keys on (class/id/etc).
|
|
10309
|
+
*
|
|
10310
|
+
* @param el - the highest node you changed; omit to re-scan the whole document;
|
|
10311
|
+
* scanning the entire doc is O(dom size)
|
|
10312
|
+
* */
|
|
10313
|
+
this.resanitize = (el) => {
|
|
10314
|
+
if (this.app === null) {
|
|
10315
|
+
return;
|
|
10316
|
+
}
|
|
10317
|
+
this.app.resanitize(el);
|
|
10318
|
+
};
|
|
10319
|
+
/**
|
|
10320
|
+
* Returns the sanitization level the tracker currently has for a node
|
|
10321
|
+
* (0 = Plain, 1 = Obscured, 2 = Hidden), or undefined if it isn't tracked.
|
|
10322
|
+
* */
|
|
10323
|
+
this.checkSanitization = (el) => {
|
|
10324
|
+
if (this.app === null) {
|
|
10325
|
+
return undefined;
|
|
10326
|
+
}
|
|
10327
|
+
return this.app.checkSanitization(el);
|
|
10328
|
+
};
|
|
10117
10329
|
this.getSessionURL = (options) => {
|
|
10118
10330
|
if (this.app === null) {
|
|
10119
10331
|
return undefined;
|
|
@@ -10127,7 +10339,10 @@ class API {
|
|
|
10127
10339
|
}
|
|
10128
10340
|
};
|
|
10129
10341
|
this.identify = this.setUserID;
|
|
10130
|
-
|
|
10342
|
+
// Delegates at call time: `this.analytics` is assigned in the constructor body,
|
|
10343
|
+
// which runs AFTER field initializers, so binding it here directly would always
|
|
10344
|
+
// capture `undefined`.
|
|
10345
|
+
this.track = (eventName, properties, options) => this.analytics?.track(eventName, properties, options);
|
|
10131
10346
|
this.userID = (id) => {
|
|
10132
10347
|
deprecationWarn("'userID' method", "'setUserID' method", '/');
|
|
10133
10348
|
this.setUserID(id);
|
|
@@ -10510,12 +10725,20 @@ class TrackerSingleton {
|
|
|
10510
10725
|
constructor() {
|
|
10511
10726
|
this.instance = null;
|
|
10512
10727
|
this.isConfigured = false;
|
|
10728
|
+
this.setUserID = (id) => {
|
|
10729
|
+
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10730
|
+
return;
|
|
10731
|
+
}
|
|
10732
|
+
this.instance.setUserID(id);
|
|
10733
|
+
};
|
|
10513
10734
|
this.identify = this.setUserID;
|
|
10514
10735
|
this.track = (eventName, properties, options) => {
|
|
10515
10736
|
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10516
10737
|
return;
|
|
10517
10738
|
}
|
|
10518
|
-
|
|
10739
|
+
// Route through analytics directly: Tracker.track is bound to analytics?.track
|
|
10740
|
+
// at field-init time (before analytics exists), so it is always undefined.
|
|
10741
|
+
this.instance.analytics?.track(eventName, properties, options);
|
|
10519
10742
|
};
|
|
10520
10743
|
}
|
|
10521
10744
|
/**
|
|
@@ -10545,11 +10768,16 @@ class TrackerSingleton {
|
|
|
10545
10768
|
if (!IN_BROWSER) {
|
|
10546
10769
|
return Promise.resolve({ success: false, reason: 'Not in browser environment' });
|
|
10547
10770
|
}
|
|
10548
|
-
if (!this.ensureConfigured()) {
|
|
10771
|
+
if (!this.ensureConfigured() || !this.instance) {
|
|
10549
10772
|
return Promise.resolve({ success: false, reason: 'Tracker not configured' });
|
|
10550
10773
|
}
|
|
10551
|
-
|
|
10552
|
-
|
|
10774
|
+
// Tracker.start() rejects (instead of resolving {success:false}) when the
|
|
10775
|
+
// underlying app failed to initialise (non-https, missing api, doNotTrack,
|
|
10776
|
+
// already initialised...). Normalize so callers always get {success, reason}.
|
|
10777
|
+
return this.instance.start(startOpts).catch((reason) => ({
|
|
10778
|
+
success: false,
|
|
10779
|
+
reason: typeof reason === 'string' ? reason : String(reason),
|
|
10780
|
+
}));
|
|
10553
10781
|
}
|
|
10554
10782
|
/**
|
|
10555
10783
|
* Stop the session and return sessionHash
|
|
@@ -10561,21 +10789,9 @@ class TrackerSingleton {
|
|
|
10561
10789
|
}
|
|
10562
10790
|
return this.instance.stop();
|
|
10563
10791
|
}
|
|
10564
|
-
setUserID(id) {
|
|
10565
|
-
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10566
|
-
return;
|
|
10567
|
-
}
|
|
10568
|
-
this.instance.setUserID(id);
|
|
10569
|
-
}
|
|
10570
10792
|
get analytics() {
|
|
10571
|
-
|
|
10572
|
-
return this.instance.analytics;
|
|
10573
|
-
}
|
|
10574
|
-
else {
|
|
10575
|
-
return null;
|
|
10576
|
-
}
|
|
10793
|
+
return this.instance?.analytics ?? null;
|
|
10577
10794
|
}
|
|
10578
|
-
;
|
|
10579
10795
|
/**
|
|
10580
10796
|
* Set metadata for the current session
|
|
10581
10797
|
*
|
|
@@ -10750,6 +10966,51 @@ class TrackerSingleton {
|
|
|
10750
10966
|
}
|
|
10751
10967
|
return this.instance.getTabId();
|
|
10752
10968
|
}
|
|
10969
|
+
/**
|
|
10970
|
+
* Re-evaluates sanitization against the current DOM and re-emits whatever
|
|
10971
|
+
* changed, updating already-recorded nodes mid-session. Call after toggling
|
|
10972
|
+
* `data-openreplay-*` attributes or after changing whatever your `domSanitizer`
|
|
10973
|
+
* keys on (class/id/etc).
|
|
10974
|
+
*
|
|
10975
|
+
* @param el - the highest node you changed; omit to re-scan the whole document.
|
|
10976
|
+
* */
|
|
10977
|
+
resanitize(el) {
|
|
10978
|
+
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10979
|
+
return;
|
|
10980
|
+
}
|
|
10981
|
+
return this.instance.resanitize(el);
|
|
10982
|
+
}
|
|
10983
|
+
/**
|
|
10984
|
+
* Returns the sanitization level the tracker currently has for a node
|
|
10985
|
+
* (0 = Plain, 1 = Obscured, 2 = Hidden), or undefined if it isn't tracked.
|
|
10986
|
+
* */
|
|
10987
|
+
checkSanitization(el) {
|
|
10988
|
+
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10989
|
+
return undefined;
|
|
10990
|
+
}
|
|
10991
|
+
return this.instance.checkSanitization(el);
|
|
10992
|
+
}
|
|
10993
|
+
incident(options) {
|
|
10994
|
+
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
10995
|
+
return;
|
|
10996
|
+
}
|
|
10997
|
+
this.instance.incident(options);
|
|
10998
|
+
}
|
|
10999
|
+
/**
|
|
11000
|
+
* Use custom token for analytics events without session recording
|
|
11001
|
+
* */
|
|
11002
|
+
setAnalyticsToken(token) {
|
|
11003
|
+
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
11004
|
+
return;
|
|
11005
|
+
}
|
|
11006
|
+
this.instance.setAnalyticsToken(token);
|
|
11007
|
+
}
|
|
11008
|
+
getAnalyticsToken() {
|
|
11009
|
+
if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
|
|
11010
|
+
return undefined;
|
|
11011
|
+
}
|
|
11012
|
+
return this.instance.getAnalyticsToken();
|
|
11013
|
+
}
|
|
10753
11014
|
}
|
|
10754
11015
|
const tracker = new TrackerSingleton();
|
|
10755
11016
|
|