@openreplay/tracker 16.4.10 → 17.0.0

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.
Files changed (69) hide show
  1. package/dist/cjs/common/messages.gen.d.ts +57 -7
  2. package/dist/cjs/entry.js +564 -1453
  3. package/dist/cjs/entry.js.map +1 -1
  4. package/dist/cjs/index.js +526 -1373
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/main/app/guards.d.ts +1 -0
  7. package/dist/cjs/main/app/index.d.ts +5 -12
  8. package/dist/cjs/main/app/messages.gen.d.ts +7 -2
  9. package/dist/cjs/main/app/observer/observer.d.ts +4 -0
  10. package/dist/cjs/main/app/observer/top_observer.d.ts +3 -1
  11. package/dist/cjs/main/index.d.ts +9 -13
  12. package/dist/cjs/main/modules/conditionsManager.d.ts +6 -1
  13. package/dist/cjs/main/modules/longAnimationTask.d.ts +25 -0
  14. package/dist/cjs/main/modules/tagWatcher.d.ts +1 -1
  15. package/dist/cjs/main/modules/webAnimations.d.ts +9 -0
  16. package/dist/cjs/main/singleton.d.ts +0 -7
  17. package/dist/cjs/main/utils.d.ts +3 -0
  18. package/dist/lib/common/messages.gen.d.ts +57 -7
  19. package/dist/lib/entry.js +564 -1453
  20. package/dist/lib/entry.js.map +1 -1
  21. package/dist/lib/index.js +526 -1373
  22. package/dist/lib/index.js.map +1 -1
  23. package/dist/lib/main/app/guards.d.ts +1 -0
  24. package/dist/lib/main/app/index.d.ts +5 -12
  25. package/dist/lib/main/app/messages.gen.d.ts +7 -2
  26. package/dist/lib/main/app/observer/observer.d.ts +4 -0
  27. package/dist/lib/main/app/observer/top_observer.d.ts +3 -1
  28. package/dist/lib/main/index.d.ts +9 -13
  29. package/dist/lib/main/modules/conditionsManager.d.ts +6 -1
  30. package/dist/lib/main/modules/longAnimationTask.d.ts +25 -0
  31. package/dist/lib/main/modules/tagWatcher.d.ts +1 -1
  32. package/dist/lib/main/modules/webAnimations.d.ts +9 -0
  33. package/dist/lib/main/singleton.d.ts +0 -7
  34. package/dist/lib/main/utils.d.ts +3 -0
  35. package/dist/types/common/messages.gen.d.ts +57 -7
  36. package/dist/types/main/app/guards.d.ts +1 -0
  37. package/dist/types/main/app/index.d.ts +5 -12
  38. package/dist/types/main/app/messages.gen.d.ts +7 -2
  39. package/dist/types/main/app/observer/observer.d.ts +4 -0
  40. package/dist/types/main/app/observer/top_observer.d.ts +3 -1
  41. package/dist/types/main/index.d.ts +9 -13
  42. package/dist/types/main/modules/conditionsManager.d.ts +6 -1
  43. package/dist/types/main/modules/longAnimationTask.d.ts +25 -0
  44. package/dist/types/main/modules/tagWatcher.d.ts +1 -1
  45. package/dist/types/main/modules/webAnimations.d.ts +9 -0
  46. package/dist/types/main/singleton.d.ts +0 -7
  47. package/dist/types/main/utils.d.ts +3 -0
  48. package/package.json +13 -13
  49. package/dist/cjs/main/modules/featureFlags.d.ts +0 -25
  50. package/dist/cjs/main/modules/userTesting/SignalManager.d.ts +0 -29
  51. package/dist/cjs/main/modules/userTesting/dnd.d.ts +0 -1
  52. package/dist/cjs/main/modules/userTesting/index.d.ts +0 -45
  53. package/dist/cjs/main/modules/userTesting/recorder.d.ts +0 -24
  54. package/dist/cjs/main/modules/userTesting/styles.d.ts +0 -277
  55. package/dist/cjs/main/modules/userTesting/utils.d.ts +0 -9
  56. package/dist/lib/main/modules/featureFlags.d.ts +0 -25
  57. package/dist/lib/main/modules/userTesting/SignalManager.d.ts +0 -29
  58. package/dist/lib/main/modules/userTesting/dnd.d.ts +0 -1
  59. package/dist/lib/main/modules/userTesting/index.d.ts +0 -45
  60. package/dist/lib/main/modules/userTesting/recorder.d.ts +0 -24
  61. package/dist/lib/main/modules/userTesting/styles.d.ts +0 -277
  62. package/dist/lib/main/modules/userTesting/utils.d.ts +0 -9
  63. package/dist/types/main/modules/featureFlags.d.ts +0 -25
  64. package/dist/types/main/modules/userTesting/SignalManager.d.ts +0 -29
  65. package/dist/types/main/modules/userTesting/dnd.d.ts +0 -1
  66. package/dist/types/main/modules/userTesting/index.d.ts +0 -45
  67. package/dist/types/main/modules/userTesting/recorder.d.ts +0 -24
  68. package/dist/types/main/modules/userTesting/styles.d.ts +0 -277
  69. package/dist/types/main/modules/userTesting/utils.d.ts +0 -9
package/dist/lib/index.js CHANGED
@@ -1110,93 +1110,6 @@ const mapCondition = (condition) => {
1110
1110
  return con;
1111
1111
  };
1112
1112
 
1113
- class FeatureFlags {
1114
- constructor(app) {
1115
- this.app = app;
1116
- this.flags = [];
1117
- this.storageKey = '__openreplay_flags';
1118
- const persistFlags = this.app.sessionStorage.getItem(this.storageKey);
1119
- if (persistFlags) {
1120
- const persistFlagsStrArr = persistFlags.split(';').filter(Boolean);
1121
- this.flags = persistFlagsStrArr.map((flag) => JSON.parse(flag));
1122
- }
1123
- }
1124
- getFeatureFlag(flagName) {
1125
- return this.flags.find((flag) => flag.key === flagName);
1126
- }
1127
- isFlagEnabled(flagName) {
1128
- return this.flags.findIndex((flag) => flag.key === flagName) !== -1;
1129
- }
1130
- onFlagsLoad(cb) {
1131
- this.onFlagsCb = cb;
1132
- }
1133
- async reloadFlags(token) {
1134
- const persistFlagsStr = this.app.sessionStorage.getItem(this.storageKey);
1135
- const persistFlags = {};
1136
- if (persistFlagsStr) {
1137
- const persistArray = persistFlagsStr.split(';').filter(Boolean);
1138
- persistArray.forEach((flag) => {
1139
- const flagObj = JSON.parse(flag);
1140
- persistFlags[flagObj.key] = { key: flagObj.key, value: flagObj.value };
1141
- });
1142
- }
1143
- const sessionInfo = this.app.session.getInfo();
1144
- const userInfo = this.app.session.userInfo;
1145
- const requestObject = {
1146
- projectID: sessionInfo.projectID,
1147
- userID: sessionInfo.userID,
1148
- metadata: sessionInfo.metadata,
1149
- referrer: document.referrer,
1150
- os: userInfo.userOS,
1151
- device: userInfo.userDevice,
1152
- country: userInfo.userCountry,
1153
- state: userInfo.userState,
1154
- city: userInfo.userCity,
1155
- browser: userInfo.userBrowser,
1156
- persistFlags: persistFlags,
1157
- };
1158
- const authToken = token ?? this.app.session.getSessionToken();
1159
- const resp = await fetch(this.app.options.ingestPoint + '/v1/web/feature-flags', {
1160
- method: 'POST',
1161
- headers: {
1162
- 'Content-Type': 'application/json',
1163
- Authorization: `Bearer ${authToken}`,
1164
- },
1165
- body: JSON.stringify(requestObject),
1166
- });
1167
- if (resp.status === 200) {
1168
- const data = await resp.json();
1169
- return this.handleFlags(data.flags);
1170
- }
1171
- }
1172
- handleFlags(flags) {
1173
- const persistFlags = [];
1174
- flags.forEach((flag) => {
1175
- if (flag.is_persist)
1176
- persistFlags.push(flag);
1177
- });
1178
- let str = '';
1179
- const uniquePersistFlags = this.diffPersist(persistFlags);
1180
- uniquePersistFlags.forEach((flag) => {
1181
- str += `${JSON.stringify(flag)};`;
1182
- });
1183
- this.app.sessionStorage.setItem(this.storageKey, str);
1184
- this.flags = flags;
1185
- return this.onFlagsCb?.(flags);
1186
- }
1187
- clearPersistFlags() {
1188
- this.app.sessionStorage.removeItem(this.storageKey);
1189
- }
1190
- diffPersist(flags) {
1191
- const persistFlags = this.app.sessionStorage.getItem(this.storageKey);
1192
- if (!persistFlags)
1193
- return flags;
1194
- const persistFlagsStrArr = persistFlags.split(';').filter(Boolean);
1195
- const persistFlagsArr = persistFlagsStrArr.map((flag) => JSON.parse(flag));
1196
- return flags.filter((flag) => persistFlagsArr.findIndex((pf) => pf.key === flag.key) === -1);
1197
- }
1198
- }
1199
-
1200
1113
  const DEPRECATED_ATTRS = { htmlmasked: 'hidden', masked: 'obscured' };
1201
1114
  const IN_BROWSER = !(typeof window === 'undefined');
1202
1115
  const IS_FIREFOX = IN_BROWSER && navigator.userAgent.match(/firefox|fxios/i);
@@ -1465,6 +1378,43 @@ function simpleMerge(defaultObj, givenObj) {
1465
1378
  }
1466
1379
  return result;
1467
1380
  }
1381
+ function throttleWithTrailing(fn, interval) {
1382
+ const lastCalls = new Map();
1383
+ const timeouts = new Map();
1384
+ const lastArgs = new Map();
1385
+ const throttled = function (key, ...args) {
1386
+ const now = Date.now();
1387
+ const lastCall = lastCalls.get(key) ?? 0;
1388
+ const remaining = interval - (now - lastCall);
1389
+ lastArgs.set(key, args);
1390
+ if (remaining <= 0) {
1391
+ if (timeouts.has(key)) {
1392
+ clearTimeout(timeouts.get(key));
1393
+ timeouts.delete(key);
1394
+ }
1395
+ lastCalls.set(key, now);
1396
+ fn(key, ...args);
1397
+ }
1398
+ else if (!timeouts.has(key)) {
1399
+ const timeoutId = setTimeout(() => {
1400
+ lastCalls.set(key, Date.now());
1401
+ timeouts.delete(key);
1402
+ const finalArgs = lastArgs.get(key);
1403
+ fn(key, ...finalArgs);
1404
+ }, remaining);
1405
+ timeouts.set(key, timeoutId);
1406
+ }
1407
+ };
1408
+ throttled.clear = () => {
1409
+ for (const timeout of timeouts.values()) {
1410
+ clearTimeout(timeout);
1411
+ }
1412
+ timeouts.clear();
1413
+ lastArgs.clear();
1414
+ lastCalls.clear();
1415
+ };
1416
+ return throttled;
1417
+ }
1468
1418
 
1469
1419
  // Auto-generated, do not edit
1470
1420
  /* eslint-disable */
@@ -1675,6 +1625,13 @@ function SetNodeAttributeDictGlobal(id, name, value) {
1675
1625
  value,
1676
1626
  ];
1677
1627
  }
1628
+ function NodeAnimationResult(id, styles) {
1629
+ return [
1630
+ 36 /* Messages.Type.NodeAnimationResult */,
1631
+ id,
1632
+ styles,
1633
+ ];
1634
+ }
1678
1635
  function CSSInsertRule(id, rule, index) {
1679
1636
  return [
1680
1637
  37 /* Messages.Type.CSSInsertRule */,
@@ -1803,9 +1760,9 @@ function SetNodeAttributeDict(id, name, value) {
1803
1760
  value,
1804
1761
  ];
1805
1762
  }
1806
- function ResourceTimingDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
1763
+ function ResourceTimingDeprecatedDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator) {
1807
1764
  return [
1808
- 53 /* Messages.Type.ResourceTimingDeprecated */,
1765
+ 53 /* Messages.Type.ResourceTimingDeprecatedDeprecated */,
1809
1766
  timestamp,
1810
1767
  duration,
1811
1768
  ttfb,
@@ -1887,6 +1844,13 @@ function CustomIssue(name, payload) {
1887
1844
  payload,
1888
1845
  ];
1889
1846
  }
1847
+ function SetNodeSlot(id, slotID) {
1848
+ return [
1849
+ 65 /* Messages.Type.SetNodeSlot */,
1850
+ id,
1851
+ slotID,
1852
+ ];
1853
+ }
1890
1854
  function CSSInsertRuleURLBased(id, rule, index, baseURL) {
1891
1855
  return [
1892
1856
  67 /* Messages.Type.CSSInsertRuleURLBased */,
@@ -2019,6 +1983,47 @@ function WSChannel(chType, channelName, data, timestamp, dir, messageType) {
2019
1983
  messageType,
2020
1984
  ];
2021
1985
  }
1986
+ function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached, queueing, dnsLookup, initialConnection, ssl, contentDownload, total, stalled) {
1987
+ return [
1988
+ 85 /* Messages.Type.ResourceTiming */,
1989
+ timestamp,
1990
+ duration,
1991
+ ttfb,
1992
+ headerSize,
1993
+ encodedBodySize,
1994
+ decodedBodySize,
1995
+ url,
1996
+ initiator,
1997
+ transferredSize,
1998
+ cached,
1999
+ queueing,
2000
+ dnsLookup,
2001
+ initialConnection,
2002
+ ssl,
2003
+ contentDownload,
2004
+ total,
2005
+ stalled,
2006
+ ];
2007
+ }
2008
+ function Incident(label, startTime, endTime) {
2009
+ return [
2010
+ 87 /* Messages.Type.Incident */,
2011
+ label,
2012
+ startTime,
2013
+ endTime,
2014
+ ];
2015
+ }
2016
+ function LongAnimationTask$1(name, duration, blockingDuration, firstUIEventTimestamp, startTime, scripts) {
2017
+ return [
2018
+ 89 /* Messages.Type.LongAnimationTask */,
2019
+ name,
2020
+ duration,
2021
+ blockingDuration,
2022
+ firstUIEventTimestamp,
2023
+ startTime,
2024
+ scripts,
2025
+ ];
2026
+ }
2022
2027
  function InputChange(id, value, valueMasked, label, hesitationTime, inputDuration) {
2023
2028
  return [
2024
2029
  112 /* Messages.Type.InputChange */,
@@ -2050,9 +2055,9 @@ function UnbindNodes(totalRemovedPercent) {
2050
2055
  totalRemovedPercent,
2051
2056
  ];
2052
2057
  }
2053
- function ResourceTiming(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached) {
2058
+ function ResourceTimingDeprecated(timestamp, duration, ttfb, headerSize, encodedBodySize, decodedBodySize, url, initiator, transferredSize, cached) {
2054
2059
  return [
2055
- 116 /* Messages.Type.ResourceTiming */,
2060
+ 116 /* Messages.Type.ResourceTimingDeprecated */,
2056
2061
  timestamp,
2057
2062
  duration,
2058
2063
  ttfb,
@@ -2149,9 +2154,11 @@ var _Messages = /*#__PURE__*/Object.freeze({
2149
2154
  Fetch: Fetch,
2150
2155
  GraphQL: GraphQL,
2151
2156
  GraphQLDeprecated: GraphQLDeprecated,
2157
+ Incident: Incident,
2152
2158
  InputChange: InputChange,
2153
2159
  JSException: JSException,
2154
2160
  LoadFontFace: LoadFontFace,
2161
+ LongAnimationTask: LongAnimationTask$1,
2155
2162
  LongTask: LongTask,
2156
2163
  Metadata: Metadata,
2157
2164
  MobX: MobX,
@@ -2163,6 +2170,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
2163
2170
  NetworkRequest: NetworkRequest,
2164
2171
  NetworkRequestDeprecated: NetworkRequestDeprecated,
2165
2172
  NgRx: NgRx,
2173
+ NodeAnimationResult: NodeAnimationResult,
2166
2174
  OTable: OTable,
2167
2175
  PageLoadTiming: PageLoadTiming,
2168
2176
  PageRenderTiming: PageRenderTiming,
@@ -2175,6 +2183,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
2175
2183
  RemoveNodeAttribute: RemoveNodeAttribute,
2176
2184
  ResourceTiming: ResourceTiming,
2177
2185
  ResourceTimingDeprecated: ResourceTimingDeprecated,
2186
+ ResourceTimingDeprecatedDeprecated: ResourceTimingDeprecatedDeprecated,
2178
2187
  SelectionChange: SelectionChange,
2179
2188
  SetCSSDataURLBased: SetCSSDataURLBased,
2180
2189
  SetInputChecked: SetInputChecked,
@@ -2188,6 +2197,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
2188
2197
  SetNodeData: SetNodeData,
2189
2198
  SetNodeFocus: SetNodeFocus,
2190
2199
  SetNodeScroll: SetNodeScroll,
2200
+ SetNodeSlot: SetNodeSlot,
2191
2201
  SetPageLocation: SetPageLocation,
2192
2202
  SetPageLocationDeprecated: SetPageLocationDeprecated,
2193
2203
  SetPageVisibility: SetPageVisibility,
@@ -2262,7 +2272,7 @@ function Performance (app, opts) {
2262
2272
  const WATCHED_TAGS_KEY = '__or__watched_tags__';
2263
2273
  class TagWatcher {
2264
2274
  constructor(params) {
2265
- this.intervals = {};
2275
+ this.interval = null;
2266
2276
  this.tags = [];
2267
2277
  this.sessionStorage = params.sessionStorage;
2268
2278
  this.errLog = params.errLog;
@@ -2304,9 +2314,12 @@ class TagWatcher {
2304
2314
  }
2305
2315
  setTags(tags) {
2306
2316
  this.tags = tags;
2307
- this.intervals = {};
2308
- tags.forEach((tag) => {
2309
- this.intervals[tag.id] = setInterval(() => {
2317
+ if (this.interval) {
2318
+ clearInterval(this.interval);
2319
+ this.interval = null;
2320
+ }
2321
+ this.interval = setInterval(() => {
2322
+ this.tags.forEach((tag) => {
2310
2323
  const possibleEls = document.querySelectorAll(tag.selector);
2311
2324
  if (possibleEls.length > 0) {
2312
2325
  const el = possibleEls[0];
@@ -2314,1002 +2327,22 @@ class TagWatcher {
2314
2327
  el.__or_watcher_tagname = tag.id;
2315
2328
  this.observer.observe(el);
2316
2329
  }
2317
- }, 500);
2318
- });
2330
+ });
2331
+ }, 500);
2319
2332
  }
2320
2333
  onTagRendered(tagId) {
2321
- if (this.intervals[tagId]) {
2322
- clearInterval(this.intervals[tagId]);
2334
+ if (this.tags.findIndex(t => t.id === tagId)) {
2335
+ this.tags = this.tags.filter((tag) => tag.id !== tagId);
2323
2336
  }
2324
2337
  this.onTag(tagId);
2325
2338
  }
2326
2339
  clear() {
2327
- this.tags.forEach((tag) => {
2328
- clearInterval(this.intervals[tag.id]);
2329
- });
2330
2340
  this.tags = [];
2331
- this.intervals = {};
2332
- this.observer.disconnect();
2333
- }
2334
- }
2335
-
2336
- const bgStyle = {
2337
- position: 'fixed',
2338
- top: 0,
2339
- left: 0,
2340
- width: '100vw',
2341
- height: '100vh',
2342
- background: 'rgba(0, 0, 0, 0.40)',
2343
- display: 'flex',
2344
- alignItems: 'center',
2345
- justifyContent: 'center',
2346
- zIndex: 999999,
2347
- fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
2348
- };
2349
- const containerStyle = {
2350
- display: 'flex',
2351
- flexDirection: 'column',
2352
- gap: '2rem',
2353
- alignItems: 'center',
2354
- padding: '1.5rem',
2355
- borderRadius: '2px',
2356
- border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2357
- background: '#FFF',
2358
- width: '22rem',
2359
- };
2360
- const containerWidgetStyle = {
2361
- display: 'flex',
2362
- 'flex-direction': 'column',
2363
- gap: 'unset',
2364
- 'align-items': 'center',
2365
- padding: 'unset',
2366
- fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
2367
- 'border-radius': '2px',
2368
- border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2369
- background: 'rgba(255, 255, 255, 0.75)',
2370
- width: '22rem',
2371
- };
2372
- const titleStyle = {
2373
- fontFamily: 'Verdana, sans-serif',
2374
- fontSize: '1.25rem',
2375
- fontStyle: 'normal',
2376
- fontWeight: '500',
2377
- lineHeight: '1.75rem',
2378
- color: 'rgba(0, 0, 0, 0.85)',
2379
- };
2380
- const descriptionStyle = {
2381
- borderTop: '1px solid rgba(0, 0, 0, 0.06)',
2382
- borderBottom: '1px solid rgba(0, 0, 0, 0.06)',
2383
- padding: '1.25rem 0rem',
2384
- color: 'rgba(0, 0, 0, 0.85)',
2385
- fontFamily: 'Verdana, sans-serif',
2386
- fontSize: '13px',
2387
- fontStyle: 'normal',
2388
- fontWeight: '400',
2389
- lineHeight: 'auto',
2390
- whiteSpace: 'pre-wrap',
2391
- };
2392
- const buttonStyle = {
2393
- display: 'flex',
2394
- padding: '0.4rem 0.9375rem',
2395
- justifyContent: 'center',
2396
- alignItems: 'center',
2397
- gap: '0.625rem',
2398
- borderRadius: '0.25rem',
2399
- border: '1px solid #394EFF',
2400
- background: '#394EFF',
2401
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2402
- color: '#FFF',
2403
- textAlign: 'center',
2404
- fontFamily: 'Verdana, sans-serif',
2405
- fontSize: '1rem',
2406
- fontStyle: 'normal',
2407
- fontWeight: '500',
2408
- lineHeight: '1.5rem',
2409
- cursor: 'pointer',
2410
- };
2411
- const sectionTitleStyle = {
2412
- fontFamily: 'Verdana, sans-serif',
2413
- fontSize: '13px',
2414
- fontWeight: '500',
2415
- lineHeight: 'auto',
2416
- display: 'flex',
2417
- justifyContent: 'space-between',
2418
- width: '100%',
2419
- cursor: 'pointer',
2420
- };
2421
- const contentStyle = {
2422
- display: 'flex',
2423
- flexDirection: 'column',
2424
- alignItems: 'flex-start',
2425
- gap: '0.625rem',
2426
- fontSize: '13px',
2427
- lineHeight: 'auto',
2428
- };
2429
- // New widget styles
2430
- const titleWidgetStyle = {
2431
- padding: '0.5rem',
2432
- gap: '0.5rem',
2433
- fontFamily: 'Verdana, sans-serif',
2434
- fontSize: '16px',
2435
- fontStyle: 'normal',
2436
- fontWeight: '500',
2437
- lineHeight: 'auto',
2438
- color: 'white',
2439
- display: 'flex',
2440
- alignItems: 'center',
2441
- width: '100%',
2442
- borderRadius: '2px',
2443
- background: 'rgba(0, 0, 0, 0.75)',
2444
- boxSizing: 'border-box',
2445
- };
2446
- const descriptionWidgetStyle = {
2447
- boxSizing: 'border-box',
2448
- display: 'block',
2449
- width: '100%',
2450
- borderBottom: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2451
- background: '#FFF',
2452
- padding: '0.65rem',
2453
- alignSelf: 'stretch',
2454
- color: '#000',
2455
- fontFamily: 'Verdana, sans-serif',
2456
- // fontSize: '0.875rem',
2457
- fontStyle: 'normal',
2458
- fontWeight: '400',
2459
- // lineHeight: '1.375rem',
2460
- };
2461
- const endSectionStyle = {
2462
- ...descriptionWidgetStyle,
2463
- display: 'flex',
2464
- flexDirection: 'column',
2465
- alignItems: 'center',
2466
- gap: '0.625rem',
2467
- };
2468
- const symbolIcon = {
2469
- fontSize: '1.25rem',
2470
- fontWeight: '500',
2471
- cursor: 'pointer',
2472
- color: '#394EFF',
2473
- };
2474
- const buttonWidgetStyle = {
2475
- display: 'flex',
2476
- padding: '0.4rem 0.9375rem',
2477
- justifyContent: 'center',
2478
- alignItems: 'center',
2479
- gap: '0.625rem',
2480
- borderRadius: '0.25rem',
2481
- border: '1px solid #394EFF',
2482
- background: '#394EFF',
2483
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2484
- color: '#FFF',
2485
- textAlign: 'center',
2486
- fontFamily: 'Verdana, sans-serif',
2487
- fontSize: '1rem',
2488
- fontStyle: 'normal',
2489
- fontWeight: '500',
2490
- lineHeight: '1.5rem',
2491
- width: '100%',
2492
- boxSizing: 'border-box',
2493
- cursor: 'pointer',
2494
- };
2495
- const stopWidgetStyle = {
2496
- marginTop: '1rem',
2497
- marginBottom: '1rem',
2498
- cursor: 'pointer',
2499
- display: 'block',
2500
- fontWeight: '500',
2501
- fontSize: '13px!important',
2502
- lineHeight: 'auto',
2503
- };
2504
- const paginationStyle = {
2505
- display: 'flex',
2506
- justifyContent: 'space-between',
2507
- alignItems: 'center',
2508
- gap: '1rem',
2509
- padding: '0.5rem',
2510
- width: '100%',
2511
- boxSizing: 'border-box',
2512
- };
2513
- const taskNumberActive = {
2514
- display: 'flex',
2515
- flexDirection: 'column',
2516
- alignItems: 'center',
2517
- justifyContent: 'center',
2518
- borderRadius: '6.25em',
2519
- outline: '1px solid #394EFF',
2520
- fontSize: '13px',
2521
- height: '24px',
2522
- width: '24px',
2523
- };
2524
- const taskNumberDone = {
2525
- display: 'flex',
2526
- flexDirection: 'column',
2527
- alignItems: 'center',
2528
- justifyContent: 'center',
2529
- borderRadius: '6.25em',
2530
- outline: '1px solid #D2DFFF',
2531
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2532
- background: '#D2DFFF',
2533
- fontSize: '13px',
2534
- height: '24px',
2535
- width: '24px',
2536
- };
2537
- const taskDescriptionCard = {
2538
- borderRadius: '0.375rem',
2539
- border: '1px solid rgba(0, 0, 0, 0.06)',
2540
- background: '#F5F7FF',
2541
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2542
- display: 'flex',
2543
- flexDirection: 'column',
2544
- padding: '0.625rem 0.9375rem',
2545
- gap: '0.5rem',
2546
- alignSelf: 'stretch',
2547
- };
2548
- const taskTextStyle = {
2549
- fontWeight: 'bold',
2550
- };
2551
- const taskDescriptionStyle = {
2552
- fontSize: '13px',
2553
- lineHeight: 'auto',
2554
- };
2555
- const taskButtonStyle = {
2556
- marginRight: '0.5rem',
2557
- cursor: 'pointer',
2558
- color: '#394EFF',
2559
- textAlign: 'center',
2560
- fontFamily: 'Verdana, sans-serif',
2561
- fontSize: '13px',
2562
- fontStyle: 'normal',
2563
- fontWeight: '500',
2564
- lineHeight: 'auto',
2565
- };
2566
- const taskButtonBorderedStyle = {
2567
- ...taskButtonStyle,
2568
- display: 'flex',
2569
- padding: '0.25rem 0.9375rem',
2570
- justifyContent: 'center',
2571
- alignItems: 'center',
2572
- gap: '0.5rem',
2573
- borderRadius: '0.25rem',
2574
- border: '1px solid #394EFF',
2575
- };
2576
- const taskButtonsRow = {
2577
- display: 'flex',
2578
- justifyContent: 'space-between',
2579
- alignItems: 'center',
2580
- width: '100%',
2581
- boxSizing: 'border-box',
2582
- };
2583
- const spinnerStyles = {
2584
- border: '4px solid rgba(255, 255, 255, 0.4)',
2585
- width: '16px',
2586
- height: '16px',
2587
- borderRadius: '50%',
2588
- borderLeftColor: '#fff',
2589
- animation: 'spin 0.5s linear infinite',
2590
- };
2591
-
2592
- const Quality = {
2593
- Standard: { width: 1280, height: 720 }};
2594
- class Recorder {
2595
- constructor(app) {
2596
- this.app = app;
2597
- this.mediaRecorder = null;
2598
- this.recordedChunks = [];
2599
- this.stream = null;
2600
- this.recStartTs = null;
2601
- }
2602
- async startRecording(fps, quality, micReq, camReq) {
2603
- this.recStartTs = this.app.timestamp();
2604
- const videoConstraints = quality;
2605
- try {
2606
- this.stream = await navigator.mediaDevices.getUserMedia({
2607
- video: camReq ? { ...videoConstraints, frameRate: { ideal: fps } } : false,
2608
- audio: micReq,
2609
- });
2610
- this.mediaRecorder = new MediaRecorder(this.stream, {
2611
- mimeType: 'video/webm;codecs=vp9',
2612
- });
2613
- this.recordedChunks = [];
2614
- this.mediaRecorder.ondataavailable = (event) => {
2615
- if (event.data.size > 0) {
2616
- this.recordedChunks.push(event.data);
2617
- }
2618
- };
2619
- this.mediaRecorder.start();
2620
- }
2621
- catch (error) {
2622
- console.error(error);
2623
- }
2624
- }
2625
- async stopRecording() {
2626
- return new Promise((resolve) => {
2627
- if (!this.mediaRecorder)
2628
- return;
2629
- this.mediaRecorder.onstop = () => {
2630
- const blob = new Blob(this.recordedChunks, {
2631
- type: 'video/webm',
2632
- });
2633
- resolve(blob);
2634
- };
2635
- this.mediaRecorder.stop();
2636
- });
2637
- }
2638
- async sendToAPI() {
2639
- const blob = await this.stopRecording();
2640
- // const formData = new FormData()
2641
- // formData.append('file', blob, 'record.webm')
2642
- // formData.append('start', this.recStartTs?.toString() ?? '')
2643
- return fetch(`${this.app.options.ingestPoint}/v1/web/uxt/upload-url`, {
2644
- headers: {
2645
- Authorization: `Bearer ${this.app.session.getSessionToken()}`,
2646
- },
2647
- })
2648
- .then((r) => {
2649
- if (r.ok) {
2650
- return r.json();
2651
- }
2652
- else {
2653
- throw new Error('Failed to get upload url');
2654
- }
2655
- })
2656
- .then(({ url }) => {
2657
- return fetch(url, {
2658
- method: 'PUT',
2659
- headers: {
2660
- 'Content-Type': 'video/webm',
2661
- },
2662
- body: blob,
2663
- });
2664
- })
2665
- .catch(console.error)
2666
- .finally(() => {
2667
- this.discard();
2668
- });
2669
- }
2670
- async saveToFile(fileName = 'recorded-video.webm') {
2671
- const blob = await this.stopRecording();
2672
- const url = URL.createObjectURL(blob);
2673
- const a = document.createElement('a');
2674
- a.style.display = 'none';
2675
- a.href = url;
2676
- a.download = fileName;
2677
- document.body.appendChild(a);
2678
- a.click();
2679
- window.URL.revokeObjectURL(url);
2680
- document.body.removeChild(a);
2681
- }
2682
- discard() {
2683
- this.mediaRecorder?.stop();
2684
- this.stream?.getTracks().forEach((track) => track.stop());
2685
- }
2686
- }
2687
-
2688
- // @ts-nocheck
2689
- function attachDND(element, dragTarget) {
2690
- dragTarget.onmousedown = function (event) {
2691
- const clientRect = element.getBoundingClientRect();
2692
- const shiftX = event.clientX - clientRect.left;
2693
- const shiftY = event.clientY - clientRect.top;
2694
- element.style.position = 'fixed';
2695
- element.style.zIndex = 99999999999999;
2696
- moveAt(event.pageX, event.pageY);
2697
- function moveAt(pageX, pageY) {
2698
- let leftC = pageX - shiftX;
2699
- let topC = pageY - shiftY;
2700
- if (leftC <= 5)
2701
- leftC = 5;
2702
- if (topC <= 5)
2703
- topC = 5;
2704
- if (leftC >= window.innerWidth - clientRect.width)
2705
- leftC = window.innerWidth - clientRect.width;
2706
- if (topC >= window.innerHeight - clientRect.height)
2707
- topC = window.innerHeight - clientRect.height;
2708
- element.style.left = `${leftC}px`;
2709
- element.style.top = `${topC}px`;
2710
- }
2711
- function onMouseMove(event) {
2712
- moveAt(event.pageX, event.pageY);
2713
- }
2714
- document.addEventListener('mousemove', onMouseMove);
2715
- const clearAll = () => {
2716
- document.removeEventListener('mousemove', onMouseMove);
2717
- document.removeEventListener('mouseup', clearAll);
2718
- };
2719
- document.addEventListener('mouseup', clearAll);
2720
- };
2721
- dragTarget.ondragstart = function () {
2722
- return false;
2723
- };
2724
- }
2725
-
2726
- function generateGrid() {
2727
- const grid = document.createElement('div');
2728
- grid.className = 'grid';
2729
- for (let i = 0; i < 16; i++) {
2730
- const cell = document.createElement('div');
2731
- Object.assign(cell.style, {
2732
- width: '2px',
2733
- height: '2px',
2734
- borderRadius: '10px',
2735
- background: 'white',
2736
- });
2737
- cell.className = 'cell';
2738
- grid.appendChild(cell);
2739
- }
2740
- Object.assign(grid.style, {
2741
- display: 'grid',
2742
- gridTemplateColumns: 'repeat(4, 1fr)',
2743
- gridTemplateRows: 'repeat(4, 1fr)',
2744
- gap: '2px',
2745
- cursor: 'grab',
2746
- });
2747
- return grid;
2748
- }
2749
- function generateChevron() {
2750
- const triangle = document.createElement('div');
2751
- Object.assign(triangle.style, {
2752
- width: '0',
2753
- height: '0',
2754
- borderLeft: '7px solid transparent',
2755
- borderRight: '7px solid transparent',
2756
- borderBottom: '7px solid white',
2757
- });
2758
- const container = document.createElement('div');
2759
- container.appendChild(triangle);
2760
- Object.assign(container.style, {
2761
- display: 'flex',
2762
- alignItems: 'center',
2763
- justifyContent: 'center',
2764
- width: '16px',
2765
- height: '16px',
2766
- cursor: 'pointer',
2767
- marginLeft: 'auto',
2768
- transform: 'rotate(180deg)',
2769
- });
2770
- return container;
2771
- }
2772
- function addKeyframes() {
2773
- const styleSheet = document.createElement('style');
2774
- styleSheet.type = 'text/css';
2775
- styleSheet.innerText = `@keyframes spin {
2776
- 0% { transform: rotate(0deg); }
2777
- 100% { transform: rotate(360deg); }
2778
- }`;
2779
- document.head.appendChild(styleSheet);
2780
- }
2781
- function createSpinner() {
2782
- addKeyframes();
2783
- const spinner = document.createElement('div');
2784
- spinner.classList.add('spinner');
2785
- Object.assign(spinner.style, spinnerStyles);
2786
- return spinner;
2787
- }
2788
- function createElement(tag, className, styles, textContent, id) {
2789
- const element = document.createElement(tag);
2790
- element.className = className;
2791
- Object.assign(element.style, styles);
2792
- if (textContent) {
2793
- element.textContent = textContent;
2794
- }
2795
- if (id) {
2796
- element.id = id;
2797
- }
2798
- return element;
2799
- }
2800
- const TEST_START = 'or_uxt_test_start';
2801
- const TASK_IND = 'or_uxt_task_index';
2802
- const SESSION_ID = 'or_uxt_session_id';
2803
- const TEST_ID = 'or_uxt_test_id';
2804
-
2805
- class SignalManager {
2806
- constructor(ingestPoint, getTimestamp, token, testId, storageKey, setStorageKey, removeStorageKey, getStorageKey, getSessionId) {
2807
- this.ingestPoint = ingestPoint;
2808
- this.getTimestamp = getTimestamp;
2809
- this.token = token;
2810
- this.testId = testId;
2811
- this.storageKey = storageKey;
2812
- this.setStorageKey = setStorageKey;
2813
- this.removeStorageKey = removeStorageKey;
2814
- this.getStorageKey = getStorageKey;
2815
- this.getSessionId = getSessionId;
2816
- this.durations = {
2817
- testStart: 0,
2818
- tasks: [],
2819
- };
2820
- this.getDurations = () => {
2821
- return this.durations;
2822
- };
2823
- this.setDurations = (durations) => {
2824
- this.durations.testStart = durations.testStart;
2825
- this.durations.tasks = durations.tasks;
2826
- };
2827
- this.signalTask = (taskId, status, taskAnswer) => {
2828
- if (!taskId)
2829
- return console.error('User Testing: No Task ID Given');
2830
- const taskStart = this.durations.tasks.find((t) => t.taskId === taskId);
2831
- const timestamp = this.getTimestamp();
2832
- const duration = taskStart ? timestamp - taskStart.started : 0;
2833
- return fetch(`${this.ingestPoint}/v1/web/uxt/signals/task`, {
2834
- method: 'POST',
2835
- headers: {
2836
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
2837
- Authorization: `Bearer ${this.token}`,
2838
- },
2839
- body: JSON.stringify({
2840
- testId: this.testId,
2841
- taskId,
2842
- status,
2843
- duration,
2844
- timestamp,
2845
- taskAnswer,
2846
- }),
2847
- });
2848
- };
2849
- this.signalTest = (status) => {
2850
- const timestamp = this.getTimestamp();
2851
- if (status === 'begin' && this.testId) {
2852
- const sessionId = this.getSessionId();
2853
- this.setStorageKey(SESSION_ID, sessionId);
2854
- this.setStorageKey(this.storageKey, this.testId.toString());
2855
- this.setStorageKey(TEST_START, timestamp.toString());
2856
- }
2857
- else {
2858
- this.removeStorageKey(this.storageKey);
2859
- this.removeStorageKey(TASK_IND);
2860
- this.removeStorageKey(TEST_START);
2861
- }
2862
- const start = this.durations.testStart || timestamp;
2863
- const duration = timestamp - start;
2864
- return fetch(`${this.ingestPoint}/v1/web/uxt/signals/test`, {
2865
- method: 'POST',
2866
- headers: {
2867
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
2868
- Authorization: `Bearer ${this.token}`,
2869
- },
2870
- body: JSON.stringify({
2871
- testId: this.testId,
2872
- status,
2873
- duration,
2874
- timestamp,
2875
- }),
2876
- });
2877
- };
2878
- const possibleStart = this.getStorageKey(TEST_START);
2879
- if (possibleStart) {
2880
- this.durations.testStart = parseInt(possibleStart, 10);
2881
- }
2882
- }
2883
- }
2884
-
2885
- class UserTestManager {
2886
- constructor(app, storageKey) {
2887
- this.app = app;
2888
- this.storageKey = storageKey;
2889
- this.bg = createElement('div', 'bg', bgStyle, undefined, '__or_ut_bg');
2890
- this.container = createElement('div', 'container', containerStyle, undefined, '__or_ut_ct');
2891
- this.widgetGuidelinesVisible = true;
2892
- this.widgetTasksVisible = false;
2893
- this.widgetVisible = true;
2894
- this.isActive = false;
2895
- this.descriptionSection = null;
2896
- this.taskSection = null;
2897
- this.endSection = null;
2898
- this.stopButton = null;
2899
- this.stopButtonContainer = null;
2900
- this.test = null;
2901
- this.testId = null;
2902
- this.signalManager = null;
2903
- this.getTest = (id, token, inProgress) => {
2904
- this.testId = id;
2905
- const ingest = this.app.options.ingestPoint;
2906
- return fetch(`${ingest}/v1/web/uxt/test/${id}`, {
2907
- headers: {
2908
- Authorization: `Bearer ${token}`,
2909
- },
2910
- })
2911
- .then((res) => res.json())
2912
- .then(({ test }) => {
2913
- this.isActive = true;
2914
- this.test = test;
2915
- this.signalManager = new SignalManager(this.app.options.ingestPoint, () => this.app.timestamp(), token, id, this.storageKey, (k, v) => this.app.localStorage.setItem(k, v), (k) => this.app.localStorage.removeItem(k), (k) => this.app.localStorage.getItem(k), () => this.app.getSessionID());
2916
- this.createGreeting(test.title, test.reqMic, test.reqCamera);
2917
- if (inProgress) {
2918
- if (test.reqMic || test.reqCamera) {
2919
- void this.userRecorder.startRecording(30, Quality.Standard, test.reqMic, test.reqCamera);
2920
- }
2921
- this.showWidget(test.description, test.tasks, true);
2922
- this.showTaskSection();
2923
- }
2924
- })
2925
- .then(() => id)
2926
- .catch((err) => {
2927
- console.log('OR: Error fetching test', err);
2928
- });
2929
- };
2930
- this.hideTaskSection = () => false;
2931
- this.showTaskSection = () => true;
2932
- this.collapseWidget = () => false;
2933
- this.removeGreeting = () => false;
2934
- // eslint-disable-next-line @typescript-eslint/no-empty-function
2935
- this.toggleDescriptionVisibility = () => { };
2936
- this.currentTaskIndex = 0;
2937
- this.userRecorder = new Recorder(app);
2938
- const sessionId = this.app.getSessionID();
2939
- const savedSessionId = this.app.localStorage.getItem(SESSION_ID);
2940
- if (sessionId !== savedSessionId) {
2941
- this.app.localStorage.removeItem(this.storageKey);
2942
- this.app.localStorage.removeItem(SESSION_ID);
2943
- this.app.localStorage.removeItem(TEST_ID);
2944
- this.app.localStorage.removeItem(TASK_IND);
2945
- this.app.localStorage.removeItem(TEST_START);
2946
- }
2947
- const taskIndex = this.app.localStorage.getItem(TASK_IND);
2948
- if (taskIndex) {
2949
- this.currentTaskIndex = parseInt(taskIndex, 10);
2950
- }
2951
- }
2952
- getTestId() {
2953
- return this.testId;
2954
- }
2955
- createGreeting(title, micRequired, cameraRequired) {
2956
- const titleElement = createElement('div', 'title', titleStyle, title);
2957
- const descriptionElement = createElement('div', 'description', descriptionStyle, `Welcome, you're here to help us improve, not to be judged. Your insights matter!\n
2958
- 📹 We're recording this browser tab to learn from your experience.
2959
- 🎤 Please enable mic and camera if asked, to give us a complete picture.`);
2960
- const buttonElement = createElement('div', 'button', buttonStyle, 'Read guidelines to begin');
2961
- this.removeGreeting = () => {
2962
- // this.container.innerHTML = ''
2963
- if (micRequired || cameraRequired) {
2964
- void this.userRecorder.startRecording(30, Quality.Standard, micRequired, cameraRequired);
2965
- }
2966
- this.container.removeChild(buttonElement);
2967
- this.container.removeChild(descriptionElement);
2968
- this.container.removeChild(titleElement);
2969
- return false;
2970
- };
2971
- buttonElement.onclick = () => {
2972
- this.removeGreeting();
2973
- const durations = this.signalManager?.getDurations();
2974
- if (durations && this.signalManager) {
2975
- durations.testStart = this.app.timestamp();
2976
- this.signalManager.setDurations(durations);
2977
- }
2978
- void this.signalManager?.signalTest('begin');
2979
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
2980
- Object.assign(this.container.style, containerWidgetStyle);
2981
- this.showWidget(this.test?.guidelines || '', this.test?.tasks || []);
2982
- };
2983
- this.container.append(titleElement, descriptionElement, buttonElement);
2984
- this.bg.appendChild(this.container);
2985
- document.body.appendChild(this.bg);
2986
- }
2987
- showWidget(guidelines, tasks, inProgress) {
2988
- this.container.innerHTML = '';
2989
- Object.assign(this.bg.style, {
2990
- position: 'fixed',
2991
- zIndex: 99999999999999,
2992
- right: '8px',
2993
- left: 'unset',
2994
- width: 'fit-content',
2995
- top: '8px',
2996
- height: 'fit-content',
2997
- background: 'unset',
2998
- display: 'unset',
2999
- alignItems: 'unset',
3000
- justifyContent: 'unset',
3001
- });
3002
- // Create title section
3003
- const titleSection = this.createTitleSection();
3004
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3005
- Object.assign(this.container.style, containerWidgetStyle);
3006
- const descriptionSection = this.createDescriptionSection(guidelines);
3007
- const tasksSection = this.createTasksSection(tasks);
3008
- const stopButton = createElement('div', 'stop_bn_or', stopWidgetStyle, 'Abort Session');
3009
- const stopContainer = createElement('div', 'stop_ct_or', { fontSize: '13px!important' });
3010
- stopContainer.style.fontSize = '13px';
3011
- stopContainer.append(stopButton);
3012
- this.container.append(titleSection, descriptionSection, tasksSection, stopContainer);
3013
- this.taskSection = tasksSection;
3014
- this.descriptionSection = descriptionSection;
3015
- this.stopButton = stopButton;
3016
- this.stopButtonContainer = stopContainer;
3017
- stopButton.onclick = () => {
3018
- this.userRecorder.discard();
3019
- void this.signalManager?.signalTest('skipped');
3020
- document.body.removeChild(this.bg);
3021
- window.close();
3022
- };
3023
- if (!inProgress) {
3024
- this.hideTaskSection();
2341
+ if (this.interval) {
2342
+ clearInterval(this.interval);
2343
+ this.interval = null;
3025
2344
  }
3026
- else {
3027
- this.toggleDescriptionVisibility();
3028
- }
3029
- }
3030
- createTitleSection() {
3031
- const title = createElement('div', 'title', titleWidgetStyle);
3032
- const leftIcon = generateGrid();
3033
- const titleText = createElement('div', 'title_text', {
3034
- maxWidth: '19rem',
3035
- overflow: 'hidden',
3036
- textOverflow: 'ellipsis',
3037
- width: '100%',
3038
- fontSize: 16,
3039
- lineHeight: 'auto',
3040
- cursor: 'pointer',
3041
- }, this.test?.title);
3042
- const rightIcon = generateChevron();
3043
- title.append(leftIcon, titleText, rightIcon);
3044
- const toggleWidget = (isVisible) => {
3045
- this.widgetVisible = isVisible;
3046
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3047
- Object.assign(this.container.style, this.widgetVisible
3048
- ? containerWidgetStyle
3049
- : { border: 'none', background: 'none', padding: 0 });
3050
- if (this.taskSection) {
3051
- Object.assign(this.taskSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3052
- }
3053
- if (this.descriptionSection) {
3054
- Object.assign(this.descriptionSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3055
- }
3056
- if (this.endSection) {
3057
- Object.assign(this.endSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3058
- }
3059
- if (this.stopButton) {
3060
- Object.assign(this.stopButton.style, this.widgetVisible ? stopWidgetStyle : { display: 'none' });
3061
- }
3062
- return isVisible;
3063
- };
3064
- const collapseWidget = () => {
3065
- Object.assign(rightIcon.style, {
3066
- transform: this.widgetVisible ? 'rotate(0deg)' : 'rotate(180deg)',
3067
- });
3068
- toggleWidget(!this.widgetVisible);
3069
- };
3070
- titleText.onclick = collapseWidget;
3071
- rightIcon.onclick = collapseWidget;
3072
- attachDND(this.bg, leftIcon);
3073
- this.collapseWidget = () => toggleWidget(false);
3074
- return title;
3075
- }
3076
- createDescriptionSection(guidelines) {
3077
- const section = createElement('div', 'description_section_or', descriptionWidgetStyle);
3078
- const titleContainer = createElement('div', 'description_s_title_or', sectionTitleStyle);
3079
- const title = createElement('div', 'title', {
3080
- fontSize: 13,
3081
- fontWeight: 500,
3082
- lineHeight: 'auto',
3083
- }, 'Introduction & Guidelines');
3084
- const icon = createElement('div', 'icon', symbolIcon, '-');
3085
- const content = createElement('div', 'content', contentStyle);
3086
- const descriptionC = createElement('div', 'text_description', {
3087
- maxHeight: '250px',
3088
- overflowY: 'auto',
3089
- whiteSpace: 'pre-wrap',
3090
- fontSize: 13,
3091
- color: '#454545',
3092
- lineHeight: 'auto',
3093
- });
3094
- descriptionC.innerHTML = guidelines;
3095
- const button = createElement('div', 'button_begin_or', buttonWidgetStyle, 'Begin Test');
3096
- titleContainer.append(title, icon);
3097
- content.append(descriptionC, button);
3098
- section.append(titleContainer, content);
3099
- const toggleDescriptionVisibility = () => {
3100
- this.widgetGuidelinesVisible = !this.widgetGuidelinesVisible;
3101
- icon.textContent = this.widgetGuidelinesVisible ? '-' : '+';
3102
- Object.assign(content.style, this.widgetGuidelinesVisible ? contentStyle : { display: 'none' });
3103
- };
3104
- titleContainer.onclick = toggleDescriptionVisibility;
3105
- this.toggleDescriptionVisibility = () => {
3106
- this.widgetGuidelinesVisible = false;
3107
- icon.textContent = this.widgetGuidelinesVisible ? '-' : '+';
3108
- Object.assign(content.style, this.widgetGuidelinesVisible ? contentStyle : { display: 'none' });
3109
- content.removeChild(button);
3110
- };
3111
- button.onclick = () => {
3112
- toggleDescriptionVisibility();
3113
- if (this.test) {
3114
- const durations = this.signalManager?.getDurations();
3115
- const taskDurationInd = durations
3116
- ? durations.tasks.findIndex((t) => this.test && t.taskId === this.test.tasks[0].task_id)
3117
- : null;
3118
- if (durations && taskDurationInd === -1) {
3119
- durations.tasks.push({
3120
- taskId: this.test.tasks[0].task_id,
3121
- started: this.app.timestamp(),
3122
- });
3123
- this.signalManager?.setDurations(durations);
3124
- }
3125
- void this.signalManager?.signalTask(this.test.tasks[0].task_id, 'begin');
3126
- }
3127
- this.showTaskSection();
3128
- content.removeChild(button);
3129
- };
3130
- return section;
3131
- }
3132
- createTasksSection(tasks) {
3133
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3134
- Object.assign(this.container.style, containerWidgetStyle);
3135
- const section = createElement('div', 'task_section_or', descriptionWidgetStyle);
3136
- const titleContainer = createElement('div', 'description_t_title_or', sectionTitleStyle);
3137
- const title = createElement('div', 'title', {
3138
- fontSize: '13px',
3139
- fontWeight: '500',
3140
- lineHeight: 'auto',
3141
- }, 'Tasks');
3142
- const icon = createElement('div', 'icon', symbolIcon, '-');
3143
- const content = createElement('div', 'content', contentStyle);
3144
- const pagination = createElement('div', 'pagination', paginationStyle);
3145
- // const leftArrow = createElement('span', 'leftArrow', {}, '<')
3146
- // const rightArrow = createElement('span', 'rightArrow', {}, '>')
3147
- const taskCard = createElement('div', 'taskCard', taskDescriptionCard);
3148
- const taskText = createElement('div', 'taskText', taskTextStyle);
3149
- const taskDescription = createElement('div', 'taskDescription', taskDescriptionStyle);
3150
- const taskButtons = createElement('div', 'taskButtons', taskButtonsRow);
3151
- const inputTitle = createElement('div', 'taskText', taskTextStyle);
3152
- inputTitle.textContent = 'Your answer';
3153
- const inputArea = createElement('textarea', 'taskDescription', {
3154
- resize: 'vertical',
3155
- });
3156
- const inputContainer = createElement('div', 'inputArea', taskDescriptionCard);
3157
- inputContainer.append(inputTitle, inputArea);
3158
- const closePanelButton = createElement('div', 'closePanelButton', taskButtonStyle, 'Collapse Panel');
3159
- const nextButton = createElement('div', 'nextButton', taskButtonBorderedStyle, 'Done, Next');
3160
- titleContainer.append(title, icon);
3161
- taskCard.append(taskText, taskDescription);
3162
- taskButtons.append(closePanelButton, nextButton);
3163
- content.append(pagination, taskCard, inputContainer, taskButtons);
3164
- section.append(titleContainer, content);
3165
- const updateTaskContent = () => {
3166
- const task = tasks[this.currentTaskIndex];
3167
- taskText.textContent = task.title;
3168
- taskDescription.textContent = task.description;
3169
- if (task.allow_typing) {
3170
- inputContainer.style.display = 'flex';
3171
- }
3172
- else {
3173
- inputContainer.style.display = 'none';
3174
- }
3175
- };
3176
- // pagination.appendChild(leftArrow)
3177
- tasks.forEach((_, index) => {
3178
- const pageNumber = createElement('span', `or_task_${index}`, {
3179
- outline: '1px solid #efefef',
3180
- fontSize: '13px',
3181
- height: '24px',
3182
- width: '24px',
3183
- display: 'flex',
3184
- flexDirection: 'column',
3185
- alignItems: 'center',
3186
- justifyContent: 'center',
3187
- borderRadius: '6.25em',
3188
- }, (index + 1).toString());
3189
- pageNumber.id = `or_task_${index}`;
3190
- pagination.append(pageNumber);
3191
- });
3192
- // pagination.appendChild(rightArrow)
3193
- const toggleTasksVisibility = () => {
3194
- this.widgetTasksVisible = !this.widgetTasksVisible;
3195
- icon.textContent = this.widgetTasksVisible ? '-' : '+';
3196
- Object.assign(content.style, this.widgetTasksVisible ? contentStyle : { display: 'none' });
3197
- };
3198
- this.hideTaskSection = () => {
3199
- icon.textContent = '+';
3200
- Object.assign(content.style, {
3201
- display: 'none',
3202
- });
3203
- this.widgetTasksVisible = false;
3204
- return false;
3205
- };
3206
- this.showTaskSection = () => {
3207
- icon.textContent = '-';
3208
- Object.assign(content.style, contentStyle);
3209
- this.widgetTasksVisible = true;
3210
- return true;
3211
- };
3212
- const highlightActive = () => {
3213
- const activeTaskEl = document.getElementById(`or_task_${this.currentTaskIndex}`);
3214
- if (activeTaskEl) {
3215
- Object.assign(activeTaskEl.style, taskNumberActive);
3216
- }
3217
- for (let i = 0; i < this.currentTaskIndex; i++) {
3218
- const taskEl = document.getElementById(`or_task_${i}`);
3219
- if (taskEl) {
3220
- Object.assign(taskEl.style, taskNumberDone);
3221
- }
3222
- }
3223
- };
3224
- titleContainer.onclick = toggleTasksVisibility;
3225
- closePanelButton.onclick = this.collapseWidget;
3226
- nextButton.onclick = () => {
3227
- const textAnswer = tasks[this.currentTaskIndex].allow_typing ? inputArea.value : undefined;
3228
- inputArea.value = '';
3229
- void this.signalManager?.signalTask(tasks[this.currentTaskIndex].task_id, 'done', textAnswer);
3230
- if (this.currentTaskIndex < tasks.length - 1) {
3231
- this.currentTaskIndex++;
3232
- updateTaskContent();
3233
- const durations = this.signalManager?.getDurations();
3234
- if (durations &&
3235
- durations.tasks.findIndex((t) => t.taskId === tasks[this.currentTaskIndex].task_id) === -1) {
3236
- durations.tasks.push({
3237
- taskId: tasks[this.currentTaskIndex].task_id,
3238
- started: this.app.timestamp(),
3239
- });
3240
- this.signalManager?.setDurations(durations);
3241
- }
3242
- void this.signalManager?.signalTask(tasks[this.currentTaskIndex].task_id, 'begin');
3243
- highlightActive();
3244
- }
3245
- else {
3246
- this.showEndSection();
3247
- }
3248
- this.app.localStorage.setItem('or_uxt_task_index', this.currentTaskIndex.toString());
3249
- };
3250
- setTimeout(() => {
3251
- const firstTaskEl = document.getElementById('or_task_0');
3252
- if (firstTaskEl) {
3253
- Object.assign(firstTaskEl.style, taskNumberActive);
3254
- }
3255
- updateTaskContent();
3256
- highlightActive();
3257
- }, 1);
3258
- return section;
3259
- }
3260
- showEndSection() {
3261
- let isLoading = true;
3262
- void this.signalManager?.signalTest('done');
3263
- const section = createElement('div', 'end_section_or', endSectionStyle);
3264
- const title = createElement('div', 'end_title_or', {
3265
- fontSize: '1.25rem',
3266
- fontWeight: '500',
3267
- }, 'Thank you! 👍');
3268
- const description = createElement('div', 'end_description_or', {}, this.test?.conclusion ??
3269
- 'Thank you for participating in our usability test. Your feedback has been captured and will be used to enhance our website. \n' +
3270
- '\n' +
3271
- 'We appreciate your time and valuable input.');
3272
- const button = createElement('div', 'end_button_or', buttonWidgetStyle, 'Submitting Feedback');
3273
- const spinner = createSpinner();
3274
- button.appendChild(spinner);
3275
- if (this.test?.reqMic || this.test?.reqCamera) {
3276
- void this.userRecorder
3277
- .sendToAPI()
3278
- .then(() => {
3279
- button.removeChild(spinner);
3280
- button.textContent = 'End Session';
3281
- isLoading = false;
3282
- })
3283
- .catch((err) => {
3284
- console.error(err);
3285
- button.removeChild(spinner);
3286
- button.textContent = 'End Session';
3287
- isLoading = false;
3288
- });
3289
- }
3290
- else {
3291
- button.removeChild(spinner);
3292
- button.textContent = 'End Session';
3293
- isLoading = false;
3294
- }
3295
- if (this.taskSection) {
3296
- this.container.removeChild(this.taskSection);
3297
- }
3298
- if (this.descriptionSection) {
3299
- this.container.removeChild(this.descriptionSection);
3300
- }
3301
- if (this.stopButton && this.stopButtonContainer) {
3302
- this.container.removeChild(this.stopButtonContainer);
3303
- }
3304
- button.onclick = () => {
3305
- if (isLoading)
3306
- return;
3307
- window.close();
3308
- document.body.removeChild(this.bg);
3309
- };
3310
- section.append(title, description, button);
3311
- this.endSection = section;
3312
- this.container.append(section);
2345
+ this.observer.disconnect();
3313
2346
  }
3314
2347
  }
3315
2348
 
@@ -4152,6 +3185,13 @@ async function parseUseEl(useElement, mode, domParser) {
4152
3185
  return;
4153
3186
  }
4154
3187
  let [url, symbolId] = href.split('#');
3188
+ if (!url && !symbolId) {
3189
+ console.warn('Openreplay: Invalid xlink:href or href found on <use>.');
3190
+ return;
3191
+ }
3192
+ if (iconCache[symbolId]) {
3193
+ return iconCache[symbolId];
3194
+ }
4155
3195
  // happens if svg spritemap is local, fastest case for us
4156
3196
  if (!url && symbolId) {
4157
3197
  const hasHashtag = href.startsWith('#');
@@ -4177,13 +3217,6 @@ async function parseUseEl(useElement, mode, domParser) {
4177
3217
  return;
4178
3218
  }
4179
3219
  }
4180
- if (!url && !symbolId) {
4181
- console.warn('Openreplay: Invalid xlink:href or href found on <use>.');
4182
- return;
4183
- }
4184
- if (iconCache[symbolId]) {
4185
- return iconCache[symbolId];
4186
- }
4187
3220
  let svgDoc;
4188
3221
  if (svgUrlCache[url]) {
4189
3222
  if (svgUrlCache[url] === 1) {
@@ -4283,6 +3316,7 @@ class Observer {
4283
3316
  this.indexes = [];
4284
3317
  this.attributesMap = new Map();
4285
3318
  this.textSet = new Set();
3319
+ this.slotMap = new Map();
4286
3320
  this.disableSprites = false;
4287
3321
  /**
4288
3322
  * this option means that, instead of using link element with href to load css,
@@ -4292,6 +3326,9 @@ class Observer {
4292
3326
  this.inlineRemoteCss = false;
4293
3327
  this.inlinerOptions = undefined;
4294
3328
  this.domParser = new DOMParser();
3329
+ this.throttling = true;
3330
+ this.throttledSetNodeData = throttleWithTrailing((id, parentElement, data) => this.sendNodeData(id, parentElement, data), 30);
3331
+ this.throttling = !Boolean(options.disableThrottling);
4295
3332
  this.disableSprites = Boolean(options.disableSprites);
4296
3333
  this.inlineRemoteCss = Boolean(options.inlineRemoteCss);
4297
3334
  this.inlinerOptions = options.inlinerOptions;
@@ -4472,6 +3509,18 @@ class Observer {
4472
3509
  }
4473
3510
  bindNode(node) {
4474
3511
  const [id, isNew] = this.app.nodes.registerNode(node);
3512
+ if (isElementNode(node) && hasTag(node, 'slot')) {
3513
+ this.app.nodes.attachNodeListener(node, 'slotchange', () => {
3514
+ const sl = node;
3515
+ sl.assignedNodes({ flatten: true }).forEach((n) => {
3516
+ const nid = this.app.nodes.getID(n);
3517
+ if (nid !== undefined) {
3518
+ this.recents.set(nid, RecentsType.Removed);
3519
+ this.commitNode(nid);
3520
+ }
3521
+ });
3522
+ });
3523
+ }
4475
3524
  if (isNew) {
4476
3525
  this.recents.set(id, RecentsType.New);
4477
3526
  }
@@ -4502,6 +3551,9 @@ class Observer {
4502
3551
  }
4503
3552
  unbindTree(node) {
4504
3553
  const id = this.app.nodes.unregisterNode(node);
3554
+ if (id !== undefined) {
3555
+ this.slotMap.delete(id);
3556
+ }
4505
3557
  if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
4506
3558
  // Sending RemoveNode only for parent to maintain
4507
3559
  this.app.send(RemoveNode(id));
@@ -4530,8 +3582,15 @@ class Observer {
4530
3582
  if (isRootNode(node)) {
4531
3583
  return true;
4532
3584
  }
4533
- // @ts-ignore SALESFORCE
4534
- const parent = node.assignedSlot ? node.assignedSlot : node.parentNode;
3585
+ let slot = node.assignedSlot;
3586
+ let isLightDom = false;
3587
+ if (slot) {
3588
+ // Check if the node is in light DOM (not in shadow DOM)
3589
+ // This is a workaround for the issue with shadow DOM and slots
3590
+ // where the slot is not assigned to the node in shadow DOM.
3591
+ isLightDom = node.getRootNode() instanceof ShadowRoot;
3592
+ }
3593
+ const parent = node.parentNode;
4535
3594
  let parentID;
4536
3595
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
4537
3596
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
@@ -4543,7 +3602,15 @@ class Observer {
4543
3602
  this.unbindTree(node);
4544
3603
  return false;
4545
3604
  }
4546
- parentID = this.app.nodes.getID(parent);
3605
+ if (isLightDom && slot) {
3606
+ parentID = this.app.nodes.getID(slot);
3607
+ // in light dom, we don't "slot" the node,
3608
+ // but rather use the slot as a parent
3609
+ slot = null;
3610
+ }
3611
+ else {
3612
+ parentID = this.app.nodes.getID(parent);
3613
+ }
4547
3614
  if (parentID === undefined) {
4548
3615
  this.unbindTree(node);
4549
3616
  return false;
@@ -4603,12 +3670,35 @@ class Observer {
4603
3670
  else if (isTextNode(node)) {
4604
3671
  // for text node id != 0, hence parentID !== undefined and parent is Element
4605
3672
  this.app.send(CreateTextNode(id, parentID, index));
4606
- this.sendNodeData(id, parent, node.data);
3673
+ if (this.throttling) {
3674
+ this.throttledSetNodeData(id, parent, node.data);
3675
+ }
3676
+ else {
3677
+ this.sendNodeData(id, parent, node.data);
3678
+ }
3679
+ }
3680
+ if (slot) {
3681
+ const slotID = this.app.nodes.getID(slot);
3682
+ if (slotID !== undefined) {
3683
+ this.slotMap.set(id, slotID);
3684
+ this.app.send(SetNodeSlot(id, slotID));
3685
+ }
4607
3686
  }
4608
3687
  return true;
4609
3688
  }
4610
3689
  if (recentsType === RecentsType.Removed && parentID !== undefined) {
4611
3690
  this.app.send(MoveNode(id, parentID, index));
3691
+ if (slot) {
3692
+ const slotID = this.app.nodes.getID(slot);
3693
+ if (slotID !== undefined && this.slotMap.get(id) !== slotID) {
3694
+ this.slotMap.set(id, slotID);
3695
+ this.app.send(SetNodeSlot(id, slotID));
3696
+ }
3697
+ }
3698
+ else if (this.slotMap.has(id)) {
3699
+ this.slotMap.delete(id);
3700
+ this.app.send(SetNodeSlot(id, 0));
3701
+ }
4612
3702
  }
4613
3703
  const attr = this.attributesMap.get(id);
4614
3704
  if (attr !== undefined) {
@@ -4624,7 +3714,12 @@ class Observer {
4624
3714
  throw 'commitNode: node is not a text';
4625
3715
  }
4626
3716
  // for text node id != 0, hence parent is Element
4627
- this.sendNodeData(id, parent, node.data);
3717
+ if (this.throttling) {
3718
+ this.throttledSetNodeData(id, parent, node.data);
3719
+ }
3720
+ else {
3721
+ this.sendNodeData(id, parent, node.data);
3722
+ }
4628
3723
  }
4629
3724
  return true;
4630
3725
  }
@@ -4667,6 +3762,7 @@ class Observer {
4667
3762
  disconnect() {
4668
3763
  this.observer.disconnect();
4669
3764
  this.clear();
3765
+ this.throttledSetNodeData.clear();
4670
3766
  }
4671
3767
  }
4672
3768
 
@@ -4770,16 +3866,18 @@ class IFrameOffsets {
4770
3866
 
4771
3867
  var InlineCssMode;
4772
3868
  (function (InlineCssMode) {
3869
+ InlineCssMode[InlineCssMode["Unset"] = -1] = "Unset";
4773
3870
  /** default behavior -- will parse and cache the css file on backend */
4774
3871
  InlineCssMode[InlineCssMode["Disabled"] = 0] = "Disabled";
4775
3872
  /** will attempt to record the linked css file as AdoptedStyleSheet object */
4776
3873
  InlineCssMode[InlineCssMode["Inline"] = 1] = "Inline";
4777
- /** will fetch the file, then simulated AdoptedStyleSheets behavior programmaticaly for the replay */
3874
+ /** will fetch the file, then simulate AdoptedStyleSheets behavior programmaticaly for the replay */
4778
3875
  InlineCssMode[InlineCssMode["InlineFetched"] = 2] = "InlineFetched";
4779
3876
  /** will fetch the file, then save it as plain css inside <style> node */
4780
3877
  InlineCssMode[InlineCssMode["PlainFetched"] = 3] = "PlainFetched";
4781
3878
  })(InlineCssMode || (InlineCssMode = {}));
4782
- function getInlineOptions(mode) {
3879
+ const localhostStylesDoc = 'https://docs.openreplay.com/en/troubleshooting/localhost/';
3880
+ function getInlineOptions(mode, logger) {
4783
3881
  switch (mode) {
4784
3882
  case InlineCssMode.Inline:
4785
3883
  return {
@@ -4805,6 +3903,27 @@ function getInlineOptions(mode) {
4805
3903
  forcePlain: true,
4806
3904
  },
4807
3905
  };
3906
+ case InlineCssMode.Unset:
3907
+ const isLocalhost = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/?/.test(window.location.href);
3908
+ if (isLocalhost) {
3909
+ logger(`Enabling InlineCssMode by default on localhost to preserve css styles, refer to ${localhostStylesDoc} for details, set InlineCssMode to 0 to skip this behavior`);
3910
+ return {
3911
+ inlineRemoteCss: true,
3912
+ inlinerOptions: {
3913
+ forceFetch: false,
3914
+ forcePlain: false,
3915
+ },
3916
+ };
3917
+ }
3918
+ else {
3919
+ return {
3920
+ inlineRemoteCss: false,
3921
+ inlinerOptions: {
3922
+ forceFetch: false,
3923
+ forcePlain: false,
3924
+ },
3925
+ };
3926
+ }
4808
3927
  case InlineCssMode.Disabled:
4809
3928
  default:
4810
3929
  return {
@@ -4826,7 +3945,8 @@ class TopObserver extends Observer {
4826
3945
  }, params.options);
4827
3946
  const observerOptions = {
4828
3947
  disableSprites: opts.disableSprites,
4829
- ...getInlineOptions(opts.inlineCss)
3948
+ disableThrottling: opts.disableThrottling,
3949
+ ...getInlineOptions(opts.inlineCss, console.warn),
4830
3950
  };
4831
3951
  super(params.app, true, observerOptions);
4832
3952
  this.iframeOffsets = new IFrameOffsets();
@@ -5234,9 +4354,8 @@ class Ticker {
5234
4354
  * this value is injected during build time via rollup
5235
4355
  * */
5236
4356
  // @ts-ignore
5237
- 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.ingestURL=t+\"/v1/web/i\",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){if(this.busy||!this.token)this.queue.push(t);else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}else this.busy=!1}retry(t,s,i){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout((()=>this.sendBatch(t,s,i)),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t,s,i){var e;const n=null==i?void 0:i.toString().replace(/^([^_]+)_([^_]+).*/,\"$1_$2_$3\");this.busy=!0;const h={Authorization:`Bearer ${this.token}`};s&&(h[\"Content-Encoding\"]=\"gzip\"),null!==this.token?fetch(`${this.ingestURL}?batch=${null!==(e=this.pageNo)&&void 0!==e?e:\"noPageNum\"}_${null!=n?n:\"noBatchNum\"}`,{body:t,method:\"POST\",headers:h,keepalive:t.length<65536}).then((e=>{if(401===e.status)return this.busy=!1,void this.onUnauthorised();e.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${e.status}`):(this.attemptsCount=0,this.sendNext())})).catch((e=>{console.warn(\"OpenReplay:\",e),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${e.message}`)})):setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`)}),500)}sendCompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!0,s)}sendUncompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=\"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 i{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}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 i=s.encode(t),e=i.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(i,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class e extends i{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 38:case 70:case 75:case 76:case 77:case 82: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 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 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);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 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 67:case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);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 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 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 n{constructor(t,s,i,n,h,r){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=n,this.tabId=h,this.onOfflineEnd=r,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new e(this.beaconSize),this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.checkpoints=[],this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}prepare(){if(!this.encoder.isEmpty)return;this.checkpoints.length=0;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url],s=[0,this.timestamp],i=[118,this.tabId];this.writeType(t),this.writeFields(t),this.writeWithSize(s),this.writeWithSize(i),this.isEmpty=!0}writeWithSize(t){const s=this.encoder;if(!this.writeType(t)||!s.skip(3))return!1;const i=s.getCurrentOffset(),e=this.writeFields(t);if(e){const e=s.getCurrentOffset()-i;if(e>16777215)return console.warn(\"OpenReplay: max message size overflow.\"),!1;this.writeSizeAt(e,i-3),s.checkpoint(),this.checkpoints.push(s.getCurrentOffset()),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}writeMessage(t){if(\"q_end\"===t[0])return this.finaliseBatch(),this.onOfflineEnd();0===t[0]&&(this.timestamp=t[1]),122===t[0]&&(this.url=t[1]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new e(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",t,this),this.encoder=new e(this.beaconSize),this.prepare()))}finaliseBatch(t=!1){if(this.isEmpty)return;const s=this.encoder.flush();this.onBatch(s,t),this.prepare()}clean(){this.encoder.reset(),this.checkpoints.length=0}}var h;!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\"}(h||(h={}));let r=null,a=null,u=h.NotActive;function o(t){a&&a.finaliseBatch(t)}function c(){return new Promise((t=>{u=h.Stopping,null!==p&&(clearInterval(p),p=null),a&&(a.clean(),a=null),r&&(r.clean(),setTimeout((()=>{r=null}),20)),setTimeout((()=>{u=h.NotActive,t(null)}),100)}))}function g(){[h.Stopped,h.Stopping].includes(u)||(postMessage(\"a_stop\"),c().then((()=>{postMessage(\"a_start\")})))}let l,p=null;self.onmessage=({data:s})=>{if(\"stop\"===s)return o(),void c().then((()=>{u=h.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&r.sendCompressed(s.batch)}if(\"uncompressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&r.sendUncompressed(s.batch)}return\"start\"===s.type?(u=h.Starting,r=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),c()}(t)}),s.connAttemptCount,s.connAttemptGap,(t=>{postMessage({type:\"compress\",batch:t},[t.buffer])}),s.pageNo),a=new n(s.pageNo,s.timestamp,s.url,((t,s)=>{r&&(s?r.sendUncompressed(t):r.push(t))}),s.tabId,(()=>postMessage({type:\"queue_empty\"}))),null===p&&(p=setInterval(o,3e4)),u=h.Active):\"auth\"===s.type?r?a?(r.authorise(s.token),void(s.beaconSizeLimit&&a.setBeaconSizeLimit(s.beaconSizeLimit))):(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(a){const t=a;s.forEach((s=>{55===s[0]&&(s[1]?l=setTimeout((()=>g()),18e5):clearTimeout(l)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else o(!0);else o()}}();\n";
4357
+ 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.ingestURL=t+\"/v1/web/i\",this.isCompressing=void 0!==h}getQueueStatus(){return 0===this.queue.length&&!this.busy}authorise(t){this.token=t,this.busy||this.sendNext()}push(t){if(this.busy||!this.token)this.queue.push(t);else if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}}sendNext(){const t=this.queue.shift();if(t)if(this.busy=!0,this.isCompressing&&this.onCompress)this.onCompress(t);else{const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}else this.busy=!1}retry(t,s,i){this.attemptsCount>=this.MAX_ATTEMPTS_COUNT?this.onFailure(`Failed to send batch after ${this.attemptsCount} attempts.`):(this.attemptsCount++,setTimeout((()=>this.sendBatch(t,s,i)),this.ATTEMPT_TIMEOUT*this.attemptsCount))}sendBatch(t,s,i){var e;const n=null==i?void 0:i.toString().replace(/^([^_]+)_([^_]+).*/,\"$1_$2_$3\");this.busy=!0;const h={Authorization:`Bearer ${this.token}`};s&&(h[\"Content-Encoding\"]=\"gzip\"),null!==this.token?fetch(`${this.ingestURL}?batch=${null!==(e=this.pageNo)&&void 0!==e?e:\"noPageNum\"}_${null!=n?n:\"noBatchNum\"}`,{body:t,method:\"POST\",headers:h,keepalive:t.length<65536}).then((e=>{if(401===e.status)return this.busy=!1,void this.onUnauthorised();e.status>=400?this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_network:${e.status}`):(this.attemptsCount=0,this.sendNext())})).catch((e=>{console.warn(\"OpenReplay:\",e),this.retry(t,s,`${null!=i?i:\"noBatchNum\"}_reject:${e.message}`)})):setTimeout((()=>{this.sendBatch(t,s,`${null!=i?i:\"noBatchNum\"}_newToken`)}),500)}sendCompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!0,s)}sendUncompressed(t){const s=++this.lastBatchNum;this.sendBatch(t,!1,s)}clean(){this.sendNext(),setTimeout((()=>{this.token=null,this.queue.length=0}),10)}}const s=\"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 i{constructor(t){this.size=t,this.offset=0,this.checkpointOffset=0,this.data=new Uint8Array(t)}getCurrentOffset(){return this.offset}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 i=s.encode(t),e=i.byteLength;return!(!this.uint(e)||this.offset+e>this.size)&&(this.data.set(i,this.offset),this.offset+=e,!0)}reset(){this.offset=0,this.checkpointOffset=0}flush(){const t=this.data.slice(0,this.checkpointOffset);return this.reset(),t}}class e extends i{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 38:case 65:case 70:case 75:case 76:case 77:case 82: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 37:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3]);case 39:return this.string(t[1])&&this.string(t[2])&&this.string(t[3])&&this.string(t[4])&&this.uint(t[5])&&this.uint(t[6])&&this.uint(t[7]);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 59:return this.uint(t[1])&&this.uint(t[2])&&this.uint(t[3])&&this.uint(t[4])&&this.string(t[5])&&this.string(t[6])&&this.string(t[7]);case 67:case 73:return this.uint(t[1])&&this.string(t[2])&&this.uint(t[3])&&this.string(t[4]);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 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 n{constructor(t,s,i,n,h,r){this.pageNo=t,this.timestamp=s,this.url=i,this.onBatch=n,this.tabId=h,this.onOfflineEnd=r,this.nextIndex=0,this.beaconSize=2e5,this.encoder=new e(this.beaconSize),this.sizeBuffer=new Uint8Array(3),this.isEmpty=!0,this.checkpoints=[],this.beaconSizeLimit=1e6,this.prepare()}writeType(t){return this.encoder.uint(t[0])}writeFields(t){return this.encoder.encode(t)}writeSizeAt(t,s){for(let s=0;s<3;s++)this.sizeBuffer[s]=t>>8*s;this.encoder.set(this.sizeBuffer,s)}prepare(){if(!this.encoder.isEmpty)return;this.checkpoints.length=0;const t=[81,1,this.pageNo,this.nextIndex,this.timestamp,this.url],s=[0,this.timestamp],i=[118,this.tabId];this.writeType(t),this.writeFields(t),this.writeWithSize(s),this.writeWithSize(i),this.isEmpty=!0}writeWithSize(t){const s=this.encoder;if(!this.writeType(t)||!s.skip(3))return!1;const i=s.getCurrentOffset(),e=this.writeFields(t);if(e){const e=s.getCurrentOffset()-i;if(e>16777215)return console.warn(\"OpenReplay: max message size overflow.\"),!1;this.writeSizeAt(e,i-3),s.checkpoint(),this.checkpoints.push(s.getCurrentOffset()),this.isEmpty=this.isEmpty&&0===t[0],this.nextIndex++}return e}setBeaconSizeLimit(t){this.beaconSizeLimit=t}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]),this.writeWithSize(t)||(this.finaliseBatch(),this.writeWithSize(t)||(this.encoder=new e(this.beaconSizeLimit),this.prepare(),this.writeWithSize(t)?this.finaliseBatch():console.warn(\"OpenReplay: beacon size overflow. Skipping large message.\",t,this),this.encoder=new e(this.beaconSize),this.prepare()))}finaliseBatch(t=!1){if(this.isEmpty)return;const s=this.encoder.flush();this.onBatch(s,t),this.prepare()}clean(){this.encoder.reset(),this.checkpoints.length=0}}var h;!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\"}(h||(h={}));let r=null,u=null,a=h.NotActive;function o(t){u&&u.finaliseBatch(t)}function c(){return new Promise((t=>{a=h.Stopping,null!==p&&(clearInterval(p),p=null),u&&(u.clean(),u=null),r&&(r.clean(),setTimeout((()=>{r=null}),20)),setTimeout((()=>{a=h.NotActive,t(null)}),100)}))}function g(){[h.Stopped,h.Stopping].includes(a)||(postMessage(\"a_stop\"),c().then((()=>{postMessage(\"a_start\")})))}let l,p=null;self.onmessage=({data:s})=>{if(\"stop\"===s)return o(),void c().then((()=>{a=h.Stopped}));if(\"forceFlushBatch\"!==s)if(\"closing\"!==s){if(!Array.isArray(s)){if(\"compressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Compressed batch.\"),void g();s.batch&&r.sendCompressed(s.batch)}if(\"uncompressed\"===s.type){if(!r)return console.debug(\"OR WebWorker: sender not initialised. Uncompressed batch.\"),void g();s.batch&&r.sendUncompressed(s.batch)}return\"start\"===s.type?(a=h.Starting,r=new t(s.ingestPoint,(()=>{g()}),(t=>{!function(t){postMessage({type:\"failure\",reason:t}),c()}(t)}),s.connAttemptCount,s.connAttemptGap,(t=>{postMessage({type:\"compress\",batch:t},[t.buffer])}),s.pageNo),u=new n(s.pageNo,s.timestamp,s.url,((t,s)=>{r&&(s?r.sendUncompressed(t):r.push(t))}),s.tabId,(()=>postMessage({type:\"queue_empty\"}))),null===p&&(p=setInterval(o,3e4)),a=h.Active):\"auth\"===s.type?r?u?(r.authorise(s.token),void(s.beaconSizeLimit&&u.setBeaconSizeLimit(s.beaconSizeLimit))):(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(u){const t=u;s.forEach((s=>{55===s[0]&&(s[1]?l=setTimeout((()=>g()),18e5):clearTimeout(l)),t.writeMessage(s)}))}else postMessage(\"not_init\"),g()}else o(!0);else o()}}();\n";
5238
4358
  const CANCELED = 'canceled';
5239
- const uxtStorageKey = 'or_uxt_active';
5240
4359
  const bufferStorageKey = 'or_buffer_1';
5241
4360
  const UnsuccessfulStart = (reason) => ({ reason, success: false });
5242
4361
  const SuccessfulStart = (body) => ({ ...body, success: true });
@@ -5288,7 +4407,7 @@ class App {
5288
4407
  this.stopCallbacks = [];
5289
4408
  this.commitCallbacks = [];
5290
4409
  this.activityState = ActivityState.NotActive;
5291
- this.version = '16.4.10'; // TODO: version compatability check inside each plugin.
4410
+ this.version = '17.0.0'; // TODO: version compatability check inside each plugin.
5292
4411
  this.socketMode = false;
5293
4412
  this.compressionThreshold = 24 * 1000;
5294
4413
  this.bc = null;
@@ -5299,7 +4418,6 @@ class App {
5299
4418
  this.pageFrames = [];
5300
4419
  this.frameOderNumber = 0;
5301
4420
  this.frameLevel = 0;
5302
- this.features = {};
5303
4421
  this.emptyBatchCounter = 0;
5304
4422
  /** used by child iframes for crossdomain only */
5305
4423
  this.parentActive = false;
@@ -5391,13 +4509,6 @@ class App {
5391
4509
  }
5392
4510
  };
5393
4511
  void signalId();
5394
- if (this.active()) {
5395
- // @ts-ignore
5396
- event.source?.postMessage({ line: proto.startIframe }, '*');
5397
- }
5398
- else {
5399
- this.addCommand(proto.startIframe);
5400
- }
5401
4512
  }
5402
4513
  /**
5403
4514
  * proxying messages from iframe to main body, so they can be in one batch (same indexes, etc)
@@ -5405,7 +4516,8 @@ class App {
5405
4516
  * */
5406
4517
  if (data.line === proto.iframeBatch) {
5407
4518
  const msgBatch = data.messages;
5408
- const mappedMessages = msgBatch.map((msg) => {
4519
+ const mappedMessages = [];
4520
+ msgBatch.forEach((msg) => {
5409
4521
  if (msg[0] === 20 /* MType.MouseMove */) {
5410
4522
  let fixedMessage = msg;
5411
4523
  this.pageFrames.forEach((frame) => {
@@ -5415,7 +4527,7 @@ class App {
5415
4527
  fixedMessage = [type, x + left, y + top];
5416
4528
  }
5417
4529
  });
5418
- return fixedMessage;
4530
+ mappedMessages.push(fixedMessage);
5419
4531
  }
5420
4532
  if (msg[0] === 68 /* MType.MouseClick */) {
5421
4533
  let fixedMessage = msg;
@@ -5441,9 +4553,11 @@ class App {
5441
4553
  ];
5442
4554
  }
5443
4555
  });
5444
- return fixedMessage;
4556
+ mappedMessages.push(fixedMessage);
4557
+ }
4558
+ if (![28 /* MType.UserID */, 29 /* MType.UserAnonymousID */, 30 /* MType.Metadata */].includes(msg[0])) {
4559
+ mappedMessages.push(msg);
5445
4560
  }
5446
- return msg;
5447
4561
  });
5448
4562
  this.messages.push(...mappedMessages);
5449
4563
  }
@@ -5582,170 +4696,9 @@ class App {
5582
4696
  this.orderNumber = 0;
5583
4697
  this.coldStartTs = 0;
5584
4698
  this.singleBuffer = false;
5585
- /**
5586
- * start buffering messages without starting the actual session, which gives
5587
- * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
5588
- * and we will then send buffered batch, so it won't get lost
5589
- * */
5590
- this.coldStart = async (startOpts = {}, conditional) => {
5591
- this.singleBuffer = false;
5592
- const second = 1000;
5593
- const isNewSession = this.checkSessionToken(startOpts.forceNew);
5594
- if (conditional) {
5595
- await this.setupConditionalStart(startOpts);
5596
- }
5597
- const cycle = () => {
5598
- this.orderNumber += 1;
5599
- adjustTimeOrigin();
5600
- this.coldStartTs = now();
5601
- if (this.orderNumber % 2 === 0) {
5602
- this.bufferedMessages1.length = 0;
5603
- this.bufferedMessages1.push(Timestamp(this.timestamp()));
5604
- this.bufferedMessages1.push(TabData(this.session.getTabId()));
5605
- }
5606
- else {
5607
- this.bufferedMessages2.length = 0;
5608
- this.bufferedMessages2.push(Timestamp(this.timestamp()));
5609
- this.bufferedMessages2.push(TabData(this.session.getTabId()));
5610
- }
5611
- this.stop(false);
5612
- this.activityState = ActivityState.ColdStart;
5613
- if (startOpts.sessionHash) {
5614
- this.session.applySessionHash(startOpts.sessionHash);
5615
- }
5616
- if (startOpts.forceNew) {
5617
- this.session.reset();
5618
- }
5619
- this.session.assign({
5620
- userID: startOpts.userID,
5621
- metadata: startOpts.metadata,
5622
- });
5623
- if (!isNewSession) {
5624
- this.debug.log('continuing session on new tab', this.session.getTabId());
5625
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5626
- this.send(TabChange(this.session.getTabId()));
5627
- }
5628
- this.observer.observe();
5629
- this.ticker.start();
5630
- };
5631
- this.coldInterval = setInterval(() => {
5632
- cycle();
5633
- }, 30 * second);
5634
- cycle();
5635
- };
5636
- this.setupConditionalStart = async (startOpts) => {
5637
- this.conditionsManager = new ConditionsManager(this, startOpts);
5638
- const r = await fetch(this.options.ingestPoint + '/v1/web/start', {
5639
- method: 'POST',
5640
- headers: {
5641
- 'Content-Type': 'application/json',
5642
- },
5643
- body: JSON.stringify({
5644
- ...this.getTrackerInfo(),
5645
- timestamp: now(),
5646
- doNotRecord: true,
5647
- bufferDiff: 0,
5648
- userID: this.session.getInfo().userID,
5649
- token: undefined,
5650
- deviceMemory,
5651
- jsHeapSizeLimit,
5652
- timezone: getTimezone(),
5653
- width: window.screen.width,
5654
- height: window.screen.height,
5655
- }),
5656
- });
5657
- const {
5658
- // this token is needed to fetch conditions and flags,
5659
- // but it can't be used to record a session
5660
- token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, features, } = await r.json();
5661
- this.features = features ? features : this.features;
5662
- this.session.assign({ projectID });
5663
- this.session.setUserInfo({
5664
- userBrowser,
5665
- userCity,
5666
- userCountry,
5667
- userDevice,
5668
- userOS,
5669
- userState,
5670
- });
5671
- const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
5672
- this.startCallbacks.forEach((cb) => cb(onStartInfo));
5673
- await this.conditionsManager?.fetchConditions(projectID, token);
5674
- if (this.features['feature-flags']) {
5675
- await this.featureFlags.reloadFlags(token);
5676
- this.conditionsManager?.processFlags(this.featureFlags.flags);
5677
- }
5678
- await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
5679
- };
5680
4699
  this.onSessionSent = () => {
5681
4700
  return;
5682
4701
  };
5683
- /**
5684
- * Starts offline session recording
5685
- * @param {Object} startOpts - options for session start, same as .start()
5686
- * @param {Function} onSessionSent - callback that will be called once session is fully sent
5687
- * */
5688
- this.offlineRecording = (startOpts = {}, onSessionSent) => {
5689
- this.onSessionSent = onSessionSent;
5690
- this.singleBuffer = true;
5691
- const isNewSession = this.checkSessionToken(startOpts.forceNew);
5692
- adjustTimeOrigin();
5693
- this.coldStartTs = now();
5694
- const saverBuffer = this.localStorage.getItem(bufferStorageKey);
5695
- if (saverBuffer) {
5696
- const data = JSON.parse(saverBuffer);
5697
- this.bufferedMessages1 = Array.isArray(data) ? data : this.bufferedMessages1;
5698
- this.localStorage.removeItem(bufferStorageKey);
5699
- }
5700
- this.bufferedMessages1.push(Timestamp(this.timestamp()));
5701
- this.bufferedMessages1.push(TabData(this.session.getTabId()));
5702
- this.activityState = ActivityState.ColdStart;
5703
- if (startOpts.sessionHash) {
5704
- this.session.applySessionHash(startOpts.sessionHash);
5705
- }
5706
- if (startOpts.forceNew) {
5707
- this.session.reset();
5708
- }
5709
- this.session.assign({
5710
- userID: startOpts.userID,
5711
- metadata: startOpts.metadata,
5712
- });
5713
- const onStartInfo = { sessionToken: '', userUUID: '', sessionID: '' };
5714
- this.startCallbacks.forEach((cb) => cb(onStartInfo));
5715
- if (!isNewSession) {
5716
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5717
- this.send(TabChange(this.session.getTabId()));
5718
- }
5719
- this.observer.observe();
5720
- this.ticker.start();
5721
- return {
5722
- saveBuffer: this.saveBuffer,
5723
- getBuffer: this.getBuffer,
5724
- setBuffer: this.setBuffer,
5725
- };
5726
- };
5727
- /**
5728
- * Saves the captured messages in localStorage (or whatever is used in its place)
5729
- *
5730
- * Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
5731
- *
5732
- * Keeping the size of local storage reasonable is up to the end users of this library
5733
- * */
5734
- this.saveBuffer = () => {
5735
- this.localStorage.setItem(bufferStorageKey, JSON.stringify(this.bufferedMessages1));
5736
- };
5737
- /**
5738
- * @returns buffer with stored messages for offline recording
5739
- * */
5740
- this.getBuffer = () => {
5741
- return this.bufferedMessages1;
5742
- };
5743
- /**
5744
- * Used to set a buffer with messages array
5745
- * */
5746
- this.setBuffer = (buffer) => {
5747
- this.bufferedMessages1 = buffer;
5748
- };
5749
4702
  this.prevOpts = {};
5750
4703
  this.restartCanvasTracking = () => {
5751
4704
  this.canvasRecorder?.restartTracking();
@@ -5775,7 +4728,6 @@ class App {
5775
4728
  });
5776
4729
  });
5777
4730
  };
5778
- this.onUxtCb = [];
5779
4731
  this.contextId = Math.random().toString(36).slice(2);
5780
4732
  this.projectKey = projectKey;
5781
4733
  this.networkOptions = options.network;
@@ -5810,8 +4762,9 @@ class App {
5810
4762
  useAnimationFrame: false,
5811
4763
  },
5812
4764
  forceNgOff: false,
5813
- inlineCss: 0,
4765
+ inlineCss: InlineCssMode.Unset,
5814
4766
  disableSprites: false,
4767
+ disableThrottling: false,
5815
4768
  };
5816
4769
  this.options = simpleMerge(defaultOptions, options);
5817
4770
  if (!this.insideIframe &&
@@ -5842,7 +4795,6 @@ class App {
5842
4795
  app: this,
5843
4796
  isDictDisabled: Boolean(this.options.disableStringDict || this.options.crossdomain?.enabled),
5844
4797
  });
5845
- this.featureFlags = new FeatureFlags(this);
5846
4798
  this.tagWatcher = new TagWatcher({
5847
4799
  sessionStorage: this.sessionStorage,
5848
4800
  errLog: this.debug.error,
@@ -6254,6 +5206,162 @@ class App {
6254
5206
  const sessionToken = this.session.getSessionToken(this.projectKey);
6255
5207
  return needNewSessionID || !sessionToken;
6256
5208
  }
5209
+ /**
5210
+ * start buffering messages without starting the actual session, which gives
5211
+ * user 30 seconds to "activate" and record session by calling `start()` on conditional trigger,
5212
+ * and we will then send buffered batch, so it won't get lost
5213
+ * */
5214
+ async coldStart(startOpts = {}, conditional) {
5215
+ this.singleBuffer = false;
5216
+ const second = 1000;
5217
+ const isNewSession = this.checkSessionToken(startOpts.forceNew);
5218
+ if (conditional) {
5219
+ await this.setupConditionalStart(startOpts);
5220
+ }
5221
+ const cycle = () => {
5222
+ this.orderNumber += 1;
5223
+ adjustTimeOrigin();
5224
+ this.coldStartTs = now();
5225
+ if (this.orderNumber % 2 === 0) {
5226
+ this.bufferedMessages1.length = 0;
5227
+ this.bufferedMessages1.push(Timestamp(this.timestamp()));
5228
+ this.bufferedMessages1.push(TabData(this.session.getTabId()));
5229
+ }
5230
+ else {
5231
+ this.bufferedMessages2.length = 0;
5232
+ this.bufferedMessages2.push(Timestamp(this.timestamp()));
5233
+ this.bufferedMessages2.push(TabData(this.session.getTabId()));
5234
+ }
5235
+ this.stop(false);
5236
+ this.activityState = ActivityState.ColdStart;
5237
+ if (startOpts.sessionHash) {
5238
+ this.session.applySessionHash(startOpts.sessionHash);
5239
+ }
5240
+ if (startOpts.forceNew) {
5241
+ this.session.reset();
5242
+ }
5243
+ this.session.assign({
5244
+ userID: startOpts.userID,
5245
+ metadata: startOpts.metadata,
5246
+ });
5247
+ if (!isNewSession) {
5248
+ this.debug.log('continuing session on new tab', this.session.getTabId());
5249
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5250
+ this.send(TabChange(this.session.getTabId()));
5251
+ }
5252
+ this.observer.observe();
5253
+ this.ticker.start();
5254
+ };
5255
+ this.coldInterval = setInterval(() => {
5256
+ cycle();
5257
+ }, 30 * second);
5258
+ cycle();
5259
+ }
5260
+ async setupConditionalStart(startOpts) {
5261
+ this.conditionsManager = new ConditionsManager(this, startOpts);
5262
+ const r = await fetch(this.options.ingestPoint + '/v1/web/start', {
5263
+ method: 'POST',
5264
+ headers: {
5265
+ 'Content-Type': 'application/json',
5266
+ },
5267
+ body: JSON.stringify({
5268
+ ...this.getTrackerInfo(),
5269
+ timestamp: now(),
5270
+ doNotRecord: true,
5271
+ bufferDiff: 0,
5272
+ userID: this.session.getInfo().userID,
5273
+ token: undefined,
5274
+ deviceMemory,
5275
+ jsHeapSizeLimit,
5276
+ timezone: getTimezone(),
5277
+ width: window.screen.width,
5278
+ height: window.screen.height,
5279
+ }),
5280
+ });
5281
+ const {
5282
+ // this token is needed to fetch conditions and flags,
5283
+ // but it can't be used to record a session
5284
+ token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, } = await r.json();
5285
+ this.session.assign({ projectID });
5286
+ this.session.setUserInfo({
5287
+ userBrowser,
5288
+ userCity,
5289
+ userCountry,
5290
+ userDevice,
5291
+ userOS,
5292
+ userState,
5293
+ });
5294
+ const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
5295
+ this.startCallbacks.forEach((cb) => cb(onStartInfo));
5296
+ await this.conditionsManager?.fetchConditions(projectID, token);
5297
+ await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
5298
+ }
5299
+ /**
5300
+ * Starts offline session recording
5301
+ * @param {Object} startOpts - options for session start, same as .start()
5302
+ * @param {Function} onSessionSent - callback that will be called once session is fully sent
5303
+ * */
5304
+ offlineRecording(startOpts = {}, onSessionSent) {
5305
+ this.onSessionSent = onSessionSent;
5306
+ this.singleBuffer = true;
5307
+ const isNewSession = this.checkSessionToken(startOpts.forceNew);
5308
+ adjustTimeOrigin();
5309
+ this.coldStartTs = now();
5310
+ const saverBuffer = this.localStorage.getItem(bufferStorageKey);
5311
+ if (saverBuffer) {
5312
+ const data = JSON.parse(saverBuffer);
5313
+ this.bufferedMessages1 = Array.isArray(data) ? data : this.bufferedMessages1;
5314
+ this.localStorage.removeItem(bufferStorageKey);
5315
+ }
5316
+ this.bufferedMessages1.push(Timestamp(this.timestamp()));
5317
+ this.bufferedMessages1.push(TabData(this.session.getTabId()));
5318
+ this.activityState = ActivityState.ColdStart;
5319
+ if (startOpts.sessionHash) {
5320
+ this.session.applySessionHash(startOpts.sessionHash);
5321
+ }
5322
+ if (startOpts.forceNew) {
5323
+ this.session.reset();
5324
+ }
5325
+ this.session.assign({
5326
+ userID: startOpts.userID,
5327
+ metadata: startOpts.metadata,
5328
+ });
5329
+ const onStartInfo = { sessionToken: '', userUUID: '', sessionID: '' };
5330
+ this.startCallbacks.forEach((cb) => cb(onStartInfo));
5331
+ if (!isNewSession) {
5332
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5333
+ this.send(TabChange(this.session.getTabId()));
5334
+ }
5335
+ this.observer.observe();
5336
+ this.ticker.start();
5337
+ return {
5338
+ saveBuffer: this.saveBuffer,
5339
+ getBuffer: this.getBuffer,
5340
+ setBuffer: this.setBuffer,
5341
+ };
5342
+ }
5343
+ /**
5344
+ * Saves the captured messages in localStorage (or whatever is used in its place)
5345
+ *
5346
+ * Then, when this.offlineRecording is called, it will preload this messages and clear the storage item
5347
+ *
5348
+ * Keeping the size of local storage reasonable is up to the end users of this library
5349
+ * */
5350
+ saveBuffer() {
5351
+ this.localStorage.setItem(bufferStorageKey, JSON.stringify(this.bufferedMessages1));
5352
+ }
5353
+ /**
5354
+ * @returns buffer with stored messages for offline recording
5355
+ * */
5356
+ getBuffer() {
5357
+ return this.bufferedMessages1;
5358
+ }
5359
+ /**
5360
+ * Used to set a buffer with messages array
5361
+ * */
5362
+ setBuffer(buffer) {
5363
+ this.bufferedMessages1 = buffer;
5364
+ }
6257
5365
  /**
6258
5366
  * Uploads the stored session buffer to backend
6259
5367
  * @returns promise that resolves once messages are loaded, it has to be awaited
@@ -6309,7 +5417,7 @@ class App {
6309
5417
  while (this.bufferedMessages1.length > 0) {
6310
5418
  await this.flushBuffer(this.bufferedMessages1);
6311
5419
  }
6312
- this.postToWorker([['q_end']]);
5420
+ this.postToWorker([[-1]]);
6313
5421
  this.clearBuffers();
6314
5422
  }
6315
5423
  async _start(startOpts = {}, resetByWorker = false, conditionName) {
@@ -6397,8 +5505,7 @@ class App {
6397
5505
  delay, // derived from token
6398
5506
  sessionID, // derived from token
6399
5507
  startTimestamp, // real startTS (server time), derived from sessionID
6400
- userBrowser, userCity, userCountry, userDevice, userOS, userState, canvasEnabled, canvasQuality, canvasFPS, assistOnly: socketOnly, features, } = await r.json();
6401
- this.features = features ? features : this.features;
5508
+ userBrowser, userCity, userCountry, userDevice, userOS, userState, canvasEnabled, canvasQuality, canvasFPS, assistOnly: socketOnly, } = await r.json();
6402
5509
  if (typeof token !== 'string' ||
6403
5510
  typeof userUUID !== 'string' ||
6404
5511
  (typeof startTimestamp !== 'number' && typeof startTimestamp !== 'undefined') ||
@@ -6451,14 +5558,6 @@ class App {
6451
5558
  if (startOpts.startCallback) {
6452
5559
  startOpts.startCallback(SuccessfulStart(onStartInfo));
6453
5560
  }
6454
- if (this.features['feature-flags']) {
6455
- try {
6456
- void this.featureFlags.reloadFlags();
6457
- }
6458
- catch (e) {
6459
- this.debug.log("Error getting feature flags", e);
6460
- }
6461
- }
6462
5561
  await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
6463
5562
  this.activityState = ActivityState.Active;
6464
5563
  if (this.options.crossdomain?.enabled) {
@@ -6487,47 +5586,14 @@ class App {
6487
5586
  this.commit();
6488
5587
  /** --------------- COLD START BUFFER ------------------*/
6489
5588
  }
5589
+ if (this.insideIframe && this.rootId) {
5590
+ this.observer.crossdomainObserve(this.rootId, this.frameOderNumber, this.frameLevel);
5591
+ }
6490
5592
  else {
6491
- if (this.insideIframe && this.rootId) {
6492
- this.observer.crossdomainObserve(this.rootId, this.frameOderNumber, this.frameLevel);
6493
- }
6494
- else {
6495
- this.observer.observe();
6496
- }
6497
- this.ticker.start();
5593
+ this.observer.observe();
6498
5594
  }
5595
+ this.ticker.start();
6499
5596
  this.canvasRecorder?.startTracking();
6500
- if (this.features['usability-test'] && !this.insideIframe) {
6501
- this.uxtManager = this.uxtManager
6502
- ? this.uxtManager
6503
- : new UserTestManager(this, uxtStorageKey);
6504
- let uxtId;
6505
- const savedUxtTag = this.localStorage.getItem(uxtStorageKey);
6506
- if (savedUxtTag) {
6507
- uxtId = parseInt(savedUxtTag, 10);
6508
- }
6509
- if (location?.search) {
6510
- const query = new URLSearchParams(location.search);
6511
- if (query.has('oruxt')) {
6512
- const qId = query.get('oruxt');
6513
- uxtId = qId ? parseInt(qId, 10) : undefined;
6514
- }
6515
- }
6516
- if (uxtId) {
6517
- if (!this.uxtManager.isActive) {
6518
- // eslint-disable-next-line
6519
- this.uxtManager.getTest(uxtId, token, Boolean(savedUxtTag)).then((id) => {
6520
- if (id) {
6521
- this.onUxtCb.forEach((cb) => cb(id));
6522
- }
6523
- });
6524
- }
6525
- else {
6526
- // @ts-ignore
6527
- this.onUxtCb.forEach((cb) => cb(uxtId));
6528
- }
6529
- }
6530
- }
6531
5597
  return SuccessfulStart(onStartInfo);
6532
5598
  }
6533
5599
  catch (reason) {
@@ -6548,13 +5614,6 @@ class App {
6548
5614
  return UnsuccessfulStart(errorMessage);
6549
5615
  }
6550
5616
  }
6551
- addOnUxtCb(cb) {
6552
- // @ts-ignore
6553
- this.onUxtCb.push(cb);
6554
- }
6555
- getUxtId() {
6556
- return this.uxtManager?.getTestId();
6557
- }
6558
5617
  async waitStart() {
6559
5618
  return new Promise((resolve) => {
6560
5619
  const int = setInterval(() => {
@@ -6675,9 +5734,16 @@ function Connection (app) {
6675
5734
  if (connection === undefined) {
6676
5735
  return;
6677
5736
  }
6678
- const sendConnectionInformation = () => app.send(ConnectionInformation(Math.round(connection.downlink * 1000), connection.type || 'unknown'));
6679
- sendConnectionInformation();
6680
- connection.addEventListener('change', sendConnectionInformation);
5737
+ const sendConnectionInformation = () => {
5738
+ app.send(ConnectionInformation(Math.round(connection.downlink * 1000), connection.effectiveType || 'unknown'));
5739
+ };
5740
+ app.attachStartCallback(() => {
5741
+ sendConnectionInformation();
5742
+ connection.addEventListener('change', sendConnectionInformation);
5743
+ });
5744
+ app.attachStopCallback(() => {
5745
+ connection.removeEventListener('change', sendConnectionInformation);
5746
+ });
6681
5747
  }
6682
5748
 
6683
5749
  const printError = IN_BROWSER && 'InstallTrigger' in window // detect Firefox
@@ -7098,7 +6164,7 @@ function Img (app) {
7098
6164
  const sendImgError = app.safe(function (img) {
7099
6165
  const resolvedSrc = resolveURL(img.src || ''); // Src type is null sometimes. - is it true?
7100
6166
  if (isURL(resolvedSrc)) {
7101
- app.send(ResourceTiming(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false));
6167
+ app.send(ResourceTiming(app.timestamp(), 0, 0, 0, 0, 0, resolvedSrc, 'img', 0, false, 0, 0, 0, 0, 0, 0, 0));
7102
6168
  }
7103
6169
  });
7104
6170
  const sendImgAttrs = app.safe(function (img) {
@@ -7702,18 +6768,38 @@ function Timing (app, opts) {
7702
6768
  if (shouldSkip) {
7703
6769
  return;
7704
6770
  }
6771
+ // will probably require custom header added to responses for tracked requests:
6772
+ // Timing-Allow-Origin: *
6773
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Timing-Allow-Origin
6774
+ let stalled = 0;
6775
+ if (entry.connectEnd && entry.connectEnd > entry.domainLookupEnd) {
6776
+ // Usual case stalled is time between connection establishment and request start
6777
+ stalled = Math.max(0, entry.requestStart - entry.connectEnd);
6778
+ }
6779
+ else {
6780
+ // Connection reuse case - stalled is time between domain lookup and request start
6781
+ stalled = Math.max(0, entry.requestStart - entry.domainLookupEnd);
6782
+ }
6783
+ const timings = {
6784
+ queueing: entry.requestStart - entry.fetchStart,
6785
+ dnsLookup: entry.domainLookupEnd - entry.domainLookupStart,
6786
+ initialConnection: entry.connectEnd - entry.connectStart,
6787
+ ssl: entry.secureConnectionStart > 0 ? entry.connectEnd - entry.secureConnectionStart : 0,
6788
+ ttfb: entry.responseStart - entry.requestStart,
6789
+ contentDownload: entry.responseEnd - entry.responseStart,
6790
+ total: entry.duration ?? entry.responseEnd - entry.startTime,
6791
+ stalled,
6792
+ };
7705
6793
  const entryName = options.resourceNameSanitizer
7706
6794
  ? options.resourceNameSanitizer(entry.name)
7707
6795
  : entry.name;
7708
- const cached =
7709
- // @ts-ignore
7710
- (entry.responseStatus && entry.responseStatus === 304) ||
6796
+ const cached = (entry.responseStatus && entry.responseStatus === 304) ||
7711
6797
  // @ts-ignore
7712
6798
  (entry.deliveryType && entry.deliveryType === 'cache') ||
7713
6799
  (entry.transferSize === 0 && entry.decodedBodySize > 0);
7714
6800
  const requestFailed = entry.responseStatus && entry.responseStatus >= 400;
7715
6801
  const decodedBodySize = requestFailed ? -111 : entry.decodedBodySize || 0;
7716
- app.send(ResourceTiming(entry.startTime + getTimeOrigin(), entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, decodedBodySize, app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entryName, entry.initiatorType, entry.transferSize, cached));
6802
+ app.send(ResourceTiming(entry.startTime + getTimeOrigin(), entry.duration, timings.ttfb, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, decodedBodySize, app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entryName, entry.initiatorType, entry.transferSize, cached, timings.queueing, timings.dnsLookup, timings.initialConnection, timings.ssl, timings.contentDownload, timings.total, timings.stalled));
7717
6803
  }
7718
6804
  const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
7719
6805
  function onVitalsSignal(msg) {
@@ -9660,8 +8746,86 @@ function Tabs (app) {
9660
8746
  app.attachEventListener(window, 'focus', changeTab, false, false);
9661
8747
  }
9662
8748
 
8749
+ function LongAnimationTask (app, opts) {
8750
+ if (!opts.longTasks || !('PerformanceObserver' in window)) {
8751
+ return;
8752
+ }
8753
+ const onEntry = (entry) => {
8754
+ app.send(LongAnimationTask$1(entry.name, entry.duration, entry.blockingDuration, entry.firstUIEventTimestamp, entry.startTime, JSON.stringify(entry.scripts ?? [])));
8755
+ };
8756
+ const observer = new PerformanceObserver((entryList) => {
8757
+ entryList.getEntries().forEach((entry) => {
8758
+ if (entry.entryType === 'long-animation-frame') {
8759
+ onEntry(entry);
8760
+ }
8761
+ });
8762
+ });
8763
+ app.attachStartCallback(() => {
8764
+ performance.getEntriesByType('long-animation-frame').forEach((lat) => {
8765
+ onEntry(lat);
8766
+ });
8767
+ observer.observe({
8768
+ entryTypes: ['long-animation-frame'],
8769
+ });
8770
+ });
8771
+ app.attachStopCallback(() => {
8772
+ observer.disconnect();
8773
+ });
8774
+ }
8775
+
8776
+ const toIgnore = ["composite", "computedOffset", "easing", "offset"];
8777
+ function webAnimations(app, options = {}) {
8778
+ const { allElements = false } = options;
8779
+ let listening = new WeakSet();
8780
+ let handled = new WeakSet();
8781
+ function wire(anim, el, nodeId) {
8782
+ if (handled.has(anim))
8783
+ return;
8784
+ handled.add(anim);
8785
+ anim.addEventListener('finish', () => {
8786
+ const lastKF = anim.effect.getKeyframes().at(-1);
8787
+ const computedStyle = getComputedStyle(el);
8788
+ const keys = Object.keys(lastKF).filter((p) => !toIgnore.includes(p));
8789
+ // @ts-ignore
8790
+ const finalStyle = {};
8791
+ keys.forEach((key) => {
8792
+ finalStyle[key] = computedStyle[key];
8793
+ });
8794
+ app.send(NodeAnimationResult(nodeId, JSON.stringify(finalStyle)));
8795
+ }, { once: true });
8796
+ }
8797
+ function scanElement(el, nodeId) {
8798
+ el.getAnimations({ subtree: false }).forEach((anim) => wire(anim, el, nodeId));
8799
+ }
8800
+ app.nodes.attachNodeCallback((node) => {
8801
+ if ((allElements || node.nodeName.includes('-')) && 'getAnimations' in node) {
8802
+ const animations = node.getAnimations({ subtree: false });
8803
+ const id = app.nodes.getID(node);
8804
+ if (animations.length > 0 && !listening.has(node) && id) {
8805
+ listening.add(node);
8806
+ scanElement(node, id);
8807
+ node.addEventListener('animationstart', () => scanElement(node, id));
8808
+ }
8809
+ }
8810
+ });
8811
+ const origAnimate = Element.prototype.animate;
8812
+ Element.prototype.animate = function (...args) {
8813
+ const anim = origAnimate.apply(this, args);
8814
+ const id = app.nodes.getID(this);
8815
+ if (!id)
8816
+ return anim;
8817
+ wire(anim, this, id);
8818
+ return anim;
8819
+ };
8820
+ app.attachStopCallback(() => {
8821
+ Element.prototype.animate = origAnimate; // Restore original animate method
8822
+ listening = new WeakSet();
8823
+ handled = new WeakSet();
8824
+ });
8825
+ }
8826
+
9663
8827
  const Messages = _Messages;
9664
- const DOCS_SETUP = '/en/sdk/constructor';
8828
+ const DOCS_SETUP = '/en/sdk';
9665
8829
  function processOptions(obj) {
9666
8830
  if (obj == null) {
9667
8831
  console.error(`OpenReplay: invalid options argument type. Please, check documentation on ${DOCS_HOST}${DOCS_SETUP}`);
@@ -9711,7 +8875,7 @@ class API {
9711
8875
  this.signalStartIssue = (reason, missingApi) => {
9712
8876
  const doNotTrack = this.checkDoNotTrack();
9713
8877
  console.log("Tracker couldn't start due to:", JSON.stringify({
9714
- trackerVersion: '16.4.10',
8878
+ trackerVersion: '17.0.0',
9715
8879
  projectKey: this.options.projectKey,
9716
8880
  doNotTrack,
9717
8881
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
@@ -9739,6 +8903,12 @@ class API {
9739
8903
  }
9740
8904
  }
9741
8905
  };
8906
+ this.incident = (options) => {
8907
+ if (this.app === null) {
8908
+ return;
8909
+ }
8910
+ this.app.send(Incident(options.label ?? '', options.startTime, options.endTime ?? options.startTime));
8911
+ };
9742
8912
  this.crossdomainMode = Boolean(inIframe() && options.crossdomain?.enabled);
9743
8913
  if (!IN_BROWSER || !processOptions(options)) {
9744
8914
  return;
@@ -9817,6 +8987,7 @@ class API {
9817
8987
  Img(app);
9818
8988
  Input(app, options);
9819
8989
  Timing(app, options);
8990
+ LongAnimationTask(app, options);
9820
8991
  Focus(app);
9821
8992
  Fonts(app);
9822
8993
  const skipNetwork = options.network?.disabled;
@@ -9824,10 +8995,8 @@ class API {
9824
8995
  Network(app, options.network);
9825
8996
  }
9826
8997
  selection(app);
8998
+ webAnimations(app, options.webAnimations);
9827
8999
  window.__OPENREPLAY__ = this;
9828
- if (options.flags && options.flags.onFlagsLoad) {
9829
- this.onFlagsLoad(options.flags.onFlagsLoad);
9830
- }
9831
9000
  const wOpen = window.open;
9832
9001
  if (options.autoResetOnWindowOpen || options.resetTabOnWindowOpen) {
9833
9002
  app.attachStartCallback(() => {
@@ -9850,24 +9019,6 @@ class API {
9850
9019
  });
9851
9020
  }
9852
9021
  }
9853
- isFlagEnabled(flagName) {
9854
- return this.featureFlags.isFlagEnabled(flagName);
9855
- }
9856
- onFlagsLoad(callback) {
9857
- this.app?.featureFlags.onFlagsLoad(callback);
9858
- }
9859
- clearPersistFlags() {
9860
- this.app?.featureFlags.clearPersistFlags();
9861
- }
9862
- reloadFlags() {
9863
- return this.app?.featureFlags.reloadFlags();
9864
- }
9865
- getFeatureFlag(flagName) {
9866
- return this.app?.featureFlags.getFeatureFlag(flagName);
9867
- }
9868
- getAllFeatureFlags() {
9869
- return this.app?.featureFlags.flags;
9870
- }
9871
9022
  use(fn) {
9872
9023
  return fn(this.app, this.options);
9873
9024
  }
@@ -9998,12 +9149,6 @@ class API {
9998
9149
  }
9999
9150
  return this.app.getTabId();
10000
9151
  }
10001
- getUxId() {
10002
- if (this.app === null) {
10003
- return null;
10004
- }
10005
- return this.app.getUxtId();
10006
- }
10007
9152
  sessionID() {
10008
9153
  deprecationWarn("'sessionID' method", "'getSessionID' method", '/');
10009
9154
  return this.getSessionID();
@@ -10047,12 +9192,20 @@ class API {
10047
9192
  return this.issue(key, payload);
10048
9193
  }
10049
9194
  else {
9195
+ if (!payload || typeof payload === 'string') {
9196
+ return this.app.send(CustomEvent(key, payload));
9197
+ }
10050
9198
  try {
9199
+ if ('or_timestamp' in payload) {
9200
+ const startTs = this.getSessionInfo()?.timestamp ?? 0;
9201
+ const diff = payload.or_timestamp - startTs;
9202
+ if (diff < 0) {
9203
+ console.error(`OpenReplay: event ${key} has or_timestamp (${payload.or_timestamp}) before session start (${startTs}). It will be ignored.`);
9204
+ }
9205
+ }
10051
9206
  payload = JSON.stringify(payload);
10052
9207
  }
10053
- catch (e) {
10054
- return;
10055
- }
9208
+ catch (_) { }
10056
9209
  this.app.send(CustomEvent(key, payload));
10057
9210
  }
10058
9211
  }