@oxvo/browser 7.3.0 → 7.3.4
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/README.md +32 -0
- package/dist/cjs/entry.js +1452 -307
- package/dist/cjs/entry.js.map +1 -1
- package/dist/cjs/index.js +1452 -307
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/main/app/canvas.d.ts +5 -0
- package/dist/cjs/main/app/cross_domain_iframe.d.ts +44 -0
- package/dist/cjs/main/app/index.d.ts +53 -13
- package/dist/cjs/main/app/messages.gen.d.ts +2 -0
- package/dist/cjs/main/app/session.d.ts +1 -0
- package/dist/cjs/main/entry.d.ts +5 -5
- package/dist/cjs/main/index.d.ts +1 -1
- package/dist/cjs/main/modules/analytics/index.d.ts +5 -0
- package/dist/cjs/package.json +1 -0
- package/dist/lib/entry.js +1452 -307
- package/dist/lib/entry.js.map +1 -1
- package/dist/lib/index.js +1452 -307
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/main/app/canvas.d.ts +5 -0
- package/dist/lib/main/app/cross_domain_iframe.d.ts +44 -0
- package/dist/lib/main/app/index.d.ts +53 -13
- package/dist/lib/main/app/messages.gen.d.ts +2 -0
- package/dist/lib/main/app/session.d.ts +1 -0
- package/dist/lib/main/entry.d.ts +5 -5
- package/dist/lib/main/index.d.ts +1 -1
- package/dist/lib/main/modules/analytics/index.d.ts +5 -0
- package/dist/types/main/app/canvas.d.ts +5 -0
- package/dist/types/main/app/cross_domain_iframe.d.ts +44 -0
- package/dist/types/main/app/index.d.ts +53 -13
- package/dist/types/main/app/messages.gen.d.ts +2 -0
- package/dist/types/main/app/session.d.ts +1 -0
- package/dist/types/main/entry.d.ts +5 -5
- package/dist/types/main/index.d.ts +1 -1
- package/dist/types/main/modules/analytics/index.d.ts +5 -0
- package/package.json +4 -3
package/dist/cjs/entry.js
CHANGED
|
@@ -534,7 +534,7 @@ function dec2hex(dec) {
|
|
|
534
534
|
return dec.toString(16).padStart(2, '0');
|
|
535
535
|
}
|
|
536
536
|
function generateRandomId(len) {
|
|
537
|
-
const arr = new Uint8Array((len) / 2);
|
|
537
|
+
const arr = new Uint8Array((len || 40) / 2);
|
|
538
538
|
// msCrypto = IE11
|
|
539
539
|
// @ts-ignore
|
|
540
540
|
const safeCrypto = window.crypto || window.msCrypto;
|
|
@@ -543,7 +543,7 @@ function generateRandomId(len) {
|
|
|
543
543
|
return Array.from(arr, dec2hex).join('');
|
|
544
544
|
}
|
|
545
545
|
else {
|
|
546
|
-
return Array.from({ length: len }, () => dec2hex(Math.floor(Math.random() * 16))).join('');
|
|
546
|
+
return Array.from({ length: len || 40 }, () => dec2hex(Math.floor(Math.random() * 16))).join('');
|
|
547
547
|
}
|
|
548
548
|
}
|
|
549
549
|
function inIframe() {
|
|
@@ -740,6 +740,88 @@ function throttleWithTrailing(fn, interval) {
|
|
|
740
740
|
|
|
741
741
|
// Auto-generated, do not edit
|
|
742
742
|
/* eslint-disable */
|
|
743
|
+
const MESSAGE_FIELD_TYPES = {
|
|
744
|
+
0: ['number'],
|
|
745
|
+
4: ['string', 'string', 'number'],
|
|
746
|
+
5: ['number', 'number'],
|
|
747
|
+
6: ['number', 'number'],
|
|
748
|
+
7: [],
|
|
749
|
+
8: ['number', 'number', 'number', 'string', 'boolean'],
|
|
750
|
+
9: ['number', 'number', 'number'],
|
|
751
|
+
10: ['number', 'number', 'number'],
|
|
752
|
+
11: ['number'],
|
|
753
|
+
12: ['number', 'string', 'string'],
|
|
754
|
+
13: ['number', 'string'],
|
|
755
|
+
14: ['number', 'string'],
|
|
756
|
+
16: ['number', 'number', 'number'],
|
|
757
|
+
17: ['number', 'string'],
|
|
758
|
+
18: ['number', 'string', 'number'],
|
|
759
|
+
19: ['number', 'boolean'],
|
|
760
|
+
20: ['number', 'number'],
|
|
761
|
+
21: ['string', 'string', 'string', 'string', 'string', 'number', 'number', 'number'],
|
|
762
|
+
22: ['string', 'string'],
|
|
763
|
+
23: ['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
|
|
764
|
+
24: ['number', 'number', 'number'],
|
|
765
|
+
27: ['string', 'string'],
|
|
766
|
+
28: ['string'],
|
|
767
|
+
29: ['string'],
|
|
768
|
+
30: ['string', 'string'],
|
|
769
|
+
34: ['number', 'string'],
|
|
770
|
+
35: ['number', 'number', 'number'],
|
|
771
|
+
36: ['number', 'string'],
|
|
772
|
+
40: ['string', 'number', 'string', 'string'],
|
|
773
|
+
41: ['string', 'string'],
|
|
774
|
+
42: ['string'],
|
|
775
|
+
44: ['string', 'string', 'number'],
|
|
776
|
+
45: ['string', 'string'],
|
|
777
|
+
46: ['string', 'string'],
|
|
778
|
+
47: ['string', 'string', 'number'],
|
|
779
|
+
48: ['string', 'string', 'string', 'string', 'number'],
|
|
780
|
+
49: ['number', 'number', 'number', 'number'],
|
|
781
|
+
50: ['number', 'string'],
|
|
782
|
+
51: ['number', 'number', 'number'],
|
|
783
|
+
43: ['string', 'string'],
|
|
784
|
+
52: ['number', 'string', 'string'],
|
|
785
|
+
53: ['number', 'number', 'number', 'number', 'number', 'number', 'string', 'string'],
|
|
786
|
+
54: ['number', 'string'],
|
|
787
|
+
55: ['boolean'],
|
|
788
|
+
57: ['number', 'string', 'string', 'string'],
|
|
789
|
+
58: ['number'],
|
|
790
|
+
60: ['number', 'string', 'string', 'string'],
|
|
791
|
+
61: ['number', 'string', 'string'],
|
|
792
|
+
63: ['string', 'string'],
|
|
793
|
+
64: ['string', 'string'],
|
|
794
|
+
65: ['number', 'number'],
|
|
795
|
+
68: ['number', 'number', 'string', 'string', 'number', 'number'],
|
|
796
|
+
69: ['number', 'number', 'string', 'string'],
|
|
797
|
+
70: ['number', 'number'],
|
|
798
|
+
71: ['number', 'string', 'string'],
|
|
799
|
+
73: ['number', 'string', 'number', 'string'],
|
|
800
|
+
75: ['number', 'number'],
|
|
801
|
+
76: ['number', 'number'],
|
|
802
|
+
77: ['number', 'number'],
|
|
803
|
+
78: ['string', 'string', 'string', 'string'],
|
|
804
|
+
79: ['string', 'string'],
|
|
805
|
+
81: ['number', 'number', 'number', 'number', 'string'],
|
|
806
|
+
83: ['string', 'string', 'string', 'string', 'string', 'number', 'number', 'number', 'number'],
|
|
807
|
+
84: ['string', 'string', 'string', 'number', 'string', 'string'],
|
|
808
|
+
85: ['number', 'number', 'number', 'number', 'number', 'number', 'string', 'string', 'number', 'boolean', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
|
|
809
|
+
87: ['string', 'number', 'number'],
|
|
810
|
+
89: ['string', 'number', 'number', 'number', 'number', 'string'],
|
|
811
|
+
112: ['number', 'string', 'boolean', 'string', 'number', 'number'],
|
|
812
|
+
113: ['number', 'number', 'string'],
|
|
813
|
+
114: ['number'],
|
|
814
|
+
115: ['number'],
|
|
815
|
+
116: ['number', 'number', 'number', 'number', 'number', 'number', 'string', 'string', 'number', 'boolean'],
|
|
816
|
+
117: ['string'],
|
|
817
|
+
118: ['string'],
|
|
818
|
+
119: ['string', 'number'],
|
|
819
|
+
120: ['number'],
|
|
820
|
+
121: ['string', 'string', 'number', 'number'],
|
|
821
|
+
122: ['string', 'string', 'number', 'string'],
|
|
822
|
+
123: ['string', 'string', 'string', 'string', 'number'],
|
|
823
|
+
124: ['string', 'string'],
|
|
824
|
+
};
|
|
743
825
|
function Timestamp(timestamp) {
|
|
744
826
|
return [
|
|
745
827
|
0 /* Messages.Type.Timestamp */,
|
|
@@ -1422,6 +1504,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
|
|
|
1422
1504
|
JSException: JSException,
|
|
1423
1505
|
LoadFontFace: LoadFontFace,
|
|
1424
1506
|
LongAnimationTask: LongAnimationTask$1,
|
|
1507
|
+
MESSAGE_FIELD_TYPES: MESSAGE_FIELD_TYPES,
|
|
1425
1508
|
Metadata: Metadata,
|
|
1426
1509
|
MobX: MobX,
|
|
1427
1510
|
MouseClick: MouseClick,
|
|
@@ -1802,6 +1885,8 @@ class CanvasRecorder {
|
|
|
1802
1885
|
this.uploadQueue = 0;
|
|
1803
1886
|
this.MAX_CONCURRENT_UPLOADS = 2;
|
|
1804
1887
|
this.MAX_QUEUE_SIZE = 50; // ~500 images max (50 batches × 10 images)
|
|
1888
|
+
this.MAX_UPLOAD_ATTEMPTS = 5;
|
|
1889
|
+
this.MAX_RETRY_DELAY_MS = 10000;
|
|
1805
1890
|
this.pendingBatches = [];
|
|
1806
1891
|
this.isProcessingQueue = false;
|
|
1807
1892
|
/**
|
|
@@ -1942,26 +2027,41 @@ class CanvasRecorder {
|
|
|
1942
2027
|
this.app.debug.warn('Upload queue full, dropping canvas batch');
|
|
1943
2028
|
return;
|
|
1944
2029
|
}
|
|
1945
|
-
this.
|
|
2030
|
+
const token = this.app.session.getSessionSecret();
|
|
2031
|
+
if (!token) {
|
|
2032
|
+
this.app.debug.warn('Canvas batch has no session token');
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
this.pendingBatches.push({ images, canvasId, createdAt, token });
|
|
1946
2036
|
if (!this.isProcessingQueue) {
|
|
1947
|
-
this.processUploadQueue();
|
|
2037
|
+
void this.processUploadQueue();
|
|
1948
2038
|
}
|
|
1949
2039
|
}
|
|
1950
2040
|
async processUploadQueue() {
|
|
2041
|
+
if (this.isProcessingQueue)
|
|
2042
|
+
return;
|
|
1951
2043
|
this.isProcessingQueue = true;
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
2044
|
+
const worker = async () => {
|
|
2045
|
+
while (true) {
|
|
2046
|
+
const batch = this.pendingBatches.shift();
|
|
2047
|
+
if (!batch)
|
|
2048
|
+
return;
|
|
2049
|
+
await this.uploadBatch(batch.images, batch.canvasId, batch.createdAt, batch.token);
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
2052
|
+
try {
|
|
2053
|
+
await Promise.all(Array.from({ length: this.MAX_CONCURRENT_UPLOADS }, () => worker()));
|
|
2054
|
+
}
|
|
2055
|
+
finally {
|
|
2056
|
+
this.isProcessingQueue = false;
|
|
2057
|
+
// A batch can be appended between the final worker check and the flag
|
|
2058
|
+
// reset. Restart once so that race cannot strand queued frames.
|
|
2059
|
+
if (this.pendingBatches.length > 0) {
|
|
2060
|
+
void this.processUploadQueue();
|
|
1956
2061
|
}
|
|
1957
|
-
const batch = this.pendingBatches.shift();
|
|
1958
|
-
if (!batch)
|
|
1959
|
-
break;
|
|
1960
|
-
this.uploadBatch(batch.images, batch.canvasId, batch.createdAt);
|
|
1961
2062
|
}
|
|
1962
|
-
this.isProcessingQueue = false;
|
|
1963
2063
|
}
|
|
1964
|
-
async uploadBatch(images, canvasId, createdAt) {
|
|
2064
|
+
async uploadBatch(images, canvasId, createdAt, token) {
|
|
1965
2065
|
if (this.options.isDebug) {
|
|
1966
2066
|
const packed = await packFrames(images);
|
|
1967
2067
|
if (packed) {
|
|
@@ -2029,6 +2129,9 @@ class CanvasRecorder {
|
|
|
2029
2129
|
});
|
|
2030
2130
|
}
|
|
2031
2131
|
const initRestart = () => {
|
|
2132
|
+
if (this.app.session.getSessionSecret() !== token) {
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2032
2135
|
this.app.debug.log('Restarting tracker; token expired');
|
|
2033
2136
|
this.app.stop(false);
|
|
2034
2137
|
setTimeout(() => {
|
|
@@ -2038,25 +2141,72 @@ class CanvasRecorder {
|
|
|
2038
2141
|
this.uploadQueue++;
|
|
2039
2142
|
const base = this.app.options.endpoint.replace(/\/$/, '');
|
|
2040
2143
|
const ingestPoint = base.endsWith('/oxvo') ? base : `${base}/oxvo`;
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2144
|
+
try {
|
|
2145
|
+
for (let attempt = 1; attempt <= this.MAX_UPLOAD_ATTEMPTS; attempt++) {
|
|
2146
|
+
try {
|
|
2147
|
+
const response = await fetch(ingestPoint + '/v1/assets', {
|
|
2148
|
+
method: 'POST',
|
|
2149
|
+
headers: {
|
|
2150
|
+
Authorization: `Bearer ${token}`,
|
|
2151
|
+
},
|
|
2152
|
+
body: formData,
|
|
2153
|
+
});
|
|
2154
|
+
if (response.ok)
|
|
2155
|
+
return;
|
|
2156
|
+
if (response.status === 401) {
|
|
2157
|
+
initRestart();
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
if (response.status === 413) {
|
|
2161
|
+
this.app.debug.warn('Canvas batch exceeds the upload size limit');
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
if (!this.isRetryableUploadStatus(response.status)) {
|
|
2165
|
+
this.app.debug.error(`Canvas upload failed with status ${response.status}`);
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
if (attempt === this.MAX_UPLOAD_ATTEMPTS) {
|
|
2169
|
+
this.app.debug.error(`Canvas upload failed after ${attempt} attempts (status ${response.status})`);
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
await this.waitBeforeUploadRetry(response, attempt);
|
|
2173
|
+
}
|
|
2174
|
+
catch (error) {
|
|
2175
|
+
if (attempt === this.MAX_UPLOAD_ATTEMPTS) {
|
|
2176
|
+
this.app.debug.error('Canvas upload failed after retrying', error);
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
await this.waitBeforeUploadRetry(undefined, attempt);
|
|
2180
|
+
}
|
|
2051
2181
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
.catch((e) => {
|
|
2055
|
-
this.app.debug.error('error saving canvas', e);
|
|
2056
|
-
})
|
|
2057
|
-
.finally(() => {
|
|
2182
|
+
}
|
|
2183
|
+
finally {
|
|
2058
2184
|
this.uploadQueue--;
|
|
2059
|
-
}
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
isRetryableUploadStatus(status) {
|
|
2188
|
+
return status === 429 || status === 408 || status >= 500;
|
|
2189
|
+
}
|
|
2190
|
+
async waitBeforeUploadRetry(response, attempt) {
|
|
2191
|
+
const retryAfter = response?.headers.get('Retry-After');
|
|
2192
|
+
let retryDelayMs = 0;
|
|
2193
|
+
if (retryAfter) {
|
|
2194
|
+
const seconds = Number(retryAfter);
|
|
2195
|
+
if (Number.isFinite(seconds) && seconds >= 0) {
|
|
2196
|
+
retryDelayMs = seconds * 1000;
|
|
2197
|
+
}
|
|
2198
|
+
else {
|
|
2199
|
+
const retryAt = Date.parse(retryAfter);
|
|
2200
|
+
if (Number.isFinite(retryAt)) {
|
|
2201
|
+
retryDelayMs = Math.max(0, retryAt - Date.now());
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
if (retryDelayMs === 0) {
|
|
2206
|
+
retryDelayMs = 250 * 2 ** (attempt - 1);
|
|
2207
|
+
}
|
|
2208
|
+
retryDelayMs = Math.min(retryDelayMs, this.MAX_RETRY_DELAY_MS);
|
|
2209
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
2060
2210
|
}
|
|
2061
2211
|
cleanupCanvas(id) {
|
|
2062
2212
|
if (this.snapshots[id]) {
|
|
@@ -2079,8 +2229,7 @@ class CanvasRecorder {
|
|
|
2079
2229
|
}
|
|
2080
2230
|
delete this.snapshots[id];
|
|
2081
2231
|
}
|
|
2082
|
-
|
|
2083
|
-
// Flush remaining images before cleanup
|
|
2232
|
+
flushPendingSnapshots() {
|
|
2084
2233
|
Object.keys(this.snapshots).forEach((idStr) => {
|
|
2085
2234
|
const id = parseInt(idStr, 10);
|
|
2086
2235
|
const snapshot = this.snapshots[id];
|
|
@@ -2089,6 +2238,9 @@ class CanvasRecorder {
|
|
|
2089
2238
|
snapshot.images = [];
|
|
2090
2239
|
}
|
|
2091
2240
|
});
|
|
2241
|
+
}
|
|
2242
|
+
clear() {
|
|
2243
|
+
this.flushPendingSnapshots();
|
|
2092
2244
|
Object.keys(this.snapshots).forEach((idStr) => {
|
|
2093
2245
|
const id = parseInt(idStr, 10);
|
|
2094
2246
|
this.cleanupCanvas(id);
|
|
@@ -2155,6 +2307,291 @@ async function packFrames(images) {
|
|
|
2155
2307
|
return packed;
|
|
2156
2308
|
}
|
|
2157
2309
|
|
|
2310
|
+
const CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION = 1;
|
|
2311
|
+
const SECRET_PATTERN = /^[A-Za-z0-9_-]{32,128}$/;
|
|
2312
|
+
const CONTEXT_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
2313
|
+
const MAX_MESSAGE_FIELDS = 24;
|
|
2314
|
+
const MAX_MESSAGE_STRING_LENGTH = 100000;
|
|
2315
|
+
const MAX_BATCH_MESSAGES = 25000;
|
|
2316
|
+
const MAX_BATCH_BYTES = 2 * 1024 * 1024;
|
|
2317
|
+
// Cross-frame batches may contain only recording events. Identity, dictionary,
|
|
2318
|
+
// batch-header, and tab/session control opcodes remain owned by the top-level
|
|
2319
|
+
// tracker.
|
|
2320
|
+
const CROSS_DOMAIN_IFRAME_ALLOWED_MESSAGE_TYPES = new Set([
|
|
2321
|
+
4 /* MType.SetPageLocationDeprecated */,
|
|
2322
|
+
5 /* MType.SetViewportSize */,
|
|
2323
|
+
6 /* MType.SetViewportScroll */,
|
|
2324
|
+
7 /* MType.CreateDocument */,
|
|
2325
|
+
8 /* MType.CreateElementNode */,
|
|
2326
|
+
9 /* MType.CreateTextNode */,
|
|
2327
|
+
10 /* MType.MoveNode */,
|
|
2328
|
+
11 /* MType.RemoveNode */,
|
|
2329
|
+
12 /* MType.SetNodeAttribute */,
|
|
2330
|
+
13 /* MType.RemoveNodeAttribute */,
|
|
2331
|
+
14 /* MType.SetNodeData */,
|
|
2332
|
+
16 /* MType.SetNodeScroll */,
|
|
2333
|
+
17 /* MType.SetInputTarget */,
|
|
2334
|
+
18 /* MType.SetInputValue */,
|
|
2335
|
+
19 /* MType.SetInputChecked */,
|
|
2336
|
+
20 /* MType.MouseMove */,
|
|
2337
|
+
21 /* MType.NetworkRequestDeprecated */,
|
|
2338
|
+
22 /* MType.ConsoleLog */,
|
|
2339
|
+
23 /* MType.PageLoadTiming */,
|
|
2340
|
+
24 /* MType.PageRenderTiming */,
|
|
2341
|
+
27 /* MType.CustomEvent */,
|
|
2342
|
+
36 /* MType.NodeAnimationResult */,
|
|
2343
|
+
40 /* MType.Profiler */,
|
|
2344
|
+
41 /* MType.OTable */,
|
|
2345
|
+
42 /* MType.StateAction */,
|
|
2346
|
+
44 /* MType.ReduxDeprecated */,
|
|
2347
|
+
45 /* MType.Vuex */,
|
|
2348
|
+
46 /* MType.MobX */,
|
|
2349
|
+
47 /* MType.NgRx */,
|
|
2350
|
+
48 /* MType.GraphQLDeprecated */,
|
|
2351
|
+
49 /* MType.PerformanceTrack */,
|
|
2352
|
+
53 /* MType.ResourceTimingDeprecatedDeprecated */,
|
|
2353
|
+
54 /* MType.ConnectionInformation */,
|
|
2354
|
+
55 /* MType.SetPageVisibility */,
|
|
2355
|
+
57 /* MType.LoadFontFace */,
|
|
2356
|
+
58 /* MType.SetNodeFocus */,
|
|
2357
|
+
60 /* MType.SetNodeAttributeURLBased */,
|
|
2358
|
+
61 /* MType.SetCSSDataURLBased */,
|
|
2359
|
+
63 /* MType.TechnicalInfo */,
|
|
2360
|
+
64 /* MType.CustomIssue */,
|
|
2361
|
+
65 /* MType.SetNodeSlot */,
|
|
2362
|
+
68 /* MType.MouseClick */,
|
|
2363
|
+
69 /* MType.MouseClickDeprecated */,
|
|
2364
|
+
70 /* MType.CreateIFrameDocument */,
|
|
2365
|
+
71 /* MType.AdoptedSSReplaceURLBased */,
|
|
2366
|
+
73 /* MType.AdoptedSSInsertRuleURLBased */,
|
|
2367
|
+
75 /* MType.AdoptedSSDeleteRule */,
|
|
2368
|
+
76 /* MType.AdoptedSSAddOwner */,
|
|
2369
|
+
77 /* MType.AdoptedSSRemoveOwner */,
|
|
2370
|
+
78 /* MType.JSException */,
|
|
2371
|
+
79 /* MType.Zustand */,
|
|
2372
|
+
83 /* MType.NetworkRequest */,
|
|
2373
|
+
84 /* MType.WSChannel */,
|
|
2374
|
+
85 /* MType.ResourceTiming */,
|
|
2375
|
+
87 /* MType.Incident */,
|
|
2376
|
+
89 /* MType.LongAnimationTask */,
|
|
2377
|
+
112 /* MType.InputChange */,
|
|
2378
|
+
113 /* MType.SelectionChange */,
|
|
2379
|
+
114 /* MType.MouseThrashing */,
|
|
2380
|
+
115 /* MType.UnbindNodes */,
|
|
2381
|
+
116 /* MType.ResourceTimingDeprecated */,
|
|
2382
|
+
119 /* MType.CanvasNode */,
|
|
2383
|
+
120 /* MType.TagTrigger */,
|
|
2384
|
+
121 /* MType.Redux */,
|
|
2385
|
+
122 /* MType.SetPageLocation */,
|
|
2386
|
+
123 /* MType.GraphQL */,
|
|
2387
|
+
124 /* MType.WebVitals */,
|
|
2388
|
+
]);
|
|
2389
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2390
|
+
const isFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value);
|
|
2391
|
+
const isSafeIntegerAtLeast = (value, minimum) => typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum;
|
|
2392
|
+
const isBoundedString = (value, maxLength) => typeof value === 'string' && value.length > 0 && value.length <= maxLength;
|
|
2393
|
+
const isCanvasQuality = (value) => value === 'low' || value === 'medium' || value === 'high';
|
|
2394
|
+
function normalizeCrossDomainOrigin(value) {
|
|
2395
|
+
if (typeof value !== 'string' || value.trim() === '' || value.trim() === '*') {
|
|
2396
|
+
return null;
|
|
2397
|
+
}
|
|
2398
|
+
try {
|
|
2399
|
+
const url = new URL(value.trim());
|
|
2400
|
+
if (!['http:', 'https:'].includes(url.protocol) ||
|
|
2401
|
+
url.origin === 'null' ||
|
|
2402
|
+
url.username !== '' ||
|
|
2403
|
+
url.password !== '' ||
|
|
2404
|
+
(url.pathname !== '' && url.pathname !== '/') ||
|
|
2405
|
+
url.search !== '' ||
|
|
2406
|
+
url.hash !== '') {
|
|
2407
|
+
return null;
|
|
2408
|
+
}
|
|
2409
|
+
return url.origin;
|
|
2410
|
+
}
|
|
2411
|
+
catch {
|
|
2412
|
+
return null;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
function normalizeCrossDomainOrigins(values) {
|
|
2416
|
+
if (!Array.isArray(values)) {
|
|
2417
|
+
return new Set();
|
|
2418
|
+
}
|
|
2419
|
+
return new Set(values.map(normalizeCrossDomainOrigin).filter((origin) => origin !== null));
|
|
2420
|
+
}
|
|
2421
|
+
function createCrossDomainSecret() {
|
|
2422
|
+
if (!globalThis.crypto?.getRandomValues) {
|
|
2423
|
+
return null;
|
|
2424
|
+
}
|
|
2425
|
+
const bytes = new Uint8Array(24);
|
|
2426
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
2427
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
2428
|
+
}
|
|
2429
|
+
function parseCrossDomainHandshake(data, expectedLine, expectedProjectKey) {
|
|
2430
|
+
if (!isRecord(data) ||
|
|
2431
|
+
data.line !== expectedLine ||
|
|
2432
|
+
data.protocolVersion !== CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION ||
|
|
2433
|
+
data.projectKey !== expectedProjectKey ||
|
|
2434
|
+
!isBoundedString(data.context, 128) ||
|
|
2435
|
+
!CONTEXT_PATTERN.test(data.context) ||
|
|
2436
|
+
!isBoundedString(data.nonce, 128) ||
|
|
2437
|
+
!SECRET_PATTERN.test(data.nonce)) {
|
|
2438
|
+
return null;
|
|
2439
|
+
}
|
|
2440
|
+
return {
|
|
2441
|
+
protocolVersion: CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION,
|
|
2442
|
+
context: data.context,
|
|
2443
|
+
nonce: data.nonce,
|
|
2444
|
+
projectKey: expectedProjectKey,
|
|
2445
|
+
};
|
|
2446
|
+
}
|
|
2447
|
+
function matchesCrossDomainEnvelope(data, expectedLine, expected) {
|
|
2448
|
+
return (isRecord(data) &&
|
|
2449
|
+
data.line === expectedLine &&
|
|
2450
|
+
data.protocolVersion === CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION &&
|
|
2451
|
+
data.context === expected.context &&
|
|
2452
|
+
data.nonce === expected.nonce &&
|
|
2453
|
+
data.capability === expected.capability);
|
|
2454
|
+
}
|
|
2455
|
+
function parseCrossDomainFrameInit(data, expectedLine, expected) {
|
|
2456
|
+
if (!isRecord(data) ||
|
|
2457
|
+
data.line !== expectedLine ||
|
|
2458
|
+
data.protocolVersion !== CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION ||
|
|
2459
|
+
data.context !== expected.context ||
|
|
2460
|
+
data.nonce !== expected.nonce ||
|
|
2461
|
+
!isBoundedString(data.capability, 128) ||
|
|
2462
|
+
!SECRET_PATTERN.test(data.capability) ||
|
|
2463
|
+
!isSafeIntegerAtLeast(data.id, 0) ||
|
|
2464
|
+
!isSafeIntegerAtLeast(data.frameOrderNumber, 1) ||
|
|
2465
|
+
!isSafeIntegerAtLeast(data.frameLevel, 0) ||
|
|
2466
|
+
!isSafeIntegerAtLeast(data.capabilityExpiresAt, 1) ||
|
|
2467
|
+
data.capabilityExpiresAt <= Date.now() ||
|
|
2468
|
+
!isRecord(data.session)) {
|
|
2469
|
+
return null;
|
|
2470
|
+
}
|
|
2471
|
+
const session = data.session;
|
|
2472
|
+
if (!isBoundedString(session.sessionID, 256) ||
|
|
2473
|
+
!isFiniteNumber(session.startedAt) ||
|
|
2474
|
+
!isFiniteNumber(session.delayMs) ||
|
|
2475
|
+
typeof session.userUUID !== 'string' ||
|
|
2476
|
+
session.userUUID.length > 256) {
|
|
2477
|
+
return null;
|
|
2478
|
+
}
|
|
2479
|
+
let projectID;
|
|
2480
|
+
if (session.projectID !== undefined) {
|
|
2481
|
+
if (!isBoundedString(session.projectID, 256)) {
|
|
2482
|
+
return null;
|
|
2483
|
+
}
|
|
2484
|
+
projectID = session.projectID;
|
|
2485
|
+
}
|
|
2486
|
+
const canvas = parseCanvasConfig(data.canvas);
|
|
2487
|
+
if (data.canvas !== undefined && canvas === null) {
|
|
2488
|
+
return null;
|
|
2489
|
+
}
|
|
2490
|
+
return {
|
|
2491
|
+
protocolVersion: CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION,
|
|
2492
|
+
context: data.context,
|
|
2493
|
+
nonce: data.nonce,
|
|
2494
|
+
capability: data.capability,
|
|
2495
|
+
id: data.id,
|
|
2496
|
+
frameOrderNumber: data.frameOrderNumber,
|
|
2497
|
+
frameLevel: data.frameLevel,
|
|
2498
|
+
capabilityExpiresAt: data.capabilityExpiresAt,
|
|
2499
|
+
session: {
|
|
2500
|
+
sessionID: session.sessionID,
|
|
2501
|
+
projectID,
|
|
2502
|
+
startedAt: session.startedAt,
|
|
2503
|
+
delayMs: session.delayMs,
|
|
2504
|
+
userUUID: session.userUUID,
|
|
2505
|
+
},
|
|
2506
|
+
...(canvas ? { canvas } : {}),
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
function parseCanvasConfig(value) {
|
|
2510
|
+
if (value === undefined) {
|
|
2511
|
+
return null;
|
|
2512
|
+
}
|
|
2513
|
+
if (!isRecord(value) || typeof value.enabled !== 'boolean') {
|
|
2514
|
+
return null;
|
|
2515
|
+
}
|
|
2516
|
+
let quality;
|
|
2517
|
+
if (value.quality !== undefined) {
|
|
2518
|
+
if (!isCanvasQuality(value.quality)) {
|
|
2519
|
+
return null;
|
|
2520
|
+
}
|
|
2521
|
+
quality = value.quality;
|
|
2522
|
+
}
|
|
2523
|
+
let fps;
|
|
2524
|
+
if (value.fps !== undefined) {
|
|
2525
|
+
if (!isFiniteNumber(value.fps)) {
|
|
2526
|
+
return null;
|
|
2527
|
+
}
|
|
2528
|
+
fps = value.fps;
|
|
2529
|
+
}
|
|
2530
|
+
let framesSupport;
|
|
2531
|
+
if (value.framesSupport !== undefined) {
|
|
2532
|
+
if (typeof value.framesSupport !== 'boolean') {
|
|
2533
|
+
return null;
|
|
2534
|
+
}
|
|
2535
|
+
framesSupport = value.framesSupport;
|
|
2536
|
+
}
|
|
2537
|
+
return {
|
|
2538
|
+
enabled: value.enabled,
|
|
2539
|
+
quality,
|
|
2540
|
+
fps,
|
|
2541
|
+
framesSupport,
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
function parseCrossDomainIframeBatch(messages) {
|
|
2545
|
+
if (!Array.isArray(messages) || messages.length > MAX_BATCH_MESSAGES) {
|
|
2546
|
+
return null;
|
|
2547
|
+
}
|
|
2548
|
+
let bytes = 0;
|
|
2549
|
+
for (const message of messages) {
|
|
2550
|
+
if (!Array.isArray(message) || message.length < 1 || message.length > MAX_MESSAGE_FIELDS) {
|
|
2551
|
+
return null;
|
|
2552
|
+
}
|
|
2553
|
+
const messageType = message[0];
|
|
2554
|
+
if (typeof messageType !== 'number' ||
|
|
2555
|
+
!Number.isInteger(messageType) ||
|
|
2556
|
+
!CROSS_DOMAIN_IFRAME_ALLOWED_MESSAGE_TYPES.has(messageType)) {
|
|
2557
|
+
return null;
|
|
2558
|
+
}
|
|
2559
|
+
const expectedFieldTypes = MESSAGE_FIELD_TYPES[messageType];
|
|
2560
|
+
if (expectedFieldTypes === undefined || message.length !== expectedFieldTypes.length + 1) {
|
|
2561
|
+
return null;
|
|
2562
|
+
}
|
|
2563
|
+
bytes += 8;
|
|
2564
|
+
for (let fieldIndex = 0; fieldIndex < expectedFieldTypes.length; fieldIndex++) {
|
|
2565
|
+
const field = message[fieldIndex + 1];
|
|
2566
|
+
if (typeof field !== expectedFieldTypes[fieldIndex]) {
|
|
2567
|
+
return null;
|
|
2568
|
+
}
|
|
2569
|
+
if (typeof field === 'string') {
|
|
2570
|
+
if (field.length > MAX_MESSAGE_STRING_LENGTH) {
|
|
2571
|
+
return null;
|
|
2572
|
+
}
|
|
2573
|
+
bytes += field.length * 3;
|
|
2574
|
+
}
|
|
2575
|
+
else if (typeof field === 'number') {
|
|
2576
|
+
if (!Number.isFinite(field)) {
|
|
2577
|
+
return null;
|
|
2578
|
+
}
|
|
2579
|
+
bytes += 8;
|
|
2580
|
+
}
|
|
2581
|
+
else if (typeof field === 'boolean') {
|
|
2582
|
+
bytes += 1;
|
|
2583
|
+
}
|
|
2584
|
+
else {
|
|
2585
|
+
return null;
|
|
2586
|
+
}
|
|
2587
|
+
if (bytes > MAX_BATCH_BYTES) {
|
|
2588
|
+
return null;
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
return { messages: messages, bytes };
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2158
2595
|
const LogLevel = {
|
|
2159
2596
|
Verbose: 5,
|
|
2160
2597
|
Log: 4,
|
|
@@ -3958,6 +4395,10 @@ class Session {
|
|
|
3958
4395
|
this.token = `${token}${tokenSeparator}${projectKey}`;
|
|
3959
4396
|
this.app.sessionStorage.setItem(this.options.session_token_key, `${token}${tokenSeparator}${projectKey}`);
|
|
3960
4397
|
};
|
|
4398
|
+
this.clearSessionSecret = () => {
|
|
4399
|
+
this.token = undefined;
|
|
4400
|
+
this.app.sessionStorage.removeItem(this.options.session_token_key);
|
|
4401
|
+
};
|
|
3961
4402
|
this.app = params.app;
|
|
3962
4403
|
this.options = params.options;
|
|
3963
4404
|
this.createTabId();
|
|
@@ -4054,7 +4495,7 @@ class Session {
|
|
|
4054
4495
|
};
|
|
4055
4496
|
}
|
|
4056
4497
|
reset() {
|
|
4057
|
-
this.
|
|
4498
|
+
this.clearSessionSecret();
|
|
4058
4499
|
this.metadata = {};
|
|
4059
4500
|
this.userID = null;
|
|
4060
4501
|
this.sessionID = undefined;
|
|
@@ -4112,10 +4553,12 @@ class Ticker {
|
|
|
4112
4553
|
* this value is injected during build time via rollup
|
|
4113
4554
|
* */
|
|
4114
4555
|
// @ts-ignore
|
|
4115
|
-
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/events\",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;if(0===t.length)return console.error(\"OxvoSessions: refusing to send 0-byte batch.\",{batchNum:i,dataType:e,isCompressed:s,batch:t}),this.attemptsCount=0,void this.sendNext();const h=null==i?void 0:i.toString().replace(/^([^_]+)_([^_]+).*/,\"$1_$2_$3\");this.busy=!0;const r={Authorization:`Bearer ${this.token}`,DataType:e};if(s&&(r[\"Content-Encoding\"]=\"gzip\"),null===this.token)return void setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`,e)}),500);const a=t.length<65536&&this.inflightKeepaliveBytes+t.length<=65536;a&&(this.inflightKeepaliveBytes+=t.length);const o=()=>{a&&(this.inflightKeepaliveBytes-=t.length)},u=`${null!==(n=this.pageNo)&&void 0!==n?n:\"noPageNum\"}_${null!=h?h:\"noBatchNum\"}`;fetch(`${this.ingestURL}?seq=${u}&keepalive=${a?\"yes\":\"no\"}`,{body:t,method:\"POST\",headers:r,keepalive:a}).then((n=>{var h;if(o(),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=>{o(),console.warn(\"OxvoSessions:\",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(\"OxvoSessions: 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 o{constructor(t,s,i,e,n,h,r=!1,o){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=e,this.tabId=n,this.onOfflineEnd=h,this.localDebug=r,this.onLocalSave=o,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(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(\"OxvoSessions: beacon size overflow. Skipping large message.\",s);this.nextIndex++;const n=e.flush();n&&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 u;!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\"}(u||(u={}));let c=null,l=null,d=u.NotActive;function p(t){l&&l.finaliseBatch(t)}function f(){return new Promise((t=>{d=u.Stopping,null!==b&&(clearInterval(b),b=null),l&&(l.clean(),l=null),c&&(c.clean(),setTimeout((()=>{c=null}),20)),setTimeout((()=>{d=u.NotActive,t(null)}),100)}))}function g(){[u.Stopped,u.Stopping].includes(d)||(postMessage(\"a_stop\"),f().then((()=>{postMessage(\"a_start\")})))}let m,b=null;self.onmessage=({data:s})=>{var i;if(\"stop\"===s)return p(),void f().then((()=>{d=u.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=u.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 o(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===b&&(b=setInterval(p,3e4)),d=u.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";
|
|
4556
|
+
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/events\",this.analyticsIngestURL=t+\"/v1/analytics\",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;if(0===t.length)return console.error(\"OxvoSessions: refusing to send 0-byte batch.\",{batchNum:i,dataType:e,isCompressed:s,batch:t}),this.attemptsCount=0,void this.sendNext();const h=null==i?void 0:i.toString().replace(/^([^_]+)_([^_]+).*/,\"$1_$2_$3\");this.busy=!0;const r={Authorization:`Bearer ${this.token}`,DataType:e};if(s&&(r[\"Content-Encoding\"]=\"gzip\"),null===this.token)return void setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`,e)}),500);const a=t.length<65536&&this.inflightKeepaliveBytes+t.length<=65536;a&&(this.inflightKeepaliveBytes+=t.length);const o=()=>{a&&(this.inflightKeepaliveBytes-=t.length)},u=`${null!==(n=this.pageNo)&&void 0!==n?n:\"noPageNum\"}_${null!=h?h:\"noBatchNum\"}`,c=\"analytics\"===e?this.analyticsIngestURL:this.ingestURL;fetch(`${c}?seq=${u}&keepalive=${a?\"yes\":\"no\"}`,{body:t,method:\"POST\",headers:r,keepalive:a}).then((n=>{var h;if(o(),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=>{o(),console.warn(\"OxvoSessions:\",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(\"OxvoSessions: 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 o{constructor(t,s,i,e,n,h,r=!1,o){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=e,this.tabId=n,this.onOfflineEnd=h,this.localDebug=r,this.onLocalSave=o,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(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(\"OxvoSessions: beacon size overflow. Skipping large message.\",s);this.nextIndex++;const n=e.flush();n&&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 u;!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\"}(u||(u={}));let c=null,l=null,d=u.NotActive;function p(t){l&&l.finaliseBatch(t)}function f(){return new Promise((t=>{d=u.Stopping,null!==y&&(clearInterval(y),y=null),l&&(l.clean(),l=null),c&&(c.clean(),setTimeout((()=>{c=null}),20)),setTimeout((()=>{d=u.NotActive,t(null)}),100)}))}function g(){[u.Stopped,u.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=u.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=u.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 o(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=u.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";
|
|
4116
4557
|
const CANCELED = 'canceled';
|
|
4117
4558
|
const bufferStorageKey = 'or_buffer_1';
|
|
4118
4559
|
const PROTO_VERSION = '2';
|
|
4560
|
+
const BOOTSTRAP_ATTEMPT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
4561
|
+
const MAX_BOOTSTRAP_ATTEMPT_STORAGE_LENGTH = 1024 * 1024;
|
|
4119
4562
|
const UnsuccessfulStart = (reason) => ({ reason, success: false });
|
|
4120
4563
|
const SuccessfulStart = (body) => ({ ...body, success: true });
|
|
4121
4564
|
var ActivityState;
|
|
@@ -4146,18 +4589,25 @@ const proto = {
|
|
|
4146
4589
|
resp: 'never-gonna-let-you-down',
|
|
4147
4590
|
// regenerating id (copied other tab)
|
|
4148
4591
|
reg: 'never-gonna-run-around-and-desert-you',
|
|
4149
|
-
iframeSignal: '
|
|
4150
|
-
iframeId: '
|
|
4151
|
-
iframeBatch: 'batch
|
|
4152
|
-
parentAlive: '
|
|
4153
|
-
killIframe: 'stop
|
|
4154
|
-
startIframe: 'start
|
|
4592
|
+
iframeSignal: 'oxvo:cross-frame:v1:handshake',
|
|
4593
|
+
iframeId: 'oxvo:cross-frame:v1:ready',
|
|
4594
|
+
iframeBatch: 'oxvo:cross-frame:v1:batch',
|
|
4595
|
+
parentAlive: 'oxvo:cross-frame:v1:pong',
|
|
4596
|
+
killIframe: 'oxvo:cross-frame:v1:stop',
|
|
4597
|
+
startIframe: 'oxvo:cross-frame:v1:start',
|
|
4155
4598
|
// checking updates
|
|
4156
|
-
polling: '
|
|
4599
|
+
polling: 'oxvo:cross-frame:v1:ping',
|
|
4157
4600
|
// happens if tab is old and has outdated token but
|
|
4158
4601
|
// not communicating with backend to update it (for whatever reason)
|
|
4159
4602
|
reset: 'reset-your-session-please',
|
|
4160
4603
|
};
|
|
4604
|
+
const FRAME_RATE_WINDOW_MS = 1000;
|
|
4605
|
+
const MAX_FRAME_ENVELOPES_PER_WINDOW = 120;
|
|
4606
|
+
const MAX_FRAME_MESSAGES_PER_WINDOW = 50000;
|
|
4607
|
+
const MAX_FRAME_BYTES_PER_WINDOW = 4 * 1024 * 1024;
|
|
4608
|
+
const FRAME_CAPABILITY_TTL_MS = 5 * 60 * 1000;
|
|
4609
|
+
const FRAME_CAPABILITY_REFRESH_LEAD_MS = 60 * 1000;
|
|
4610
|
+
const FRAME_PREVIOUS_CAPABILITY_GRACE_MS = 5 * 1000;
|
|
4161
4611
|
class App {
|
|
4162
4612
|
get tagMatcher() {
|
|
4163
4613
|
return this.markerWatcher.matcher;
|
|
@@ -4178,18 +4628,31 @@ class App {
|
|
|
4178
4628
|
this.stopCallbacks = [];
|
|
4179
4629
|
this.commitCallbacks = [];
|
|
4180
4630
|
this.activityState = ActivityState.NotActive;
|
|
4181
|
-
this.version = '7.3.
|
|
4631
|
+
this.version = '7.3.4'; // TODO: version compatability check inside each plugin.
|
|
4182
4632
|
this.socketMode = false;
|
|
4183
4633
|
this.compressionThreshold = 24 * 1000;
|
|
4184
4634
|
this.bc = null;
|
|
4185
4635
|
this.canvasRecorder = null;
|
|
4186
4636
|
this.conditionsManager = null;
|
|
4637
|
+
this.bootstrapAttempts = {};
|
|
4187
4638
|
this.canStart = false;
|
|
4188
4639
|
this.rootId = null;
|
|
4189
4640
|
this.pageFrames = [];
|
|
4190
4641
|
this.frameOderNumber = 0;
|
|
4191
4642
|
this.frameLevel = 0;
|
|
4192
4643
|
this.emptyBatchCounter = 0;
|
|
4644
|
+
this.crossDomainFrameCapability = null;
|
|
4645
|
+
this.crossDomainFrameCapabilityExpiresAt = 0;
|
|
4646
|
+
this.crossDomainFrameSession = null;
|
|
4647
|
+
this.crossDomainFrameCanvas = null;
|
|
4648
|
+
this.crossDomainResumeAfterHandshake = false;
|
|
4649
|
+
this.crossDomainConnectionRequested = false;
|
|
4650
|
+
this.crossDomainFrames = new Map();
|
|
4651
|
+
this.crossDomainEnvelopeBudgets = new WeakMap();
|
|
4652
|
+
this.pendingCrossDomainFrames = new Map();
|
|
4653
|
+
this.crossDomainAuthorizationGeneration = 0;
|
|
4654
|
+
this.nextCrossDomainFrameOrder = 1;
|
|
4655
|
+
this.crossDomainCanvasConfig = null;
|
|
4193
4656
|
/** used by child iframes for crossdomain only */
|
|
4194
4657
|
this.parentActive = false;
|
|
4195
4658
|
this.checkStatus = () => {
|
|
@@ -4197,230 +4660,201 @@ class App {
|
|
|
4197
4660
|
};
|
|
4198
4661
|
this.parentCrossDomainFrameListener = (event) => {
|
|
4199
4662
|
const { data } = event;
|
|
4200
|
-
if (!data ||
|
|
4663
|
+
if (!data ||
|
|
4664
|
+
this.crossDomainParentOrigin === null ||
|
|
4665
|
+
this.crossDomainFrameNonce === null ||
|
|
4666
|
+
event.source !== window.parent ||
|
|
4667
|
+
event.origin !== this.crossDomainParentOrigin) {
|
|
4668
|
+
return;
|
|
4669
|
+
}
|
|
4670
|
+
if (data.line === proto.iframeId) {
|
|
4671
|
+
const init = parseCrossDomainFrameInit(data, proto.iframeId, {
|
|
4672
|
+
context: this.contextId,
|
|
4673
|
+
nonce: this.crossDomainFrameNonce,
|
|
4674
|
+
});
|
|
4675
|
+
if (init === null)
|
|
4676
|
+
return;
|
|
4677
|
+
const sessionChanged = this.crossDomainFrameSession !== null &&
|
|
4678
|
+
this.crossDomainFrameSession.sessionID !== init.session.sessionID;
|
|
4679
|
+
if (sessionChanged && this.activityState === ActivityState.Starting) {
|
|
4680
|
+
this.postHandshakeToParent();
|
|
4681
|
+
return;
|
|
4682
|
+
}
|
|
4683
|
+
const shouldResume = this.crossDomainResumeAfterHandshake || (sessionChanged && this.active());
|
|
4684
|
+
if (sessionChanged && this.active()) {
|
|
4685
|
+
this.stop();
|
|
4686
|
+
}
|
|
4687
|
+
this.applyCrossDomainFrameInit(init);
|
|
4688
|
+
if (shouldResume &&
|
|
4689
|
+
this.activityState !== ActivityState.Active &&
|
|
4690
|
+
this.activityState !== ActivityState.Starting) {
|
|
4691
|
+
this.crossDomainResumeAfterHandshake = false;
|
|
4692
|
+
this.startCrossDomainFrame(this.prevOpts);
|
|
4693
|
+
}
|
|
4694
|
+
this.debug.log('Starting secure cross-domain iframe tracking');
|
|
4695
|
+
return;
|
|
4696
|
+
}
|
|
4697
|
+
const expectedLine = data.line === proto.parentAlive
|
|
4698
|
+
? proto.parentAlive
|
|
4699
|
+
: data.line === proto.startIframe
|
|
4700
|
+
? proto.startIframe
|
|
4701
|
+
: data.line === proto.killIframe
|
|
4702
|
+
? proto.killIframe
|
|
4703
|
+
: null;
|
|
4704
|
+
if (this.crossDomainFrameCapability === null ||
|
|
4705
|
+
expectedLine === null ||
|
|
4706
|
+
(expectedLine !== proto.killIframe &&
|
|
4707
|
+
Date.now() >= this.crossDomainFrameCapabilityExpiresAt) ||
|
|
4708
|
+
!matchesCrossDomainEnvelope(data, expectedLine, {
|
|
4709
|
+
context: this.contextId,
|
|
4710
|
+
nonce: this.crossDomainFrameNonce,
|
|
4711
|
+
capability: this.crossDomainFrameCapability,
|
|
4712
|
+
})) {
|
|
4713
|
+
return;
|
|
4714
|
+
}
|
|
4715
|
+
if (data.line === proto.parentAlive) {
|
|
4716
|
+
this.parentActive = true;
|
|
4201
4717
|
return;
|
|
4718
|
+
}
|
|
4202
4719
|
if (data.line === proto.startIframe) {
|
|
4203
4720
|
// Avoid corrupting an in-flight start; let it complete.
|
|
4204
4721
|
if (this.activityState === ActivityState.Starting)
|
|
4205
4722
|
return;
|
|
4723
|
+
const init = parseCrossDomainFrameInit(data, proto.startIframe, {
|
|
4724
|
+
context: this.contextId,
|
|
4725
|
+
nonce: this.crossDomainFrameNonce,
|
|
4726
|
+
});
|
|
4727
|
+
if (init === null)
|
|
4728
|
+
return;
|
|
4206
4729
|
try {
|
|
4207
4730
|
if (this.active()) {
|
|
4208
4731
|
this.stop();
|
|
4209
4732
|
}
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
}
|
|
4213
|
-
if (data.id !== undefined) {
|
|
4214
|
-
this.rootId = data.id;
|
|
4215
|
-
}
|
|
4216
|
-
this.allowAppStart();
|
|
4217
|
-
void this.start();
|
|
4733
|
+
this.applyCrossDomainFrameInit(init);
|
|
4734
|
+
this.startCrossDomainFrame(this.prevOpts);
|
|
4218
4735
|
}
|
|
4219
4736
|
catch (e) {
|
|
4220
4737
|
console.error('children frame restart failed:', e);
|
|
4221
4738
|
}
|
|
4222
|
-
|
|
4223
|
-
if (data.line === proto.parentAlive) {
|
|
4224
|
-
this.parentActive = true;
|
|
4225
|
-
}
|
|
4226
|
-
if (data.line === proto.iframeId) {
|
|
4227
|
-
this.parentActive = true;
|
|
4228
|
-
this.rootId = data.id;
|
|
4229
|
-
this.session.setSessionSecret(data.token, this.projectKey);
|
|
4230
|
-
this.frameOderNumber = data.frameOrderNumber;
|
|
4231
|
-
this.frameLevel = data.frameLevel;
|
|
4232
|
-
this.debug.log('starting iframe tracking', data);
|
|
4233
|
-
this.allowAppStart();
|
|
4739
|
+
return;
|
|
4234
4740
|
}
|
|
4235
4741
|
if (data.line === proto.killIframe) {
|
|
4236
|
-
|
|
4742
|
+
const shouldResume = this.activityState === ActivityState.Active || this.activityState === ActivityState.Starting;
|
|
4743
|
+
if (this.activityState !== ActivityState.NotActive) {
|
|
4237
4744
|
this.stop();
|
|
4238
4745
|
}
|
|
4746
|
+
this.clearCrossDomainFrameAuthorization();
|
|
4747
|
+
this.crossDomainResumeAfterHandshake = shouldResume;
|
|
4748
|
+
this.crossDomainConnectionRequested = shouldResume;
|
|
4239
4749
|
}
|
|
4240
4750
|
};
|
|
4241
|
-
this.trackedFrames = [];
|
|
4242
|
-
this.frameLastSeen = new Map();
|
|
4243
|
-
this.FRAME_STALE_MS = 1500;
|
|
4244
4751
|
this.crossDomainIframeListener = (event) => {
|
|
4245
|
-
if (event.source === window)
|
|
4246
|
-
return;
|
|
4247
4752
|
const { data } = event;
|
|
4248
|
-
if (!data)
|
|
4753
|
+
if (!data || event.source === window)
|
|
4754
|
+
return;
|
|
4755
|
+
if (data.line === proto.iframeSignal) {
|
|
4756
|
+
const handshake = parseCrossDomainHandshake(data, proto.iframeSignal, this.projectKey);
|
|
4757
|
+
if (handshake !== null) {
|
|
4758
|
+
void this.establishCrossDomainFrame(event, handshake);
|
|
4759
|
+
}
|
|
4249
4760
|
return;
|
|
4250
|
-
// Record liveness regardless of our own active state so the queue can prune
|
|
4251
|
-
// stale contexts reliably once we resume dispatching commands after a cycle.
|
|
4252
|
-
if ((data.line === proto.polling || data.line === proto.iframeSignal) && data.context) {
|
|
4253
|
-
this.frameLastSeen.set(data.context, Date.now());
|
|
4254
4761
|
}
|
|
4255
4762
|
if (!this.active())
|
|
4256
4763
|
return;
|
|
4257
|
-
if (data.line === proto.
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
}
|
|
4264
|
-
const id = await this.checkNodeId(event.source);
|
|
4265
|
-
if (!id) {
|
|
4266
|
-
this.debug.log('Couldnt get node id for iframe', event.source);
|
|
4267
|
-
return;
|
|
4268
|
-
}
|
|
4269
|
-
try {
|
|
4270
|
-
if (this.trackedFrames.includes(data.context)) {
|
|
4271
|
-
this.debug.log('Trying to observe already added iframe; ignore if its a restart');
|
|
4272
|
-
}
|
|
4273
|
-
else {
|
|
4274
|
-
this.trackedFrames.push(data.context);
|
|
4275
|
-
}
|
|
4276
|
-
await this.waitStarted();
|
|
4277
|
-
const token = this.session.getSessionSecret(this.projectKey);
|
|
4278
|
-
const order = this.trackedFrames.findIndex((f) => f === data.context) + 1;
|
|
4279
|
-
if (order === 0) {
|
|
4280
|
-
this.debug.error('Couldnt get order number for iframe', data.context, this.trackedFrames);
|
|
4281
|
-
}
|
|
4282
|
-
const iframeData = {
|
|
4283
|
-
line: proto.iframeId,
|
|
4284
|
-
id,
|
|
4285
|
-
token,
|
|
4286
|
-
frameOrderNumber: order,
|
|
4287
|
-
frameLevel: this.frameLevel + 1,
|
|
4288
|
-
};
|
|
4289
|
-
this.debug.log('Got child frame signal; nodeId', id, event.source, iframeData);
|
|
4290
|
-
// @ts-ignore
|
|
4291
|
-
event.source?.postMessage(iframeData, '*');
|
|
4292
|
-
}
|
|
4293
|
-
catch (e) {
|
|
4294
|
-
console.error(e);
|
|
4295
|
-
}
|
|
4296
|
-
};
|
|
4297
|
-
void signalId();
|
|
4764
|
+
if (data.line === proto.polling) {
|
|
4765
|
+
const state = this.validatedCrossDomainFrame(event, proto.polling);
|
|
4766
|
+
if (state !== null && this.consumeCrossDomainEnvelopeBudget(state.source)) {
|
|
4767
|
+
this.postToCrossDomainFrame(state, proto.parentAlive);
|
|
4768
|
+
}
|
|
4769
|
+
return;
|
|
4298
4770
|
}
|
|
4299
4771
|
/**
|
|
4300
4772
|
* proxying messages from iframe to main body, so they can be in one batch (same indexes, etc)
|
|
4301
4773
|
* plus we rewrite some of the messages to be relative to the main context/window
|
|
4302
4774
|
* */
|
|
4303
4775
|
if (data.line === proto.iframeBatch) {
|
|
4304
|
-
const
|
|
4776
|
+
const state = this.validatedCrossDomainFrame(event, proto.iframeBatch);
|
|
4777
|
+
if (state === null || !this.consumeCrossDomainEnvelopeBudget(state.source)) {
|
|
4778
|
+
return;
|
|
4779
|
+
}
|
|
4780
|
+
const batch = parseCrossDomainIframeBatch(data.messages);
|
|
4781
|
+
if (batch === null ||
|
|
4782
|
+
!this.consumeCrossDomainFrameBudget(state, batch.messages.length, batch.bytes)) {
|
|
4783
|
+
return;
|
|
4784
|
+
}
|
|
4305
4785
|
const mappedMessages = [];
|
|
4306
|
-
|
|
4786
|
+
batch.messages.forEach((msg) => {
|
|
4787
|
+
let fixedMessage = msg;
|
|
4307
4788
|
if (msg[0] === 20 /* MType.MouseMove */) {
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
const [type, x, y] = msg;
|
|
4312
|
-
const { left, top } = frame.getBoundingClientRect();
|
|
4313
|
-
fixedMessage = [type, x + left, y + top];
|
|
4314
|
-
}
|
|
4315
|
-
});
|
|
4316
|
-
mappedMessages.push(fixedMessage);
|
|
4789
|
+
const [type, x, y] = msg;
|
|
4790
|
+
const { left, top } = state.frame.getBoundingClientRect();
|
|
4791
|
+
fixedMessage = [type, x + left, y + top];
|
|
4317
4792
|
}
|
|
4318
4793
|
if (msg[0] === 68 /* MType.MouseClick */) {
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
selector,
|
|
4337
|
-
Math.round(fixedX * 1e3) / 1e1,
|
|
4338
|
-
Math.round(fixedY * 1e3) / 1e1,
|
|
4339
|
-
];
|
|
4340
|
-
}
|
|
4341
|
-
});
|
|
4342
|
-
mappedMessages.push(fixedMessage);
|
|
4343
|
-
}
|
|
4344
|
-
if (![28 /* MType.UserID */, 29 /* MType.UserAnonymousID */, 30 /* MType.Metadata */].includes(msg[0])) {
|
|
4345
|
-
mappedMessages.push(msg);
|
|
4794
|
+
const [type, id, hesitationTime, label, selector, normX, normY] = msg;
|
|
4795
|
+
const { left, top, width, height } = state.frame.getBoundingClientRect();
|
|
4796
|
+
const contentWidth = document.documentElement.scrollWidth;
|
|
4797
|
+
const contentHeight = document.documentElement.scrollHeight;
|
|
4798
|
+
const fullX = (normX / 100) * width + left;
|
|
4799
|
+
const fullY = (normY / 100) * height + top;
|
|
4800
|
+
const fixedX = fullX / contentWidth;
|
|
4801
|
+
const fixedY = fullY / contentHeight;
|
|
4802
|
+
fixedMessage = [
|
|
4803
|
+
type,
|
|
4804
|
+
id,
|
|
4805
|
+
hesitationTime,
|
|
4806
|
+
label,
|
|
4807
|
+
selector,
|
|
4808
|
+
Math.round(fixedX * 1e3) / 1e1,
|
|
4809
|
+
Math.round(fixedY * 1e3) / 1e1,
|
|
4810
|
+
];
|
|
4346
4811
|
}
|
|
4812
|
+
mappedMessages.push(fixedMessage);
|
|
4347
4813
|
});
|
|
4348
4814
|
this.messages.push(...mappedMessages);
|
|
4349
4815
|
}
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
const
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
break;
|
|
4365
|
-
}
|
|
4816
|
+
};
|
|
4817
|
+
this.bootChildrenFrames = async () => {
|
|
4818
|
+
await this.waitStarted();
|
|
4819
|
+
this.pruneDetachedCrossDomainFrames();
|
|
4820
|
+
for (const state of this.crossDomainFrames.values()) {
|
|
4821
|
+
const authorizationGeneration = this.crossDomainAuthorizationGeneration;
|
|
4822
|
+
const id = await this.checkNodeId(state.source);
|
|
4823
|
+
const session = this.currentCrossDomainSession();
|
|
4824
|
+
if (id === null ||
|
|
4825
|
+
session === null ||
|
|
4826
|
+
authorizationGeneration !== this.crossDomainAuthorizationGeneration ||
|
|
4827
|
+
this.crossDomainFrames.get(state.source) !== state ||
|
|
4828
|
+
!this.active()) {
|
|
4829
|
+
continue;
|
|
4366
4830
|
}
|
|
4367
|
-
|
|
4368
|
-
|
|
4831
|
+
const rotated = this.crossDomainFrameNeedsRotation(state, session.sessionID);
|
|
4832
|
+
if (rotated && !this.rotateCrossDomainFrameCapability(state, session.sessionID)) {
|
|
4833
|
+
continue;
|
|
4369
4834
|
}
|
|
4370
|
-
const
|
|
4371
|
-
if (
|
|
4372
|
-
this.
|
|
4373
|
-
const message = { line: nextCommand };
|
|
4374
|
-
if (nextCommand === proto.startIframe) {
|
|
4375
|
-
message.token = this.session.getSessionSecret(this.projectKey);
|
|
4376
|
-
const targetFrame = this.pageFrames.find((f) => f.contentWindow === event.source) ||
|
|
4377
|
-
Array.from(document.querySelectorAll('iframe')).find((f) => f.contentWindow === event.source);
|
|
4378
|
-
if (targetFrame) {
|
|
4379
|
-
const nodeId = targetFrame[this.options.node_id];
|
|
4380
|
-
if (nodeId !== undefined) {
|
|
4381
|
-
message.id = nodeId;
|
|
4382
|
-
}
|
|
4383
|
-
}
|
|
4384
|
-
}
|
|
4385
|
-
// @ts-ignore
|
|
4386
|
-
event.source?.postMessage(message, '*');
|
|
4387
|
-
if (this.pollingQueue[nextCommand].length === 0) {
|
|
4388
|
-
delete this.pollingQueue[nextCommand];
|
|
4389
|
-
this.pollingQueue.order.shift();
|
|
4390
|
-
}
|
|
4835
|
+
const payload = id === null ? null : this.frameInitPayload(state, id);
|
|
4836
|
+
if (payload) {
|
|
4837
|
+
this.postToCrossDomainFrame(state, rotated ? proto.iframeId : proto.startIframe, payload);
|
|
4391
4838
|
}
|
|
4392
4839
|
}
|
|
4393
4840
|
};
|
|
4394
|
-
/**
|
|
4395
|
-
* { command : [remaining iframes] }
|
|
4396
|
-
* + order of commands
|
|
4397
|
-
**/
|
|
4398
|
-
this.pollingQueue = {
|
|
4399
|
-
order: [],
|
|
4400
|
-
};
|
|
4401
|
-
this.addCommand = (cmd) => {
|
|
4402
|
-
this.pruneStaleFrames();
|
|
4403
|
-
if (this.pollingQueue[cmd]) {
|
|
4404
|
-
this.pollingQueue[cmd] = Array.from(new Set([...this.pollingQueue[cmd], ...this.trackedFrames]));
|
|
4405
|
-
return;
|
|
4406
|
-
}
|
|
4407
|
-
this.pollingQueue.order.push(cmd);
|
|
4408
|
-
this.pollingQueue[cmd] = [...this.trackedFrames];
|
|
4409
|
-
};
|
|
4410
|
-
this.bootChildrenFrames = async () => {
|
|
4411
|
-
await this.waitStarted();
|
|
4412
|
-
this.addCommand(proto.startIframe);
|
|
4413
|
-
};
|
|
4414
4841
|
this.killChildrenFrames = () => {
|
|
4415
|
-
this.
|
|
4842
|
+
this.pruneDetachedCrossDomainFrames();
|
|
4843
|
+
const states = Array.from(this.crossDomainFrames.values());
|
|
4844
|
+
this.crossDomainAuthorizationGeneration += 1;
|
|
4845
|
+
this.crossDomainFrames.clear();
|
|
4846
|
+
this.pendingCrossDomainFrames.clear();
|
|
4847
|
+
states.forEach((state) => {
|
|
4848
|
+
this.postToCrossDomainFrame(state, proto.killIframe);
|
|
4849
|
+
});
|
|
4416
4850
|
};
|
|
4417
4851
|
this.signalIframeTracker = () => {
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4852
|
+
if (this.crossDomainParentOrigin === null || this.crossDomainFrameNonce === null) {
|
|
4853
|
+
this.debug.error('Cross-domain iframe capture requires an explicit parentDomain and secure random support.');
|
|
4854
|
+
return;
|
|
4855
|
+
}
|
|
4856
|
+
this.crossDomainConnectionRequested = true;
|
|
4857
|
+
this.postHandshakeToParent();
|
|
4424
4858
|
/**
|
|
4425
4859
|
* since we need to wait uncertain amount of time
|
|
4426
4860
|
* and I don't want to have recursion going on,
|
|
@@ -4432,20 +4866,16 @@ class App {
|
|
|
4432
4866
|
let cumulativeDelay = 0;
|
|
4433
4867
|
let stopAttempts = false;
|
|
4434
4868
|
const checkAndSendMessage = () => {
|
|
4435
|
-
if (stopAttempts || this.checkStatus()) {
|
|
4869
|
+
if (stopAttempts || !this.crossDomainConnectionRequested || this.checkStatus()) {
|
|
4436
4870
|
stopAttempts = true;
|
|
4437
4871
|
return;
|
|
4438
4872
|
}
|
|
4439
|
-
|
|
4440
|
-
line: proto.iframeSignal,
|
|
4441
|
-
source: thisTab,
|
|
4442
|
-
context: this.contextId,
|
|
4443
|
-
}, this.options.crossdomain?.parentDomain ?? '*');
|
|
4873
|
+
this.postHandshakeToParent();
|
|
4444
4874
|
this.debug.info('Trying to signal to parent, attempt:', retries + 1);
|
|
4445
4875
|
retries++;
|
|
4446
4876
|
};
|
|
4447
4877
|
for (let i = 0; i < maxRetries; i++) {
|
|
4448
|
-
if (this.checkStatus()) {
|
|
4878
|
+
if (!this.crossDomainConnectionRequested || this.checkStatus()) {
|
|
4449
4879
|
stopAttempts = true;
|
|
4450
4880
|
break;
|
|
4451
4881
|
}
|
|
@@ -4602,9 +5032,7 @@ class App {
|
|
|
4602
5032
|
maskTextEmails: false,
|
|
4603
5033
|
maskTextNumbers: false,
|
|
4604
5034
|
disableStringDict: false,
|
|
4605
|
-
crossdomain: {
|
|
4606
|
-
parentDomain: '*',
|
|
4607
|
-
},
|
|
5035
|
+
crossdomain: {},
|
|
4608
5036
|
canvas: {
|
|
4609
5037
|
disableCanvas: false,
|
|
4610
5038
|
fixedCanvasScaling: false,
|
|
@@ -4617,6 +5045,9 @@ class App {
|
|
|
4617
5045
|
disableThrottling: false,
|
|
4618
5046
|
};
|
|
4619
5047
|
this.options = simpleMerge(defaultOptions, options);
|
|
5048
|
+
this.crossDomainChildOrigins = normalizeCrossDomainOrigins(this.options.crossdomain?.childOrigins);
|
|
5049
|
+
this.crossDomainParentOrigin = normalizeCrossDomainOrigin(this.options.crossdomain?.parentDomain);
|
|
5050
|
+
this.crossDomainFrameNonce = this.insideIframe ? createCrossDomainSecret() : null;
|
|
4620
5051
|
if (!this.insideIframe &&
|
|
4621
5052
|
!this.options.forceSingleTab &&
|
|
4622
5053
|
globalThis &&
|
|
@@ -4663,22 +5094,33 @@ class App {
|
|
|
4663
5094
|
}
|
|
4664
5095
|
});
|
|
4665
5096
|
// @deprecated (use sessionHash on start instead)
|
|
4666
|
-
if (sessionToken != null) {
|
|
5097
|
+
if (sessionToken != null && !this.insideIframe) {
|
|
4667
5098
|
this.session.applySessionHash(sessionToken);
|
|
4668
5099
|
}
|
|
5100
|
+
if (this.insideIframe) {
|
|
5101
|
+
this.session.clearSessionSecret();
|
|
5102
|
+
}
|
|
4669
5103
|
const thisTab = this.session.getTabId();
|
|
4670
5104
|
if (this.insideIframe) {
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
5105
|
+
if (this.crossDomainParentOrigin !== null && this.crossDomainFrameNonce !== null) {
|
|
5106
|
+
/**
|
|
5107
|
+
* listen for messages from parent window, so we can signal that we're alive
|
|
5108
|
+
* */
|
|
5109
|
+
window.addEventListener('message', this.parentCrossDomainFrameListener);
|
|
5110
|
+
setInterval(() => {
|
|
5111
|
+
if (!this.crossDomainConnectionRequested) {
|
|
5112
|
+
return;
|
|
5113
|
+
}
|
|
5114
|
+
if (this.crossDomainFrameCapability === null ||
|
|
5115
|
+
Date.now() >=
|
|
5116
|
+
this.crossDomainFrameCapabilityExpiresAt - FRAME_CAPABILITY_REFRESH_LEAD_MS) {
|
|
5117
|
+
this.postHandshakeToParent();
|
|
5118
|
+
}
|
|
5119
|
+
else {
|
|
5120
|
+
this.postToParent(proto.polling);
|
|
5121
|
+
}
|
|
5122
|
+
}, 1000);
|
|
5123
|
+
}
|
|
4682
5124
|
}
|
|
4683
5125
|
else {
|
|
4684
5126
|
this.initWorker();
|
|
@@ -4686,7 +5128,9 @@ class App {
|
|
|
4686
5128
|
* if we get a signal from child iframes, we check for their node_id and send it back,
|
|
4687
5129
|
* so they can act as if it was just a same-domain iframe
|
|
4688
5130
|
* */
|
|
4689
|
-
|
|
5131
|
+
if (this.options.crossdomain?.enabled && this.crossDomainChildOrigins.size > 0) {
|
|
5132
|
+
window.addEventListener('message', this.crossDomainIframeListener);
|
|
5133
|
+
}
|
|
4690
5134
|
}
|
|
4691
5135
|
if (this.bc !== null) {
|
|
4692
5136
|
this.bc.postMessage({
|
|
@@ -4736,16 +5180,319 @@ class App {
|
|
|
4736
5180
|
};
|
|
4737
5181
|
}
|
|
4738
5182
|
}
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
5183
|
+
postToParent(line, extra = {}) {
|
|
5184
|
+
if (this.crossDomainParentOrigin === null ||
|
|
5185
|
+
this.crossDomainFrameNonce === null ||
|
|
5186
|
+
this.crossDomainFrameCapability === null ||
|
|
5187
|
+
Date.now() >= this.crossDomainFrameCapabilityExpiresAt) {
|
|
5188
|
+
return;
|
|
5189
|
+
}
|
|
5190
|
+
window.parent.postMessage({
|
|
5191
|
+
line,
|
|
5192
|
+
protocolVersion: CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION,
|
|
5193
|
+
context: this.contextId,
|
|
5194
|
+
nonce: this.crossDomainFrameNonce,
|
|
5195
|
+
capability: this.crossDomainFrameCapability,
|
|
5196
|
+
...extra,
|
|
5197
|
+
}, this.crossDomainParentOrigin);
|
|
5198
|
+
}
|
|
5199
|
+
postHandshakeToParent() {
|
|
5200
|
+
if (this.crossDomainParentOrigin === null || this.crossDomainFrameNonce === null) {
|
|
5201
|
+
return;
|
|
5202
|
+
}
|
|
5203
|
+
window.parent.postMessage({
|
|
5204
|
+
line: proto.iframeSignal,
|
|
5205
|
+
protocolVersion: CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION,
|
|
5206
|
+
context: this.contextId,
|
|
5207
|
+
nonce: this.crossDomainFrameNonce,
|
|
5208
|
+
projectKey: this.projectKey,
|
|
5209
|
+
}, this.crossDomainParentOrigin);
|
|
5210
|
+
}
|
|
5211
|
+
applyCrossDomainFrameInit(init) {
|
|
5212
|
+
this.parentActive = true;
|
|
5213
|
+
this.crossDomainConnectionRequested = true;
|
|
5214
|
+
this.crossDomainFrameCapability = init.capability;
|
|
5215
|
+
this.crossDomainFrameCapabilityExpiresAt = init.capabilityExpiresAt;
|
|
5216
|
+
this.crossDomainFrameSession = init.session;
|
|
5217
|
+
this.crossDomainFrameCanvas = init.canvas ?? null;
|
|
5218
|
+
this.rootId = init.id;
|
|
5219
|
+
this.frameOderNumber = init.frameOrderNumber;
|
|
5220
|
+
this.frameLevel = init.frameLevel;
|
|
5221
|
+
this.allowAppStart();
|
|
5222
|
+
}
|
|
5223
|
+
clearCrossDomainFrameAuthorization() {
|
|
5224
|
+
this.parentActive = false;
|
|
5225
|
+
this.canStart = false;
|
|
5226
|
+
this.crossDomainFrameCapability = null;
|
|
5227
|
+
this.crossDomainFrameCapabilityExpiresAt = 0;
|
|
5228
|
+
this.crossDomainFrameSession = null;
|
|
5229
|
+
this.crossDomainFrameCanvas = null;
|
|
5230
|
+
this.rootId = null;
|
|
5231
|
+
}
|
|
5232
|
+
findCrossDomainFrame(source) {
|
|
5233
|
+
const frame = this.pageFrames.find((candidate) => candidate.contentWindow === source) ??
|
|
5234
|
+
Array.from(document.querySelectorAll('iframe')).find((candidate) => candidate.contentWindow === source);
|
|
5235
|
+
if (!frame)
|
|
5236
|
+
return null;
|
|
5237
|
+
if (!this.pageFrames.includes(frame)) {
|
|
5238
|
+
this.pageFrames.push(frame);
|
|
5239
|
+
}
|
|
5240
|
+
return frame;
|
|
5241
|
+
}
|
|
5242
|
+
revokeCrossDomainFrame(source, expectedState) {
|
|
5243
|
+
const state = this.crossDomainFrames.get(source);
|
|
5244
|
+
if (!state || (expectedState !== undefined && state !== expectedState)) {
|
|
5245
|
+
return;
|
|
5246
|
+
}
|
|
5247
|
+
this.crossDomainFrames.delete(source);
|
|
5248
|
+
}
|
|
5249
|
+
pruneDetachedCrossDomainFrames() {
|
|
5250
|
+
this.crossDomainFrames.forEach((state, source) => {
|
|
5251
|
+
if (!state.frame.isConnected || state.frame.contentWindow !== source) {
|
|
5252
|
+
this.revokeCrossDomainFrame(source);
|
|
5253
|
+
}
|
|
4747
5254
|
});
|
|
4748
5255
|
}
|
|
5256
|
+
currentCrossDomainSession() {
|
|
5257
|
+
const session = this.session.getInfo();
|
|
5258
|
+
if (!session.sessionID) {
|
|
5259
|
+
return null;
|
|
5260
|
+
}
|
|
5261
|
+
return {
|
|
5262
|
+
sessionID: session.sessionID,
|
|
5263
|
+
projectID: session.projectID,
|
|
5264
|
+
startedAt: session.timestamp,
|
|
5265
|
+
delayMs: this.delay,
|
|
5266
|
+
userUUID: this.localStorage.getItem(this.options.local_uuid_key) ?? '',
|
|
5267
|
+
};
|
|
5268
|
+
}
|
|
5269
|
+
frameInitPayload(state, id) {
|
|
5270
|
+
const session = this.currentCrossDomainSession();
|
|
5271
|
+
if (session === null ||
|
|
5272
|
+
session.sessionID !== state.sessionID ||
|
|
5273
|
+
Date.now() >= state.capabilityExpiresAt) {
|
|
5274
|
+
return null;
|
|
5275
|
+
}
|
|
5276
|
+
return {
|
|
5277
|
+
id,
|
|
5278
|
+
frameOrderNumber: state.frameOrderNumber,
|
|
5279
|
+
frameLevel: this.frameLevel + 1,
|
|
5280
|
+
capabilityExpiresAt: state.capabilityExpiresAt,
|
|
5281
|
+
session,
|
|
5282
|
+
...(this.crossDomainCanvasConfig ? { canvas: this.crossDomainCanvasConfig } : {}),
|
|
5283
|
+
};
|
|
5284
|
+
}
|
|
5285
|
+
rotateCrossDomainFrameCapability(state, sessionID, now = Date.now()) {
|
|
5286
|
+
const capability = createCrossDomainSecret();
|
|
5287
|
+
if (capability === null) {
|
|
5288
|
+
return false;
|
|
5289
|
+
}
|
|
5290
|
+
const preservePreviousCapability = state.sessionID === sessionID && now < state.capabilityExpiresAt;
|
|
5291
|
+
state.previousCapability = preservePreviousCapability ? state.capability : null;
|
|
5292
|
+
state.previousCapabilityExpiresAt = preservePreviousCapability
|
|
5293
|
+
? Math.min(state.capabilityExpiresAt, now + FRAME_PREVIOUS_CAPABILITY_GRACE_MS)
|
|
5294
|
+
: 0;
|
|
5295
|
+
state.capability = capability;
|
|
5296
|
+
state.sessionID = sessionID;
|
|
5297
|
+
state.capabilityExpiresAt = now + FRAME_CAPABILITY_TTL_MS;
|
|
5298
|
+
return true;
|
|
5299
|
+
}
|
|
5300
|
+
crossDomainFrameNeedsRotation(state, sessionID, now = Date.now()) {
|
|
5301
|
+
return (state.sessionID !== sessionID ||
|
|
5302
|
+
now >= state.capabilityExpiresAt - FRAME_CAPABILITY_REFRESH_LEAD_MS);
|
|
5303
|
+
}
|
|
5304
|
+
postToCrossDomainFrame(state, line, extra = {}) {
|
|
5305
|
+
state.source.postMessage({
|
|
5306
|
+
line,
|
|
5307
|
+
protocolVersion: CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION,
|
|
5308
|
+
context: state.context,
|
|
5309
|
+
nonce: state.nonce,
|
|
5310
|
+
capability: state.capability,
|
|
5311
|
+
...extra,
|
|
5312
|
+
}, state.origin);
|
|
5313
|
+
}
|
|
5314
|
+
consumeCrossDomainEnvelopeBudget(source) {
|
|
5315
|
+
const now = Date.now();
|
|
5316
|
+
let budget = this.crossDomainEnvelopeBudgets.get(source);
|
|
5317
|
+
if (budget === undefined || now - budget.windowStartedAt >= FRAME_RATE_WINDOW_MS) {
|
|
5318
|
+
budget = {
|
|
5319
|
+
windowStartedAt: now,
|
|
5320
|
+
envelopesInWindow: 0,
|
|
5321
|
+
};
|
|
5322
|
+
this.crossDomainEnvelopeBudgets.set(source, budget);
|
|
5323
|
+
}
|
|
5324
|
+
if (budget.envelopesInWindow >= MAX_FRAME_ENVELOPES_PER_WINDOW) {
|
|
5325
|
+
return false;
|
|
5326
|
+
}
|
|
5327
|
+
budget.envelopesInWindow += 1;
|
|
5328
|
+
return true;
|
|
5329
|
+
}
|
|
5330
|
+
async establishCrossDomainFrame(event, handshake) {
|
|
5331
|
+
if (!this.active() ||
|
|
5332
|
+
event.source === null ||
|
|
5333
|
+
!this.crossDomainChildOrigins.has(event.origin)) {
|
|
5334
|
+
return;
|
|
5335
|
+
}
|
|
5336
|
+
const candidateSource = event.source;
|
|
5337
|
+
if (!this.consumeCrossDomainEnvelopeBudget(candidateSource)) {
|
|
5338
|
+
return;
|
|
5339
|
+
}
|
|
5340
|
+
const frame = this.findCrossDomainFrame(candidateSource);
|
|
5341
|
+
if (frame === null || !frame.isConnected || frame.contentWindow === null) {
|
|
5342
|
+
return;
|
|
5343
|
+
}
|
|
5344
|
+
const source = frame.contentWindow;
|
|
5345
|
+
const existing = this.crossDomainFrames.get(source);
|
|
5346
|
+
if (this.pendingCrossDomainFrames.has(source)) {
|
|
5347
|
+
return;
|
|
5348
|
+
}
|
|
5349
|
+
const existingMatches = existing !== undefined &&
|
|
5350
|
+
existing.origin === event.origin &&
|
|
5351
|
+
existing.context === handshake.context &&
|
|
5352
|
+
existing.nonce === handshake.nonce &&
|
|
5353
|
+
existing.frame === frame;
|
|
5354
|
+
if (existing !== undefined && !existingMatches) {
|
|
5355
|
+
return;
|
|
5356
|
+
}
|
|
5357
|
+
const authorizationGeneration = this.crossDomainAuthorizationGeneration;
|
|
5358
|
+
const pendingToken = Symbol();
|
|
5359
|
+
this.pendingCrossDomainFrames.set(source, pendingToken);
|
|
5360
|
+
let navigated = false;
|
|
5361
|
+
const markNavigated = () => {
|
|
5362
|
+
navigated = true;
|
|
5363
|
+
};
|
|
5364
|
+
frame.addEventListener('load', markNavigated, { once: true });
|
|
5365
|
+
try {
|
|
5366
|
+
const id = await this.checkNodeId(source);
|
|
5367
|
+
if (id === null ||
|
|
5368
|
+
navigated ||
|
|
5369
|
+
!this.active() ||
|
|
5370
|
+
authorizationGeneration !== this.crossDomainAuthorizationGeneration ||
|
|
5371
|
+
!frame.isConnected ||
|
|
5372
|
+
frame.contentWindow !== source) {
|
|
5373
|
+
return;
|
|
5374
|
+
}
|
|
5375
|
+
const session = this.currentCrossDomainSession();
|
|
5376
|
+
if (session === null) {
|
|
5377
|
+
return;
|
|
5378
|
+
}
|
|
5379
|
+
const now = Date.now();
|
|
5380
|
+
let state;
|
|
5381
|
+
if (existingMatches) {
|
|
5382
|
+
state = existing;
|
|
5383
|
+
if (this.crossDomainFrameNeedsRotation(state, session.sessionID, now) &&
|
|
5384
|
+
!this.rotateCrossDomainFrameCapability(state, session.sessionID, now)) {
|
|
5385
|
+
return;
|
|
5386
|
+
}
|
|
5387
|
+
state.lastSeenAt = now;
|
|
5388
|
+
}
|
|
5389
|
+
else {
|
|
5390
|
+
const capability = createCrossDomainSecret();
|
|
5391
|
+
if (capability === null) {
|
|
5392
|
+
return;
|
|
5393
|
+
}
|
|
5394
|
+
state = {
|
|
5395
|
+
source,
|
|
5396
|
+
frame,
|
|
5397
|
+
origin: event.origin,
|
|
5398
|
+
protocolVersion: CROSS_DOMAIN_IFRAME_PROTOCOL_VERSION,
|
|
5399
|
+
context: handshake.context,
|
|
5400
|
+
nonce: handshake.nonce,
|
|
5401
|
+
capability,
|
|
5402
|
+
frameOrderNumber: this.nextCrossDomainFrameOrder++,
|
|
5403
|
+
sessionID: session.sessionID,
|
|
5404
|
+
capabilityExpiresAt: now + FRAME_CAPABILITY_TTL_MS,
|
|
5405
|
+
previousCapability: null,
|
|
5406
|
+
previousCapabilityExpiresAt: 0,
|
|
5407
|
+
lastSeenAt: now,
|
|
5408
|
+
rateWindowStartedAt: now,
|
|
5409
|
+
messagesInRateWindow: 0,
|
|
5410
|
+
bytesInRateWindow: 0,
|
|
5411
|
+
};
|
|
5412
|
+
}
|
|
5413
|
+
const payload = this.frameInitPayload(state, id);
|
|
5414
|
+
if (payload === null) {
|
|
5415
|
+
return;
|
|
5416
|
+
}
|
|
5417
|
+
if (!existingMatches) {
|
|
5418
|
+
if (existing !== undefined) {
|
|
5419
|
+
this.revokeCrossDomainFrame(source, existing);
|
|
5420
|
+
}
|
|
5421
|
+
this.crossDomainFrames.set(source, state);
|
|
5422
|
+
frame.addEventListener('load', () => {
|
|
5423
|
+
this.revokeCrossDomainFrame(source, state);
|
|
5424
|
+
}, { once: true });
|
|
5425
|
+
}
|
|
5426
|
+
this.postToCrossDomainFrame(state, proto.iframeId, payload);
|
|
5427
|
+
}
|
|
5428
|
+
finally {
|
|
5429
|
+
frame.removeEventListener('load', markNavigated);
|
|
5430
|
+
if (this.pendingCrossDomainFrames.get(source) === pendingToken) {
|
|
5431
|
+
this.pendingCrossDomainFrames.delete(source);
|
|
5432
|
+
}
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
validatedCrossDomainFrame(event, expectedLine) {
|
|
5436
|
+
if (event.source === null ||
|
|
5437
|
+
!this.crossDomainChildOrigins.has(event.origin) ||
|
|
5438
|
+
event.source === window) {
|
|
5439
|
+
return null;
|
|
5440
|
+
}
|
|
5441
|
+
const source = event.source;
|
|
5442
|
+
const state = this.crossDomainFrames.get(source);
|
|
5443
|
+
const now = Date.now();
|
|
5444
|
+
const currentSessionID = this.session.getInfo().sessionID;
|
|
5445
|
+
if (!state ||
|
|
5446
|
+
state.origin !== event.origin ||
|
|
5447
|
+
!state.frame.isConnected ||
|
|
5448
|
+
state.frame.contentWindow !== source) {
|
|
5449
|
+
if (state) {
|
|
5450
|
+
this.revokeCrossDomainFrame(source);
|
|
5451
|
+
}
|
|
5452
|
+
return null;
|
|
5453
|
+
}
|
|
5454
|
+
if (currentSessionID === undefined || state.sessionID !== currentSessionID) {
|
|
5455
|
+
this.revokeCrossDomainFrame(source);
|
|
5456
|
+
return null;
|
|
5457
|
+
}
|
|
5458
|
+
const matchesCurrentCapability = now < state.capabilityExpiresAt && matchesCrossDomainEnvelope(event.data, expectedLine, state);
|
|
5459
|
+
const matchesPreviousCapability = state.previousCapability !== null &&
|
|
5460
|
+
now < state.previousCapabilityExpiresAt &&
|
|
5461
|
+
matchesCrossDomainEnvelope(event.data, expectedLine, {
|
|
5462
|
+
protocolVersion: state.protocolVersion,
|
|
5463
|
+
context: state.context,
|
|
5464
|
+
nonce: state.nonce,
|
|
5465
|
+
capability: state.previousCapability,
|
|
5466
|
+
});
|
|
5467
|
+
if (!matchesCurrentCapability && !matchesPreviousCapability) {
|
|
5468
|
+
if (now >= state.capabilityExpiresAt && now >= state.previousCapabilityExpiresAt) {
|
|
5469
|
+
this.revokeCrossDomainFrame(source);
|
|
5470
|
+
}
|
|
5471
|
+
return null;
|
|
5472
|
+
}
|
|
5473
|
+
if ((matchesCurrentCapability && state.previousCapability !== null) ||
|
|
5474
|
+
now >= state.previousCapabilityExpiresAt) {
|
|
5475
|
+
state.previousCapability = null;
|
|
5476
|
+
state.previousCapabilityExpiresAt = 0;
|
|
5477
|
+
}
|
|
5478
|
+
state.lastSeenAt = now;
|
|
5479
|
+
return state;
|
|
5480
|
+
}
|
|
5481
|
+
consumeCrossDomainFrameBudget(state, messageCount, bytes) {
|
|
5482
|
+
const now = Date.now();
|
|
5483
|
+
if (now - state.rateWindowStartedAt >= FRAME_RATE_WINDOW_MS) {
|
|
5484
|
+
state.rateWindowStartedAt = now;
|
|
5485
|
+
state.messagesInRateWindow = 0;
|
|
5486
|
+
state.bytesInRateWindow = 0;
|
|
5487
|
+
}
|
|
5488
|
+
if (state.messagesInRateWindow + messageCount > MAX_FRAME_MESSAGES_PER_WINDOW ||
|
|
5489
|
+
state.bytesInRateWindow + bytes > MAX_FRAME_BYTES_PER_WINDOW) {
|
|
5490
|
+
return false;
|
|
5491
|
+
}
|
|
5492
|
+
state.messagesInRateWindow += messageCount;
|
|
5493
|
+
state.bytesInRateWindow += bytes;
|
|
5494
|
+
return true;
|
|
5495
|
+
}
|
|
4749
5496
|
allowAppStart() {
|
|
4750
5497
|
this.canStart = true;
|
|
4751
5498
|
if (this.startTimeout) {
|
|
@@ -4805,6 +5552,7 @@ class App {
|
|
|
4805
5552
|
setTimeout(() => {
|
|
4806
5553
|
closing = false;
|
|
4807
5554
|
}, 500);
|
|
5555
|
+
this.canvasRecorder?.flushPendingSnapshots();
|
|
4808
5556
|
if (this.worker) {
|
|
4809
5557
|
this.worker.postMessage('closing');
|
|
4810
5558
|
}
|
|
@@ -4908,10 +5656,7 @@ class App {
|
|
|
4908
5656
|
return;
|
|
4909
5657
|
}
|
|
4910
5658
|
if (this.insideIframe) {
|
|
4911
|
-
|
|
4912
|
-
line: proto.iframeBatch,
|
|
4913
|
-
messages: this.messages,
|
|
4914
|
-
}, this.options.crossdomain?.parentDomain ?? '*');
|
|
5659
|
+
this.postToParent(proto.iframeBatch, { messages: this.messages });
|
|
4915
5660
|
this.commitCallbacks.forEach((cb) => cb(this.messages));
|
|
4916
5661
|
this.messages.length = 0;
|
|
4917
5662
|
return;
|
|
@@ -5067,6 +5812,159 @@ class App {
|
|
|
5067
5812
|
},
|
|
5068
5813
|
};
|
|
5069
5814
|
}
|
|
5815
|
+
getBootstrapAttemptStorageKey(kind) {
|
|
5816
|
+
return `${this.options.session_token_key}_bootstrap_idempotency_${kind}`;
|
|
5817
|
+
}
|
|
5818
|
+
isValidBootstrapAttempt(value, kind, currentTime) {
|
|
5819
|
+
if (typeof value !== 'object' || value === null) {
|
|
5820
|
+
return false;
|
|
5821
|
+
}
|
|
5822
|
+
const attempt = value;
|
|
5823
|
+
if (attempt.version !== 1 ||
|
|
5824
|
+
attempt.projectKey !== this.projectKey ||
|
|
5825
|
+
typeof attempt.createdAt !== 'number' ||
|
|
5826
|
+
!Number.isFinite(attempt.createdAt) ||
|
|
5827
|
+
currentTime - attempt.createdAt >= BOOTSTRAP_ATTEMPT_TTL_MS ||
|
|
5828
|
+
typeof attempt.idempotencyKey !== 'string' ||
|
|
5829
|
+
attempt.idempotencyKey.length < 8 ||
|
|
5830
|
+
attempt.idempotencyKey.length > 128 ||
|
|
5831
|
+
!/^[A-Za-z0-9._:-]+$/.test(attempt.idempotencyKey) ||
|
|
5832
|
+
typeof attempt.intentFingerprint !== 'string' ||
|
|
5833
|
+
attempt.intentFingerprint.length === 0 ||
|
|
5834
|
+
attempt.intentFingerprint.length > MAX_BOOTSTRAP_ATTEMPT_STORAGE_LENGTH ||
|
|
5835
|
+
typeof attempt.requestTimestamp !== 'number' ||
|
|
5836
|
+
!Number.isFinite(attempt.requestTimestamp) ||
|
|
5837
|
+
typeof attempt.workerTimestamp !== 'number' ||
|
|
5838
|
+
!Number.isFinite(attempt.workerTimestamp) ||
|
|
5839
|
+
typeof attempt.requestBody !== 'string' ||
|
|
5840
|
+
attempt.requestBody.length > MAX_BOOTSTRAP_ATTEMPT_STORAGE_LENGTH) {
|
|
5841
|
+
return false;
|
|
5842
|
+
}
|
|
5843
|
+
try {
|
|
5844
|
+
const body = JSON.parse(attempt.requestBody);
|
|
5845
|
+
const intent = this.parseBootstrapIntent(attempt.intentFingerprint);
|
|
5846
|
+
return (intent?.projectKey === this.projectKey &&
|
|
5847
|
+
intent.kind === kind &&
|
|
5848
|
+
body.app?.key === this.projectKey &&
|
|
5849
|
+
body.session?.dry === false &&
|
|
5850
|
+
body.session?.offline === (kind === 'offline') &&
|
|
5851
|
+
typeof body.session?.token === 'string' &&
|
|
5852
|
+
body.session.token === intent.token &&
|
|
5853
|
+
body.session?.rule === intent.rule &&
|
|
5854
|
+
(body.user?.id ?? '') === intent.userID &&
|
|
5855
|
+
(body.user?.uuid ?? '') === intent.userUUID &&
|
|
5856
|
+
body.meta?.ts === attempt.requestTimestamp &&
|
|
5857
|
+
attempt.idempotencyKey.startsWith(`bootstrap:${kind}:`));
|
|
5858
|
+
}
|
|
5859
|
+
catch {
|
|
5860
|
+
return false;
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5863
|
+
removeStoredBootstrapAttempt(kind) {
|
|
5864
|
+
delete this.bootstrapAttempts[kind];
|
|
5865
|
+
try {
|
|
5866
|
+
this.sessionStorage.removeItem(this.getBootstrapAttemptStorageKey(kind));
|
|
5867
|
+
}
|
|
5868
|
+
catch {
|
|
5869
|
+
// The in-memory copy is still cleared when storage is unavailable.
|
|
5870
|
+
}
|
|
5871
|
+
}
|
|
5872
|
+
loadBootstrapAttempt(kind) {
|
|
5873
|
+
const currentTime = Date.now();
|
|
5874
|
+
const inMemoryAttempt = this.bootstrapAttempts[kind];
|
|
5875
|
+
if (this.isValidBootstrapAttempt(inMemoryAttempt, kind, currentTime)) {
|
|
5876
|
+
return inMemoryAttempt;
|
|
5877
|
+
}
|
|
5878
|
+
delete this.bootstrapAttempts[kind];
|
|
5879
|
+
try {
|
|
5880
|
+
const storedAttempt = this.sessionStorage.getItem(this.getBootstrapAttemptStorageKey(kind));
|
|
5881
|
+
if (storedAttempt === null || storedAttempt.length > MAX_BOOTSTRAP_ATTEMPT_STORAGE_LENGTH) {
|
|
5882
|
+
if (storedAttempt !== null) {
|
|
5883
|
+
this.removeStoredBootstrapAttempt(kind);
|
|
5884
|
+
}
|
|
5885
|
+
return undefined;
|
|
5886
|
+
}
|
|
5887
|
+
const parsedAttempt = JSON.parse(storedAttempt);
|
|
5888
|
+
if (this.isValidBootstrapAttempt(parsedAttempt, kind, currentTime)) {
|
|
5889
|
+
this.bootstrapAttempts[kind] = parsedAttempt;
|
|
5890
|
+
return parsedAttempt;
|
|
5891
|
+
}
|
|
5892
|
+
}
|
|
5893
|
+
catch {
|
|
5894
|
+
// Treat corrupt or inaccessible storage as an absent pending attempt.
|
|
5895
|
+
}
|
|
5896
|
+
this.removeStoredBootstrapAttempt(kind);
|
|
5897
|
+
return undefined;
|
|
5898
|
+
}
|
|
5899
|
+
getOrCreateBootstrapAttempt(kind, request) {
|
|
5900
|
+
const existingAttempt = this.loadBootstrapAttempt(kind);
|
|
5901
|
+
if (existingAttempt &&
|
|
5902
|
+
this.bootstrapIntentsMatch(existingAttempt.intentFingerprint, request.intentFingerprint)) {
|
|
5903
|
+
return existingAttempt;
|
|
5904
|
+
}
|
|
5905
|
+
if (existingAttempt) {
|
|
5906
|
+
this.removeStoredBootstrapAttempt(kind);
|
|
5907
|
+
}
|
|
5908
|
+
const attempt = {
|
|
5909
|
+
version: 1,
|
|
5910
|
+
projectKey: this.projectKey,
|
|
5911
|
+
createdAt: Date.now(),
|
|
5912
|
+
idempotencyKey: `bootstrap:${kind}:${generateRandomId(40)}`,
|
|
5913
|
+
...request,
|
|
5914
|
+
};
|
|
5915
|
+
this.bootstrapAttempts[kind] = attempt;
|
|
5916
|
+
try {
|
|
5917
|
+
this.sessionStorage.setItem(this.getBootstrapAttemptStorageKey(kind), JSON.stringify(attempt));
|
|
5918
|
+
}
|
|
5919
|
+
catch {
|
|
5920
|
+
// The in-memory copy still protects retries within this tracker instance.
|
|
5921
|
+
}
|
|
5922
|
+
return attempt;
|
|
5923
|
+
}
|
|
5924
|
+
clearBootstrapAttempt(kind) {
|
|
5925
|
+
this.removeStoredBootstrapAttempt(kind);
|
|
5926
|
+
}
|
|
5927
|
+
buildBootstrapIntentFingerprint(params) {
|
|
5928
|
+
const tracker = this.getTrackerInfo();
|
|
5929
|
+
return JSON.stringify({
|
|
5930
|
+
projectKey: this.projectKey,
|
|
5931
|
+
kind: params.kind,
|
|
5932
|
+
token: params.token ?? '',
|
|
5933
|
+
rule: params.rule ?? '',
|
|
5934
|
+
userID: this.session.getInfo().userID ?? '',
|
|
5935
|
+
userUUID: tracker.userUUID ?? '',
|
|
5936
|
+
});
|
|
5937
|
+
}
|
|
5938
|
+
parseBootstrapIntent(value) {
|
|
5939
|
+
try {
|
|
5940
|
+
const intent = JSON.parse(value);
|
|
5941
|
+
if (intent.projectKey !== this.projectKey ||
|
|
5942
|
+
(intent.kind !== 'recording' && intent.kind !== 'offline') ||
|
|
5943
|
+
typeof intent.token !== 'string' ||
|
|
5944
|
+
typeof intent.rule !== 'string' ||
|
|
5945
|
+
typeof intent.userID !== 'string' ||
|
|
5946
|
+
typeof intent.userUUID !== 'string') {
|
|
5947
|
+
return undefined;
|
|
5948
|
+
}
|
|
5949
|
+
return intent;
|
|
5950
|
+
}
|
|
5951
|
+
catch {
|
|
5952
|
+
return undefined;
|
|
5953
|
+
}
|
|
5954
|
+
}
|
|
5955
|
+
bootstrapIntentsMatch(existingFingerprint, currentFingerprint) {
|
|
5956
|
+
const existing = this.parseBootstrapIntent(existingFingerprint);
|
|
5957
|
+
const current = this.parseBootstrapIntent(currentFingerprint);
|
|
5958
|
+
if (!existing || !current) {
|
|
5959
|
+
return false;
|
|
5960
|
+
}
|
|
5961
|
+
return (existing.projectKey === current.projectKey &&
|
|
5962
|
+
existing.kind === current.kind &&
|
|
5963
|
+
existing.token === current.token &&
|
|
5964
|
+
(current.rule === '' || existing.rule === current.rule) &&
|
|
5965
|
+
(current.userID === '' || existing.userID === current.userID) &&
|
|
5966
|
+
(current.userUUID === '' || existing.userUUID === current.userUUID));
|
|
5967
|
+
}
|
|
5070
5968
|
getSessionMeta() {
|
|
5071
5969
|
return {
|
|
5072
5970
|
...this.session.getInfo(),
|
|
@@ -5137,12 +6035,19 @@ class App {
|
|
|
5137
6035
|
const needReset = this.sessionStorage.getItem(this.options.session_reset_key) !== null;
|
|
5138
6036
|
let needNewSessionID = startNewSession || needReset;
|
|
5139
6037
|
const sessionToken = this.session.getSessionSecret(this.projectKey);
|
|
6038
|
+
if (needReset) {
|
|
6039
|
+
this.sessionStorage.removeItem(this.options.session_reset_key);
|
|
6040
|
+
}
|
|
5140
6041
|
const storedVersion = this.sessionStorage.getItem(`${this.options.session_token_key}_version`);
|
|
5141
6042
|
if (!storedVersion || storedVersion !== PROTO_VERSION) {
|
|
5142
6043
|
needNewSessionID = true;
|
|
5143
6044
|
this.sessionStorage.setItem(`${this.options.session_token_key}_version`, PROTO_VERSION);
|
|
5144
6045
|
}
|
|
5145
|
-
|
|
6046
|
+
const isNewSession = needNewSessionID || !sessionToken;
|
|
6047
|
+
if (isNewSession && sessionToken) {
|
|
6048
|
+
this.session.clearSessionSecret();
|
|
6049
|
+
}
|
|
6050
|
+
return isNewSession;
|
|
5146
6051
|
}
|
|
5147
6052
|
/**
|
|
5148
6053
|
* start buffering messages without starting the actual session, which gives
|
|
@@ -5214,7 +6119,7 @@ class App {
|
|
|
5214
6119
|
})),
|
|
5215
6120
|
});
|
|
5216
6121
|
const { session = {}, client = {}, device = {} } = await r.json();
|
|
5217
|
-
const { token, projectId } = session;
|
|
6122
|
+
const { token, projectId, assistToken } = session;
|
|
5218
6123
|
const { browser: userBrowser, city: userCity, country: userCountry, device: userDevice, os: userOS, state: userState, } = client;
|
|
5219
6124
|
this.session.assign({ projectID: projectId });
|
|
5220
6125
|
this.session.setUserInfo({
|
|
@@ -5227,6 +6132,7 @@ class App {
|
|
|
5227
6132
|
});
|
|
5228
6133
|
const onStartInfo = {
|
|
5229
6134
|
sessionToken: token,
|
|
6135
|
+
assistToken: typeof assistToken === 'string' ? assistToken : undefined,
|
|
5230
6136
|
userUUID: device?.id || '',
|
|
5231
6137
|
sessionID: session?.id || '',
|
|
5232
6138
|
};
|
|
@@ -5242,7 +6148,6 @@ class App {
|
|
|
5242
6148
|
offlineRecording(startOpts = {}, onSessionSent) {
|
|
5243
6149
|
this.onSessionSent = onSessionSent;
|
|
5244
6150
|
this.singleBuffer = true;
|
|
5245
|
-
const isNewSession = this.checkSessionToken(startOpts.startNewSession);
|
|
5246
6151
|
adjustTimeOrigin();
|
|
5247
6152
|
this.coldStartTs = now();
|
|
5248
6153
|
const saverBuffer = this.localStorage.getItem(bufferStorageKey);
|
|
@@ -5260,6 +6165,10 @@ class App {
|
|
|
5260
6165
|
if (startOpts.startNewSession) {
|
|
5261
6166
|
this.session.reset();
|
|
5262
6167
|
}
|
|
6168
|
+
const isNewSession = this.checkSessionToken(startOpts.startNewSession);
|
|
6169
|
+
this.offlineRecordingSessionToken = isNewSession
|
|
6170
|
+
? undefined
|
|
6171
|
+
: this.session.getSessionSecret(this.projectKey);
|
|
5263
6172
|
this.session.assign({
|
|
5264
6173
|
userID: startOpts.userID,
|
|
5265
6174
|
metadata: startOpts.metadata,
|
|
@@ -5309,12 +6218,29 @@ class App {
|
|
|
5309
6218
|
* */
|
|
5310
6219
|
async uploadOfflineRecording() {
|
|
5311
6220
|
this.stop(false);
|
|
5312
|
-
const
|
|
6221
|
+
const freshTimestamp = now();
|
|
6222
|
+
const bootstrapToken = this.offlineRecordingSessionToken ?? '';
|
|
6223
|
+
const freshRequestBody = () => JSON.stringify(this.buildStartPayload({
|
|
6224
|
+
timestamp: freshTimestamp,
|
|
6225
|
+
dry: false,
|
|
6226
|
+
offline: true,
|
|
6227
|
+
bufferMs: freshTimestamp - this.coldStartTs,
|
|
6228
|
+
token: bootstrapToken,
|
|
6229
|
+
}));
|
|
6230
|
+
const bootstrapAttempt = this.getOrCreateBootstrapAttempt('offline', {
|
|
6231
|
+
intentFingerprint: this.buildBootstrapIntentFingerprint({
|
|
6232
|
+
kind: 'offline',
|
|
6233
|
+
token: bootstrapToken,
|
|
6234
|
+
}),
|
|
6235
|
+
requestTimestamp: freshTimestamp,
|
|
6236
|
+
workerTimestamp: this.coldStartTs,
|
|
6237
|
+
requestBody: freshRequestBody(),
|
|
6238
|
+
});
|
|
5313
6239
|
this.worker?.postMessage({
|
|
5314
6240
|
type: 'start',
|
|
5315
6241
|
pageNo: this.session.incPageNo(),
|
|
5316
6242
|
ingestPoint: normalizeEndpoint(this.options.endpoint),
|
|
5317
|
-
timestamp:
|
|
6243
|
+
timestamp: bootstrapAttempt.workerTimestamp,
|
|
5318
6244
|
url: document.URL,
|
|
5319
6245
|
connAttemptCount: this.options.connAttemptCount,
|
|
5320
6246
|
connAttemptGap: this.options.connAttemptGap,
|
|
@@ -5322,28 +6248,30 @@ class App {
|
|
|
5322
6248
|
localDebug: this.options.__local_debug,
|
|
5323
6249
|
});
|
|
5324
6250
|
const ingestPoint = normalizeEndpoint(this.options.endpoint);
|
|
6251
|
+
const headers = {
|
|
6252
|
+
'Content-Type': 'application/json',
|
|
6253
|
+
'Idempotency-Key': bootstrapAttempt.idempotencyKey,
|
|
6254
|
+
};
|
|
5325
6255
|
const r = await fetch(ingestPoint + '/v1/bootstrap', {
|
|
5326
6256
|
method: 'POST',
|
|
5327
|
-
headers
|
|
5328
|
-
|
|
5329
|
-
},
|
|
5330
|
-
body: JSON.stringify(this.buildStartPayload({
|
|
5331
|
-
timestamp,
|
|
5332
|
-
dry: false,
|
|
5333
|
-
bufferMs: timestamp - this.coldStartTs,
|
|
5334
|
-
token: '',
|
|
5335
|
-
})),
|
|
6257
|
+
headers,
|
|
6258
|
+
body: bootstrapAttempt.requestBody,
|
|
5336
6259
|
});
|
|
5337
|
-
|
|
6260
|
+
if (r.status !== 200) {
|
|
6261
|
+
throw new Error(`Server error: ${r.status}. ${await r.text()}`);
|
|
6262
|
+
}
|
|
6263
|
+
const { session = {}, client = {}, limits = {}, protocolVersion: offlineProtocolVersion, } = await r.json();
|
|
5338
6264
|
const { token, projectId } = session;
|
|
5339
6265
|
const { browser: userBrowser, city: userCity, country: userCountry, device: userDevice, os: userOS, state: userState, } = client;
|
|
5340
6266
|
const { beacon: beaconSizeLimit } = limits;
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
6267
|
+
if (typeof token !== 'string' ||
|
|
6268
|
+
token.length === 0 ||
|
|
6269
|
+
(typeof beaconSizeLimit !== 'number' && typeof beaconSizeLimit !== 'undefined')) {
|
|
6270
|
+
throw new Error('Incorrect server response for offline recording bootstrap');
|
|
6271
|
+
}
|
|
6272
|
+
this.session.setSessionSecret(token, this.projectKey);
|
|
6273
|
+
this.clearBootstrapAttempt('offline');
|
|
6274
|
+
this.offlineRecordingSessionToken = token;
|
|
5347
6275
|
this.session.assign({ projectID: projectId });
|
|
5348
6276
|
this.session.setUserInfo({
|
|
5349
6277
|
userBrowser,
|
|
@@ -5353,12 +6281,77 @@ class App {
|
|
|
5353
6281
|
userOS,
|
|
5354
6282
|
userState,
|
|
5355
6283
|
});
|
|
6284
|
+
this.worker?.postMessage({
|
|
6285
|
+
type: 'auth',
|
|
6286
|
+
token,
|
|
6287
|
+
beaconSizeLimit,
|
|
6288
|
+
protocolVersion: offlineProtocolVersion,
|
|
6289
|
+
});
|
|
5356
6290
|
while (this.bufferedMessages1.length > 0) {
|
|
5357
6291
|
await this.flushBuffer(this.bufferedMessages1);
|
|
5358
6292
|
}
|
|
5359
6293
|
this.postToWorker([[-1]]);
|
|
5360
6294
|
this.clearBuffers();
|
|
5361
6295
|
}
|
|
6296
|
+
startCrossDomainFrame(startOpts = {}) {
|
|
6297
|
+
if (this.activityState === ActivityState.Active ||
|
|
6298
|
+
this.activityState === ActivityState.Starting) {
|
|
6299
|
+
const reason = 'OxvoSessions: trying to call `start()` on the instance that has been started already.';
|
|
6300
|
+
return UnsuccessfulStart(reason);
|
|
6301
|
+
}
|
|
6302
|
+
if (this.crossDomainFrameSession === null ||
|
|
6303
|
+
this.crossDomainFrameCapability === null ||
|
|
6304
|
+
this.rootId === null) {
|
|
6305
|
+
return UnsuccessfulStart('Cross-domain iframe handshake was not established.');
|
|
6306
|
+
}
|
|
6307
|
+
if (Object.keys(startOpts).length !== 0) {
|
|
6308
|
+
this.prevOpts = startOpts;
|
|
6309
|
+
}
|
|
6310
|
+
if (startOpts.startCallback) {
|
|
6311
|
+
this.userStartCallback = startOpts.startCallback;
|
|
6312
|
+
}
|
|
6313
|
+
try {
|
|
6314
|
+
adjustTimeOrigin();
|
|
6315
|
+
this.activityState = ActivityState.Starting;
|
|
6316
|
+
this.delay = this.crossDomainFrameSession.delayMs;
|
|
6317
|
+
this.session.clearSessionSecret();
|
|
6318
|
+
this.session.assign({
|
|
6319
|
+
sessionID: this.crossDomainFrameSession.sessionID,
|
|
6320
|
+
projectID: this.crossDomainFrameSession.projectID,
|
|
6321
|
+
timestamp: this.crossDomainFrameSession.startedAt,
|
|
6322
|
+
});
|
|
6323
|
+
const onStartInfo = {
|
|
6324
|
+
sessionToken: '',
|
|
6325
|
+
userUUID: this.crossDomainFrameSession.userUUID,
|
|
6326
|
+
sessionID: this.crossDomainFrameSession.sessionID,
|
|
6327
|
+
};
|
|
6328
|
+
this.startCallbacks.forEach((callback) => callback(onStartInfo));
|
|
6329
|
+
this.userStartCallback?.(SuccessfulStart(onStartInfo));
|
|
6330
|
+
this.activityState = ActivityState.Active;
|
|
6331
|
+
if (this.crossDomainFrameCanvas?.enabled && !this.options.canvas.disableCanvas) {
|
|
6332
|
+
this.canvasRecorder =
|
|
6333
|
+
this.canvasRecorder ??
|
|
6334
|
+
new CanvasRecorder(this, {
|
|
6335
|
+
fps: this.crossDomainFrameCanvas.fps ?? 15,
|
|
6336
|
+
quality: this.crossDomainFrameCanvas.quality ?? 'medium',
|
|
6337
|
+
isDebug: this.options.canvas.__save_canvas_locally,
|
|
6338
|
+
fixedScaling: this.options.canvas.fixedCanvasScaling,
|
|
6339
|
+
useAnimationFrame: this.options.canvas.useAnimationFrame,
|
|
6340
|
+
framesSupport: this.crossDomainFrameCanvas.framesSupport,
|
|
6341
|
+
});
|
|
6342
|
+
}
|
|
6343
|
+
this.observer.crossdomainObserve(this.rootId, this.frameOderNumber, this.frameLevel);
|
|
6344
|
+
this.ticker.start();
|
|
6345
|
+
this.canvasRecorder?.startTracking();
|
|
6346
|
+
return SuccessfulStart(onStartInfo);
|
|
6347
|
+
}
|
|
6348
|
+
catch (reason) {
|
|
6349
|
+
this.stop();
|
|
6350
|
+
const message = reason instanceof Error ? reason.message : String(reason);
|
|
6351
|
+
this.signalError(message, []);
|
|
6352
|
+
return UnsuccessfulStart(message);
|
|
6353
|
+
}
|
|
6354
|
+
}
|
|
5362
6355
|
async _start(startOpts = {}, resetByWorker = false, conditionName) {
|
|
5363
6356
|
if (Object.keys(startOpts).length !== 0) {
|
|
5364
6357
|
this.prevOpts = startOpts;
|
|
@@ -5397,68 +6390,89 @@ class App {
|
|
|
5397
6390
|
userID: userId || undefined,
|
|
5398
6391
|
metadata: startOpts.metadata,
|
|
5399
6392
|
});
|
|
5400
|
-
const
|
|
6393
|
+
const sessionToken = this.session.getSessionSecret(this.projectKey);
|
|
6394
|
+
const isNewSession = this.checkSessionToken(startOpts.startNewSession);
|
|
6395
|
+
const freshTimestamp = now();
|
|
6396
|
+
const freshWorkerTimestamp = isColdStart ? this.coldStartTs : freshTimestamp;
|
|
6397
|
+
const bootstrapToken = isNewSession ? '' : sessionToken;
|
|
6398
|
+
const freshRequestBody = () => JSON.stringify(this.buildStartPayload({
|
|
6399
|
+
timestamp: freshTimestamp,
|
|
6400
|
+
dry: false,
|
|
6401
|
+
bufferMs: this.coldStartTs ? freshTimestamp - this.coldStartTs : 0,
|
|
6402
|
+
token: bootstrapToken,
|
|
6403
|
+
rule: conditionName,
|
|
6404
|
+
width: window.screen.width,
|
|
6405
|
+
height: window.screen.height,
|
|
6406
|
+
}));
|
|
6407
|
+
const bootstrapAttempt = this.getOrCreateBootstrapAttempt('recording', {
|
|
6408
|
+
intentFingerprint: this.buildBootstrapIntentFingerprint({
|
|
6409
|
+
kind: 'recording',
|
|
6410
|
+
token: bootstrapToken,
|
|
6411
|
+
rule: conditionName,
|
|
6412
|
+
}),
|
|
6413
|
+
requestTimestamp: freshTimestamp,
|
|
6414
|
+
workerTimestamp: freshWorkerTimestamp,
|
|
6415
|
+
requestBody: freshRequestBody(),
|
|
6416
|
+
});
|
|
6417
|
+
const timestamp = bootstrapAttempt.requestTimestamp;
|
|
5401
6418
|
this.worker?.postMessage({
|
|
5402
6419
|
type: 'start',
|
|
5403
6420
|
pageNo: this.session.incPageNo(),
|
|
5404
6421
|
ingestPoint: normalizeEndpoint(this.options.endpoint),
|
|
5405
|
-
timestamp:
|
|
6422
|
+
timestamp: bootstrapAttempt.workerTimestamp,
|
|
5406
6423
|
url: document.URL,
|
|
5407
6424
|
connAttemptCount: this.options.connAttemptCount,
|
|
5408
6425
|
connAttemptGap: this.options.connAttemptGap,
|
|
5409
6426
|
tabId: this.session.getTabId(),
|
|
5410
6427
|
localDebug: this.options.__local_debug,
|
|
5411
6428
|
});
|
|
5412
|
-
const sessionToken = this.session.getSessionSecret(this.projectKey);
|
|
5413
|
-
const isNewSession = this.checkSessionToken(startOpts.startNewSession);
|
|
5414
|
-
this.sessionStorage.removeItem(this.options.session_reset_key);
|
|
5415
6429
|
this.debug.log('OxvoSessions: starting session; need new session id?', isNewSession, 'session token: ', sessionToken);
|
|
5416
6430
|
try {
|
|
5417
6431
|
const ingestPoint = normalizeEndpoint(this.options.endpoint);
|
|
6432
|
+
const headers = {
|
|
6433
|
+
'Content-Type': 'application/json',
|
|
6434
|
+
'Idempotency-Key': bootstrapAttempt.idempotencyKey,
|
|
6435
|
+
};
|
|
5418
6436
|
const r = await window.fetch(ingestPoint + '/v1/bootstrap', {
|
|
5419
6437
|
method: 'POST',
|
|
5420
|
-
headers
|
|
5421
|
-
|
|
5422
|
-
},
|
|
5423
|
-
body: JSON.stringify(this.buildStartPayload({
|
|
5424
|
-
timestamp,
|
|
5425
|
-
dry: false,
|
|
5426
|
-
bufferMs: this.coldStartTs ? timestamp - this.coldStartTs : 0,
|
|
5427
|
-
token: isNewSession ? '' : sessionToken,
|
|
5428
|
-
rule: conditionName,
|
|
5429
|
-
width: window.screen.width,
|
|
5430
|
-
height: window.screen.height,
|
|
5431
|
-
})),
|
|
6438
|
+
headers,
|
|
6439
|
+
body: bootstrapAttempt.requestBody,
|
|
5432
6440
|
});
|
|
5433
6441
|
if (r.status !== 200) {
|
|
5434
6442
|
const error = await r.text();
|
|
5435
6443
|
const reason = error === CANCELED ? CANCELED : `Server error: ${r.status}. ${error}`;
|
|
5436
|
-
|
|
6444
|
+
throw reason;
|
|
5437
6445
|
}
|
|
5438
6446
|
if (!this.worker && !this.insideIframe) {
|
|
5439
6447
|
const reason = 'no worker found after start request (this should not happen in real world)';
|
|
5440
|
-
|
|
5441
|
-
return UnsuccessfulStart(reason);
|
|
6448
|
+
throw new Error(reason);
|
|
5442
6449
|
}
|
|
5443
6450
|
const { session = {}, device = {}, client = {}, limits = {}, canvas = {}, flags = {}, protocolVersion, } = await r.json();
|
|
5444
|
-
const { token, id: sessionID, projectId: projectID, delayMs: delay, startedAt: startTimestamp, } = session;
|
|
6451
|
+
const { token, assistToken, id: sessionID, projectId: projectID, delayMs: delay, startedAt: startTimestamp, } = session;
|
|
5445
6452
|
const { id: userUUID } = device;
|
|
5446
6453
|
const { browser: userBrowser, city: userCity, country: userCountry, device: userDevice, os: userOS, state: userState, } = client;
|
|
5447
6454
|
const { beacon: beaconSizeLimit, compressAt: compressionThreshold } = limits;
|
|
5448
6455
|
const { enabled: canvasEnabled, quality: canvasQuality, fps: canvasFPS, framesSupport, } = canvas;
|
|
5449
6456
|
const socketOnly = flags?.socketOnly;
|
|
5450
6457
|
if (typeof token !== 'string' ||
|
|
6458
|
+
(typeof assistToken !== 'string' && typeof assistToken !== 'undefined') ||
|
|
5451
6459
|
typeof userUUID !== 'string' ||
|
|
5452
6460
|
(typeof startTimestamp !== 'number' && typeof startTimestamp !== 'undefined') ||
|
|
5453
6461
|
typeof sessionID !== 'string' ||
|
|
5454
6462
|
typeof delay !== 'number' ||
|
|
5455
6463
|
(typeof beaconSizeLimit !== 'number' && typeof beaconSizeLimit !== 'undefined')) {
|
|
5456
6464
|
const reason = `Incorrect server response: ${JSON.stringify(r)}`;
|
|
5457
|
-
|
|
5458
|
-
return UnsuccessfulStart(reason);
|
|
6465
|
+
throw new Error(reason);
|
|
5459
6466
|
}
|
|
6467
|
+
this.crossDomainCanvasConfig = {
|
|
6468
|
+
enabled: canvasEnabled === true,
|
|
6469
|
+
...(['low', 'medium', 'high'].includes(canvasQuality) ? { quality: canvasQuality } : {}),
|
|
6470
|
+
...(typeof canvasFPS === 'number' ? { fps: canvasFPS } : {}),
|
|
6471
|
+
...(typeof framesSupport === 'boolean' ? { framesSupport } : {}),
|
|
6472
|
+
};
|
|
5460
6473
|
this.delay = delay;
|
|
5461
6474
|
this.session.setSessionSecret(token, this.projectKey);
|
|
6475
|
+
this.clearBootstrapAttempt('recording');
|
|
5462
6476
|
if (sessionToken && sessionToken !== token) {
|
|
5463
6477
|
this.bc?.postMessage({
|
|
5464
6478
|
type: proto.reset,
|
|
@@ -5499,7 +6513,7 @@ class App {
|
|
|
5499
6513
|
Object.entries(this.session.getInfo().metadata).forEach(([key, value]) => this.send(Metadata(key, value)));
|
|
5500
6514
|
this.localStorage.setItem(this.options.local_uuid_key, userUUID);
|
|
5501
6515
|
this.compressionThreshold = compressionThreshold;
|
|
5502
|
-
const onStartInfo = { sessionToken: token, userUUID, sessionID };
|
|
6516
|
+
const onStartInfo = { sessionToken: token, assistToken, userUUID, sessionID };
|
|
5503
6517
|
// TODO: start as early as possible (before receiving the token)
|
|
5504
6518
|
/** after start */
|
|
5505
6519
|
this.startCallbacks.forEach((cb) => cb(onStartInfo)); // MBTODO: callbacks after DOM "mounted" (observed)
|
|
@@ -5547,7 +6561,6 @@ class App {
|
|
|
5547
6561
|
}
|
|
5548
6562
|
catch (reason) {
|
|
5549
6563
|
this.stop();
|
|
5550
|
-
this.session.reset();
|
|
5551
6564
|
if (!reason) {
|
|
5552
6565
|
console.error('Unknown error during start');
|
|
5553
6566
|
this.signalError('Unknown error', []);
|
|
@@ -5573,6 +6586,23 @@ class App {
|
|
|
5573
6586
|
}, 100);
|
|
5574
6587
|
});
|
|
5575
6588
|
}
|
|
6589
|
+
async waitCrossDomainStart(timeoutMs = 15000) {
|
|
6590
|
+
if (this.canStart)
|
|
6591
|
+
return true;
|
|
6592
|
+
return new Promise((resolve) => {
|
|
6593
|
+
const interval = setInterval(() => {
|
|
6594
|
+
if (!this.canStart)
|
|
6595
|
+
return;
|
|
6596
|
+
clearInterval(interval);
|
|
6597
|
+
clearTimeout(timeout);
|
|
6598
|
+
resolve(true);
|
|
6599
|
+
}, 100);
|
|
6600
|
+
const timeout = setTimeout(() => {
|
|
6601
|
+
clearInterval(interval);
|
|
6602
|
+
resolve(false);
|
|
6603
|
+
}, timeoutMs);
|
|
6604
|
+
});
|
|
6605
|
+
}
|
|
5576
6606
|
async waitStarted() {
|
|
5577
6607
|
return this.waitStatus(ActivityState.Active);
|
|
5578
6608
|
}
|
|
@@ -5600,20 +6630,35 @@ class App {
|
|
|
5600
6630
|
return Promise.resolve(UnsuccessfulStart(reason));
|
|
5601
6631
|
}
|
|
5602
6632
|
if (this.insideIframe) {
|
|
6633
|
+
if (this.crossDomainParentOrigin === null || this.crossDomainFrameNonce === null) {
|
|
6634
|
+
return UnsuccessfulStart('Cross-domain iframe capture requires an explicit parentDomain and secure random support.');
|
|
6635
|
+
}
|
|
5603
6636
|
this.signalIframeTracker();
|
|
5604
6637
|
}
|
|
5605
|
-
|
|
6638
|
+
const startWhenReady = async () => {
|
|
6639
|
+
if (this.insideIframe) {
|
|
6640
|
+
const ready = await this.waitCrossDomainStart();
|
|
6641
|
+
if (!ready) {
|
|
6642
|
+
this.crossDomainConnectionRequested = false;
|
|
6643
|
+
this.crossDomainResumeAfterHandshake = false;
|
|
6644
|
+
this.clearCrossDomainFrameAuthorization();
|
|
6645
|
+
return UnsuccessfulStart('Cross-domain iframe handshake timed out.');
|
|
6646
|
+
}
|
|
6647
|
+
return this.startCrossDomainFrame(args[0]);
|
|
6648
|
+
}
|
|
5606
6649
|
await this.waitStart();
|
|
5607
6650
|
return this._start(...args);
|
|
6651
|
+
};
|
|
6652
|
+
if (!document.hidden) {
|
|
6653
|
+
return startWhenReady();
|
|
5608
6654
|
}
|
|
5609
6655
|
else {
|
|
5610
6656
|
return new Promise((resolve) => {
|
|
5611
6657
|
const onVisibilityChange = async () => {
|
|
5612
6658
|
if (!document.hidden) {
|
|
5613
|
-
await this.waitStart();
|
|
5614
6659
|
// eslint-disable-next-line
|
|
5615
6660
|
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
5616
|
-
resolve(
|
|
6661
|
+
resolve(startWhenReady());
|
|
5617
6662
|
}
|
|
5618
6663
|
};
|
|
5619
6664
|
// eslint-disable-next-line
|
|
@@ -5674,6 +6719,11 @@ class App {
|
|
|
5674
6719
|
this.debug.log('OxvoSessions tracking stopped.');
|
|
5675
6720
|
}
|
|
5676
6721
|
}
|
|
6722
|
+
if (this.insideIframe) {
|
|
6723
|
+
this.crossDomainConnectionRequested = false;
|
|
6724
|
+
this.crossDomainResumeAfterHandshake = false;
|
|
6725
|
+
this.clearCrossDomainFrameAuthorization();
|
|
6726
|
+
}
|
|
5677
6727
|
}
|
|
5678
6728
|
}
|
|
5679
6729
|
|
|
@@ -9233,7 +10283,7 @@ class ConstantProperties {
|
|
|
9233
10283
|
user_id: this.user_id,
|
|
9234
10284
|
distinct_id: this.deviceId,
|
|
9235
10285
|
sdk_edition: 'web',
|
|
9236
|
-
sdk_version: '7.3.
|
|
10286
|
+
sdk_version: '7.3.4',
|
|
9237
10287
|
timezone: getUTCOffsetString(),
|
|
9238
10288
|
search_engine: this.searchEngine,
|
|
9239
10289
|
};
|
|
@@ -9816,6 +10866,8 @@ class Batcher {
|
|
|
9816
10866
|
}
|
|
9817
10867
|
|
|
9818
10868
|
const STORAGEKEY = '__or_sdk_analytics_token';
|
|
10869
|
+
const START_ATTEMPT_STORAGE_KEY = '__or_sdk_analytics_start_attempt';
|
|
10870
|
+
const START_ATTEMPT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
9819
10871
|
const normalizeIngestPoint = (ingest) => {
|
|
9820
10872
|
const base = ingest.replace(/\/$/, '');
|
|
9821
10873
|
return base.endsWith('/oxvo') ? base : `${base}/oxvo`;
|
|
@@ -9839,6 +10891,7 @@ class Analytics {
|
|
|
9839
10891
|
constructor(options) {
|
|
9840
10892
|
this.token = null;
|
|
9841
10893
|
this.standalone = false;
|
|
10894
|
+
this.pendingStartAttempt = null;
|
|
9842
10895
|
this._getToken = () => {
|
|
9843
10896
|
if (this.standalone) {
|
|
9844
10897
|
return this.token;
|
|
@@ -9882,9 +10935,14 @@ class Analytics {
|
|
|
9882
10935
|
timezone: defaultFields.timezone,
|
|
9883
10936
|
search_engine: defaultFields.search_engine,
|
|
9884
10937
|
};
|
|
10938
|
+
const attempt = this.startAttempt(JSON.stringify(data));
|
|
9885
10939
|
const resp = await fetch(apiEdp, {
|
|
9886
10940
|
method: 'POST',
|
|
9887
|
-
|
|
10941
|
+
headers: {
|
|
10942
|
+
'Content-Type': 'application/json',
|
|
10943
|
+
'Idempotency-Key': attempt.idempotencyKey,
|
|
10944
|
+
},
|
|
10945
|
+
body: attempt.requestBody,
|
|
9888
10946
|
});
|
|
9889
10947
|
if (!resp.ok) {
|
|
9890
10948
|
throw new Error(`HTTP error! status: ${resp.status}`);
|
|
@@ -9893,6 +10951,7 @@ class Analytics {
|
|
|
9893
10951
|
if (result.token) {
|
|
9894
10952
|
this.token = result.token;
|
|
9895
10953
|
this.sessionStorage.setItem(STORAGEKEY, result.token);
|
|
10954
|
+
this.clearStartAttempt();
|
|
9896
10955
|
}
|
|
9897
10956
|
else {
|
|
9898
10957
|
throw new Error('No token received from server');
|
|
@@ -9919,6 +10978,7 @@ class Analytics {
|
|
|
9919
10978
|
if (this.standalone) {
|
|
9920
10979
|
this.token = null;
|
|
9921
10980
|
this.sessionStorage.setItem(STORAGEKEY, '');
|
|
10981
|
+
this.clearStartAttempt();
|
|
9922
10982
|
}
|
|
9923
10983
|
};
|
|
9924
10984
|
/**
|
|
@@ -9957,6 +11017,91 @@ class Analytics {
|
|
|
9957
11017
|
this.init();
|
|
9958
11018
|
}
|
|
9959
11019
|
}
|
|
11020
|
+
loadStartAttempt() {
|
|
11021
|
+
if (this.isValidStartAttempt(this.pendingStartAttempt)) {
|
|
11022
|
+
return this.pendingStartAttempt;
|
|
11023
|
+
}
|
|
11024
|
+
this.pendingStartAttempt = null;
|
|
11025
|
+
let serialized;
|
|
11026
|
+
try {
|
|
11027
|
+
serialized = this.sessionStorage.getItem(START_ATTEMPT_STORAGE_KEY);
|
|
11028
|
+
}
|
|
11029
|
+
catch {
|
|
11030
|
+
return null;
|
|
11031
|
+
}
|
|
11032
|
+
if (!serialized || serialized.length > 1024 * 1024) {
|
|
11033
|
+
return null;
|
|
11034
|
+
}
|
|
11035
|
+
try {
|
|
11036
|
+
const attempt = JSON.parse(serialized);
|
|
11037
|
+
if (this.isValidStartAttempt(attempt)) {
|
|
11038
|
+
this.pendingStartAttempt = attempt;
|
|
11039
|
+
return attempt;
|
|
11040
|
+
}
|
|
11041
|
+
}
|
|
11042
|
+
catch {
|
|
11043
|
+
// Treat corrupt storage as an absent pending attempt.
|
|
11044
|
+
}
|
|
11045
|
+
return null;
|
|
11046
|
+
}
|
|
11047
|
+
isValidStartAttempt(value) {
|
|
11048
|
+
if (typeof value !== 'object' || value === null) {
|
|
11049
|
+
return false;
|
|
11050
|
+
}
|
|
11051
|
+
const attempt = value;
|
|
11052
|
+
const currentTime = Date.now();
|
|
11053
|
+
if (attempt.version !== 1 ||
|
|
11054
|
+
attempt.projectKey !== this.projectKey ||
|
|
11055
|
+
typeof attempt.createdAt !== 'number' ||
|
|
11056
|
+
!Number.isFinite(attempt.createdAt) ||
|
|
11057
|
+
attempt.createdAt > currentTime ||
|
|
11058
|
+
currentTime - attempt.createdAt >= START_ATTEMPT_TTL_MS ||
|
|
11059
|
+
typeof attempt.idempotencyKey !== 'string' ||
|
|
11060
|
+
attempt.idempotencyKey.length < 8 ||
|
|
11061
|
+
attempt.idempotencyKey.length > 128 ||
|
|
11062
|
+
!/^[A-Za-z0-9._:-]+$/.test(attempt.idempotencyKey) ||
|
|
11063
|
+
typeof attempt.requestBody !== 'string' ||
|
|
11064
|
+
attempt.requestBody.length > 1024 * 1024) {
|
|
11065
|
+
return false;
|
|
11066
|
+
}
|
|
11067
|
+
try {
|
|
11068
|
+
const request = JSON.parse(attempt.requestBody);
|
|
11069
|
+
return request.projectKey === this.projectKey;
|
|
11070
|
+
}
|
|
11071
|
+
catch {
|
|
11072
|
+
return false;
|
|
11073
|
+
}
|
|
11074
|
+
}
|
|
11075
|
+
startAttempt(requestBody) {
|
|
11076
|
+
const existing = this.loadStartAttempt();
|
|
11077
|
+
if (existing) {
|
|
11078
|
+
return existing;
|
|
11079
|
+
}
|
|
11080
|
+
const attempt = {
|
|
11081
|
+
version: 1,
|
|
11082
|
+
projectKey: this.projectKey,
|
|
11083
|
+
createdAt: Date.now(),
|
|
11084
|
+
idempotencyKey: `insights:start:${generateRandomId(40)}`,
|
|
11085
|
+
requestBody,
|
|
11086
|
+
};
|
|
11087
|
+
this.pendingStartAttempt = attempt;
|
|
11088
|
+
try {
|
|
11089
|
+
this.sessionStorage.setItem(START_ATTEMPT_STORAGE_KEY, JSON.stringify(attempt));
|
|
11090
|
+
}
|
|
11091
|
+
catch {
|
|
11092
|
+
// The in-memory copy still protects retries within this Analytics instance.
|
|
11093
|
+
}
|
|
11094
|
+
return attempt;
|
|
11095
|
+
}
|
|
11096
|
+
clearStartAttempt() {
|
|
11097
|
+
this.pendingStartAttempt = null;
|
|
11098
|
+
try {
|
|
11099
|
+
this.sessionStorage.setItem(START_ATTEMPT_STORAGE_KEY, '');
|
|
11100
|
+
}
|
|
11101
|
+
catch {
|
|
11102
|
+
// The in-memory copy is still cleared when storage is unavailable.
|
|
11103
|
+
}
|
|
11104
|
+
}
|
|
9960
11105
|
}
|
|
9961
11106
|
|
|
9962
11107
|
const Messages = _Messages;
|
|
@@ -10003,7 +11148,7 @@ class API {
|
|
|
10003
11148
|
this.signalStartIssue = (reason, missingApi) => {
|
|
10004
11149
|
const doNotTrack = this.checkDoNotTrack();
|
|
10005
11150
|
console.log("Tracker couldn't start due to:", JSON.stringify({
|
|
10006
|
-
trackerVersion: '7.3.
|
|
11151
|
+
trackerVersion: '7.3.4',
|
|
10007
11152
|
siteKey: this.options.siteKey,
|
|
10008
11153
|
doNotTrack,
|
|
10009
11154
|
reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
|