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