@openreplay/tracker 17.0.0-beta.0 → 17.0.1

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 (81) hide show
  1. package/dist/cjs/common/interaction.d.ts +1 -1
  2. package/dist/cjs/common/messages.gen.d.ts +13 -1
  3. package/dist/cjs/entry.js +625 -1795
  4. package/dist/cjs/entry.js.map +1 -1
  5. package/dist/cjs/index.js +625 -1753
  6. package/dist/cjs/index.js.map +1 -1
  7. package/dist/cjs/main/app/guards.d.ts +1 -0
  8. package/dist/cjs/main/app/index.d.ts +1 -7
  9. package/dist/cjs/main/app/messages.gen.d.ts +2 -0
  10. package/dist/cjs/main/app/nodes/idSeq.d.ts +14 -0
  11. package/dist/cjs/main/app/nodes/index.d.ts +2 -1
  12. package/dist/cjs/main/app/observer/observer.d.ts +4 -0
  13. package/dist/cjs/main/app/observer/top_observer.d.ts +4 -2
  14. package/dist/cjs/main/app/session.d.ts +3 -2
  15. package/dist/cjs/main/index.d.ts +4 -12
  16. package/dist/cjs/main/modules/conditionsManager.d.ts +6 -1
  17. package/dist/cjs/main/modules/mouse.d.ts +2 -20
  18. package/dist/cjs/main/modules/timing.d.ts +1 -0
  19. package/dist/cjs/main/modules/viewport.d.ts +5 -1
  20. package/dist/cjs/main/modules/webAnimations.d.ts +9 -0
  21. package/dist/cjs/main/singleton.d.ts +0 -7
  22. package/dist/lib/common/interaction.d.ts +1 -1
  23. package/dist/lib/common/messages.gen.d.ts +13 -1
  24. package/dist/lib/entry.js +625 -1795
  25. package/dist/lib/entry.js.map +1 -1
  26. package/dist/lib/index.js +625 -1753
  27. package/dist/lib/index.js.map +1 -1
  28. package/dist/lib/main/app/guards.d.ts +1 -0
  29. package/dist/lib/main/app/index.d.ts +1 -7
  30. package/dist/lib/main/app/messages.gen.d.ts +2 -0
  31. package/dist/lib/main/app/nodes/idSeq.d.ts +14 -0
  32. package/dist/lib/main/app/nodes/index.d.ts +2 -1
  33. package/dist/lib/main/app/observer/observer.d.ts +4 -0
  34. package/dist/lib/main/app/observer/top_observer.d.ts +4 -2
  35. package/dist/lib/main/app/session.d.ts +3 -2
  36. package/dist/lib/main/index.d.ts +4 -12
  37. package/dist/lib/main/modules/conditionsManager.d.ts +6 -1
  38. package/dist/lib/main/modules/mouse.d.ts +2 -20
  39. package/dist/lib/main/modules/timing.d.ts +1 -0
  40. package/dist/lib/main/modules/viewport.d.ts +5 -1
  41. package/dist/lib/main/modules/webAnimations.d.ts +9 -0
  42. package/dist/lib/main/singleton.d.ts +0 -7
  43. package/dist/types/common/interaction.d.ts +1 -1
  44. package/dist/types/common/messages.gen.d.ts +13 -1
  45. package/dist/types/main/app/guards.d.ts +1 -0
  46. package/dist/types/main/app/index.d.ts +1 -7
  47. package/dist/types/main/app/messages.gen.d.ts +2 -0
  48. package/dist/types/main/app/nodes/idSeq.d.ts +14 -0
  49. package/dist/types/main/app/nodes/index.d.ts +2 -1
  50. package/dist/types/main/app/observer/observer.d.ts +4 -0
  51. package/dist/types/main/app/observer/top_observer.d.ts +4 -2
  52. package/dist/types/main/app/session.d.ts +3 -2
  53. package/dist/types/main/index.d.ts +4 -12
  54. package/dist/types/main/modules/conditionsManager.d.ts +6 -1
  55. package/dist/types/main/modules/mouse.d.ts +2 -20
  56. package/dist/types/main/modules/timing.d.ts +1 -0
  57. package/dist/types/main/modules/viewport.d.ts +5 -1
  58. package/dist/types/main/modules/webAnimations.d.ts +9 -0
  59. package/dist/types/main/singleton.d.ts +0 -7
  60. package/package.json +13 -14
  61. package/dist/cjs/main/modules/featureFlags.d.ts +0 -25
  62. package/dist/cjs/main/modules/userTesting/SignalManager.d.ts +0 -29
  63. package/dist/cjs/main/modules/userTesting/dnd.d.ts +0 -1
  64. package/dist/cjs/main/modules/userTesting/index.d.ts +0 -45
  65. package/dist/cjs/main/modules/userTesting/recorder.d.ts +0 -24
  66. package/dist/cjs/main/modules/userTesting/styles.d.ts +0 -277
  67. package/dist/cjs/main/modules/userTesting/utils.d.ts +0 -9
  68. package/dist/lib/main/modules/featureFlags.d.ts +0 -25
  69. package/dist/lib/main/modules/userTesting/SignalManager.d.ts +0 -29
  70. package/dist/lib/main/modules/userTesting/dnd.d.ts +0 -1
  71. package/dist/lib/main/modules/userTesting/index.d.ts +0 -45
  72. package/dist/lib/main/modules/userTesting/recorder.d.ts +0 -24
  73. package/dist/lib/main/modules/userTesting/styles.d.ts +0 -277
  74. package/dist/lib/main/modules/userTesting/utils.d.ts +0 -9
  75. package/dist/types/main/modules/featureFlags.d.ts +0 -25
  76. package/dist/types/main/modules/userTesting/SignalManager.d.ts +0 -29
  77. package/dist/types/main/modules/userTesting/dnd.d.ts +0 -1
  78. package/dist/types/main/modules/userTesting/index.d.ts +0 -45
  79. package/dist/types/main/modules/userTesting/recorder.d.ts +0 -24
  80. package/dist/types/main/modules/userTesting/styles.d.ts +0 -277
  81. package/dist/types/main/modules/userTesting/utils.d.ts +0 -9
package/dist/lib/entry.js CHANGED
@@ -56,12 +56,12 @@ fl[28] = 258, revfl[258] = 28;
56
56
  var _b = freb(fdeb, 0), revfd = _b.r;
57
57
  // map of value to reverse (assuming 16 bits)
58
58
  var rev = new u16(32768);
59
- for (var i = 0; i < 32768; ++i) {
59
+ for (var i$1 = 0; i$1 < 32768; ++i$1) {
60
60
  // reverse table algorithm from SO
61
- var x$1 = ((i & 0xAAAA) >> 1) | ((i & 0x5555) << 1);
61
+ var x$1 = ((i$1 & 0xAAAA) >> 1) | ((i$1 & 0x5555) << 1);
62
62
  x$1 = ((x$1 & 0xCCCC) >> 2) | ((x$1 & 0x3333) << 2);
63
63
  x$1 = ((x$1 & 0xF0F0) >> 4) | ((x$1 & 0x0F0F) << 4);
64
- rev[i] = (((x$1 & 0xFF00) >> 8) | ((x$1 & 0x00FF) << 8)) >> 1;
64
+ rev[i$1] = (((x$1 & 0xFF00) >> 8) | ((x$1 & 0x00FF) << 8)) >> 1;
65
65
  }
66
66
  // create huffman tree from u8 "map": index -> code length for code index
67
67
  // mb (max bits) must be at most 15
@@ -117,18 +117,18 @@ var hMap = (function (cd, mb, r) {
117
117
  });
118
118
  // fixed length tree
119
119
  var flt = new u8(288);
120
- for (var i = 0; i < 144; ++i)
121
- flt[i] = 8;
122
- for (var i = 144; i < 256; ++i)
123
- flt[i] = 9;
124
- for (var i = 256; i < 280; ++i)
125
- flt[i] = 7;
126
- for (var i = 280; i < 288; ++i)
127
- flt[i] = 8;
120
+ for (var i$1 = 0; i$1 < 144; ++i$1)
121
+ flt[i$1] = 8;
122
+ for (var i$1 = 144; i$1 < 256; ++i$1)
123
+ flt[i$1] = 9;
124
+ for (var i$1 = 256; i$1 < 280; ++i$1)
125
+ flt[i$1] = 7;
126
+ for (var i$1 = 280; i$1 < 288; ++i$1)
127
+ flt[i$1] = 8;
128
128
  // fixed distance tree
129
129
  var fdt = new u8(32);
130
- for (var i = 0; i < 32; ++i)
131
- fdt[i] = 5;
130
+ for (var i$1 = 0; i$1 < 32; ++i$1)
131
+ fdt[i$1] = 5;
132
132
  // fixed length map
133
133
  var flm = /*#__PURE__*/ hMap(flt, 9, 0);
134
134
  // fixed distance map
@@ -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);
@@ -1712,6 +1625,13 @@ function SetNodeAttributeDictGlobal(id, name, value) {
1712
1625
  value,
1713
1626
  ];
1714
1627
  }
1628
+ function NodeAnimationResult(id, styles) {
1629
+ return [
1630
+ 36 /* Messages.Type.NodeAnimationResult */,
1631
+ id,
1632
+ styles,
1633
+ ];
1634
+ }
1715
1635
  function CSSInsertRule(id, rule, index) {
1716
1636
  return [
1717
1637
  37 /* Messages.Type.CSSInsertRule */,
@@ -1924,6 +1844,13 @@ function CustomIssue(name, payload) {
1924
1844
  payload,
1925
1845
  ];
1926
1846
  }
1847
+ function SetNodeSlot(id, slotID) {
1848
+ return [
1849
+ 65 /* Messages.Type.SetNodeSlot */,
1850
+ id,
1851
+ slotID,
1852
+ ];
1853
+ }
1927
1854
  function CSSInsertRuleURLBased(id, rule, index, baseURL) {
1928
1855
  return [
1929
1856
  67 /* Messages.Type.CSSInsertRuleURLBased */,
@@ -2243,6 +2170,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
2243
2170
  NetworkRequest: NetworkRequest,
2244
2171
  NetworkRequestDeprecated: NetworkRequestDeprecated,
2245
2172
  NgRx: NgRx,
2173
+ NodeAnimationResult: NodeAnimationResult,
2246
2174
  OTable: OTable,
2247
2175
  PageLoadTiming: PageLoadTiming,
2248
2176
  PageRenderTiming: PageRenderTiming,
@@ -2269,6 +2197,7 @@ var _Messages = /*#__PURE__*/Object.freeze({
2269
2197
  SetNodeData: SetNodeData,
2270
2198
  SetNodeFocus: SetNodeFocus,
2271
2199
  SetNodeScroll: SetNodeScroll,
2200
+ SetNodeSlot: SetNodeSlot,
2272
2201
  SetPageLocation: SetPageLocation,
2273
2202
  SetPageLocationDeprecated: SetPageLocationDeprecated,
2274
2203
  SetPageVisibility: SetPageVisibility,
@@ -2417,986 +2346,6 @@ class TagWatcher {
2417
2346
  }
2418
2347
  }
2419
2348
 
2420
- const bgStyle = {
2421
- position: 'fixed',
2422
- top: 0,
2423
- left: 0,
2424
- width: '100vw',
2425
- height: '100vh',
2426
- background: 'rgba(0, 0, 0, 0.40)',
2427
- display: 'flex',
2428
- alignItems: 'center',
2429
- justifyContent: 'center',
2430
- zIndex: 999999,
2431
- fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
2432
- };
2433
- const containerStyle = {
2434
- display: 'flex',
2435
- flexDirection: 'column',
2436
- gap: '2rem',
2437
- alignItems: 'center',
2438
- padding: '1.5rem',
2439
- borderRadius: '2px',
2440
- border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2441
- background: '#FFF',
2442
- width: '22rem',
2443
- };
2444
- const containerWidgetStyle = {
2445
- display: 'flex',
2446
- 'flex-direction': 'column',
2447
- gap: 'unset',
2448
- 'align-items': 'center',
2449
- padding: 'unset',
2450
- fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
2451
- 'border-radius': '2px',
2452
- border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2453
- background: 'rgba(255, 255, 255, 0.75)',
2454
- width: '22rem',
2455
- };
2456
- const titleStyle = {
2457
- fontFamily: 'Verdana, sans-serif',
2458
- fontSize: '1.25rem',
2459
- fontStyle: 'normal',
2460
- fontWeight: '500',
2461
- lineHeight: '1.75rem',
2462
- color: 'rgba(0, 0, 0, 0.85)',
2463
- };
2464
- const descriptionStyle = {
2465
- borderTop: '1px solid rgba(0, 0, 0, 0.06)',
2466
- borderBottom: '1px solid rgba(0, 0, 0, 0.06)',
2467
- padding: '1.25rem 0rem',
2468
- color: 'rgba(0, 0, 0, 0.85)',
2469
- fontFamily: 'Verdana, sans-serif',
2470
- fontSize: '13px',
2471
- fontStyle: 'normal',
2472
- fontWeight: '400',
2473
- lineHeight: 'auto',
2474
- whiteSpace: 'pre-wrap',
2475
- };
2476
- const buttonStyle = {
2477
- display: 'flex',
2478
- padding: '0.4rem 0.9375rem',
2479
- justifyContent: 'center',
2480
- alignItems: 'center',
2481
- gap: '0.625rem',
2482
- borderRadius: '0.25rem',
2483
- border: '1px solid #394EFF',
2484
- background: '#394EFF',
2485
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2486
- color: '#FFF',
2487
- textAlign: 'center',
2488
- fontFamily: 'Verdana, sans-serif',
2489
- fontSize: '1rem',
2490
- fontStyle: 'normal',
2491
- fontWeight: '500',
2492
- lineHeight: '1.5rem',
2493
- cursor: 'pointer',
2494
- };
2495
- const sectionTitleStyle = {
2496
- fontFamily: 'Verdana, sans-serif',
2497
- fontSize: '13px',
2498
- fontWeight: '500',
2499
- lineHeight: 'auto',
2500
- display: 'flex',
2501
- justifyContent: 'space-between',
2502
- width: '100%',
2503
- cursor: 'pointer',
2504
- };
2505
- const contentStyle = {
2506
- display: 'flex',
2507
- flexDirection: 'column',
2508
- alignItems: 'flex-start',
2509
- gap: '0.625rem',
2510
- fontSize: '13px',
2511
- lineHeight: 'auto',
2512
- };
2513
- // New widget styles
2514
- const titleWidgetStyle = {
2515
- padding: '0.5rem',
2516
- gap: '0.5rem',
2517
- fontFamily: 'Verdana, sans-serif',
2518
- fontSize: '16px',
2519
- fontStyle: 'normal',
2520
- fontWeight: '500',
2521
- lineHeight: 'auto',
2522
- color: 'white',
2523
- display: 'flex',
2524
- alignItems: 'center',
2525
- width: '100%',
2526
- borderRadius: '2px',
2527
- background: 'rgba(0, 0, 0, 0.75)',
2528
- boxSizing: 'border-box',
2529
- };
2530
- const descriptionWidgetStyle = {
2531
- boxSizing: 'border-box',
2532
- display: 'block',
2533
- width: '100%',
2534
- borderBottom: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2535
- background: '#FFF',
2536
- padding: '0.65rem',
2537
- alignSelf: 'stretch',
2538
- color: '#000',
2539
- fontFamily: 'Verdana, sans-serif',
2540
- // fontSize: '0.875rem',
2541
- fontStyle: 'normal',
2542
- fontWeight: '400',
2543
- // lineHeight: '1.375rem',
2544
- };
2545
- const endSectionStyle = {
2546
- ...descriptionWidgetStyle,
2547
- display: 'flex',
2548
- flexDirection: 'column',
2549
- alignItems: 'center',
2550
- gap: '0.625rem',
2551
- };
2552
- const symbolIcon = {
2553
- fontSize: '1.25rem',
2554
- fontWeight: '500',
2555
- cursor: 'pointer',
2556
- color: '#394EFF',
2557
- };
2558
- const buttonWidgetStyle = {
2559
- display: 'flex',
2560
- padding: '0.4rem 0.9375rem',
2561
- justifyContent: 'center',
2562
- alignItems: 'center',
2563
- gap: '0.625rem',
2564
- borderRadius: '0.25rem',
2565
- border: '1px solid #394EFF',
2566
- background: '#394EFF',
2567
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2568
- color: '#FFF',
2569
- textAlign: 'center',
2570
- fontFamily: 'Verdana, sans-serif',
2571
- fontSize: '1rem',
2572
- fontStyle: 'normal',
2573
- fontWeight: '500',
2574
- lineHeight: '1.5rem',
2575
- width: '100%',
2576
- boxSizing: 'border-box',
2577
- cursor: 'pointer',
2578
- };
2579
- const stopWidgetStyle = {
2580
- marginTop: '1rem',
2581
- marginBottom: '1rem',
2582
- cursor: 'pointer',
2583
- display: 'block',
2584
- fontWeight: '500',
2585
- fontSize: '13px!important',
2586
- lineHeight: 'auto',
2587
- };
2588
- const paginationStyle = {
2589
- display: 'flex',
2590
- justifyContent: 'space-between',
2591
- alignItems: 'center',
2592
- gap: '1rem',
2593
- padding: '0.5rem',
2594
- width: '100%',
2595
- boxSizing: 'border-box',
2596
- };
2597
- const taskNumberActive = {
2598
- display: 'flex',
2599
- flexDirection: 'column',
2600
- alignItems: 'center',
2601
- justifyContent: 'center',
2602
- borderRadius: '6.25em',
2603
- outline: '1px solid #394EFF',
2604
- fontSize: '13px',
2605
- height: '24px',
2606
- width: '24px',
2607
- };
2608
- const taskNumberDone = {
2609
- display: 'flex',
2610
- flexDirection: 'column',
2611
- alignItems: 'center',
2612
- justifyContent: 'center',
2613
- borderRadius: '6.25em',
2614
- outline: '1px solid #D2DFFF',
2615
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2616
- background: '#D2DFFF',
2617
- fontSize: '13px',
2618
- height: '24px',
2619
- width: '24px',
2620
- };
2621
- const taskDescriptionCard = {
2622
- borderRadius: '0.375rem',
2623
- border: '1px solid rgba(0, 0, 0, 0.06)',
2624
- background: '#F5F7FF',
2625
- boxShadow: '0px 2px 0px 0px rgba(0, 0, 0, 0.04)',
2626
- display: 'flex',
2627
- flexDirection: 'column',
2628
- padding: '0.625rem 0.9375rem',
2629
- gap: '0.5rem',
2630
- alignSelf: 'stretch',
2631
- };
2632
- const taskTextStyle = {
2633
- fontWeight: 'bold',
2634
- };
2635
- const taskDescriptionStyle = {
2636
- fontSize: '13px',
2637
- lineHeight: 'auto',
2638
- };
2639
- const taskButtonStyle = {
2640
- marginRight: '0.5rem',
2641
- cursor: 'pointer',
2642
- color: '#394EFF',
2643
- textAlign: 'center',
2644
- fontFamily: 'Verdana, sans-serif',
2645
- fontSize: '13px',
2646
- fontStyle: 'normal',
2647
- fontWeight: '500',
2648
- lineHeight: 'auto',
2649
- };
2650
- const taskButtonBorderedStyle = {
2651
- ...taskButtonStyle,
2652
- display: 'flex',
2653
- padding: '0.25rem 0.9375rem',
2654
- justifyContent: 'center',
2655
- alignItems: 'center',
2656
- gap: '0.5rem',
2657
- borderRadius: '0.25rem',
2658
- border: '1px solid #394EFF',
2659
- };
2660
- const taskButtonsRow = {
2661
- display: 'flex',
2662
- justifyContent: 'space-between',
2663
- alignItems: 'center',
2664
- width: '100%',
2665
- boxSizing: 'border-box',
2666
- };
2667
- const spinnerStyles = {
2668
- border: '4px solid rgba(255, 255, 255, 0.4)',
2669
- width: '16px',
2670
- height: '16px',
2671
- borderRadius: '50%',
2672
- borderLeftColor: '#fff',
2673
- animation: 'spin 0.5s linear infinite',
2674
- };
2675
-
2676
- const Quality = {
2677
- Standard: { width: 1280, height: 720 }};
2678
- class Recorder {
2679
- constructor(app) {
2680
- this.app = app;
2681
- this.mediaRecorder = null;
2682
- this.recordedChunks = [];
2683
- this.stream = null;
2684
- this.recStartTs = null;
2685
- }
2686
- async startRecording(fps, quality, micReq, camReq) {
2687
- this.recStartTs = this.app.timestamp();
2688
- const videoConstraints = quality;
2689
- try {
2690
- this.stream = await navigator.mediaDevices.getUserMedia({
2691
- video: camReq ? { ...videoConstraints, frameRate: { ideal: fps } } : false,
2692
- audio: micReq,
2693
- });
2694
- this.mediaRecorder = new MediaRecorder(this.stream, {
2695
- mimeType: 'video/webm;codecs=vp9',
2696
- });
2697
- this.recordedChunks = [];
2698
- this.mediaRecorder.ondataavailable = (event) => {
2699
- if (event.data.size > 0) {
2700
- this.recordedChunks.push(event.data);
2701
- }
2702
- };
2703
- this.mediaRecorder.start();
2704
- }
2705
- catch (error) {
2706
- console.error(error);
2707
- }
2708
- }
2709
- async stopRecording() {
2710
- return new Promise((resolve) => {
2711
- if (!this.mediaRecorder)
2712
- return;
2713
- this.mediaRecorder.onstop = () => {
2714
- const blob = new Blob(this.recordedChunks, {
2715
- type: 'video/webm',
2716
- });
2717
- resolve(blob);
2718
- };
2719
- this.mediaRecorder.stop();
2720
- });
2721
- }
2722
- async sendToAPI() {
2723
- const blob = await this.stopRecording();
2724
- // const formData = new FormData()
2725
- // formData.append('file', blob, 'record.webm')
2726
- // formData.append('start', this.recStartTs?.toString() ?? '')
2727
- return fetch(`${this.app.options.ingestPoint}/v1/web/uxt/upload-url`, {
2728
- headers: {
2729
- Authorization: `Bearer ${this.app.session.getSessionToken()}`,
2730
- },
2731
- })
2732
- .then((r) => {
2733
- if (r.ok) {
2734
- return r.json();
2735
- }
2736
- else {
2737
- throw new Error('Failed to get upload url');
2738
- }
2739
- })
2740
- .then(({ url }) => {
2741
- return fetch(url, {
2742
- method: 'PUT',
2743
- headers: {
2744
- 'Content-Type': 'video/webm',
2745
- },
2746
- body: blob,
2747
- });
2748
- })
2749
- .catch(console.error)
2750
- .finally(() => {
2751
- this.discard();
2752
- });
2753
- }
2754
- async saveToFile(fileName = 'recorded-video.webm') {
2755
- const blob = await this.stopRecording();
2756
- const url = URL.createObjectURL(blob);
2757
- const a = document.createElement('a');
2758
- a.style.display = 'none';
2759
- a.href = url;
2760
- a.download = fileName;
2761
- document.body.appendChild(a);
2762
- a.click();
2763
- window.URL.revokeObjectURL(url);
2764
- document.body.removeChild(a);
2765
- }
2766
- discard() {
2767
- this.mediaRecorder?.stop();
2768
- this.stream?.getTracks().forEach((track) => track.stop());
2769
- }
2770
- }
2771
-
2772
- // @ts-nocheck
2773
- function attachDND(element, dragTarget) {
2774
- dragTarget.onmousedown = function (event) {
2775
- const clientRect = element.getBoundingClientRect();
2776
- const shiftX = event.clientX - clientRect.left;
2777
- const shiftY = event.clientY - clientRect.top;
2778
- element.style.position = 'fixed';
2779
- element.style.zIndex = 99999999999999;
2780
- moveAt(event.pageX, event.pageY);
2781
- function moveAt(pageX, pageY) {
2782
- let leftC = pageX - shiftX;
2783
- let topC = pageY - shiftY;
2784
- if (leftC <= 5)
2785
- leftC = 5;
2786
- if (topC <= 5)
2787
- topC = 5;
2788
- if (leftC >= window.innerWidth - clientRect.width)
2789
- leftC = window.innerWidth - clientRect.width;
2790
- if (topC >= window.innerHeight - clientRect.height)
2791
- topC = window.innerHeight - clientRect.height;
2792
- element.style.left = `${leftC}px`;
2793
- element.style.top = `${topC}px`;
2794
- }
2795
- function onMouseMove(event) {
2796
- moveAt(event.pageX, event.pageY);
2797
- }
2798
- document.addEventListener('mousemove', onMouseMove);
2799
- const clearAll = () => {
2800
- document.removeEventListener('mousemove', onMouseMove);
2801
- document.removeEventListener('mouseup', clearAll);
2802
- };
2803
- document.addEventListener('mouseup', clearAll);
2804
- };
2805
- dragTarget.ondragstart = function () {
2806
- return false;
2807
- };
2808
- }
2809
-
2810
- function generateGrid() {
2811
- const grid = document.createElement('div');
2812
- grid.className = 'grid';
2813
- for (let i = 0; i < 16; i++) {
2814
- const cell = document.createElement('div');
2815
- Object.assign(cell.style, {
2816
- width: '2px',
2817
- height: '2px',
2818
- borderRadius: '10px',
2819
- background: 'white',
2820
- });
2821
- cell.className = 'cell';
2822
- grid.appendChild(cell);
2823
- }
2824
- Object.assign(grid.style, {
2825
- display: 'grid',
2826
- gridTemplateColumns: 'repeat(4, 1fr)',
2827
- gridTemplateRows: 'repeat(4, 1fr)',
2828
- gap: '2px',
2829
- cursor: 'grab',
2830
- });
2831
- return grid;
2832
- }
2833
- function generateChevron() {
2834
- const triangle = document.createElement('div');
2835
- Object.assign(triangle.style, {
2836
- width: '0',
2837
- height: '0',
2838
- borderLeft: '7px solid transparent',
2839
- borderRight: '7px solid transparent',
2840
- borderBottom: '7px solid white',
2841
- });
2842
- const container = document.createElement('div');
2843
- container.appendChild(triangle);
2844
- Object.assign(container.style, {
2845
- display: 'flex',
2846
- alignItems: 'center',
2847
- justifyContent: 'center',
2848
- width: '16px',
2849
- height: '16px',
2850
- cursor: 'pointer',
2851
- marginLeft: 'auto',
2852
- transform: 'rotate(180deg)',
2853
- });
2854
- return container;
2855
- }
2856
- function addKeyframes() {
2857
- const styleSheet = document.createElement('style');
2858
- styleSheet.type = 'text/css';
2859
- styleSheet.innerText = `@keyframes spin {
2860
- 0% { transform: rotate(0deg); }
2861
- 100% { transform: rotate(360deg); }
2862
- }`;
2863
- document.head.appendChild(styleSheet);
2864
- }
2865
- function createSpinner() {
2866
- addKeyframes();
2867
- const spinner = document.createElement('div');
2868
- spinner.classList.add('spinner');
2869
- Object.assign(spinner.style, spinnerStyles);
2870
- return spinner;
2871
- }
2872
- function createElement(tag, className, styles, textContent, id) {
2873
- const element = document.createElement(tag);
2874
- element.className = className;
2875
- Object.assign(element.style, styles);
2876
- if (textContent) {
2877
- element.textContent = textContent;
2878
- }
2879
- if (id) {
2880
- element.id = id;
2881
- }
2882
- return element;
2883
- }
2884
- const TEST_START = 'or_uxt_test_start';
2885
- const TASK_IND = 'or_uxt_task_index';
2886
- const SESSION_ID = 'or_uxt_session_id';
2887
- const TEST_ID = 'or_uxt_test_id';
2888
-
2889
- class SignalManager {
2890
- constructor(ingestPoint, getTimestamp, token, testId, storageKey, setStorageKey, removeStorageKey, getStorageKey, getSessionId) {
2891
- this.ingestPoint = ingestPoint;
2892
- this.getTimestamp = getTimestamp;
2893
- this.token = token;
2894
- this.testId = testId;
2895
- this.storageKey = storageKey;
2896
- this.setStorageKey = setStorageKey;
2897
- this.removeStorageKey = removeStorageKey;
2898
- this.getStorageKey = getStorageKey;
2899
- this.getSessionId = getSessionId;
2900
- this.durations = {
2901
- testStart: 0,
2902
- tasks: [],
2903
- };
2904
- this.getDurations = () => {
2905
- return this.durations;
2906
- };
2907
- this.setDurations = (durations) => {
2908
- this.durations.testStart = durations.testStart;
2909
- this.durations.tasks = durations.tasks;
2910
- };
2911
- this.signalTask = (taskId, status, taskAnswer) => {
2912
- if (!taskId)
2913
- return console.error('User Testing: No Task ID Given');
2914
- const taskStart = this.durations.tasks.find((t) => t.taskId === taskId);
2915
- const timestamp = this.getTimestamp();
2916
- const duration = taskStart ? timestamp - taskStart.started : 0;
2917
- return fetch(`${this.ingestPoint}/v1/web/uxt/signals/task`, {
2918
- method: 'POST',
2919
- headers: {
2920
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
2921
- Authorization: `Bearer ${this.token}`,
2922
- },
2923
- body: JSON.stringify({
2924
- testId: this.testId,
2925
- taskId,
2926
- status,
2927
- duration,
2928
- timestamp,
2929
- taskAnswer,
2930
- }),
2931
- });
2932
- };
2933
- this.signalTest = (status) => {
2934
- const timestamp = this.getTimestamp();
2935
- if (status === 'begin' && this.testId) {
2936
- const sessionId = this.getSessionId();
2937
- this.setStorageKey(SESSION_ID, sessionId);
2938
- this.setStorageKey(this.storageKey, this.testId.toString());
2939
- this.setStorageKey(TEST_START, timestamp.toString());
2940
- }
2941
- else {
2942
- this.removeStorageKey(this.storageKey);
2943
- this.removeStorageKey(TASK_IND);
2944
- this.removeStorageKey(TEST_START);
2945
- }
2946
- const start = this.durations.testStart || timestamp;
2947
- const duration = timestamp - start;
2948
- return fetch(`${this.ingestPoint}/v1/web/uxt/signals/test`, {
2949
- method: 'POST',
2950
- headers: {
2951
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
2952
- Authorization: `Bearer ${this.token}`,
2953
- },
2954
- body: JSON.stringify({
2955
- testId: this.testId,
2956
- status,
2957
- duration,
2958
- timestamp,
2959
- }),
2960
- });
2961
- };
2962
- const possibleStart = this.getStorageKey(TEST_START);
2963
- if (possibleStart) {
2964
- this.durations.testStart = parseInt(possibleStart, 10);
2965
- }
2966
- }
2967
- }
2968
-
2969
- class UserTestManager {
2970
- constructor(app, storageKey) {
2971
- this.app = app;
2972
- this.storageKey = storageKey;
2973
- this.bg = createElement('div', 'bg', bgStyle, undefined, '__or_ut_bg');
2974
- this.container = createElement('div', 'container', containerStyle, undefined, '__or_ut_ct');
2975
- this.widgetGuidelinesVisible = true;
2976
- this.widgetTasksVisible = false;
2977
- this.widgetVisible = true;
2978
- this.isActive = false;
2979
- this.descriptionSection = null;
2980
- this.taskSection = null;
2981
- this.endSection = null;
2982
- this.stopButton = null;
2983
- this.stopButtonContainer = null;
2984
- this.test = null;
2985
- this.testId = null;
2986
- this.signalManager = null;
2987
- this.getTest = (id, token, inProgress) => {
2988
- this.testId = id;
2989
- const ingest = this.app.options.ingestPoint;
2990
- return fetch(`${ingest}/v1/web/uxt/test/${id}`, {
2991
- headers: {
2992
- Authorization: `Bearer ${token}`,
2993
- },
2994
- })
2995
- .then((res) => res.json())
2996
- .then(({ test }) => {
2997
- this.isActive = true;
2998
- this.test = test;
2999
- 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());
3000
- this.createGreeting(test.title, test.reqMic, test.reqCamera);
3001
- if (inProgress) {
3002
- if (test.reqMic || test.reqCamera) {
3003
- void this.userRecorder.startRecording(30, Quality.Standard, test.reqMic, test.reqCamera);
3004
- }
3005
- this.showWidget(test.description, test.tasks, true);
3006
- this.showTaskSection();
3007
- }
3008
- })
3009
- .then(() => id)
3010
- .catch((err) => {
3011
- console.log('OR: Error fetching test', err);
3012
- });
3013
- };
3014
- this.hideTaskSection = () => false;
3015
- this.showTaskSection = () => true;
3016
- this.collapseWidget = () => false;
3017
- this.removeGreeting = () => false;
3018
- // eslint-disable-next-line @typescript-eslint/no-empty-function
3019
- this.toggleDescriptionVisibility = () => { };
3020
- this.currentTaskIndex = 0;
3021
- this.userRecorder = new Recorder(app);
3022
- const sessionId = this.app.getSessionID();
3023
- const savedSessionId = this.app.localStorage.getItem(SESSION_ID);
3024
- if (sessionId !== savedSessionId) {
3025
- this.app.localStorage.removeItem(this.storageKey);
3026
- this.app.localStorage.removeItem(SESSION_ID);
3027
- this.app.localStorage.removeItem(TEST_ID);
3028
- this.app.localStorage.removeItem(TASK_IND);
3029
- this.app.localStorage.removeItem(TEST_START);
3030
- }
3031
- const taskIndex = this.app.localStorage.getItem(TASK_IND);
3032
- if (taskIndex) {
3033
- this.currentTaskIndex = parseInt(taskIndex, 10);
3034
- }
3035
- }
3036
- getTestId() {
3037
- return this.testId;
3038
- }
3039
- createGreeting(title, micRequired, cameraRequired) {
3040
- const titleElement = createElement('div', 'title', titleStyle, title);
3041
- const descriptionElement = createElement('div', 'description', descriptionStyle, `Welcome, you're here to help us improve, not to be judged. Your insights matter!\n
3042
- 📹 We're recording this browser tab to learn from your experience.
3043
- 🎤 Please enable mic and camera if asked, to give us a complete picture.`);
3044
- const buttonElement = createElement('div', 'button', buttonStyle, 'Read guidelines to begin');
3045
- this.removeGreeting = () => {
3046
- // this.container.innerHTML = ''
3047
- if (micRequired || cameraRequired) {
3048
- void this.userRecorder.startRecording(30, Quality.Standard, micRequired, cameraRequired);
3049
- }
3050
- this.container.removeChild(buttonElement);
3051
- this.container.removeChild(descriptionElement);
3052
- this.container.removeChild(titleElement);
3053
- return false;
3054
- };
3055
- buttonElement.onclick = () => {
3056
- this.removeGreeting();
3057
- const durations = this.signalManager?.getDurations();
3058
- if (durations && this.signalManager) {
3059
- durations.testStart = this.app.timestamp();
3060
- this.signalManager.setDurations(durations);
3061
- }
3062
- void this.signalManager?.signalTest('begin');
3063
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3064
- Object.assign(this.container.style, containerWidgetStyle);
3065
- this.showWidget(this.test?.guidelines || '', this.test?.tasks || []);
3066
- };
3067
- this.container.append(titleElement, descriptionElement, buttonElement);
3068
- this.bg.appendChild(this.container);
3069
- document.body.appendChild(this.bg);
3070
- }
3071
- showWidget(guidelines, tasks, inProgress) {
3072
- this.container.innerHTML = '';
3073
- Object.assign(this.bg.style, {
3074
- position: 'fixed',
3075
- zIndex: 99999999999999,
3076
- right: '8px',
3077
- left: 'unset',
3078
- width: 'fit-content',
3079
- top: '8px',
3080
- height: 'fit-content',
3081
- background: 'unset',
3082
- display: 'unset',
3083
- alignItems: 'unset',
3084
- justifyContent: 'unset',
3085
- });
3086
- // Create title section
3087
- const titleSection = this.createTitleSection();
3088
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3089
- Object.assign(this.container.style, containerWidgetStyle);
3090
- const descriptionSection = this.createDescriptionSection(guidelines);
3091
- const tasksSection = this.createTasksSection(tasks);
3092
- const stopButton = createElement('div', 'stop_bn_or', stopWidgetStyle, 'Abort Session');
3093
- const stopContainer = createElement('div', 'stop_ct_or', { fontSize: '13px!important' });
3094
- stopContainer.style.fontSize = '13px';
3095
- stopContainer.append(stopButton);
3096
- this.container.append(titleSection, descriptionSection, tasksSection, stopContainer);
3097
- this.taskSection = tasksSection;
3098
- this.descriptionSection = descriptionSection;
3099
- this.stopButton = stopButton;
3100
- this.stopButtonContainer = stopContainer;
3101
- stopButton.onclick = () => {
3102
- this.userRecorder.discard();
3103
- void this.signalManager?.signalTest('skipped');
3104
- document.body.removeChild(this.bg);
3105
- window.close();
3106
- };
3107
- if (!inProgress) {
3108
- this.hideTaskSection();
3109
- }
3110
- else {
3111
- this.toggleDescriptionVisibility();
3112
- }
3113
- }
3114
- createTitleSection() {
3115
- const title = createElement('div', 'title', titleWidgetStyle);
3116
- const leftIcon = generateGrid();
3117
- const titleText = createElement('div', 'title_text', {
3118
- maxWidth: '19rem',
3119
- overflow: 'hidden',
3120
- textOverflow: 'ellipsis',
3121
- width: '100%',
3122
- fontSize: 16,
3123
- lineHeight: 'auto',
3124
- cursor: 'pointer',
3125
- }, this.test?.title);
3126
- const rightIcon = generateChevron();
3127
- title.append(leftIcon, titleText, rightIcon);
3128
- const toggleWidget = (isVisible) => {
3129
- this.widgetVisible = isVisible;
3130
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3131
- Object.assign(this.container.style, this.widgetVisible
3132
- ? containerWidgetStyle
3133
- : { border: 'none', background: 'none', padding: 0 });
3134
- if (this.taskSection) {
3135
- Object.assign(this.taskSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3136
- }
3137
- if (this.descriptionSection) {
3138
- Object.assign(this.descriptionSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3139
- }
3140
- if (this.endSection) {
3141
- Object.assign(this.endSection.style, this.widgetVisible ? descriptionWidgetStyle : { display: 'none' });
3142
- }
3143
- if (this.stopButton) {
3144
- Object.assign(this.stopButton.style, this.widgetVisible ? stopWidgetStyle : { display: 'none' });
3145
- }
3146
- return isVisible;
3147
- };
3148
- const collapseWidget = () => {
3149
- Object.assign(rightIcon.style, {
3150
- transform: this.widgetVisible ? 'rotate(0deg)' : 'rotate(180deg)',
3151
- });
3152
- toggleWidget(!this.widgetVisible);
3153
- };
3154
- titleText.onclick = collapseWidget;
3155
- rightIcon.onclick = collapseWidget;
3156
- attachDND(this.bg, leftIcon);
3157
- this.collapseWidget = () => toggleWidget(false);
3158
- return title;
3159
- }
3160
- createDescriptionSection(guidelines) {
3161
- const section = createElement('div', 'description_section_or', descriptionWidgetStyle);
3162
- const titleContainer = createElement('div', 'description_s_title_or', sectionTitleStyle);
3163
- const title = createElement('div', 'title', {
3164
- fontSize: 13,
3165
- fontWeight: 500,
3166
- lineHeight: 'auto',
3167
- }, 'Introduction & Guidelines');
3168
- const icon = createElement('div', 'icon', symbolIcon, '-');
3169
- const content = createElement('div', 'content', contentStyle);
3170
- const descriptionC = createElement('div', 'text_description', {
3171
- maxHeight: '250px',
3172
- overflowY: 'auto',
3173
- whiteSpace: 'pre-wrap',
3174
- fontSize: 13,
3175
- color: '#454545',
3176
- lineHeight: 'auto',
3177
- });
3178
- descriptionC.innerHTML = guidelines;
3179
- const button = createElement('div', 'button_begin_or', buttonWidgetStyle, 'Begin Test');
3180
- titleContainer.append(title, icon);
3181
- content.append(descriptionC, button);
3182
- section.append(titleContainer, content);
3183
- const toggleDescriptionVisibility = () => {
3184
- this.widgetGuidelinesVisible = !this.widgetGuidelinesVisible;
3185
- icon.textContent = this.widgetGuidelinesVisible ? '-' : '+';
3186
- Object.assign(content.style, this.widgetGuidelinesVisible ? contentStyle : { display: 'none' });
3187
- };
3188
- titleContainer.onclick = toggleDescriptionVisibility;
3189
- this.toggleDescriptionVisibility = () => {
3190
- this.widgetGuidelinesVisible = false;
3191
- icon.textContent = this.widgetGuidelinesVisible ? '-' : '+';
3192
- Object.assign(content.style, this.widgetGuidelinesVisible ? contentStyle : { display: 'none' });
3193
- content.removeChild(button);
3194
- };
3195
- button.onclick = () => {
3196
- toggleDescriptionVisibility();
3197
- if (this.test) {
3198
- const durations = this.signalManager?.getDurations();
3199
- const taskDurationInd = durations
3200
- ? durations.tasks.findIndex((t) => this.test && t.taskId === this.test.tasks[0].task_id)
3201
- : null;
3202
- if (durations && taskDurationInd === -1) {
3203
- durations.tasks.push({
3204
- taskId: this.test.tasks[0].task_id,
3205
- started: this.app.timestamp(),
3206
- });
3207
- this.signalManager?.setDurations(durations);
3208
- }
3209
- void this.signalManager?.signalTask(this.test.tasks[0].task_id, 'begin');
3210
- }
3211
- this.showTaskSection();
3212
- content.removeChild(button);
3213
- };
3214
- return section;
3215
- }
3216
- createTasksSection(tasks) {
3217
- this.container.style.fontFamily = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`;
3218
- Object.assign(this.container.style, containerWidgetStyle);
3219
- const section = createElement('div', 'task_section_or', descriptionWidgetStyle);
3220
- const titleContainer = createElement('div', 'description_t_title_or', sectionTitleStyle);
3221
- const title = createElement('div', 'title', {
3222
- fontSize: '13px',
3223
- fontWeight: '500',
3224
- lineHeight: 'auto',
3225
- }, 'Tasks');
3226
- const icon = createElement('div', 'icon', symbolIcon, '-');
3227
- const content = createElement('div', 'content', contentStyle);
3228
- const pagination = createElement('div', 'pagination', paginationStyle);
3229
- // const leftArrow = createElement('span', 'leftArrow', {}, '<')
3230
- // const rightArrow = createElement('span', 'rightArrow', {}, '>')
3231
- const taskCard = createElement('div', 'taskCard', taskDescriptionCard);
3232
- const taskText = createElement('div', 'taskText', taskTextStyle);
3233
- const taskDescription = createElement('div', 'taskDescription', taskDescriptionStyle);
3234
- const taskButtons = createElement('div', 'taskButtons', taskButtonsRow);
3235
- const inputTitle = createElement('div', 'taskText', taskTextStyle);
3236
- inputTitle.textContent = 'Your answer';
3237
- const inputArea = createElement('textarea', 'taskDescription', {
3238
- resize: 'vertical',
3239
- });
3240
- const inputContainer = createElement('div', 'inputArea', taskDescriptionCard);
3241
- inputContainer.append(inputTitle, inputArea);
3242
- const closePanelButton = createElement('div', 'closePanelButton', taskButtonStyle, 'Collapse Panel');
3243
- const nextButton = createElement('div', 'nextButton', taskButtonBorderedStyle, 'Done, Next');
3244
- titleContainer.append(title, icon);
3245
- taskCard.append(taskText, taskDescription);
3246
- taskButtons.append(closePanelButton, nextButton);
3247
- content.append(pagination, taskCard, inputContainer, taskButtons);
3248
- section.append(titleContainer, content);
3249
- const updateTaskContent = () => {
3250
- const task = tasks[this.currentTaskIndex];
3251
- taskText.textContent = task.title;
3252
- taskDescription.textContent = task.description;
3253
- if (task.allow_typing) {
3254
- inputContainer.style.display = 'flex';
3255
- }
3256
- else {
3257
- inputContainer.style.display = 'none';
3258
- }
3259
- };
3260
- // pagination.appendChild(leftArrow)
3261
- tasks.forEach((_, index) => {
3262
- const pageNumber = createElement('span', `or_task_${index}`, {
3263
- outline: '1px solid #efefef',
3264
- fontSize: '13px',
3265
- height: '24px',
3266
- width: '24px',
3267
- display: 'flex',
3268
- flexDirection: 'column',
3269
- alignItems: 'center',
3270
- justifyContent: 'center',
3271
- borderRadius: '6.25em',
3272
- }, (index + 1).toString());
3273
- pageNumber.id = `or_task_${index}`;
3274
- pagination.append(pageNumber);
3275
- });
3276
- // pagination.appendChild(rightArrow)
3277
- const toggleTasksVisibility = () => {
3278
- this.widgetTasksVisible = !this.widgetTasksVisible;
3279
- icon.textContent = this.widgetTasksVisible ? '-' : '+';
3280
- Object.assign(content.style, this.widgetTasksVisible ? contentStyle : { display: 'none' });
3281
- };
3282
- this.hideTaskSection = () => {
3283
- icon.textContent = '+';
3284
- Object.assign(content.style, {
3285
- display: 'none',
3286
- });
3287
- this.widgetTasksVisible = false;
3288
- return false;
3289
- };
3290
- this.showTaskSection = () => {
3291
- icon.textContent = '-';
3292
- Object.assign(content.style, contentStyle);
3293
- this.widgetTasksVisible = true;
3294
- return true;
3295
- };
3296
- const highlightActive = () => {
3297
- const activeTaskEl = document.getElementById(`or_task_${this.currentTaskIndex}`);
3298
- if (activeTaskEl) {
3299
- Object.assign(activeTaskEl.style, taskNumberActive);
3300
- }
3301
- for (let i = 0; i < this.currentTaskIndex; i++) {
3302
- const taskEl = document.getElementById(`or_task_${i}`);
3303
- if (taskEl) {
3304
- Object.assign(taskEl.style, taskNumberDone);
3305
- }
3306
- }
3307
- };
3308
- titleContainer.onclick = toggleTasksVisibility;
3309
- closePanelButton.onclick = this.collapseWidget;
3310
- nextButton.onclick = () => {
3311
- const textAnswer = tasks[this.currentTaskIndex].allow_typing ? inputArea.value : undefined;
3312
- inputArea.value = '';
3313
- void this.signalManager?.signalTask(tasks[this.currentTaskIndex].task_id, 'done', textAnswer);
3314
- if (this.currentTaskIndex < tasks.length - 1) {
3315
- this.currentTaskIndex++;
3316
- updateTaskContent();
3317
- const durations = this.signalManager?.getDurations();
3318
- if (durations &&
3319
- durations.tasks.findIndex((t) => t.taskId === tasks[this.currentTaskIndex].task_id) === -1) {
3320
- durations.tasks.push({
3321
- taskId: tasks[this.currentTaskIndex].task_id,
3322
- started: this.app.timestamp(),
3323
- });
3324
- this.signalManager?.setDurations(durations);
3325
- }
3326
- void this.signalManager?.signalTask(tasks[this.currentTaskIndex].task_id, 'begin');
3327
- highlightActive();
3328
- }
3329
- else {
3330
- this.showEndSection();
3331
- }
3332
- this.app.localStorage.setItem('or_uxt_task_index', this.currentTaskIndex.toString());
3333
- };
3334
- setTimeout(() => {
3335
- const firstTaskEl = document.getElementById('or_task_0');
3336
- if (firstTaskEl) {
3337
- Object.assign(firstTaskEl.style, taskNumberActive);
3338
- }
3339
- updateTaskContent();
3340
- highlightActive();
3341
- }, 1);
3342
- return section;
3343
- }
3344
- showEndSection() {
3345
- let isLoading = true;
3346
- void this.signalManager?.signalTest('done');
3347
- const section = createElement('div', 'end_section_or', endSectionStyle);
3348
- const title = createElement('div', 'end_title_or', {
3349
- fontSize: '1.25rem',
3350
- fontWeight: '500',
3351
- }, 'Thank you! 👍');
3352
- const description = createElement('div', 'end_description_or', {}, this.test?.conclusion ??
3353
- 'Thank you for participating in our usability test. Your feedback has been captured and will be used to enhance our website. \n' +
3354
- '\n' +
3355
- 'We appreciate your time and valuable input.');
3356
- const button = createElement('div', 'end_button_or', buttonWidgetStyle, 'Submitting Feedback');
3357
- const spinner = createSpinner();
3358
- button.appendChild(spinner);
3359
- if (this.test?.reqMic || this.test?.reqCamera) {
3360
- void this.userRecorder
3361
- .sendToAPI()
3362
- .then(() => {
3363
- button.removeChild(spinner);
3364
- button.textContent = 'End Session';
3365
- isLoading = false;
3366
- })
3367
- .catch((err) => {
3368
- console.error(err);
3369
- button.removeChild(spinner);
3370
- button.textContent = 'End Session';
3371
- isLoading = false;
3372
- });
3373
- }
3374
- else {
3375
- button.removeChild(spinner);
3376
- button.textContent = 'End Session';
3377
- isLoading = false;
3378
- }
3379
- if (this.taskSection) {
3380
- this.container.removeChild(this.taskSection);
3381
- }
3382
- if (this.descriptionSection) {
3383
- this.container.removeChild(this.descriptionSection);
3384
- }
3385
- if (this.stopButton && this.stopButtonContainer) {
3386
- this.container.removeChild(this.stopButtonContainer);
3387
- }
3388
- button.onclick = () => {
3389
- if (isLoading)
3390
- return;
3391
- window.close();
3392
- document.body.removeChild(this.bg);
3393
- };
3394
- section.append(title, description, button);
3395
- this.endSection = section;
3396
- this.container.append(section);
3397
- }
3398
- }
3399
-
3400
2349
  //@ts-ignore
3401
2350
  function isNode(sth) {
3402
2351
  return !!sth && sth.nodeType != null;
@@ -3729,6 +2678,27 @@ class Maintainer {
3729
2678
  }
3730
2679
  }
3731
2680
 
2681
+ // 4 levels, 128 frames between each level, 8_388_608 nodes per page
2682
+ // lets hope no one will need more :D
2683
+ const BITS_LEVEL = 2; // 4
2684
+ const BITS_ORDER = 7; // 128
2685
+ const BITS_NODE = 22; // 8_388_608
2686
+ const SHIFT_ORDER = BITS_NODE;
2687
+ const SHIFT_LEVEL = BITS_NODE + BITS_ORDER;
2688
+ const MASK_NODE = (1 << BITS_NODE) - 1;
2689
+ const MASK_ORDER = (1 << BITS_ORDER) - 1;
2690
+ const MASK_LEVEL = (1 << BITS_LEVEL) - 1;
2691
+ function pack(level, order, nodeId) {
2692
+ if (level < 0 || level > MASK_LEVEL)
2693
+ throw new RangeError('OR: nesting level overflow, max 4');
2694
+ if (order < 0 || order > MASK_ORDER)
2695
+ throw new RangeError('OR: frame order overflow, max 128');
2696
+ const v = ((level & MASK_LEVEL) << SHIFT_LEVEL) |
2697
+ ((order & MASK_ORDER) << SHIFT_ORDER) |
2698
+ (nodeId & MASK_NODE);
2699
+ return v >>> 0;
2700
+ }
2701
+
3732
2702
  class Nodes {
3733
2703
  constructor(params) {
3734
2704
  this.nodes = new Map();
@@ -3756,6 +2726,9 @@ class Nodes {
3756
2726
  }
3757
2727
  listeners.push([type, listener, useCapture]);
3758
2728
  };
2729
+ this.createFrameId = (level, frameOrder) => {
2730
+ return pack(level, frameOrder, 0);
2731
+ };
3759
2732
  this.unregisterNode = (node) => {
3760
2733
  const id = node[this.node_id];
3761
2734
  if (id !== undefined) {
@@ -3776,16 +2749,8 @@ class Nodes {
3776
2749
  this.maintainer = new Maintainer(this.nodes, this.unregisterNode, params.maintainer);
3777
2750
  this.maintainer.start();
3778
2751
  }
3779
- syntheticMode(frameOrder) {
3780
- const maxSafeNumber = Number.MAX_SAFE_INTEGER;
3781
- const placeholderSize = 99999999;
3782
- const nextFrameId = placeholderSize * frameOrder;
3783
- // I highly doubt that this will ever happen,
3784
- // but it will be easier to debug if it does
3785
- if (nextFrameId > maxSafeNumber) {
3786
- throw new Error('Placeholder id overflow');
3787
- }
3788
- this.nextNodeId = nextFrameId;
2752
+ crossdomainMode(level, frameOrder) {
2753
+ this.nextNodeId = this.createFrameId(level, frameOrder);
3789
2754
  }
3790
2755
  registerNode(node) {
3791
2756
  let id = node[this.node_id];
@@ -4220,9 +3185,24 @@ async function parseUseEl(useElement, mode, domParser) {
4220
3185
  return;
4221
3186
  }
4222
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
+ }
4223
3195
  // happens if svg spritemap is local, fastest case for us
4224
3196
  if (!url && symbolId) {
4225
- const symbol = document.querySelector(href);
3197
+ const hasHashtag = href.startsWith('#');
3198
+ let escapedURL;
3199
+ if (hasHashtag) {
3200
+ escapedURL = `#${CSS.escape(href.substring(1))}`;
3201
+ }
3202
+ else {
3203
+ escapedURL = CSS.escape(href);
3204
+ }
3205
+ const symbol = document.querySelector(escapedURL);
4226
3206
  if (symbol) {
4227
3207
  const inlineSvg = `
4228
3208
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="${symbol.getAttribute('viewBox') || '0 0 24 24'}">
@@ -4237,13 +3217,6 @@ async function parseUseEl(useElement, mode, domParser) {
4237
3217
  return;
4238
3218
  }
4239
3219
  }
4240
- if (!url && !symbolId) {
4241
- console.warn('Openreplay: Invalid xlink:href or href found on <use>.');
4242
- return;
4243
- }
4244
- if (iconCache[symbolId]) {
4245
- return iconCache[symbolId];
4246
- }
4247
3220
  let svgDoc;
4248
3221
  if (svgUrlCache[url]) {
4249
3222
  if (svgUrlCache[url] === 1) {
@@ -4343,6 +3316,7 @@ class Observer {
4343
3316
  this.indexes = [];
4344
3317
  this.attributesMap = new Map();
4345
3318
  this.textSet = new Set();
3319
+ this.slotMap = new Map();
4346
3320
  this.disableSprites = false;
4347
3321
  /**
4348
3322
  * this option means that, instead of using link element with href to load css,
@@ -4352,7 +3326,9 @@ class Observer {
4352
3326
  this.inlineRemoteCss = false;
4353
3327
  this.inlinerOptions = undefined;
4354
3328
  this.domParser = new DOMParser();
3329
+ this.throttling = true;
4355
3330
  this.throttledSetNodeData = throttleWithTrailing((id, parentElement, data) => this.sendNodeData(id, parentElement, data), 30);
3331
+ this.throttling = !Boolean(options.disableThrottling);
4356
3332
  this.disableSprites = Boolean(options.disableSprites);
4357
3333
  this.inlineRemoteCss = Boolean(options.inlineRemoteCss);
4358
3334
  this.inlinerOptions = options.inlinerOptions;
@@ -4533,6 +3509,17 @@ class Observer {
4533
3509
  }
4534
3510
  bindNode(node) {
4535
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.Changed);
3519
+ }
3520
+ });
3521
+ });
3522
+ }
4536
3523
  if (isNew) {
4537
3524
  this.recents.set(id, RecentsType.New);
4538
3525
  }
@@ -4563,6 +3550,9 @@ class Observer {
4563
3550
  }
4564
3551
  unbindTree(node) {
4565
3552
  const id = this.app.nodes.unregisterNode(node);
3553
+ if (id !== undefined) {
3554
+ this.slotMap.delete(id);
3555
+ }
4566
3556
  if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
4567
3557
  // Sending RemoveNode only for parent to maintain
4568
3558
  this.app.send(RemoveNode(id));
@@ -4591,8 +3581,8 @@ class Observer {
4591
3581
  if (isRootNode(node)) {
4592
3582
  return true;
4593
3583
  }
4594
- // @ts-ignore SALESFORCE
4595
- const parent = node.assignedSlot ? node.assignedSlot : node.parentNode;
3584
+ let slot = node.assignedSlot;
3585
+ const parent = node.parentNode;
4596
3586
  let parentID;
4597
3587
  // Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
4598
3588
  // TODO: get rid of "special" cases (there is an issue with CreateDocument altered behaviour though)
@@ -4664,12 +3654,35 @@ class Observer {
4664
3654
  else if (isTextNode(node)) {
4665
3655
  // for text node id != 0, hence parentID !== undefined and parent is Element
4666
3656
  this.app.send(CreateTextNode(id, parentID, index));
4667
- this.throttledSetNodeData(id, parent, node.data);
3657
+ if (this.throttling) {
3658
+ this.throttledSetNodeData(id, parent, node.data);
3659
+ }
3660
+ else {
3661
+ this.sendNodeData(id, parent, node.data);
3662
+ }
3663
+ }
3664
+ if (slot) {
3665
+ const slotID = this.app.nodes.getID(slot);
3666
+ if (slotID !== undefined) {
3667
+ this.slotMap.set(id, slotID);
3668
+ this.app.send(SetNodeSlot(id, slotID));
3669
+ }
4668
3670
  }
4669
3671
  return true;
4670
3672
  }
4671
3673
  if (recentsType === RecentsType.Removed && parentID !== undefined) {
4672
3674
  this.app.send(MoveNode(id, parentID, index));
3675
+ if (slot) {
3676
+ const slotID = this.app.nodes.getID(slot);
3677
+ if (slotID !== undefined && this.slotMap.get(id) !== slotID) {
3678
+ this.slotMap.set(id, slotID);
3679
+ this.app.send(SetNodeSlot(id, slotID));
3680
+ }
3681
+ }
3682
+ else if (this.slotMap.has(id)) {
3683
+ this.slotMap.delete(id);
3684
+ this.app.send(SetNodeSlot(id, 0));
3685
+ }
4673
3686
  }
4674
3687
  const attr = this.attributesMap.get(id);
4675
3688
  if (attr !== undefined) {
@@ -4685,7 +3698,12 @@ class Observer {
4685
3698
  throw 'commitNode: node is not a text';
4686
3699
  }
4687
3700
  // for text node id != 0, hence parent is Element
4688
- this.throttledSetNodeData(id, parent, node.data);
3701
+ if (this.throttling) {
3702
+ this.throttledSetNodeData(id, parent, node.data);
3703
+ }
3704
+ else {
3705
+ this.sendNodeData(id, parent, node.data);
3706
+ }
4689
3707
  }
4690
3708
  return true;
4691
3709
  }
@@ -4832,16 +3850,18 @@ class IFrameOffsets {
4832
3850
 
4833
3851
  var InlineCssMode;
4834
3852
  (function (InlineCssMode) {
3853
+ InlineCssMode[InlineCssMode["Unset"] = -1] = "Unset";
4835
3854
  /** default behavior -- will parse and cache the css file on backend */
4836
3855
  InlineCssMode[InlineCssMode["Disabled"] = 0] = "Disabled";
4837
3856
  /** will attempt to record the linked css file as AdoptedStyleSheet object */
4838
3857
  InlineCssMode[InlineCssMode["Inline"] = 1] = "Inline";
4839
- /** will fetch the file, then simulated AdoptedStyleSheets behavior programmaticaly for the replay */
3858
+ /** will fetch the file, then simulate AdoptedStyleSheets behavior programmaticaly for the replay */
4840
3859
  InlineCssMode[InlineCssMode["InlineFetched"] = 2] = "InlineFetched";
4841
3860
  /** will fetch the file, then save it as plain css inside <style> node */
4842
3861
  InlineCssMode[InlineCssMode["PlainFetched"] = 3] = "PlainFetched";
4843
3862
  })(InlineCssMode || (InlineCssMode = {}));
4844
- function getInlineOptions(mode) {
3863
+ const localhostStylesDoc = 'https://docs.openreplay.com/en/troubleshooting/localhost/';
3864
+ function getInlineOptions(mode, logger) {
4845
3865
  switch (mode) {
4846
3866
  case InlineCssMode.Inline:
4847
3867
  return {
@@ -4867,6 +3887,27 @@ function getInlineOptions(mode) {
4867
3887
  forcePlain: true,
4868
3888
  },
4869
3889
  };
3890
+ case InlineCssMode.Unset:
3891
+ const isLocalhost = /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?\/?/.test(window.location.href);
3892
+ if (isLocalhost) {
3893
+ logger(`Enabling InlineCssMode by default on localhost to preserve css styles, refer to ${localhostStylesDoc} for details, set InlineCssMode to 0 to skip this behavior`);
3894
+ return {
3895
+ inlineRemoteCss: true,
3896
+ inlinerOptions: {
3897
+ forceFetch: false,
3898
+ forcePlain: false,
3899
+ },
3900
+ };
3901
+ }
3902
+ else {
3903
+ return {
3904
+ inlineRemoteCss: false,
3905
+ inlinerOptions: {
3906
+ forceFetch: false,
3907
+ forcePlain: false,
3908
+ },
3909
+ };
3910
+ }
4870
3911
  case InlineCssMode.Disabled:
4871
3912
  default:
4872
3913
  return {
@@ -4888,7 +3929,8 @@ class TopObserver extends Observer {
4888
3929
  }, params.options);
4889
3930
  const observerOptions = {
4890
3931
  disableSprites: opts.disableSprites,
4891
- ...getInlineOptions(opts.inlineCss)
3932
+ disableThrottling: opts.disableThrottling,
3933
+ ...getInlineOptions(opts.inlineCss, console.warn),
4892
3934
  };
4893
3935
  super(params.app, true, observerOptions);
4894
3936
  this.iframeOffsets = new IFrameOffsets();
@@ -4987,7 +4029,7 @@ class TopObserver extends Observer {
4987
4029
  this.app.nodes.callNodeCallbacks(document, true);
4988
4030
  }, window.document.documentElement);
4989
4031
  }
4990
- crossdomainObserve(rootNodeId, frameOder) {
4032
+ crossdomainObserve(rootNodeId, frameOder, frameLevel) {
4991
4033
  const observer = this;
4992
4034
  Element.prototype.attachShadow = function () {
4993
4035
  // eslint-disable-next-line
@@ -4996,7 +4038,7 @@ class TopObserver extends Observer {
4996
4038
  return shadow;
4997
4039
  };
4998
4040
  this.app.nodes.clear();
4999
- this.app.nodes.syntheticMode(frameOder);
4041
+ this.app.nodes.crossdomainMode(frameLevel, frameOder);
5000
4042
  const iframeObserver = new IFrameObserver(this.app);
5001
4043
  this.iframeObservers.set(window.document, iframeObserver);
5002
4044
  iframeObserver.syntheticObserve(rootNodeId, window.document);
@@ -5101,6 +4143,7 @@ class Sanitizer {
5101
4143
  }
5102
4144
  }
5103
4145
 
4146
+ const tokenSeparator = '_$_';
5104
4147
  class Session {
5105
4148
  constructor(params) {
5106
4149
  this.metadata = {};
@@ -5125,6 +4168,26 @@ class Session {
5125
4168
  this.app.sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString());
5126
4169
  return pageNo;
5127
4170
  };
4171
+ this.getSessionToken = (projectKey) => {
4172
+ const tokenWithProject = this.token || this.app.sessionStorage.getItem(this.options.session_token_key);
4173
+ if (projectKey && tokenWithProject) {
4174
+ const savedProject = tokenWithProject.split(tokenSeparator)[1];
4175
+ if (!savedProject || savedProject !== projectKey) {
4176
+ this.app.sessionStorage.removeItem(this.options.session_token_key);
4177
+ this.token = undefined;
4178
+ return undefined;
4179
+ }
4180
+ }
4181
+ const token = tokenWithProject ? tokenWithProject.split(tokenSeparator)[0] : null;
4182
+ return token || undefined;
4183
+ };
4184
+ this.getRawTokenWithProject = () => {
4185
+ return this.token || this.app.sessionStorage.getItem(this.options.session_token_key);
4186
+ };
4187
+ this.setSessionToken = (token, projectKey) => {
4188
+ this.token = `${token}${tokenSeparator}${projectKey}`;
4189
+ this.app.sessionStorage.setItem(this.options.session_token_key, `${token}${tokenSeparator}${projectKey}`);
4190
+ };
5128
4191
  this.app = params.app;
5129
4192
  this.options = params.options;
5130
4193
  this.createTabId();
@@ -5171,14 +4234,6 @@ class Session {
5171
4234
  setUserInfo(userInfo) {
5172
4235
  this.userInfo = userInfo;
5173
4236
  }
5174
- getSessionToken() {
5175
- const token = this.token || this.app.sessionStorage.getItem(this.options.session_token_key);
5176
- return token || undefined;
5177
- }
5178
- setSessionToken(token) {
5179
- this.token = token;
5180
- this.app.sessionStorage.setItem(this.options.session_token_key, token);
5181
- }
5182
4237
  applySessionHash(hash) {
5183
4238
  const hashParts = decodeURI(hash).split('&');
5184
4239
  let token = hash;
@@ -5194,7 +4249,7 @@ class Session {
5194
4249
  }
5195
4250
  getSessionHash() {
5196
4251
  const pageNo = this.getPageNumber();
5197
- const token = this.getSessionToken();
4252
+ const token = this.getRawTokenWithProject();
5198
4253
  if (pageNo === undefined || token === undefined) {
5199
4254
  return;
5200
4255
  }
@@ -5237,7 +4292,7 @@ class Session {
5237
4292
  }
5238
4293
  }
5239
4294
 
5240
- function wrap(callback, n) {
4295
+ function wrap$1(callback, n) {
5241
4296
  let t = 0;
5242
4297
  return () => {
5243
4298
  if (t++ >= n) {
@@ -5265,7 +4320,7 @@ class Ticker {
5265
4320
  if (useSafe) {
5266
4321
  callback = this.app.safe(callback);
5267
4322
  }
5268
- this.callbacks.unshift(n ? wrap(callback, n) : callback) - 1;
4323
+ this.callbacks.unshift(n ? wrap$1(callback, n) : callback) - 1;
5269
4324
  }
5270
4325
  start() {
5271
4326
  if (this.timer === null) {
@@ -5287,9 +4342,8 @@ class Ticker {
5287
4342
  * this value is injected during build time via rollup
5288
4343
  * */
5289
4344
  // @ts-ignore
5290
- 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 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.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;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.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(){if(this.isEmpty)return;const t=this.encoder.flush();this.onBatch(t),this.prepare()}clean(){this.encoder.reset()}}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(){u&&u.finaliseBatch()}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(null!=s){if(\"stop\"===s)return o(),void c().then((()=>{a=h.Stopped}));if(\"forceFlushBatch\"!==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=>{r&&r.push(t)}),s.tabId,(()=>postMessage({type:\"queue_empty\"}))),null===p&&(p=setInterval(o,1e4)),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()}else o()}}();\n";
4345
+ 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";
5291
4346
  const CANCELED = 'canceled';
5292
- const uxtStorageKey = 'or_uxt_active';
5293
4347
  const bufferStorageKey = 'or_buffer_1';
5294
4348
  const UnsuccessfulStart = (reason) => ({ reason, success: false });
5295
4349
  const SuccessfulStart = (body) => ({ ...body, success: true });
@@ -5341,7 +4395,7 @@ class App {
5341
4395
  this.stopCallbacks = [];
5342
4396
  this.commitCallbacks = [];
5343
4397
  this.activityState = ActivityState.NotActive;
5344
- this.version = '17.0.0-beta.0'; // TODO: version compatability check inside each plugin.
4398
+ this.version = '17.0.1'; // TODO: version compatability check inside each plugin.
5345
4399
  this.socketMode = false;
5346
4400
  this.compressionThreshold = 24 * 1000;
5347
4401
  this.bc = null;
@@ -5351,10 +4405,7 @@ class App {
5351
4405
  this.rootId = null;
5352
4406
  this.pageFrames = [];
5353
4407
  this.frameOderNumber = 0;
5354
- this.features = {
5355
- 'feature-flags': true,
5356
- 'usability-test': true,
5357
- };
4408
+ this.frameLevel = 0;
5358
4409
  this.emptyBatchCounter = 0;
5359
4410
  /** used by child iframes for crossdomain only */
5360
4411
  this.parentActive = false;
@@ -5382,8 +4433,9 @@ class App {
5382
4433
  if (data.line === proto.iframeId) {
5383
4434
  this.parentActive = true;
5384
4435
  this.rootId = data.id;
5385
- this.session.setSessionToken(data.token);
4436
+ this.session.setSessionToken(data.token, this.projectKey);
5386
4437
  this.frameOderNumber = data.frameOrderNumber;
4438
+ this.frameLevel = data.frameLevel;
5387
4439
  this.debug.log('starting iframe tracking', data);
5388
4440
  this.allowAppStart();
5389
4441
  }
@@ -5424,7 +4476,7 @@ class App {
5424
4476
  this.trackedFrames.push(data.context);
5425
4477
  }
5426
4478
  await this.waitStarted();
5427
- const token = this.session.getSessionToken();
4479
+ const token = this.session.getSessionToken(this.projectKey);
5428
4480
  const order = this.trackedFrames.findIndex((f) => f === data.context) + 1;
5429
4481
  if (order === 0) {
5430
4482
  this.debug.error('Couldnt get order number for iframe', data.context, this.trackedFrames);
@@ -5433,8 +4485,8 @@ class App {
5433
4485
  line: proto.iframeId,
5434
4486
  id,
5435
4487
  token,
5436
- // since indexes go from 0 we +1
5437
4488
  frameOrderNumber: order,
4489
+ frameLevel: this.frameLevel + 1,
5438
4490
  };
5439
4491
  this.debug.log('Got child frame signal; nodeId', id, event.source, iframeData);
5440
4492
  // @ts-ignore
@@ -5452,7 +4504,8 @@ class App {
5452
4504
  * */
5453
4505
  if (data.line === proto.iframeBatch) {
5454
4506
  const msgBatch = data.messages;
5455
- const mappedMessages = msgBatch.map((msg) => {
4507
+ const mappedMessages = [];
4508
+ msgBatch.forEach((msg) => {
5456
4509
  if (msg[0] === 20 /* MType.MouseMove */) {
5457
4510
  let fixedMessage = msg;
5458
4511
  this.pageFrames.forEach((frame) => {
@@ -5462,7 +4515,7 @@ class App {
5462
4515
  fixedMessage = [type, x + left, y + top];
5463
4516
  }
5464
4517
  });
5465
- return fixedMessage;
4518
+ mappedMessages.push(fixedMessage);
5466
4519
  }
5467
4520
  if (msg[0] === 68 /* MType.MouseClick */) {
5468
4521
  let fixedMessage = msg;
@@ -5488,9 +4541,11 @@ class App {
5488
4541
  ];
5489
4542
  }
5490
4543
  });
5491
- return fixedMessage;
4544
+ mappedMessages.push(fixedMessage);
4545
+ }
4546
+ if (![28 /* MType.UserID */, 29 /* MType.UserAnonymousID */, 30 /* MType.Metadata */].includes(msg[0])) {
4547
+ mappedMessages.push(msg);
5492
4548
  }
5493
- return msg;
5494
4549
  });
5495
4550
  this.messages.push(...mappedMessages);
5496
4551
  }
@@ -5661,7 +4716,6 @@ class App {
5661
4716
  });
5662
4717
  });
5663
4718
  };
5664
- this.onUxtCb = [];
5665
4719
  this.contextId = Math.random().toString(36).slice(2);
5666
4720
  this.projectKey = projectKey;
5667
4721
  this.networkOptions = options.network;
@@ -5696,8 +4750,9 @@ class App {
5696
4750
  useAnimationFrame: false,
5697
4751
  },
5698
4752
  forceNgOff: false,
5699
- inlineCss: 0,
4753
+ inlineCss: InlineCssMode.Unset,
5700
4754
  disableSprites: false,
4755
+ disableThrottling: false,
5701
4756
  };
5702
4757
  this.options = simpleMerge(defaultOptions, options);
5703
4758
  if (!this.insideIframe &&
@@ -5728,7 +4783,6 @@ class App {
5728
4783
  app: this,
5729
4784
  isDictDisabled: Boolean(this.options.disableStringDict || this.options.crossdomain?.enabled),
5730
4785
  });
5731
- this.featureFlags = new FeatureFlags(this);
5732
4786
  this.tagWatcher = new TagWatcher({
5733
4787
  sessionStorage: this.sessionStorage,
5734
4788
  errLog: this.debug.error,
@@ -5753,6 +4807,7 @@ class App {
5753
4807
  * listen for messages from parent window, so we can signal that we're alive
5754
4808
  * */
5755
4809
  window.addEventListener('message', this.parentCrossDomainFrameListener);
4810
+ window.addEventListener('message', this.crossDomainIframeListener);
5756
4811
  setInterval(() => {
5757
4812
  if (document.hidden) {
5758
4813
  return;
@@ -5776,34 +4831,37 @@ class App {
5776
4831
  line: proto.ask,
5777
4832
  source: thisTab,
5778
4833
  context: this.contextId,
4834
+ projectKey: this.projectKey,
5779
4835
  });
5780
4836
  this.startTimeout = setTimeout(() => {
5781
4837
  this.allowAppStart();
5782
4838
  }, 250);
5783
4839
  this.bc.onmessage = (ev) => {
5784
- if (ev.data.context === this.contextId) {
4840
+ if (ev.data.context === this.contextId || this.projectKey !== ev.data.projectKey) {
4841
+ this.debug.log('same ctx event', ev);
5785
4842
  return;
5786
4843
  }
5787
4844
  this.debug.log(ev);
5788
4845
  if (ev.data.line === proto.resp) {
5789
4846
  const sessionToken = ev.data.token;
5790
- this.session.setSessionToken(sessionToken);
4847
+ this.session.setSessionToken(sessionToken, this.projectKey);
5791
4848
  this.allowAppStart();
5792
4849
  }
5793
4850
  if (ev.data.line === proto.reg) {
5794
4851
  const sessionToken = ev.data.token;
5795
4852
  this.session.regenerateTabId();
5796
- this.session.setSessionToken(sessionToken);
4853
+ this.session.setSessionToken(sessionToken, this.projectKey);
5797
4854
  this.allowAppStart();
5798
4855
  }
5799
4856
  if (ev.data.line === proto.ask) {
5800
- const token = this.session.getSessionToken();
4857
+ const token = this.session.getSessionToken(this.projectKey);
5801
4858
  if (token && this.bc) {
5802
4859
  this.bc.postMessage({
5803
4860
  line: ev.data.source === thisTab ? proto.reg : proto.resp,
5804
4861
  token,
5805
4862
  source: thisTab,
5806
4863
  context: this.contextId,
4864
+ projectKey: this.projectKey,
5807
4865
  });
5808
4866
  }
5809
4867
  }
@@ -5860,16 +4918,23 @@ class App {
5860
4918
  this.worker.onmessage = ({ data }) => {
5861
4919
  this.handleWorkerMsg(data);
5862
4920
  };
4921
+ let closing = false;
5863
4922
  const alertWorker = () => {
4923
+ if (closing) {
4924
+ return;
4925
+ }
4926
+ closing = true;
4927
+ setTimeout(() => {
4928
+ closing = false;
4929
+ }, 500);
5864
4930
  if (this.worker) {
5865
- this.worker.postMessage(null);
4931
+ this.worker.postMessage('closing');
5866
4932
  }
5867
4933
  };
5868
- // keep better tactics, discard others?
5869
- this.attachEventListener(window, 'beforeunload', alertWorker, false);
5870
4934
  this.attachEventListener(document.body, 'mouseleave', alertWorker, false, false);
4935
+ this.attachEventListener(window, 'pagehide', alertWorker, false, false);
5871
4936
  // TODO: stop session after inactivity timeout (make configurable)
5872
- this.attachEventListener(document, 'visibilitychange', alertWorker, false);
4937
+ this.attachEventListener(document, 'visibilitychange', (e) => document.visibilityState === 'hidden' && alertWorker(), false);
5873
4938
  }
5874
4939
  catch (e) {
5875
4940
  this._debug('worker_start', e);
@@ -6064,7 +5129,7 @@ class App {
6064
5129
  };
6065
5130
  }
6066
5131
  getSessionToken() {
6067
- return this.session.getSessionToken();
5132
+ return this.session.getSessionToken(this.projectKey);
6068
5133
  }
6069
5134
  getSessionID() {
6070
5135
  return this.session.getInfo().sessionID || undefined;
@@ -6126,7 +5191,7 @@ class App {
6126
5191
  checkSessionToken(forceNew) {
6127
5192
  const lsReset = this.sessionStorage.getItem(this.options.session_reset_key) !== null;
6128
5193
  const needNewSessionID = forceNew || lsReset;
6129
- const sessionToken = this.session.getSessionToken();
5194
+ const sessionToken = this.session.getSessionToken(this.projectKey);
6130
5195
  return needNewSessionID || !sessionToken;
6131
5196
  }
6132
5197
  /**
@@ -6204,8 +5269,7 @@ class App {
6204
5269
  const {
6205
5270
  // this token is needed to fetch conditions and flags,
6206
5271
  // but it can't be used to record a session
6207
- token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, features, } = await r.json();
6208
- this.features = features ? features : this.features;
5272
+ token, userBrowser, userCity, userCountry, userDevice, userOS, userState, projectID, } = await r.json();
6209
5273
  this.session.assign({ projectID });
6210
5274
  this.session.setUserInfo({
6211
5275
  userBrowser,
@@ -6218,10 +5282,6 @@ class App {
6218
5282
  const onStartInfo = { sessionToken: token, userUUID: '', sessionID: '' };
6219
5283
  this.startCallbacks.forEach((cb) => cb(onStartInfo));
6220
5284
  await this.conditionsManager?.fetchConditions(projectID, token);
6221
- if (this.features['feature-flags']) {
6222
- await this.featureFlags.reloadFlags(token);
6223
- this.conditionsManager?.processFlags(this.featureFlags.flags);
6224
- }
6225
5285
  await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
6226
5286
  }
6227
5287
  /**
@@ -6345,7 +5405,7 @@ class App {
6345
5405
  while (this.bufferedMessages1.length > 0) {
6346
5406
  await this.flushBuffer(this.bufferedMessages1);
6347
5407
  }
6348
- this.postToWorker([['q_end']]);
5408
+ this.postToWorker([[-1]]);
6349
5409
  this.clearBuffers();
6350
5410
  }
6351
5411
  async _start(startOpts = {}, resetByWorker = false, conditionName) {
@@ -6393,7 +5453,7 @@ class App {
6393
5453
  connAttemptGap: this.options.connAttemptGap,
6394
5454
  tabId: this.session.getTabId(),
6395
5455
  });
6396
- const sessionToken = this.session.getSessionToken();
5456
+ const sessionToken = this.session.getSessionToken(this.projectKey);
6397
5457
  const isNewSession = this.checkSessionToken(startOpts.forceNew);
6398
5458
  this.sessionStorage.removeItem(this.options.session_reset_key);
6399
5459
  this.debug.log('OpenReplay: starting session; need new session id?', isNewSession, 'session token: ', sessionToken);
@@ -6433,8 +5493,7 @@ class App {
6433
5493
  delay, // derived from token
6434
5494
  sessionID, // derived from token
6435
5495
  startTimestamp, // real startTS (server time), derived from sessionID
6436
- userBrowser, userCity, userCountry, userDevice, userOS, userState, canvasEnabled, canvasQuality, canvasFPS, assistOnly: socketOnly, features, } = await r.json();
6437
- this.features = features ? features : this.features;
5496
+ userBrowser, userCity, userCountry, userDevice, userOS, userState, canvasEnabled, canvasQuality, canvasFPS, assistOnly: socketOnly, } = await r.json();
6438
5497
  if (typeof token !== 'string' ||
6439
5498
  typeof userUUID !== 'string' ||
6440
5499
  (typeof startTimestamp !== 'number' && typeof startTimestamp !== 'undefined') ||
@@ -6446,7 +5505,7 @@ class App {
6446
5505
  return UnsuccessfulStart(reason);
6447
5506
  }
6448
5507
  this.delay = delay;
6449
- this.session.setSessionToken(token);
5508
+ this.session.setSessionToken(token, this.projectKey);
6450
5509
  this.session.setUserInfo({
6451
5510
  userBrowser,
6452
5511
  userCity,
@@ -6487,12 +5546,9 @@ class App {
6487
5546
  if (startOpts.startCallback) {
6488
5547
  startOpts.startCallback(SuccessfulStart(onStartInfo));
6489
5548
  }
6490
- if (this.features['feature-flags']) {
6491
- void this.featureFlags.reloadFlags();
6492
- }
6493
5549
  await this.tagWatcher.fetchTags(this.options.ingestPoint, token);
6494
5550
  this.activityState = ActivityState.Active;
6495
- if (this.options.crossdomain?.enabled && !this.insideIframe) {
5551
+ if (this.options.crossdomain?.enabled) {
6496
5552
  void this.bootChildrenFrames();
6497
5553
  }
6498
5554
  if (canvasEnabled && !this.options.canvas.disableCanvas) {
@@ -6518,47 +5574,14 @@ class App {
6518
5574
  this.commit();
6519
5575
  /** --------------- COLD START BUFFER ------------------*/
6520
5576
  }
5577
+ if (this.insideIframe && this.rootId) {
5578
+ this.observer.crossdomainObserve(this.rootId, this.frameOderNumber, this.frameLevel);
5579
+ }
6521
5580
  else {
6522
- if (this.insideIframe && this.rootId) {
6523
- this.observer.crossdomainObserve(this.rootId, this.frameOderNumber);
6524
- }
6525
- else {
6526
- this.observer.observe();
6527
- }
6528
- this.ticker.start();
5581
+ this.observer.observe();
6529
5582
  }
5583
+ this.ticker.start();
6530
5584
  this.canvasRecorder?.startTracking();
6531
- if (this.features['usability-test'] && !this.insideIframe) {
6532
- this.uxtManager = this.uxtManager
6533
- ? this.uxtManager
6534
- : new UserTestManager(this, uxtStorageKey);
6535
- let uxtId;
6536
- const savedUxtTag = this.localStorage.getItem(uxtStorageKey);
6537
- if (savedUxtTag) {
6538
- uxtId = parseInt(savedUxtTag, 10);
6539
- }
6540
- if (location?.search) {
6541
- const query = new URLSearchParams(location.search);
6542
- if (query.has('oruxt')) {
6543
- const qId = query.get('oruxt');
6544
- uxtId = qId ? parseInt(qId, 10) : undefined;
6545
- }
6546
- }
6547
- if (uxtId) {
6548
- if (!this.uxtManager.isActive) {
6549
- // eslint-disable-next-line
6550
- this.uxtManager.getTest(uxtId, token, Boolean(savedUxtTag)).then((id) => {
6551
- if (id) {
6552
- this.onUxtCb.forEach((cb) => cb(id));
6553
- }
6554
- });
6555
- }
6556
- else {
6557
- // @ts-ignore
6558
- this.onUxtCb.forEach((cb) => cb(uxtId));
6559
- }
6560
- }
6561
- }
6562
5585
  return SuccessfulStart(onStartInfo);
6563
5586
  }
6564
5587
  catch (reason) {
@@ -6579,13 +5602,6 @@ class App {
6579
5602
  return UnsuccessfulStart(errorMessage);
6580
5603
  }
6581
5604
  }
6582
- addOnUxtCb(cb) {
6583
- // @ts-ignore
6584
- this.onUxtCb.push(cb);
6585
- }
6586
- getUxtId() {
6587
- return this.uxtManager?.getTestId();
6588
- }
6589
5605
  async waitStart() {
6590
5606
  return new Promise((resolve) => {
6591
5607
  const int = setInterval(() => {
@@ -6674,7 +5690,7 @@ class App {
6674
5690
  stop(stopWorker = true) {
6675
5691
  if (this.activityState !== ActivityState.NotActive) {
6676
5692
  try {
6677
- if (!this.insideIframe && this.options.crossdomain?.enabled) {
5693
+ if (this.options.crossdomain?.enabled) {
6678
5694
  this.killChildrenFrames();
6679
5695
  }
6680
5696
  this.attributeSender.clear();
@@ -6706,9 +5722,16 @@ function Connection (app) {
6706
5722
  if (connection === undefined) {
6707
5723
  return;
6708
5724
  }
6709
- const sendConnectionInformation = () => app.send(ConnectionInformation(Math.round(connection.downlink * 1000), connection.type || 'unknown'));
6710
- sendConnectionInformation();
6711
- connection.addEventListener('change', sendConnectionInformation);
5725
+ const sendConnectionInformation = () => {
5726
+ app.send(ConnectionInformation(Math.round(connection.downlink * 1000), connection.effectiveType || 'unknown'));
5727
+ };
5728
+ app.attachStartCallback(() => {
5729
+ sendConnectionInformation();
5730
+ connection.addEventListener('change', sendConnectionInformation);
5731
+ });
5732
+ app.attachStopCallback(() => {
5733
+ connection.removeEventListener('change', sendConnectionInformation);
5734
+ });
6712
5735
  }
6713
5736
 
6714
5737
  const printError = IN_BROWSER && 'InstallTrigger' in window // detect Firefox
@@ -7381,308 +6404,11 @@ function Input (app, opts) {
7381
6404
  }));
7382
6405
  }
7383
6406
 
7384
- // License: MIT
7385
- // Author: Anton Medvedev <anton@medv.io>
7386
- // Source: https://github.com/antonmedv/finder
7387
- const acceptedAttrNames = new Set(['role', 'name', 'aria-label', 'rel', 'href']);
7388
- /** Check if attribute name and value are word-like. */
7389
- function attr(name, value) {
7390
- let nameIsOk = acceptedAttrNames.has(name);
7391
- nameIsOk ||= name.startsWith('data-') && wordLike(name);
7392
- let valueIsOk = wordLike(value) && value.length < 100;
7393
- valueIsOk ||= value.startsWith('#') && wordLike(value.slice(1));
7394
- return nameIsOk && valueIsOk;
7395
- }
7396
- /** Check if id name is word-like. */
7397
- function idName(name) {
7398
- return wordLike(name);
7399
- }
7400
- /** Check if class name is word-like. */
7401
- function className(name) {
7402
- return wordLike(name);
7403
- }
7404
- /** Check if tag name is word-like. */
7405
- function tagName(name) {
7406
- return true;
7407
- }
7408
- /** Finds unique CSS selectors for the given element. */
7409
- function finder(input, options) {
7410
- if (input.nodeType !== Node.ELEMENT_NODE) {
7411
- throw new Error(`Can't generate CSS selector for non-element node type.`);
7412
- }
7413
- if (input.tagName.toLowerCase() === 'html') {
7414
- return 'html';
7415
- }
7416
- const defaults = {
7417
- root: document.body,
7418
- idName: idName,
7419
- className: className,
7420
- tagName: tagName,
7421
- attr: attr,
7422
- timeoutMs: 1000,
7423
- seedMinLength: 3,
7424
- optimizedMinLength: 2,
7425
- maxNumberOfPathChecks: Infinity,
7426
- };
7427
- const startTime = new Date();
7428
- const config = { ...defaults, ...options };
7429
- const rootDocument = findRootDocument(config.root, defaults);
7430
- let foundPath;
7431
- let count = 0;
7432
- for (const candidate of search(input, config, rootDocument)) {
7433
- const elapsedTimeMs = new Date().getTime() - startTime.getTime();
7434
- if (elapsedTimeMs > config.timeoutMs ||
7435
- count >= config.maxNumberOfPathChecks) {
7436
- const fPath = fallback(input, rootDocument);
7437
- if (!fPath) {
7438
- throw new Error(`Timeout: Can't find a unique selector after ${config.timeoutMs}ms`);
7439
- }
7440
- return selector(fPath);
7441
- }
7442
- count++;
7443
- if (unique(candidate, rootDocument)) {
7444
- foundPath = candidate;
7445
- break;
7446
- }
7447
- }
7448
- if (!foundPath) {
7449
- throw new Error(`Selector was not found.`);
7450
- }
7451
- const optimized = [
7452
- ...optimize(foundPath, input, config, rootDocument, startTime),
7453
- ];
7454
- optimized.sort(byPenalty);
7455
- if (optimized.length > 0) {
7456
- return selector(optimized[0]);
7457
- }
7458
- return selector(foundPath);
7459
- }
7460
- function* search(input, config, rootDocument) {
7461
- const stack = [];
7462
- let paths = [];
7463
- let current = input;
7464
- let i = 0;
7465
- while (current && current !== rootDocument) {
7466
- const level = tie(current, config);
7467
- for (const node of level) {
7468
- node.level = i;
7469
- }
7470
- stack.push(level);
7471
- current = current.parentElement;
7472
- i++;
7473
- paths.push(...combinations(stack));
7474
- if (i >= config.seedMinLength) {
7475
- paths.sort(byPenalty);
7476
- for (const candidate of paths) {
7477
- yield candidate;
7478
- }
7479
- paths = [];
7480
- }
7481
- }
7482
- paths.sort(byPenalty);
7483
- for (const candidate of paths) {
7484
- yield candidate;
7485
- }
7486
- }
7487
- function wordLike(name) {
7488
- if (/^[a-z\-]{3,}$/i.test(name)) {
7489
- const words = name.split(/-|[A-Z]/);
7490
- for (const word of words) {
7491
- if (word.length <= 2) {
7492
- return false;
7493
- }
7494
- if (/[^aeiou]{4,}/i.test(word)) {
7495
- return false;
7496
- }
7497
- }
7498
- return true;
7499
- }
7500
- return false;
7501
- }
7502
- function tie(element, config) {
7503
- const level = [];
7504
- const elementId = element.getAttribute('id');
7505
- if (elementId && config.idName(elementId)) {
7506
- level.push({
7507
- name: '#' + CSS.escape(elementId),
7508
- penalty: 0,
7509
- });
7510
- }
7511
- for (let i = 0; i < element.classList.length; i++) {
7512
- const name = element.classList[i];
7513
- if (config.className(name)) {
7514
- level.push({
7515
- name: '.' + CSS.escape(name),
7516
- penalty: 1,
7517
- });
7518
- }
7519
- }
7520
- for (let i = 0; i < element.attributes.length; i++) {
7521
- const attr = element.attributes[i];
7522
- if (config.attr(attr.name, attr.value)) {
7523
- level.push({
7524
- name: `[${CSS.escape(attr.name)}="${CSS.escape(attr.value)}"]`,
7525
- penalty: 2,
7526
- });
7527
- }
7528
- }
7529
- const tagName = element.tagName.toLowerCase();
7530
- if (config.tagName(tagName)) {
7531
- level.push({
7532
- name: tagName,
7533
- penalty: 5,
7534
- });
7535
- const index = indexOf(element, tagName);
7536
- if (index !== undefined) {
7537
- level.push({
7538
- name: nthOfType(tagName, index),
7539
- penalty: 10,
7540
- });
7541
- }
7542
- }
7543
- const nth = indexOf(element);
7544
- if (nth !== undefined) {
7545
- level.push({
7546
- name: nthChild(tagName, nth),
7547
- penalty: 50,
7548
- });
7549
- }
7550
- return level;
7551
- }
7552
- function selector(path) {
7553
- let node = path[0];
7554
- let query = node.name;
7555
- for (let i = 1; i < path.length; i++) {
7556
- const level = path[i].level || 0;
7557
- if (node.level === level - 1) {
7558
- query = `${path[i].name} > ${query}`;
7559
- }
7560
- else {
7561
- query = `${path[i].name} ${query}`;
7562
- }
7563
- node = path[i];
7564
- }
7565
- return query;
7566
- }
7567
- function penalty(path) {
7568
- return path.map((node) => node.penalty).reduce((acc, i) => acc + i, 0);
7569
- }
7570
- function byPenalty(a, b) {
7571
- return penalty(a) - penalty(b);
7572
- }
7573
- function indexOf(input, tagName) {
7574
- const parent = input.parentNode;
7575
- if (!parent) {
7576
- return undefined;
7577
- }
7578
- let child = parent.firstChild;
7579
- if (!child) {
7580
- return undefined;
7581
- }
7582
- let i = 0;
7583
- while (child) {
7584
- if (child.nodeType === Node.ELEMENT_NODE &&
7585
- (tagName === undefined ||
7586
- child.tagName.toLowerCase() === tagName)) {
7587
- i++;
7588
- }
7589
- if (child === input) {
7590
- break;
7591
- }
7592
- child = child.nextSibling;
7593
- }
7594
- return i;
7595
- }
7596
- function fallback(input, rootDocument) {
7597
- let i = 0;
7598
- let current = input;
7599
- const path = [];
7600
- while (current && current !== rootDocument) {
7601
- const tagName = current.tagName.toLowerCase();
7602
- const index = indexOf(current, tagName);
7603
- if (index === undefined) {
7604
- return;
7605
- }
7606
- path.push({
7607
- name: nthOfType(tagName, index),
7608
- penalty: NaN,
7609
- level: i,
7610
- });
7611
- current = current.parentElement;
7612
- i++;
7613
- }
7614
- if (unique(path, rootDocument)) {
7615
- return path;
7616
- }
7617
- }
7618
- function nthChild(tagName, index) {
7619
- if (tagName === 'html') {
7620
- return 'html';
7621
- }
7622
- return `${tagName}:nth-child(${index})`;
7623
- }
7624
- function nthOfType(tagName, index) {
7625
- if (tagName === 'html') {
7626
- return 'html';
7627
- }
7628
- return `${tagName}:nth-of-type(${index})`;
7629
- }
7630
- function* combinations(stack, path = []) {
7631
- if (stack.length > 0) {
7632
- for (let node of stack[0]) {
7633
- yield* combinations(stack.slice(1, stack.length), path.concat(node));
7634
- }
7635
- }
7636
- else {
7637
- yield path;
7638
- }
7639
- }
7640
- function findRootDocument(rootNode, defaults) {
7641
- if (rootNode.nodeType === Node.DOCUMENT_NODE) {
7642
- return rootNode;
7643
- }
7644
- if (rootNode === defaults.root) {
7645
- return rootNode.ownerDocument;
7646
- }
7647
- return rootNode;
7648
- }
7649
- function unique(path, rootDocument) {
7650
- const css = selector(path);
7651
- switch (rootDocument.querySelectorAll(css).length) {
7652
- case 0:
7653
- throw new Error(`Can't select any node with this selector: ${css}`);
7654
- case 1:
7655
- return true;
7656
- default:
7657
- return false;
7658
- }
7659
- }
7660
- function* optimize(path, input, config, rootDocument, startTime) {
7661
- if (path.length > 2 && path.length > config.optimizedMinLength) {
7662
- for (let i = 1; i < path.length - 1; i++) {
7663
- const elapsedTimeMs = new Date().getTime() - startTime.getTime();
7664
- if (elapsedTimeMs > config.timeoutMs) {
7665
- return;
7666
- }
7667
- const newPath = [...path];
7668
- newPath.splice(i, 1);
7669
- if (unique(newPath, rootDocument) &&
7670
- rootDocument.querySelector(selector(newPath)) === input) {
7671
- yield newPath;
7672
- yield* optimize(newPath, input, config, rootDocument, startTime);
7673
- }
7674
- }
7675
- }
7676
- }
7677
-
7678
- function _getSelector(target, document, options) {
7679
- const selector = finder(target, {
7680
- root: document.body,
7681
- seedMinLength: 3,
7682
- optimizedMinLength: options?.minSelectorDepth || 2,
7683
- maxNumberOfPathChecks: options?.maxOptimiseTries || 10000,
7684
- });
7685
- return selector;
6407
+ const cssEscape = (typeof CSS !== 'undefined' && CSS.escape) || ((t) => t);
6408
+ const docClassCache = new WeakMap();
6409
+ function _getSelector(target) {
6410
+ const selector = getCSSPath(target);
6411
+ return selector || '';
7686
6412
  }
7687
6413
  function isClickable(element) {
7688
6414
  const tag = element.tagName.toUpperCase();
@@ -7802,8 +6528,8 @@ function Mouse (app, options) {
7802
6528
  }
7803
6529
  };
7804
6530
  const patchDocument = (document, topframe = false) => {
7805
- function getSelector(id, target, options) {
7806
- return (selectorMap[id] = selectorMap[id] || _getSelector(target, document, options));
6531
+ function getSelector(id, target) {
6532
+ return (selectorMap[id] = selectorMap[id] || _getSelector(target));
7807
6533
  }
7808
6534
  const attachListener = topframe
7809
6535
  ? app.attachEventListener.bind(app) // attached/removed on start/stop
@@ -7842,7 +6568,7 @@ function Mouse (app, options) {
7842
6568
  const normalizedY = roundNumber(clickY / contentHeight);
7843
6569
  sendMouseMove();
7844
6570
  const label = getTargetLabel(target);
7845
- app.send(MouseClick(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, app.sanitizer.privateMode ? label.replaceAll(/./g, '*') : label, isClickable(target) && !disableClickmaps ? getSelector(id, target, options) : '', normalizedX, normalizedY), true);
6571
+ app.send(MouseClick(id, mouseTarget === target ? Math.round(performance.now() - mouseTargetTime) : 0, app.sanitizer.privateMode ? label.replaceAll(/./g, '*') : label, isClickable(target) && !disableClickmaps ? getSelector(id, target) : '', normalizedX, normalizedY), true);
7846
6572
  }
7847
6573
  mouseTarget = null;
7848
6574
  });
@@ -7862,8 +6588,90 @@ function Mouse (app, options) {
7862
6588
  function roundNumber(num) {
7863
6589
  return Math.round(num * 1e4);
7864
6590
  }
6591
+ function isDocUniqueClass(cls, doc) {
6592
+ let cache = docClassCache.get(doc);
6593
+ if (!cache) {
6594
+ cache = Object.create(null);
6595
+ docClassCache.set(doc, cache);
6596
+ }
6597
+ if (cls in cache)
6598
+ return cache[cls];
6599
+ const unique = doc.querySelectorAll(`.${cssEscape(cls)}`).length === 1;
6600
+ cache[cls] = unique;
6601
+ return unique;
6602
+ }
6603
+ function wordLike(name) {
6604
+ if (/^[a-z\-]{3,}$/i.test(name)) {
6605
+ const words = name.split(/-|[A-Z]/);
6606
+ for (const word of words) {
6607
+ if (word.length <= 2) {
6608
+ return false;
6609
+ }
6610
+ if (/[^aeiou]{4,}/i.test(word)) {
6611
+ return false;
6612
+ }
6613
+ }
6614
+ return true;
6615
+ }
6616
+ return false;
6617
+ }
6618
+ function getCSSPath(el) {
6619
+ if (!el || el.nodeType !== 1)
6620
+ return false;
6621
+ if (el.id)
6622
+ return `#${cssEscape(el.id)}`;
6623
+ const parts = [];
6624
+ while (el && el.nodeType === 1 && el !== el.ownerDocument) {
6625
+ if (el.id) {
6626
+ parts.unshift(`#${cssEscape(el.id)}`);
6627
+ break;
6628
+ }
6629
+ const tag = el.tagName.toLowerCase();
6630
+ if (el.classList?.length) {
6631
+ for (const cls of el.classList) {
6632
+ if (wordLike(cls) && isDocUniqueClass(cls, el.ownerDocument)) {
6633
+ parts.unshift(`${tag}.${cssEscape(cls)}`);
6634
+ return parts.join(' > ');
6635
+ }
6636
+ }
6637
+ }
6638
+ const sibCls = getUniqueSiblingClass(el);
6639
+ if (sibCls) {
6640
+ parts.unshift(`${tag}.${cssEscape(sibCls)}`);
6641
+ }
6642
+ else if (el === el.ownerDocument.body ||
6643
+ el === el.ownerDocument.documentElement) {
6644
+ parts.unshift(tag);
6645
+ }
6646
+ else {
6647
+ let idx = 1;
6648
+ for (let sib = el.previousElementSibling; sib; sib = sib.previousElementSibling) {
6649
+ if (sib.tagName.toLowerCase() === tag)
6650
+ idx++;
6651
+ }
6652
+ parts.unshift(`${tag}:nth-of-type(${idx})`);
6653
+ }
6654
+ el = el.parentNode;
6655
+ }
6656
+ return parts.join(' > ');
6657
+ }
6658
+ function getUniqueSiblingClass(el) {
6659
+ if (!el.classList?.length || !el.parentNode)
6660
+ return null;
6661
+ const sibs = el.parentNode.children;
6662
+ outer: for (const cls of el.classList) {
6663
+ if (!wordLike(cls) || !isDocUniqueClass(cls, el.ownerDocument))
6664
+ continue;
6665
+ for (const sib of sibs) {
6666
+ if (sib !== el && sib.classList?.contains(cls))
6667
+ continue outer;
6668
+ }
6669
+ return cls;
6670
+ }
6671
+ return null;
6672
+ }
7865
6673
 
7866
- var e,o=-1,a=function(e){addEventListener("pageshow",(function(n){n.persisted&&(o=n.timeStamp,e(n));}),true);},c=function(){var e=self.performance&&performance.getEntriesByType&&performance.getEntriesByType("navigation")[0];if(e&&e.responseStart>0&&e.responseStart<performance.now())return e},u=function(){var e=c();return e&&e.activationStart||0},f=function(e,n){var t=c(),r="navigate";o>=0?r="back-forward-cache":t&&(document.prerendering||u()>0?r="prerender":document.wasDiscarded?r="restore":t.type&&(r=t.type.replace(/_/g,"-")));return {name:e,value:void 0===n?-1:n,rating:"good",delta:0,entries:[],id:"v4-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12),navigationType:r}},s=function(e,n,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){var r=new PerformanceObserver((function(e){Promise.resolve().then((function(){n(e.getEntries());}));}));return r.observe(Object.assign({type:e,buffered:!0},t||{})),r}}catch(e){}},d=function(e,n,t,r){var i,o;return function(a){n.value>=0&&(a||r)&&((o=n.value-(i||0))||void 0===i)&&(i=n.value,n.delta=o,n.rating=function(e,n){return e>n[1]?"poor":e>n[0]?"needs-improvement":"good"}(n.value,t),e(n));}},l=function(e){requestAnimationFrame((function(){return requestAnimationFrame((function(){return e()}))}));},p=function(e){document.addEventListener("visibilitychange",(function(){"hidden"===document.visibilityState&&e();}));},v=function(e){var n=false;return function(){n||(e(),n=true);}},m=-1,h=function(){return "hidden"!==document.visibilityState||document.prerendering?1/0:0},g=function(e){"hidden"===document.visibilityState&&m>-1&&(m="visibilitychange"===e.type?e.timeStamp:0,T());},y=function(){addEventListener("visibilitychange",g,true),addEventListener("prerenderingchange",g,true);},T=function(){removeEventListener("visibilitychange",g,true),removeEventListener("prerenderingchange",g,true);},E=function(){return m<0&&(m=h(),y(),a((function(){setTimeout((function(){m=h(),y();}),0);}))),{get firstHiddenTime(){return m}}},C=function(e){document.prerendering?addEventListener("prerenderingchange",(function(){return e()}),true):e();},b=[1800,3e3],S=function(e,n){n=n||{},C((function(){var t,r=E(),i=f("FCP"),o=s("paint",(function(e){e.forEach((function(e){"first-contentful-paint"===e.name&&(o.disconnect(),e.startTime<r.firstHiddenTime&&(i.value=Math.max(e.startTime-u(),0),i.entries.push(e),t(true)));}));}));o&&(t=d(e,i,b,n.reportAllChanges),a((function(r){i=f("FCP"),t=d(e,i,b,n.reportAllChanges),l((function(){i.value=performance.now()-r.timeStamp,t(true);}));})));}));},L=[.1,.25],w=function(e,n){n=n||{},S(v((function(){var t,r=f("CLS",0),i=0,o=[],c=function(e){e.forEach((function(e){if(!e.hadRecentInput){var n=o[0],t=o[o.length-1];i&&e.startTime-t.startTime<1e3&&e.startTime-n.startTime<5e3?(i+=e.value,o.push(e)):(i=e.value,o=[e]);}})),i>r.value&&(r.value=i,r.entries=o,t());},u=s("layout-shift",c);u&&(t=d(e,r,L,n.reportAllChanges),p((function(){c(u.takeRecords()),t(true);})),a((function(){i=0,r=f("CLS",0),t=d(e,r,L,n.reportAllChanges),l((function(){return t()}));})),setTimeout(t,0));})));},A=0,I=1/0,P=0,M=function(e){e.forEach((function(e){e.interactionId&&(I=Math.min(I,e.interactionId),P=Math.max(P,e.interactionId),A=P?(P-I)/7+1:0);}));},k=function(){return e?A:performance.interactionCount||0},F=function(){"interactionCount"in performance||e||(e=s("event",M,{type:"event",buffered:true,durationThreshold:0}));},D=[],x=new Map,R=0,B=function(){var e=Math.min(D.length-1,Math.floor((k()-R)/50));return D[e]},H=[],q=function(e){if(H.forEach((function(n){return n(e)})),e.interactionId||"first-input"===e.entryType){var n=D[D.length-1],t=x.get(e.interactionId);if(t||D.length<10||e.duration>n.latency){if(t)e.duration>t.latency?(t.entries=[e],t.latency=e.duration):e.duration===t.latency&&e.startTime===t.entries[0].startTime&&t.entries.push(e);else {var r={id:e.interactionId,latency:e.duration,entries:[e]};x.set(r.id,r),D.push(r);}D.sort((function(e,n){return n.latency-e.latency})),D.length>10&&D.splice(10).forEach((function(e){return x.delete(e.id)}));}}},O=function(e){var n=self.requestIdleCallback||self.setTimeout,t=-1;return e=v(e),"hidden"===document.visibilityState?e():(t=n(e),p(e)),t},N=[200,500],j=function(e,n){"PerformanceEventTiming"in self&&"interactionId"in PerformanceEventTiming.prototype&&(n=n||{},C((function(){var t;F();var r,i=f("INP"),o=function(e){O((function(){e.forEach(q);var n=B();n&&n.latency!==i.value&&(i.value=n.latency,i.entries=n.entries,r());}));},c=s("event",o,{durationThreshold:null!==(t=n.durationThreshold)&&void 0!==t?t:40});r=d(e,i,N,n.reportAllChanges),c&&(c.observe({type:"first-input",buffered:true}),p((function(){o(c.takeRecords()),r(true);})),a((function(){R=k(),D.length=0,x.clear(),i=f("INP"),r=d(e,i,N,n.reportAllChanges);})));})));},_=[2500,4e3],z={},G=function(e,n){n=n||{},C((function(){var t,r=E(),i=f("LCP"),o=function(e){n.reportAllChanges||(e=e.slice(-1)),e.forEach((function(e){e.startTime<r.firstHiddenTime&&(i.value=Math.max(e.startTime-u(),0),i.entries=[e],t());}));},c=s("largest-contentful-paint",o);if(c){t=d(e,i,_,n.reportAllChanges);var m=v((function(){z[i.id]||(o(c.takeRecords()),c.disconnect(),z[i.id]=true,t(true));}));["keydown","click"].forEach((function(e){addEventListener(e,(function(){return O(m)}),{once:true,capture:true});})),p(m),a((function(r){i=f("LCP"),t=d(e,i,_,n.reportAllChanges),l((function(){i.value=performance.now()-r.timeStamp,z[i.id]=true,t(true);}));}));}}));},J=[800,1800],K=function e(n){document.prerendering?C((function(){return e(n)})):"complete"!==document.readyState?addEventListener("load",(function(){return e(n)}),true):setTimeout(n,0);},Q=function(e,n){n=n||{};var t=f("TTFB"),r=d(e,t,J,n.reportAllChanges);K((function(){var i=c();i&&(t.value=Math.max(i.responseStart-u(),0),t.entries=[i],r(true),a((function(){t=f("TTFB",0),(r=d(e,t,J,n.reportAllChanges))(true);})));}));};
6674
+ let e=-1;const t=t=>{addEventListener("pageshow",(n=>{n.persisted&&(e=n.timeStamp,t(n));}),true);},n=(e,t,n,i)=>{let s,o;return r=>{t.value>=0&&(r||i)&&(o=t.value-(s??0),(o||void 0===s)&&(s=t.value,t.delta=o,t.rating=((e,t)=>e>t[1]?"poor":e>t[0]?"needs-improvement":"good")(t.value,n),e(t)));}},i=e=>{requestAnimationFrame((()=>requestAnimationFrame((()=>e()))));},s=()=>{const e=performance.getEntriesByType("navigation")[0];if(e&&e.responseStart>0&&e.responseStart<performance.now())return e},o=()=>{const e=s();return e?.activationStart??0},r=(t,n=-1)=>{const i=s();let r="navigate";e>=0?r="back-forward-cache":i&&(document.prerendering||o()>0?r="prerender":document.wasDiscarded?r="restore":i.type&&(r=i.type.replace(/_/g,"-")));return {name:t,value:n,rating:"good",delta:0,entries:[],id:`v5-${Date.now()}-${Math.floor(8999999999999*Math.random())+1e12}`,navigationType:r}},c=new WeakMap;function a(e,t){return c.get(e)||c.set(e,new t),c.get(e)}class d{t;i=0;o=[];h(e){if(e.hadRecentInput)return;const t=this.o[0],n=this.o.at(-1);this.i&&t&&n&&e.startTime-n.startTime<1e3&&e.startTime-t.startTime<5e3?(this.i+=e.value,this.o.push(e)):(this.i=e.value,this.o=[e]),this.t?.(e);}}const h=(e,t,n={})=>{try{if(PerformanceObserver.supportedEntryTypes.includes(e)){const i=new PerformanceObserver((e=>{Promise.resolve().then((()=>{t(e.getEntries());}));}));return i.observe({type:e,buffered:!0,...n}),i}}catch{}},f=e=>{let t=false;return ()=>{t||(e(),t=true);}};let u=-1;const l=new Set,m=()=>"hidden"!==document.visibilityState||document.prerendering?1/0:0,p=e=>{if("hidden"===document.visibilityState){if("visibilitychange"===e.type)for(const e of l)e();isFinite(u)||(u="visibilitychange"===e.type?e.timeStamp:0,removeEventListener("prerenderingchange",p,true));}},v=()=>{if(u<0){const e=o(),n=document.prerendering?void 0:globalThis.performance.getEntriesByType("visibility-state").filter((t=>"hidden"===t.name&&t.startTime>e))[0]?.startTime;u=n??m(),addEventListener("visibilitychange",p,true),addEventListener("prerenderingchange",p,true),t((()=>{setTimeout((()=>{u=m();}));}));}return {get firstHiddenTime(){return u},onHidden(e){l.add(e);}}},g=e=>{document.prerendering?addEventListener("prerenderingchange",(()=>e()),true):e();},y=[1800,3e3],E=(e,s={})=>{g((()=>{const c=v();let a,d=r("FCP");const f=h("paint",(e=>{for(const t of e)"first-contentful-paint"===t.name&&(f.disconnect(),t.startTime<c.firstHiddenTime&&(d.value=Math.max(t.startTime-o(),0),d.entries.push(t),a(true)));}));f&&(a=n(e,d,y,s.reportAllChanges),t((t=>{d=r("FCP"),a=n(e,d,y,s.reportAllChanges),i((()=>{d.value=performance.now()-t.timeStamp,a(true);}));})));}));},b=[.1,.25],L=(e,s={})=>{const o=v();E(f((()=>{let c,f=r("CLS",0);const u=a(s,d),l=e=>{for(const t of e)u.h(t);u.i>f.value&&(f.value=u.i,f.entries=u.o,c());},m=h("layout-shift",l);m&&(c=n(e,f,b,s.reportAllChanges),o.onHidden((()=>{l(m.takeRecords()),c(true);})),t((()=>{u.i=0,f=r("CLS",0),c=n(e,f,b,s.reportAllChanges),i((()=>c()));})),setTimeout(c));})));};let P=0,T=1/0,_=0;const M=e=>{for(const t of e)t.interactionId&&(T=Math.min(T,t.interactionId),_=Math.max(_,t.interactionId),P=_?(_-T)/7+1:0);};let w;const C=()=>w?P:performance.interactionCount??0,I=()=>{"interactionCount"in performance||w||(w=h("event",M,{type:"event",buffered:true,durationThreshold:0}));};let F=0;class k{u=[];l=new Map;m;p;v(){F=C(),this.u.length=0,this.l.clear();}L(){const e=Math.min(this.u.length-1,Math.floor((C()-F)/50));return this.u[e]}h(e){if(this.m?.(e),!e.interactionId&&"first-input"!==e.entryType)return;const t=this.u.at(-1);let n=this.l.get(e.interactionId);if(n||this.u.length<10||e.duration>t.P){if(n?e.duration>n.P?(n.entries=[e],n.P=e.duration):e.duration===n.P&&e.startTime===n.entries[0].startTime&&n.entries.push(e):(n={id:e.interactionId,entries:[e],P:e.duration},this.l.set(n.id,n),this.u.push(n)),this.u.sort(((e,t)=>t.P-e.P)),this.u.length>10){const e=this.u.splice(10);for(const t of e)this.l.delete(t.id);}this.p?.(n);}}}const A=e=>{const t=globalThis.requestIdleCallback||setTimeout;"hidden"===document.visibilityState?e():(e=f(e),addEventListener("visibilitychange",e,{once:true,capture:true}),t((()=>{e(),removeEventListener("visibilitychange",e,{capture:true});})));},B=[200,500],S=(e,i={})=>{if(!globalThis.PerformanceEventTiming||!("interactionId"in PerformanceEventTiming.prototype))return;const s=v();g((()=>{I();let o,c=r("INP");const d=a(i,k),f=e=>{A((()=>{for(const t of e)d.h(t);const t=d.L();t&&t.P!==c.value&&(c.value=t.P,c.entries=t.entries,o());}));},u=h("event",f,{durationThreshold:i.durationThreshold??40});o=n(e,c,B,i.reportAllChanges),u&&(u.observe({type:"first-input",buffered:true}),s.onHidden((()=>{f(u.takeRecords()),o(true);})),t((()=>{d.v(),c=r("INP"),o=n(e,c,B,i.reportAllChanges);})));}));};class N{m;h(e){this.m?.(e);}}const q=[2500,4e3],x=(e,s={})=>{g((()=>{const c=v();let d,u=r("LCP");const l=a(s,N),m=e=>{s.reportAllChanges||(e=e.slice(-1));for(const t of e)l.h(t),t.startTime<c.firstHiddenTime&&(u.value=Math.max(t.startTime-o(),0),u.entries=[t],d());},p=h("largest-contentful-paint",m);if(p){d=n(e,u,q,s.reportAllChanges);const o=f((()=>{m(p.takeRecords()),p.disconnect(),d(true);})),c=e=>{e.isTrusted&&(A(o),removeEventListener(e.type,c,{capture:true}));};for(const e of ["keydown","click","visibilitychange"])addEventListener(e,c,{capture:true});t((t=>{u=r("LCP"),d=n(e,u,q,s.reportAllChanges),i((()=>{u.value=performance.now()-t.timeStamp,d(true);}));}));}}));},H=[800,1800],O=e=>{document.prerendering?g((()=>O(e))):"complete"!==document.readyState?addEventListener("load",(()=>O(e)),true):setTimeout(e);},$=(e,i={})=>{let c=r("TTFB"),a=n(e,c,H,i.reportAllChanges);O((()=>{const d=s();d&&(c.value=Math.max(d.responseStart-o(),0),c.entries=[d],a(true),t((()=>{c=r("TTFB",0),a=n(e,c,H,i.reportAllChanges),a(true);})));}));};
7867
6675
 
7868
6676
  function getPaintBlocks(resources) {
7869
6677
  const paintBlocks = [];
@@ -7948,12 +6756,7 @@ function Timing (app, opts) {
7948
6756
  if (shouldSkip) {
7949
6757
  return;
7950
6758
  }
7951
- const failed = entry.initiatorType !== 'fetch'
7952
- ? entry.responseEnd === 0
7953
- || (entry.transferSize === 0 && entry.decodedBodySize === 0)
7954
- || (entry.responseStatus && entry.responseStatus >= 400)
7955
- : entry.responseStatus && entry.responseStatus >= 400;
7956
- // will probably require custom header added to responses for tracked fetch/xhr requests:
6759
+ // will probably require custom header added to responses for tracked requests:
7957
6760
  // Timing-Allow-Origin: *
7958
6761
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Timing-Allow-Origin
7959
6762
  let stalled = 0;
@@ -7969,17 +6772,22 @@ function Timing (app, opts) {
7969
6772
  queueing: entry.requestStart - entry.fetchStart,
7970
6773
  dnsLookup: entry.domainLookupEnd - entry.domainLookupStart,
7971
6774
  initialConnection: entry.connectEnd - entry.connectStart,
7972
- ssl: entry.secureConnectionStart > 0
7973
- ? entry.connectEnd - entry.secureConnectionStart : 0,
6775
+ ssl: entry.secureConnectionStart > 0 ? entry.connectEnd - entry.secureConnectionStart : 0,
7974
6776
  ttfb: entry.responseStart - entry.requestStart,
7975
6777
  contentDownload: entry.responseEnd - entry.responseStart,
7976
- total: entry.duration ?? (entry.responseEnd - entry.startTime),
6778
+ total: entry.duration ?? entry.responseEnd - entry.startTime,
7977
6779
  stalled,
7978
6780
  };
7979
- if (failed) {
7980
- app.send(ResourceTiming(entry.startTime + getTimeOrigin(), 0, timings.ttfb, 0, 0, 0, entry.name, entry.initiatorType, 0, false, timings.queueing, timings.dnsLookup, timings.initialConnection, timings.ssl, timings.contentDownload, timings.total, timings.stalled));
7981
- }
7982
- app.send(ResourceTiming(entry.startTime + getTimeOrigin(), entry.duration, timings.ttfb, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, app.sanitizer.privateMode ? entry.name.replaceAll(/./g, '*') : entry.name, entry.initiatorType, entry.transferSize, (entry.responseStatus && entry.responseStatus === 304) || entry.transferSize === 0, timings.queueing, timings.dnsLookup, timings.initialConnection, timings.ssl, timings.contentDownload, timings.total, timings.stalled));
6781
+ const entryName = options.resourceNameSanitizer
6782
+ ? options.resourceNameSanitizer(entry.name)
6783
+ : entry.name;
6784
+ const cached = (entry.responseStatus && entry.responseStatus === 304) ||
6785
+ // @ts-ignore
6786
+ (entry.deliveryType && entry.deliveryType === 'cache') ||
6787
+ (entry.transferSize === 0 && entry.decodedBodySize > 0);
6788
+ const requestFailed = entry.responseStatus && entry.responseStatus >= 400;
6789
+ const decodedBodySize = requestFailed ? -111 : entry.decodedBodySize || 0;
6790
+ 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));
7983
6791
  }
7984
6792
  const observer = new PerformanceObserver((list) => list.getEntries().forEach(resourceTiming));
7985
6793
  function onVitalsSignal(msg) {
@@ -7987,14 +6795,8 @@ function Timing (app, opts) {
7987
6795
  return app.send(WebVitals(msg.name, String(msg.value)));
7988
6796
  }
7989
6797
  }
7990
- let prevSessionID;
7991
6798
  app.attachStartCallback(function ({ sessionID }) {
7992
- if (sessionID !== prevSessionID) {
7993
- // Send past page resources on a newly started session
7994
- performance.getEntriesByType('resource').forEach(resourceTiming);
7995
- prevSessionID = sessionID;
7996
- }
7997
- observer.observe({ entryTypes: ['resource'] });
6799
+ observer.observe({ type: 'resource', buffered: true });
7998
6800
  // browser support:
7999
6801
  // onCLS(): Chromium
8000
6802
  // onFCP(): Chromium, Firefox, Safari
@@ -8002,10 +6804,10 @@ function Timing (app, opts) {
8002
6804
  // onINP(): Chromium
8003
6805
  // onLCP(): Chromium, Firefox
8004
6806
  // onTTFB(): Chromium, Firefox, Safari
8005
- w(onVitalsSignal);
8006
- j(onVitalsSignal);
8007
- G(onVitalsSignal);
8008
- Q(onVitalsSignal);
6807
+ L(onVitalsSignal);
6808
+ S(onVitalsSignal);
6809
+ x(onVitalsSignal);
6810
+ $(onVitalsSignal);
8009
6811
  });
8010
6812
  app.attachStopCallback(function () {
8011
6813
  observer.disconnect();
@@ -8158,16 +6960,19 @@ function Scroll (app, insideIframe) {
8158
6960
  }, 5, false);
8159
6961
  }
8160
6962
 
8161
- function Viewport (app) {
6963
+ function Viewport (app, options) {
8162
6964
  let url, width, height;
8163
6965
  let navigationStart;
8164
6966
  let referrer = document.referrer;
6967
+ const urlSanitizer = options?.urlSanitizer || ((u) => u);
6968
+ const titleSanitizer = options?.titleSanitizer || ((t) => t);
8165
6969
  const sendSetPageLocation = app.safe(() => {
8166
6970
  const { URL } = document;
8167
6971
  if (URL !== url) {
8168
6972
  url = URL;
8169
- const safeTitle = app.sanitizer.privateMode ? stringWiper(document.title) : document.title;
8170
- const safeUrl = app.sanitizer.privateMode ? stringWiper(url) : url;
6973
+ const sanitized = urlSanitizer(url);
6974
+ const safeTitle = app.sanitizer.privateMode ? stringWiper(document.title) : titleSanitizer(document.title);
6975
+ const safeUrl = app.sanitizer.privateMode ? stringWiper(sanitized) : sanitized;
8171
6976
  const safeReferrer = app.sanitizer.privateMode ? stringWiper(referrer) : referrer;
8172
6977
  app.send(SetPageLocation(safeUrl, safeReferrer, navigationStart, safeTitle));
8173
6978
  navigationStart = 0;
@@ -8793,7 +7598,9 @@ class NetworkMessage {
8793
7598
  });
8794
7599
  if (!messageInfo)
8795
7600
  return null;
8796
- const isGraphql = messageInfo.url.includes("/graphql");
7601
+ const gqlHeader = "application/graphql-response";
7602
+ const isGraphql = messageInfo.url.includes("/graphql")
7603
+ || Object.values(messageInfo.request.headers).some(v => v.includes(gqlHeader));
8797
7604
  if (isGraphql && messageInfo.response.body && typeof messageInfo.response.body === 'string') {
8798
7605
  const isError = messageInfo.response.body.includes("errors");
8799
7606
  messageInfo.status = isError ? 400 : 200;
@@ -8871,42 +7678,47 @@ const genStringBody = (body) => {
8871
7678
  return null;
8872
7679
  }
8873
7680
  let result;
8874
- if (typeof body === 'string') {
8875
- if (body[0] === '{' || body[0] === '[') {
8876
- result = body;
7681
+ try {
7682
+ if (typeof body === 'string') {
7683
+ if (body[0] === '{' || body[0] === '[') {
7684
+ result = body;
7685
+ }
7686
+ // 'a=1&b=2' => try to parse as query
7687
+ const arr = body.split('&');
7688
+ if (arr.length === 1) {
7689
+ // not a query, parse as original string
7690
+ result = body;
7691
+ }
7692
+ else {
7693
+ // 'a=1&b=2&c' => parse as query
7694
+ result = arr.join(',');
7695
+ }
7696
+ }
7697
+ else if (isIterable(body)) {
7698
+ // FormData or URLSearchParams or Array
7699
+ const arr = [];
7700
+ for (const [key, value] of body) {
7701
+ arr.push(`${key}=${typeof value === 'string' ? value : '[object Object]'}`);
7702
+ }
7703
+ result = arr.join(',');
7704
+ }
7705
+ else if (body instanceof Blob ||
7706
+ body instanceof ReadableStream ||
7707
+ body instanceof ArrayBuffer) {
7708
+ result = 'byte data';
8877
7709
  }
8878
- // 'a=1&b=2' => try to parse as query
8879
- const arr = body.split('&');
8880
- if (arr.length === 1) {
8881
- // not a query, parse as original string
7710
+ else if (isPureObject(body)) {
7711
+ // overriding ArrayBufferView which is not convertable to string
8882
7712
  result = body;
8883
7713
  }
8884
7714
  else {
8885
- // 'a=1&b=2&c' => parse as query
8886
- result = arr.join(',');
8887
- }
8888
- }
8889
- else if (isIterable(body)) {
8890
- // FormData or URLSearchParams or Array
8891
- const arr = [];
8892
- for (const [key, value] of body) {
8893
- arr.push(`${key}=${typeof value === 'string' ? value : '[object Object]'}`);
7715
+ result = `can't parse body ${typeof body}`;
8894
7716
  }
8895
- result = arr.join(',');
8896
- }
8897
- else if (body instanceof Blob ||
8898
- body instanceof ReadableStream ||
8899
- body instanceof ArrayBuffer) {
8900
- result = 'byte data';
7717
+ return result;
8901
7718
  }
8902
- else if (isPureObject(body)) {
8903
- // overriding ArrayBufferView which is not convertable to string
8904
- result = body;
7719
+ catch (_) {
7720
+ return "can't parse body";
8905
7721
  }
8906
- else {
8907
- result = `can't parse body ${typeof body}`;
8908
- }
8909
- return result;
8910
7722
  };
8911
7723
  const genGetDataByUrl = (url, getData = {}) => {
8912
7724
  if (!isPureObject(getData)) {
@@ -9043,15 +7855,6 @@ class BeaconProxy {
9043
7855
  }
9044
7856
  }
9045
7857
 
9046
- var RequestState;
9047
- (function (RequestState) {
9048
- RequestState[RequestState["UNSENT"] = 0] = "UNSENT";
9049
- RequestState[RequestState["OPENED"] = 1] = "OPENED";
9050
- RequestState[RequestState["HEADERS_RECEIVED"] = 2] = "HEADERS_RECEIVED";
9051
- RequestState[RequestState["LOADING"] = 3] = "LOADING";
9052
- RequestState[RequestState["DONE"] = 4] = "DONE";
9053
- })(RequestState || (RequestState = {}));
9054
-
9055
7858
  /**
9056
7859
  * I took inspiration in few stack exchange posts
9057
7860
  * and Tencent vConsole library (MIT)
@@ -9063,19 +7866,19 @@ class ResponseProxyHandler {
9063
7866
  constructor(resp, item) {
9064
7867
  this.resp = resp;
9065
7868
  this.item = item;
9066
- this.mockReader();
9067
7869
  }
9068
7870
  set(target, key, value) {
9069
7871
  return Reflect.set(target, key, value);
9070
7872
  }
9071
7873
  get(target, key) {
9072
7874
  const value = Reflect.get(target, key);
7875
+ if (key === "arrayBuffer" || key === "blob") {
7876
+ return typeof value === "function" ? value.bind(target) : value;
7877
+ }
9073
7878
  switch (key) {
9074
- case 'arrayBuffer':
9075
- case 'blob':
9076
- case 'formData':
9077
- case 'json':
9078
- case 'text':
7879
+ case "formData":
7880
+ case "json":
7881
+ case "text":
9079
7882
  return () => {
9080
7883
  this.item.responseType = key.toLowerCase();
9081
7884
  // @ts-ignore
@@ -9085,74 +7888,13 @@ class ResponseProxyHandler {
9085
7888
  });
9086
7889
  };
9087
7890
  }
9088
- if (typeof value === 'function') {
7891
+ if (typeof value === "function") {
9089
7892
  return value.bind(target);
9090
7893
  }
9091
7894
  else {
9092
7895
  return value;
9093
7896
  }
9094
7897
  }
9095
- mockReader() {
9096
- let readerReceivedValue;
9097
- if (!this.resp.body) {
9098
- // some browsers do not return `body` in some cases, like `OPTIONS` method
9099
- return;
9100
- }
9101
- if (typeof this.resp.body.getReader !== 'function') {
9102
- return;
9103
- }
9104
- const clonedResp = this.resp.clone();
9105
- const _getReader = clonedResp.body.getReader;
9106
- // @ts-ignore
9107
- clonedResp.body.getReader = () => {
9108
- const reader = _getReader.apply(this.resp.body);
9109
- // when readyState is already 4,
9110
- // it's not a chunked stream, or it had already been read.
9111
- // so should not update status.
9112
- if (this.item.readyState === RequestState.DONE) {
9113
- return reader;
9114
- }
9115
- const _read = reader.read;
9116
- const _cancel = reader.cancel;
9117
- this.item.responseType = 'arraybuffer';
9118
- // @ts-ignore
9119
- reader.read = () => {
9120
- return _read.apply(reader).then((result) => {
9121
- if (!readerReceivedValue) {
9122
- // @ts-ignore
9123
- readerReceivedValue = new Uint8Array(result.value);
9124
- }
9125
- else {
9126
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
9127
- const newValue = new Uint8Array(readerReceivedValue.length + result.value.length);
9128
- newValue.set(readerReceivedValue);
9129
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
9130
- newValue.set(result.value, readerReceivedValue.length);
9131
- readerReceivedValue = newValue;
9132
- }
9133
- this.item.endTime = performance.now();
9134
- this.item.duration = this.item.endTime - (this.item.startTime || this.item.endTime);
9135
- this.item.readyState = result.done ? 4 : 3;
9136
- this.item.statusText = result.done ? String(this.item.status) : 'Loading';
9137
- this.item.responseSize = readerReceivedValue.length;
9138
- this.item.responseSizeText = formatByteSize(this.item.responseSize);
9139
- if (result.done) {
9140
- this.item.response = getStringResponseByType(this.item.responseType, readerReceivedValue);
9141
- }
9142
- return result;
9143
- });
9144
- };
9145
- reader.cancel = (...args) => {
9146
- this.item.cancelState = 2;
9147
- this.item.statusText = 'Cancel';
9148
- this.item.endTime = performance.now();
9149
- this.item.duration = this.item.endTime - (this.item.startTime || this.item.endTime);
9150
- this.item.response = getStringResponseByType(this.item.responseType, readerReceivedValue);
9151
- return _cancel.apply(reader, args);
9152
- };
9153
- return reader;
9154
- };
9155
- }
9156
7898
  }
9157
7899
  class FetchProxyHandler {
9158
7900
  constructor(ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl, tokenUrlMatcher) {
@@ -9164,14 +7906,15 @@ class FetchProxyHandler {
9164
7906
  this.tokenUrlMatcher = tokenUrlMatcher;
9165
7907
  }
9166
7908
  apply(target, _, argsList) {
7909
+ var _a;
9167
7910
  const input = argsList[0];
9168
7911
  const init = argsList[1];
9169
7912
  if (!input ||
9170
7913
  // @ts-ignore
9171
- (typeof input !== 'string' && !(input === null || input === void 0 ? void 0 : input.url))) {
7914
+ (typeof input !== "string" && !(input === null || input === void 0 ? void 0 : input.url))) {
9172
7915
  return target.apply(window, argsList);
9173
7916
  }
9174
- const isORUrl = input instanceof URL || typeof input === 'string'
7917
+ const isORUrl = input instanceof URL || typeof input === "string"
9175
7918
  ? this.isServiceUrl(String(input))
9176
7919
  : this.isServiceUrl(String(input.url));
9177
7920
  if (isORUrl) {
@@ -9179,6 +7922,31 @@ class FetchProxyHandler {
9179
7922
  }
9180
7923
  const item = new NetworkMessage(this.ignoredHeaders, this.setSessionTokenHeader, this.sanitize);
9181
7924
  this.beforeFetch(item, input, init);
7925
+ const signal = (argsList[0] instanceof Request ? argsList[0].signal : undefined) ||
7926
+ ((_a = argsList[1]) === null || _a === void 0 ? void 0 : _a.signal);
7927
+ // guard to avoid double-send
7928
+ let abortedNotified = false;
7929
+ const notifyAbort = () => {
7930
+ if (abortedNotified)
7931
+ return;
7932
+ abortedNotified = true;
7933
+ item.endTime = performance.now();
7934
+ item.duration = item.endTime - (item.startTime || item.endTime);
7935
+ item.status = 0;
7936
+ item.statusText = "Aborted";
7937
+ item.readyState = 0;
7938
+ const msg = item.getMessage();
7939
+ if (msg)
7940
+ this.sendMessage(msg);
7941
+ };
7942
+ if (signal) {
7943
+ if (signal.aborted) {
7944
+ notifyAbort();
7945
+ }
7946
+ else {
7947
+ signal.addEventListener("abort", notifyAbort, { once: true });
7948
+ }
7949
+ }
9182
7950
  this.setSessionTokenHeader((name, value) => {
9183
7951
  if (this.tokenUrlMatcher !== undefined) {
9184
7952
  if (!this.tokenUrlMatcher(item.url)) {
@@ -9207,42 +7975,53 @@ class FetchProxyHandler {
9207
7975
  }
9208
7976
  });
9209
7977
  return target.apply(window, argsList)
9210
- .then(this.afterFetch(item))
7978
+ .then(this.afterFetch(item, () => {
7979
+ abortedNotified = true;
7980
+ }))
9211
7981
  .catch((e) => {
9212
- // mock finally
9213
7982
  item.endTime = performance.now();
9214
7983
  item.duration = item.endTime - (item.startTime || item.endTime);
7984
+ if (e && e.name === "AbortError") {
7985
+ item.status = 0;
7986
+ item.statusText = "Aborted";
7987
+ item.readyState = 0;
7988
+ if (!abortedNotified) {
7989
+ const msg = item.getMessage();
7990
+ if (msg)
7991
+ this.sendMessage(msg);
7992
+ }
7993
+ }
9215
7994
  throw e;
9216
7995
  });
9217
7996
  }
9218
7997
  beforeFetch(item, input, init) {
9219
- let url, method = 'GET', requestHeader = {};
7998
+ let url, method = "GET", requestHeader = {};
9220
7999
  // handle `input` content
9221
- if (typeof input === 'string') {
8000
+ if (typeof input === "string") {
9222
8001
  // when `input` is a string
9223
- method = (init === null || init === void 0 ? void 0 : init.method) || 'GET';
8002
+ method = (init === null || init === void 0 ? void 0 : init.method) || "GET";
9224
8003
  url = getURL(input);
9225
8004
  requestHeader = (init === null || init === void 0 ? void 0 : init.headers) || {};
9226
8005
  }
9227
8006
  else {
9228
8007
  // when `input` is a `Request` object
9229
- method = input.method || 'GET';
8008
+ method = input.method || "GET";
9230
8009
  url = getURL(input.url);
9231
8010
  requestHeader = input.headers;
9232
8011
  }
9233
8012
  item.method = method;
9234
- item.requestType = 'fetch';
8013
+ item.requestType = "fetch";
9235
8014
  item.requestHeader = requestHeader;
9236
8015
  item.url = url.toString();
9237
- item.name = (url.pathname.split('/').pop() || '') + url.search;
8016
+ item.name = (url.pathname.split("/").pop() || "") + url.search;
9238
8017
  item.status = 0;
9239
- item.statusText = 'Pending';
8018
+ item.statusText = "Pending";
9240
8019
  item.readyState = 1;
9241
8020
  if (!item.startTime) {
9242
8021
  // UNSENT
9243
8022
  item.startTime = performance.now();
9244
8023
  }
9245
- if (Object.prototype.toString.call(requestHeader) === '[object Headers]') {
8024
+ if (Object.prototype.toString.call(requestHeader) === "[object Headers]") {
9246
8025
  item.requestHeader = {};
9247
8026
  for (const [key, value] of requestHeader) {
9248
8027
  item.requestHeader[key] = value;
@@ -9263,8 +8042,10 @@ class FetchProxyHandler {
9263
8042
  item.requestData = genStringBody(init.body);
9264
8043
  }
9265
8044
  }
9266
- afterFetch(item) {
8045
+ afterFetch(item, onResolved) {
9267
8046
  return (resp) => {
8047
+ if (onResolved)
8048
+ onResolved === null || onResolved === void 0 ? void 0 : onResolved();
9268
8049
  item.endTime = performance.now();
9269
8050
  item.duration = item.endTime - (item.startTime || item.endTime);
9270
8051
  item.status = resp.status;
@@ -9273,7 +8054,8 @@ class FetchProxyHandler {
9273
8054
  item.header = {};
9274
8055
  for (const [key, value] of resp.headers) {
9275
8056
  item.header[key] = value;
9276
- isChunked = value.toLowerCase().indexOf('chunked') > -1 ? true : isChunked;
8057
+ isChunked =
8058
+ value.toLowerCase().indexOf("chunked") > -1 ? true : isChunked;
9277
8059
  }
9278
8060
  if (isChunked) {
9279
8061
  // when `transfer-encoding` is chunked, the response is a stream which is under loading,
@@ -9288,7 +8070,9 @@ class FetchProxyHandler {
9288
8070
  this.handleResponseBody(resp.clone(), item)
9289
8071
  .then((responseValue) => {
9290
8072
  item.responseSize =
9291
- typeof responseValue === 'string' ? responseValue.length : responseValue.byteLength;
8073
+ typeof responseValue === "string"
8074
+ ? responseValue.length
8075
+ : responseValue.byteLength;
9292
8076
  item.responseSizeText = formatByteSize(item.responseSize);
9293
8077
  item.response = getStringResponseByType(item.responseType, responseValue);
9294
8078
  const msg = item.getMessage();
@@ -9297,28 +8081,40 @@ class FetchProxyHandler {
9297
8081
  }
9298
8082
  })
9299
8083
  .catch((e) => {
9300
- if (e.name !== 'AbortError') {
8084
+ if (e.name === "AbortError") {
8085
+ item.status = 0;
8086
+ item.statusText = "Aborted";
8087
+ item.readyState = 0;
8088
+ const msg = item.getMessage();
8089
+ if (msg)
8090
+ this.sendMessage(msg);
8091
+ }
8092
+ else {
9301
8093
  throw e;
9302
8094
  }
9303
8095
  });
9304
8096
  }
9305
- return new Proxy(resp, new ResponseProxyHandler(resp, item));
8097
+ const ct = (resp.headers.get("content-type") || "").toLowerCase();
8098
+ const isTextLike = ct.includes("application/json") || ct.startsWith("text/");
8099
+ return isTextLike
8100
+ ? new Proxy(resp, new ResponseProxyHandler(resp, item))
8101
+ : resp;
9306
8102
  };
9307
8103
  }
9308
8104
  handleResponseBody(resp, item) {
9309
8105
  // parse response body by Content-Type
9310
- const contentType = resp.headers.get('content-type');
9311
- if (contentType && contentType.includes('application/json')) {
9312
- item.responseType = 'json';
8106
+ const contentType = resp.headers.get("content-type");
8107
+ if (contentType && contentType.includes("application/json")) {
8108
+ item.responseType = "json";
9313
8109
  return resp.text();
9314
8110
  }
9315
8111
  else if (contentType &&
9316
- (contentType.includes('text/html') || contentType.includes('text/plain'))) {
9317
- item.responseType = 'text';
8112
+ (contentType.includes("text/html") || contentType.includes("text/plain"))) {
8113
+ item.responseType = "text";
9318
8114
  return resp.text();
9319
8115
  }
9320
8116
  else {
9321
- item.responseType = 'arraybuffer';
8117
+ item.responseType = "arraybuffer";
9322
8118
  return resp.arrayBuffer();
9323
8119
  }
9324
8120
  }
@@ -9329,6 +8125,15 @@ class FetchProxy {
9329
8125
  }
9330
8126
  }
9331
8127
 
8128
+ var RequestState;
8129
+ (function (RequestState) {
8130
+ RequestState[RequestState["UNSENT"] = 0] = "UNSENT";
8131
+ RequestState[RequestState["OPENED"] = 1] = "OPENED";
8132
+ RequestState[RequestState["HEADERS_RECEIVED"] = 2] = "HEADERS_RECEIVED";
8133
+ RequestState[RequestState["LOADING"] = 3] = "LOADING";
8134
+ RequestState[RequestState["DONE"] = 4] = "DONE";
8135
+ })(RequestState || (RequestState = {}));
8136
+
9332
8137
  /**
9333
8138
  * I took inspiration in few stack exchange posts
9334
8139
  * and Tencent vConsole library (MIT)
@@ -9355,28 +8160,29 @@ class XHRProxyHandler {
9355
8160
  this.onTimeout();
9356
8161
  };
9357
8162
  this.item = new NetworkMessage(ignoredHeaders, setSessionTokenHeader, sanitize);
9358
- this.item.requestType = 'xhr';
8163
+ this.item.requestType = "xhr";
9359
8164
  }
9360
8165
  get(target, key) {
9361
8166
  switch (key) {
9362
- case 'open':
8167
+ case "open":
9363
8168
  return this.getOpen(target);
9364
- case 'send':
8169
+ case "send":
9365
8170
  this.setSessionTokenHeader((name, value) => {
9366
8171
  if (this.tokenUrlMatcher !== undefined) {
9367
8172
  if (!this.tokenUrlMatcher(this.item.url)) {
9368
8173
  return;
9369
8174
  }
9370
8175
  }
9371
- target.setRequestHeader(name, value);
8176
+ if (target.readyState === 1)
8177
+ target.setRequestHeader(name, value);
9372
8178
  });
9373
8179
  return this.getSend(target);
9374
- case 'setRequestHeader':
8180
+ case "setRequestHeader":
9375
8181
  return this.getSetRequestHeader(target);
9376
8182
  default:
9377
8183
  // eslint-disable-next-line no-case-declarations
9378
8184
  const value = Reflect.get(target, key);
9379
- if (typeof value === 'function') {
8185
+ if (typeof value === "function") {
9380
8186
  return value.bind(target);
9381
8187
  }
9382
8188
  else {
@@ -9386,11 +8192,11 @@ class XHRProxyHandler {
9386
8192
  }
9387
8193
  set(target, key, value) {
9388
8194
  switch (key) {
9389
- case 'onreadystatechange':
8195
+ case "onreadystatechange":
9390
8196
  return this.setOnReadyStateChange(target, key, value);
9391
- case 'onabort':
8197
+ case "onabort":
9392
8198
  return this.setOnAbort(target, key, value);
9393
- case 'ontimeout':
8199
+ case "ontimeout":
9394
8200
  return this.setOnTimeout(target, key, value);
9395
8201
  // not tracked methods
9396
8202
  }
@@ -9404,9 +8210,12 @@ class XHRProxyHandler {
9404
8210
  this.item.endTime = performance.now();
9405
8211
  this.item.duration = this.item.endTime - this.item.startTime;
9406
8212
  this.updateItemByReadyState();
9407
- setTimeout(() => {
9408
- this.item.response = getStringResponseByType(this.item.responseType, this.item.response);
9409
- }, 0);
8213
+ const rt = this.item.responseType || "";
8214
+ if (rt === "" || rt === "text" || rt === "json") {
8215
+ setTimeout(() => {
8216
+ this.item.response = getStringResponseByType(rt, this.XMLReq.response);
8217
+ }, 0);
8218
+ }
9410
8219
  if (this.XMLReq.readyState === RequestState.DONE) {
9411
8220
  const msg = this.item.getMessage();
9412
8221
  if (msg) {
@@ -9416,7 +8225,7 @@ class XHRProxyHandler {
9416
8225
  }
9417
8226
  onAbort() {
9418
8227
  this.item.cancelState = 1;
9419
- this.item.statusText = 'Abort';
8228
+ this.item.statusText = "Abort";
9420
8229
  const msg = this.item.getMessage();
9421
8230
  if (msg) {
9422
8231
  this.sendMessage(msg);
@@ -9424,27 +8233,28 @@ class XHRProxyHandler {
9424
8233
  }
9425
8234
  onTimeout() {
9426
8235
  this.item.cancelState = 3;
9427
- this.item.statusText = 'Timeout';
8236
+ this.item.statusText = "Timeout";
9428
8237
  const msg = this.item.getMessage();
9429
8238
  if (msg) {
9430
8239
  this.sendMessage(msg);
9431
8240
  }
9432
8241
  }
9433
8242
  getOpen(target) {
9434
- const targetFunction = Reflect.get(target, 'open');
8243
+ const targetFunction = Reflect.get(target, "open");
9435
8244
  return (...args) => {
9436
8245
  var _a, _b, _c;
9437
8246
  const method = args[0];
9438
8247
  const url = args[1];
9439
- this.item.method = method ? method.toUpperCase() : 'GET';
9440
- this.item.url = ((_a = url.toString) === null || _a === void 0 ? void 0 : _a.call(url)) || '';
9441
- this.item.name = (_c = (_b = this.item.url) === null || _b === void 0 ? void 0 : _b.replace(new RegExp('/*$'), '').split('/').pop()) !== null && _c !== void 0 ? _c : '';
8248
+ this.item.method = method ? method.toUpperCase() : "GET";
8249
+ this.item.url = ((_a = url.toString) === null || _a === void 0 ? void 0 : _a.call(url)) || "";
8250
+ this.item.name =
8251
+ (_c = (_b = this.item.url) === null || _b === void 0 ? void 0 : _b.replace(new RegExp("/*$"), "").split("/").pop()) !== null && _c !== void 0 ? _c : "";
9442
8252
  this.item.getData = genGetDataByUrl(this.item.url, {});
9443
8253
  return targetFunction.apply(target, args);
9444
8254
  };
9445
8255
  }
9446
8256
  getSend(target) {
9447
- const targetFunction = Reflect.get(target, 'send');
8257
+ const targetFunction = Reflect.get(target, "send");
9448
8258
  return (...args) => {
9449
8259
  const data = args[0];
9450
8260
  this.item.requestData = genStringBody(data);
@@ -9452,7 +8262,7 @@ class XHRProxyHandler {
9452
8262
  };
9453
8263
  }
9454
8264
  getSetRequestHeader(target) {
9455
- const targetFunction = Reflect.get(target, 'setRequestHeader');
8265
+ const targetFunction = Reflect.get(target, "setRequestHeader");
9456
8266
  return (...args) => {
9457
8267
  if (!this.item.requestHeader) {
9458
8268
  this.item.requestHeader = {};
@@ -9488,34 +8298,44 @@ class XHRProxyHandler {
9488
8298
  case RequestState.UNSENT:
9489
8299
  case RequestState.OPENED:
9490
8300
  this.item.status = RequestState.UNSENT;
9491
- this.item.statusText = 'Pending';
8301
+ this.item.statusText = "Pending";
9492
8302
  if (!this.item.startTime) {
9493
8303
  this.item.startTime = performance.now();
9494
8304
  }
9495
8305
  break;
9496
8306
  case RequestState.HEADERS_RECEIVED:
9497
8307
  this.item.status = this.XMLReq.status;
9498
- this.item.statusText = 'Loading';
8308
+ this.item.statusText = "Loading";
9499
8309
  this.item.header = {};
9500
8310
  // eslint-disable-next-line no-case-declarations
9501
- const header = this.XMLReq.getAllResponseHeaders() || '', headerArr = header.split('\n');
8311
+ const header = this.XMLReq.getAllResponseHeaders() || "", headerArr = header.split("\n");
9502
8312
  // extract plain text to key-value format
9503
8313
  for (let i = 0; i < headerArr.length; i++) {
9504
8314
  const line = headerArr[i];
9505
8315
  if (!line) {
9506
8316
  continue;
9507
8317
  }
9508
- const arr = line.split(': ');
8318
+ const arr = line.split(": ");
9509
8319
  const key = arr[0];
9510
- this.item.header[key] = arr.slice(1).join(': ');
8320
+ this.item.header[key] = arr.slice(1).join(": ");
9511
8321
  }
9512
8322
  break;
9513
8323
  case RequestState.LOADING:
9514
8324
  this.item.status = this.XMLReq.status;
9515
- this.item.statusText = 'Loading';
9516
- if (!!this.XMLReq.response && this.XMLReq.response.length) {
9517
- this.item.responseSize = this.XMLReq.response.length;
9518
- this.item.responseSizeText = formatByteSize(this.item.responseSize);
8325
+ this.item.statusText = "Loading";
8326
+ const response = this.XMLReq.response;
8327
+ if (response) {
8328
+ const respSize = typeof response === "string"
8329
+ ? response.length
8330
+ : response instanceof ArrayBuffer
8331
+ ? response.byteLength
8332
+ : typeof Blob !== "undefined" && response instanceof Blob
8333
+ ? response.size
8334
+ : 0;
8335
+ if (respSize) {
8336
+ this.item.responseSize = respSize;
8337
+ this.item.responseSizeText = formatByteSize(this.item.responseSize);
8338
+ }
9519
8339
  }
9520
8340
  break;
9521
8341
  case RequestState.DONE:
@@ -9524,16 +8344,30 @@ class XHRProxyHandler {
9524
8344
  // show status code when request completed
9525
8345
  this.item.statusText = String(this.item.status);
9526
8346
  this.item.endTime = performance.now();
9527
- this.item.duration = this.item.endTime - (this.item.startTime || this.item.endTime);
9528
- this.item.response = this.XMLReq.response;
9529
- if (!!this.XMLReq.response && this.XMLReq.response.length) {
9530
- this.item.responseSize = this.XMLReq.response.length;
9531
- this.item.responseSizeText = formatByteSize(this.item.responseSize);
8347
+ this.item.duration =
8348
+ this.item.endTime - (this.item.startTime || this.item.endTime);
8349
+ const resp = this.XMLReq.response;
8350
+ const respType = this.XMLReq.responseType || "";
8351
+ if (respType === "" || respType === "text" || respType === "json") {
8352
+ this.item.response = resp;
8353
+ }
8354
+ if (resp) {
8355
+ const respSize = typeof resp === "string"
8356
+ ? resp.length
8357
+ : resp instanceof ArrayBuffer
8358
+ ? resp.byteLength
8359
+ : typeof Blob !== "undefined" && resp instanceof Blob
8360
+ ? resp.size
8361
+ : 0;
8362
+ if (respSize) {
8363
+ this.item.responseSize = respSize;
8364
+ this.item.responseSizeText = formatByteSize(respSize);
8365
+ }
9532
8366
  }
9533
8367
  break;
9534
8368
  default:
9535
8369
  this.item.status = this.XMLReq.status;
9536
- this.item.statusText = 'Unknown';
8370
+ this.item.statusText = "Unknown";
9537
8371
  break;
9538
8372
  }
9539
8373
  }
@@ -9549,10 +8383,17 @@ class XHRProxy {
9549
8383
  }
9550
8384
  }
9551
8385
 
9552
- const getWarning = (api) => {
8386
+ const warn = (api) => {
9553
8387
  const str = `Openreplay: Can't find ${api} in global context.`;
9554
8388
  console.warn(str);
9555
8389
  };
8390
+ const OR_FLAG = Symbol('OpenReplayProxyOriginal');
8391
+ const isProxied = (fn) => !!fn && fn[OR_FLAG] !== undefined;
8392
+ const unwrap = (fn) => isProxied(fn) ? fn[OR_FLAG] : fn;
8393
+ const wrap = (proxy, orig) => {
8394
+ proxy[OR_FLAG] = orig;
8395
+ return proxy;
8396
+ };
9556
8397
  /**
9557
8398
  * Creates network proxies for XMLHttpRequest, fetch, and sendBeacon to intercept and monitor network requests and
9558
8399
  * responses.
@@ -9586,26 +8427,24 @@ function createNetworkProxy(context, ignoredHeaders, setSessionTokenHeader, sani
9586
8427
  if (!context)
9587
8428
  return;
9588
8429
  if (modules.xhr) {
9589
- if (context.XMLHttpRequest) {
9590
- context.XMLHttpRequest = XHRProxy.create(ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl, tokenUrlMatcher);
9591
- }
8430
+ const original = unwrap(context.XMLHttpRequest);
8431
+ if (!original)
8432
+ warn('XMLHttpRequest');
9592
8433
  else {
9593
- getWarning("XMLHttpRequest");
8434
+ context.XMLHttpRequest = wrap(XHRProxy.create(ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl, tokenUrlMatcher), original);
9594
8435
  }
9595
8436
  }
9596
8437
  if (modules.fetch) {
9597
- if (context.fetch) {
9598
- context.fetch = FetchProxy.create(ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl, tokenUrlMatcher);
9599
- }
8438
+ const original = unwrap(context.fetch);
8439
+ if (!original)
8440
+ warn('fetch');
9600
8441
  else {
9601
- getWarning("fetch");
8442
+ context.fetch = wrap(FetchProxy.create(ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl, tokenUrlMatcher), original);
9602
8443
  }
9603
8444
  }
9604
- if (modules.beacon) {
9605
- if ((_a = context.navigator) === null || _a === void 0 ? void 0 : _a.sendBeacon) {
9606
- const origBeacon = context.navigator.sendBeacon;
9607
- context.navigator.sendBeacon = BeaconProxy.create(origBeacon, ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl);
9608
- }
8445
+ if (modules.beacon && ((_a = context.navigator) === null || _a === void 0 ? void 0 : _a.sendBeacon)) {
8446
+ const original = unwrap(context.navigator.sendBeacon);
8447
+ context.navigator.sendBeacon = wrap(BeaconProxy.create(original, ignoredHeaders, setSessionTokenHeader, sanitize, sendMessage, isServiceUrl), original);
9609
8448
  }
9610
8449
  }
9611
8450
 
@@ -9922,6 +8761,57 @@ function LongAnimationTask (app, opts) {
9922
8761
  });
9923
8762
  }
9924
8763
 
8764
+ const toIgnore = ["composite", "computedOffset", "easing", "offset"];
8765
+ function webAnimations(app, options = {}) {
8766
+ const { allElements = false } = options;
8767
+ let listening = new WeakSet();
8768
+ let handled = new WeakSet();
8769
+ function wire(anim, el, nodeId) {
8770
+ if (handled.has(anim))
8771
+ return;
8772
+ handled.add(anim);
8773
+ anim.addEventListener('finish', () => {
8774
+ const lastKF = anim.effect.getKeyframes().at(-1);
8775
+ const computedStyle = getComputedStyle(el);
8776
+ const keys = Object.keys(lastKF).filter((p) => !toIgnore.includes(p));
8777
+ // @ts-ignore
8778
+ const finalStyle = {};
8779
+ keys.forEach((key) => {
8780
+ finalStyle[key] = computedStyle[key];
8781
+ });
8782
+ app.send(NodeAnimationResult(nodeId, JSON.stringify(finalStyle)));
8783
+ }, { once: true });
8784
+ }
8785
+ function scanElement(el, nodeId) {
8786
+ el.getAnimations({ subtree: false }).forEach((anim) => wire(anim, el, nodeId));
8787
+ }
8788
+ app.nodes.attachNodeCallback((node) => {
8789
+ if ((allElements || node.nodeName.includes('-')) && 'getAnimations' in node) {
8790
+ const animations = node.getAnimations({ subtree: false });
8791
+ const id = app.nodes.getID(node);
8792
+ if (animations.length > 0 && !listening.has(node) && id) {
8793
+ listening.add(node);
8794
+ scanElement(node, id);
8795
+ node.addEventListener('animationstart', () => scanElement(node, id));
8796
+ }
8797
+ }
8798
+ });
8799
+ const origAnimate = Element.prototype.animate;
8800
+ Element.prototype.animate = function (...args) {
8801
+ const anim = origAnimate.apply(this, args);
8802
+ const id = app.nodes.getID(this);
8803
+ if (!id)
8804
+ return anim;
8805
+ wire(anim, this, id);
8806
+ return anim;
8807
+ };
8808
+ app.attachStopCallback(() => {
8809
+ Element.prototype.animate = origAnimate; // Restore original animate method
8810
+ listening = new WeakSet();
8811
+ handled = new WeakSet();
8812
+ });
8813
+ }
8814
+
9925
8815
  const Messages = _Messages;
9926
8816
  const DOCS_SETUP = '/en/sdk';
9927
8817
  function processOptions(obj) {
@@ -9973,7 +8863,7 @@ class API {
9973
8863
  this.signalStartIssue = (reason, missingApi) => {
9974
8864
  const doNotTrack = this.checkDoNotTrack();
9975
8865
  console.log("Tracker couldn't start due to:", JSON.stringify({
9976
- trackerVersion: '17.0.0-beta.0',
8866
+ trackerVersion: '17.0.1',
9977
8867
  projectKey: this.options.projectKey,
9978
8868
  doNotTrack,
9979
8869
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,
@@ -10067,7 +8957,7 @@ class API {
10067
8957
  this.app = app;
10068
8958
  if (!this.crossdomainMode) {
10069
8959
  // no need to send iframe viewport data since its a node for us
10070
- Viewport(app);
8960
+ Viewport(app, options.urls);
10071
8961
  // calculated in main window
10072
8962
  Connection(app);
10073
8963
  // while we can calculate it here, trying to compute it for all parts is hard
@@ -10093,10 +8983,8 @@ class API {
10093
8983
  Network(app, options.network);
10094
8984
  }
10095
8985
  selection(app);
8986
+ webAnimations(app, options.webAnimations);
10096
8987
  window.__OPENREPLAY__ = this;
10097
- if (options.flags && options.flags.onFlagsLoad) {
10098
- this.onFlagsLoad(options.flags.onFlagsLoad);
10099
- }
10100
8988
  const wOpen = window.open;
10101
8989
  if (options.autoResetOnWindowOpen || options.resetTabOnWindowOpen) {
10102
8990
  app.attachStartCallback(() => {
@@ -10119,24 +9007,6 @@ class API {
10119
9007
  });
10120
9008
  }
10121
9009
  }
10122
- isFlagEnabled(flagName) {
10123
- return this.featureFlags.isFlagEnabled(flagName);
10124
- }
10125
- onFlagsLoad(callback) {
10126
- this.app?.featureFlags.onFlagsLoad(callback);
10127
- }
10128
- clearPersistFlags() {
10129
- this.app?.featureFlags.clearPersistFlags();
10130
- }
10131
- reloadFlags() {
10132
- return this.app?.featureFlags.reloadFlags();
10133
- }
10134
- getFeatureFlag(flagName) {
10135
- return this.app?.featureFlags.getFeatureFlag(flagName);
10136
- }
10137
- getAllFeatureFlags() {
10138
- return this.app?.featureFlags.flags;
10139
- }
10140
9010
  use(fn) {
10141
9011
  return fn(this.app, this.options);
10142
9012
  }
@@ -10267,12 +9137,6 @@ class API {
10267
9137
  }
10268
9138
  return this.app.getTabId();
10269
9139
  }
10270
- getUxId() {
10271
- if (this.app === null) {
10272
- return null;
10273
- }
10274
- return this.app.getUxtId();
10275
- }
10276
9140
  sessionID() {
10277
9141
  deprecationWarn("'sessionID' method", "'getSessionID' method", '/');
10278
9142
  return this.getSessionID();
@@ -10316,12 +9180,20 @@ class API {
10316
9180
  return this.issue(key, payload);
10317
9181
  }
10318
9182
  else {
9183
+ if (!payload || typeof payload === 'string') {
9184
+ return this.app.send(CustomEvent(key, payload));
9185
+ }
10319
9186
  try {
9187
+ if ('or_timestamp' in payload) {
9188
+ const startTs = this.getSessionInfo()?.timestamp ?? 0;
9189
+ const diff = payload.or_timestamp - startTs;
9190
+ if (diff < 0) {
9191
+ console.error(`OpenReplay: event ${key} has or_timestamp (${payload.or_timestamp}) before session start (${startTs}). It will be ignored.`);
9192
+ }
9193
+ }
10320
9194
  payload = JSON.stringify(payload);
10321
9195
  }
10322
- catch (e) {
10323
- return;
10324
- }
9196
+ catch (_) { }
10325
9197
  this.app.send(CustomEvent(key, payload));
10326
9198
  }
10327
9199
  }
@@ -10445,42 +9317,6 @@ class TrackerSingleton {
10445
9317
  }
10446
9318
  this.instance.handleError(e, metadata);
10447
9319
  }
10448
- isFlagEnabled(flagName) {
10449
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10450
- return false;
10451
- }
10452
- return this.instance.isFlagEnabled(flagName);
10453
- }
10454
- onFlagsLoad(...args) {
10455
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10456
- return;
10457
- }
10458
- this.instance.onFlagsLoad(...args);
10459
- }
10460
- clearPersistFlags() {
10461
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10462
- return;
10463
- }
10464
- this.instance.clearPersistFlags();
10465
- }
10466
- reloadFlags() {
10467
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10468
- return;
10469
- }
10470
- return this.instance.reloadFlags();
10471
- }
10472
- getFeatureFlag(flagName) {
10473
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10474
- return;
10475
- }
10476
- return this.instance.getFeatureFlag(flagName);
10477
- }
10478
- getAllFeatureFlags() {
10479
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10480
- return;
10481
- }
10482
- return this.instance.getAllFeatureFlags();
10483
- }
10484
9320
  restartCanvasTracking() {
10485
9321
  if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10486
9322
  return;
@@ -10603,12 +9439,6 @@ class TrackerSingleton {
10603
9439
  }
10604
9440
  return this.instance.getTabId();
10605
9441
  }
10606
- getUxId() {
10607
- if (!IN_BROWSER || !this.ensureConfigured() || !this.instance) {
10608
- return null;
10609
- }
10610
- return this.instance.getUxId();
10611
- }
10612
9442
  }
10613
9443
  const tracker = new TrackerSingleton();
10614
9444