@openreplay/tracker 15.0.2 → 15.0.5-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  export declare function isNode(sth: any): sth is Node;
2
2
  export declare function isSVGElement(node: Element): node is SVGElement;
3
+ export declare function isUseElement(node: Element): node is SVGUseElement;
3
4
  export declare function isElementNode(node: Node): node is Element;
4
5
  export declare function isCommentNode(node: Node): node is Comment;
5
6
  export declare function isTextNode(node: Node): node is Text;
package/dist/lib/index.js CHANGED
@@ -2326,7 +2326,7 @@ const containerStyle = {
2326
2326
  alignItems: 'center',
2327
2327
  padding: '1.5rem',
2328
2328
  borderRadius: '2px',
2329
- border: '1px solid #D9D9D9',
2329
+ border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2330
2330
  background: '#FFF',
2331
2331
  width: '22rem',
2332
2332
  };
@@ -2338,7 +2338,7 @@ const containerWidgetStyle = {
2338
2338
  padding: 'unset',
2339
2339
  fontFamily: `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"`,
2340
2340
  'border-radius': '2px',
2341
- border: '1px solid #D9D9D9',
2341
+ border: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2342
2342
  background: 'rgba(255, 255, 255, 0.75)',
2343
2343
  width: '22rem',
2344
2344
  };
@@ -2420,7 +2420,7 @@ const descriptionWidgetStyle = {
2420
2420
  boxSizing: 'border-box',
2421
2421
  display: 'block',
2422
2422
  width: '100%',
2423
- borderBottom: '1px solid #D9D9D9',
2423
+ borderBottom: '1px solid rgb(255 255 255 / var(--tw-bg-opacity, 1))',
2424
2424
  background: '#FFF',
2425
2425
  padding: '0.65rem',
2426
2426
  alignSelf: 'stretch',
@@ -3293,7 +3293,10 @@ function isNode(sth) {
3293
3293
  return !!sth && sth.nodeType != null;
3294
3294
  }
3295
3295
  function isSVGElement(node) {
3296
- return node.namespaceURI === 'http://www.w3.org/2000/svg';
3296
+ return (node.namespaceURI === 'http://www.w3.org/2000/svg' || node.localName === 'svg');
3297
+ }
3298
+ function isUseElement(node) {
3299
+ return node.localName === 'use';
3297
3300
  }
3298
3301
  function isElementNode(node) {
3299
3302
  return node.nodeType === Node.ELEMENT_NODE;
@@ -3723,6 +3726,48 @@ class Nodes {
3723
3726
  }
3724
3727
  }
3725
3728
 
3729
+ const iconCache = {};
3730
+ const domParser = new DOMParser();
3731
+ async function parseUseEl(useElement, mode) {
3732
+ try {
3733
+ const href = useElement.getAttribute('xlink:href') || useElement.getAttribute('href');
3734
+ if (!href) {
3735
+ console.debug('Openreplay: xlink:href or href not found on <use>.');
3736
+ return;
3737
+ }
3738
+ const [url, symbolId] = href.split('#');
3739
+ if (!url || !symbolId) {
3740
+ console.debug('Openreplay: Invalid xlink:href or href found on <use>.');
3741
+ return;
3742
+ }
3743
+ if (iconCache[symbolId]) {
3744
+ return iconCache[symbolId];
3745
+ }
3746
+ const response = await fetch(url);
3747
+ const svgText = await response.text();
3748
+ const svgDoc = domParser.parseFromString(svgText, 'image/svg+xml');
3749
+ const symbol = svgDoc.getElementById(symbolId);
3750
+ if (!symbol) {
3751
+ console.debug('Openreplay: Symbol not found in SVG.');
3752
+ return;
3753
+ }
3754
+ if (mode === 'inline') ;
3755
+ if (mode === 'svgtext') {
3756
+ const inlineSvg = `
3757
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="${symbol.getAttribute('viewBox') || '0 0 24 24'}">
3758
+ ${symbol.innerHTML}
3759
+ </svg>
3760
+ `.trim();
3761
+ iconCache[symbolId] = inlineSvg;
3762
+ return inlineSvg;
3763
+ }
3764
+ if (mode === 'dataurl') ;
3765
+ console.debug(`Openreplay: Unknown mode: ${mode}. Use "inline" or "dataurl".`);
3766
+ }
3767
+ catch (error) {
3768
+ console.error('Openreplay: Error processing <use> element:', error);
3769
+ }
3770
+ }
3726
3771
  function isIgnored(node) {
3727
3772
  if (isCommentNode(node)) {
3728
3773
  return true;
@@ -3861,7 +3906,19 @@ class Observer {
3861
3906
  if (value === null) {
3862
3907
  this.app.send(RemoveNodeAttribute(id, name));
3863
3908
  }
3864
- else if (name === 'href') {
3909
+ if (isUseElement(node) && name === 'href') {
3910
+ parseUseEl(node, 'svgtext')
3911
+ .then((svgData) => {
3912
+ if (svgData) {
3913
+ this.app.send(SetNodeAttribute(id, name, `_$OPENREPLAY_SPRITE$_${svgData}`));
3914
+ }
3915
+ })
3916
+ .catch((e) => {
3917
+ console.error('Openreplay: Error parsing <use> element:', e);
3918
+ });
3919
+ return;
3920
+ }
3921
+ if (name === 'href') {
3865
3922
  if (value.length > 1e5) {
3866
3923
  value = '';
3867
3924
  }
@@ -4644,7 +4701,7 @@ class App {
4644
4701
  this.stopCallbacks = [];
4645
4702
  this.commitCallbacks = [];
4646
4703
  this.activityState = ActivityState.NotActive;
4647
- this.version = '15.0.2'; // TODO: version compatability check inside each plugin.
4704
+ this.version = '15.0.5-beta.0'; // TODO: version compatability check inside each plugin.
4648
4705
  this.socketMode = false;
4649
4706
  this.compressionThreshold = 24 * 1000;
4650
4707
  this.bc = null;
@@ -4990,6 +5047,9 @@ class App {
4990
5047
  const host = location.hostname.split('.').slice(-2).join('_');
4991
5048
  this.bc = new BroadcastChannel(`rick_${host}`);
4992
5049
  }
5050
+ else if (this.options.forceSingleTab) {
5051
+ this.allowAppStart();
5052
+ }
4993
5053
  this.revID = this.options.revID;
4994
5054
  this.localStorage = this.options.localStorage ?? window.localStorage;
4995
5055
  this.sessionStorage = this.options.sessionStorage ?? window.sessionStorage;
@@ -6674,122 +6734,170 @@ function Input (app, opts) {
6674
6734
  // License: MIT
6675
6735
  // Author: Anton Medvedev <anton@medv.io>
6676
6736
  // Source: https://github.com/antonmedv/finder
6677
- let config;
6678
- let rootDocument;
6679
- let start;
6737
+ const acceptedAttrNames = new Set(['role', 'name', 'aria-label', 'rel', 'href']);
6738
+ /** Check if attribute name and value are word-like. */
6739
+ function attr(name, value) {
6740
+ let nameIsOk = acceptedAttrNames.has(name);
6741
+ nameIsOk ||= name.startsWith('data-') && wordLike(name);
6742
+ let valueIsOk = wordLike(value) && value.length < 100;
6743
+ valueIsOk ||= value.startsWith('#') && wordLike(value.slice(1));
6744
+ return nameIsOk && valueIsOk;
6745
+ }
6746
+ /** Check if id name is word-like. */
6747
+ function idName(name) {
6748
+ return wordLike(name);
6749
+ }
6750
+ /** Check if class name is word-like. */
6751
+ function className(name) {
6752
+ return wordLike(name);
6753
+ }
6754
+ /** Check if tag name is word-like. */
6755
+ function tagName(name) {
6756
+ return true;
6757
+ }
6758
+ /** Finds unique CSS selectors for the given element. */
6680
6759
  function finder(input, options) {
6681
- start = new Date();
6682
6760
  if (input.nodeType !== Node.ELEMENT_NODE) {
6683
6761
  throw new Error(`Can't generate CSS selector for non-element node type.`);
6684
6762
  }
6685
- if ('html' === input.tagName.toLowerCase()) {
6763
+ if (input.tagName.toLowerCase() === 'html') {
6686
6764
  return 'html';
6687
6765
  }
6688
6766
  const defaults = {
6689
6767
  root: document.body,
6690
- idName: (name) => true,
6691
- className: (name) => true,
6692
- tagName: (name) => true,
6693
- attr: (name, value) => false,
6694
- seedMinLength: 1,
6768
+ idName: idName,
6769
+ className: className,
6770
+ tagName: tagName,
6771
+ attr: attr,
6772
+ timeoutMs: 1000,
6773
+ seedMinLength: 3,
6695
6774
  optimizedMinLength: 2,
6696
- threshold: 1000,
6697
- maxNumberOfTries: 10000,
6698
- timeoutMs: undefined,
6775
+ maxNumberOfPathChecks: Infinity,
6699
6776
  };
6700
- config = { ...defaults, ...options };
6701
- rootDocument = findRootDocument(config.root, defaults);
6702
- let path = bottomUpSearch(input, 'all', () => bottomUpSearch(input, 'two', () => bottomUpSearch(input, 'one', () => bottomUpSearch(input, 'none'))));
6703
- if (path) {
6704
- const optimized = sort(optimize(path, input));
6705
- if (optimized.length > 0) {
6706
- path = optimized[0];
6777
+ const startTime = new Date();
6778
+ const config = { ...defaults, ...options };
6779
+ const rootDocument = findRootDocument(config.root, defaults);
6780
+ let foundPath;
6781
+ let count = 0;
6782
+ for (const candidate of search(input, config, rootDocument)) {
6783
+ const elapsedTimeMs = new Date().getTime() - startTime.getTime();
6784
+ if (elapsedTimeMs > config.timeoutMs ||
6785
+ count >= config.maxNumberOfPathChecks) {
6786
+ const fPath = fallback(input, rootDocument);
6787
+ if (!fPath) {
6788
+ throw new Error(`Timeout: Can't find a unique selector after ${config.timeoutMs}ms`);
6789
+ }
6790
+ return selector(fPath);
6791
+ }
6792
+ count++;
6793
+ if (unique(candidate, rootDocument)) {
6794
+ foundPath = candidate;
6795
+ break;
6707
6796
  }
6708
- return selector(path);
6709
6797
  }
6710
- else {
6798
+ if (!foundPath) {
6711
6799
  throw new Error(`Selector was not found.`);
6712
6800
  }
6713
- }
6714
- function findRootDocument(rootNode, defaults) {
6715
- if (rootNode.nodeType === Node.DOCUMENT_NODE) {
6716
- return rootNode;
6717
- }
6718
- if (rootNode === defaults.root) {
6719
- return rootNode.ownerDocument;
6801
+ const optimized = [
6802
+ ...optimize(foundPath, input, config, rootDocument, startTime),
6803
+ ];
6804
+ optimized.sort(byPenalty);
6805
+ if (optimized.length > 0) {
6806
+ return selector(optimized[0]);
6720
6807
  }
6721
- return rootNode;
6808
+ return selector(foundPath);
6722
6809
  }
6723
- function bottomUpSearch(input, limit, fallback) {
6724
- let path = null;
6725
- let stack = [];
6810
+ function* search(input, config, rootDocument) {
6811
+ const stack = [];
6812
+ let paths = [];
6726
6813
  let current = input;
6727
6814
  let i = 0;
6728
- while (current) {
6729
- const elapsedTime = new Date().getTime() - start.getTime();
6730
- if (config.timeoutMs !== undefined && elapsedTime > config.timeoutMs) {
6731
- throw new Error(`Timeout: Can't find a unique selector after ${elapsedTime}ms`);
6732
- }
6733
- let level = maybe(id(current)) ||
6734
- maybe(...attr(current)) ||
6735
- maybe(...classNames(current)) ||
6736
- maybe(tagName(current)) || [any()];
6737
- const nth = index(current);
6738
- if (limit == 'all') {
6739
- if (nth) {
6740
- level = level.concat(level.filter(dispensableNth).map((node) => nthChild(node, nth)));
6741
- }
6742
- }
6743
- else if (limit == 'two') {
6744
- level = level.slice(0, 1);
6745
- if (nth) {
6746
- level = level.concat(level.filter(dispensableNth).map((node) => nthChild(node, nth)));
6747
- }
6748
- }
6749
- else if (limit == 'one') {
6750
- const [node] = (level = level.slice(0, 1));
6751
- if (nth && dispensableNth(node)) {
6752
- level = [nthChild(node, nth)];
6753
- }
6754
- }
6755
- else if (limit == 'none') {
6756
- level = [any()];
6757
- if (nth) {
6758
- level = [nthChild(level[0], nth)];
6759
- }
6760
- }
6761
- for (let node of level) {
6815
+ while (current && current !== rootDocument) {
6816
+ const level = tie(current, config);
6817
+ for (const node of level) {
6762
6818
  node.level = i;
6763
6819
  }
6764
6820
  stack.push(level);
6765
- if (stack.length >= config.seedMinLength) {
6766
- path = findUniquePath(stack, fallback);
6767
- if (path) {
6768
- break;
6769
- }
6770
- }
6771
6821
  current = current.parentElement;
6772
6822
  i++;
6823
+ paths.push(...combinations(stack));
6824
+ if (i >= config.seedMinLength) {
6825
+ paths.sort(byPenalty);
6826
+ for (const candidate of paths) {
6827
+ yield candidate;
6828
+ }
6829
+ paths = [];
6830
+ }
6773
6831
  }
6774
- if (!path) {
6775
- path = findUniquePath(stack, fallback);
6832
+ paths.sort(byPenalty);
6833
+ for (const candidate of paths) {
6834
+ yield candidate;
6776
6835
  }
6777
- if (!path && fallback) {
6778
- return fallback();
6836
+ }
6837
+ function wordLike(name) {
6838
+ if (/^[a-z\-]{3,}$/i.test(name)) {
6839
+ const words = name.split(/-|[A-Z]/);
6840
+ for (const word of words) {
6841
+ if (word.length <= 2) {
6842
+ return false;
6843
+ }
6844
+ if (/[^aeiou]{4,}/i.test(word)) {
6845
+ return false;
6846
+ }
6847
+ }
6848
+ return true;
6779
6849
  }
6780
- return path;
6850
+ return false;
6781
6851
  }
6782
- function findUniquePath(stack, fallback) {
6783
- const paths = sort(combinations(stack));
6784
- if (paths.length > config.threshold) {
6785
- return fallback ? fallback() : null;
6852
+ function tie(element, config) {
6853
+ const level = [];
6854
+ const elementId = element.getAttribute('id');
6855
+ if (elementId && config.idName(elementId)) {
6856
+ level.push({
6857
+ name: '#' + CSS.escape(elementId),
6858
+ penalty: 0,
6859
+ });
6786
6860
  }
6787
- for (let candidate of paths) {
6788
- if (unique(candidate)) {
6789
- return candidate;
6861
+ for (let i = 0; i < element.classList.length; i++) {
6862
+ const name = element.classList[i];
6863
+ if (config.className(name)) {
6864
+ level.push({
6865
+ name: '.' + CSS.escape(name),
6866
+ penalty: 1,
6867
+ });
6790
6868
  }
6791
6869
  }
6792
- return null;
6870
+ for (let i = 0; i < element.attributes.length; i++) {
6871
+ const attr = element.attributes[i];
6872
+ if (config.attr(attr.name, attr.value)) {
6873
+ level.push({
6874
+ name: `[${CSS.escape(attr.name)}="${CSS.escape(attr.value)}"]`,
6875
+ penalty: 2,
6876
+ });
6877
+ }
6878
+ }
6879
+ const tagName = element.tagName.toLowerCase();
6880
+ if (config.tagName(tagName)) {
6881
+ level.push({
6882
+ name: tagName,
6883
+ penalty: 5,
6884
+ });
6885
+ const index = indexOf(element, tagName);
6886
+ if (index !== undefined) {
6887
+ level.push({
6888
+ name: nthOfType(tagName, index),
6889
+ penalty: 10,
6890
+ });
6891
+ }
6892
+ }
6893
+ const nth = indexOf(element);
6894
+ if (nth !== undefined) {
6895
+ level.push({
6896
+ name: nthChild(tagName, nth),
6897
+ penalty: 50,
6898
+ });
6899
+ }
6900
+ return level;
6793
6901
  }
6794
6902
  function selector(path) {
6795
6903
  let node = path[0];
@@ -6809,69 +6917,23 @@ function selector(path) {
6809
6917
  function penalty(path) {
6810
6918
  return path.map((node) => node.penalty).reduce((acc, i) => acc + i, 0);
6811
6919
  }
6812
- function unique(path) {
6813
- const css = selector(path);
6814
- switch (rootDocument.querySelectorAll(css).length) {
6815
- case 0:
6816
- throw new Error(`Can't select any node with this selector: ${css}`);
6817
- case 1:
6818
- return true;
6819
- default:
6820
- return false;
6821
- }
6822
- }
6823
- function id(input) {
6824
- const elementId = input.getAttribute('id');
6825
- if (elementId && config.idName(elementId)) {
6826
- return {
6827
- name: '#' + CSS.escape(elementId),
6828
- penalty: 0,
6829
- };
6830
- }
6831
- return null;
6832
- }
6833
- function attr(input) {
6834
- const attrs = Array.from(input.attributes).filter((attr) => config.attr(attr.name, attr.value));
6835
- return attrs.map((attr) => ({
6836
- name: `[${CSS.escape(attr.name)}="${CSS.escape(attr.value)}"]`,
6837
- penalty: 0.5,
6838
- }));
6839
- }
6840
- function classNames(input) {
6841
- const names = Array.from(input.classList).filter(config.className);
6842
- return names.map((name) => ({
6843
- name: '.' + CSS.escape(name),
6844
- penalty: 1,
6845
- }));
6846
- }
6847
- function tagName(input) {
6848
- const name = input.tagName.toLowerCase();
6849
- if (config.tagName(name)) {
6850
- return {
6851
- name,
6852
- penalty: 2,
6853
- };
6854
- }
6855
- return null;
6856
- }
6857
- function any() {
6858
- return {
6859
- name: '*',
6860
- penalty: 3,
6861
- };
6920
+ function byPenalty(a, b) {
6921
+ return penalty(a) - penalty(b);
6862
6922
  }
6863
- function index(input) {
6923
+ function indexOf(input, tagName) {
6864
6924
  const parent = input.parentNode;
6865
6925
  if (!parent) {
6866
- return null;
6926
+ return undefined;
6867
6927
  }
6868
6928
  let child = parent.firstChild;
6869
6929
  if (!child) {
6870
- return null;
6930
+ return undefined;
6871
6931
  }
6872
6932
  let i = 0;
6873
6933
  while (child) {
6874
- if (child.nodeType === Node.ELEMENT_NODE) {
6934
+ if (child.nodeType === Node.ELEMENT_NODE &&
6935
+ (tagName === undefined ||
6936
+ child.tagName.toLowerCase() === tagName)) {
6875
6937
  i++;
6876
6938
  }
6877
6939
  if (child === input) {
@@ -6881,24 +6943,39 @@ function index(input) {
6881
6943
  }
6882
6944
  return i;
6883
6945
  }
6884
- function nthChild(node, i) {
6885
- return {
6886
- name: node.name + `:nth-child(${i})`,
6887
- penalty: node.penalty + 1,
6888
- };
6889
- }
6890
- function dispensableNth(node) {
6891
- return node.name !== 'html' && !node.name.startsWith('#');
6946
+ function fallback(input, rootDocument) {
6947
+ let i = 0;
6948
+ let current = input;
6949
+ const path = [];
6950
+ while (current && current !== rootDocument) {
6951
+ const tagName = current.tagName.toLowerCase();
6952
+ const index = indexOf(current, tagName);
6953
+ if (index === undefined) {
6954
+ return;
6955
+ }
6956
+ path.push({
6957
+ name: nthOfType(tagName, index),
6958
+ penalty: NaN,
6959
+ level: i,
6960
+ });
6961
+ current = current.parentElement;
6962
+ i++;
6963
+ }
6964
+ if (unique(path, rootDocument)) {
6965
+ return path;
6966
+ }
6892
6967
  }
6893
- function maybe(...level) {
6894
- const list = level.filter(notEmpty);
6895
- if (list.length > 0) {
6896
- return list;
6968
+ function nthChild(tagName, index) {
6969
+ if (tagName === 'html') {
6970
+ return 'html';
6897
6971
  }
6898
- return null;
6972
+ return `${tagName}:nth-child(${index})`;
6899
6973
  }
6900
- function notEmpty(value) {
6901
- return value !== null && value !== undefined;
6974
+ function nthOfType(tagName, index) {
6975
+ if (tagName === 'html') {
6976
+ return 'html';
6977
+ }
6978
+ return `${tagName}:nth-of-type(${index})`;
6902
6979
  }
6903
6980
  function* combinations(stack, path = []) {
6904
6981
  if (stack.length > 0) {
@@ -6910,44 +6987,50 @@ function* combinations(stack, path = []) {
6910
6987
  yield path;
6911
6988
  }
6912
6989
  }
6913
- function sort(paths) {
6914
- return [...paths].sort((a, b) => penalty(a) - penalty(b));
6990
+ function findRootDocument(rootNode, defaults) {
6991
+ if (rootNode.nodeType === Node.DOCUMENT_NODE) {
6992
+ return rootNode;
6993
+ }
6994
+ if (rootNode === defaults.root) {
6995
+ return rootNode.ownerDocument;
6996
+ }
6997
+ return rootNode;
6998
+ }
6999
+ function unique(path, rootDocument) {
7000
+ const css = selector(path);
7001
+ switch (rootDocument.querySelectorAll(css).length) {
7002
+ case 0:
7003
+ throw new Error(`Can't select any node with this selector: ${css}`);
7004
+ case 1:
7005
+ return true;
7006
+ default:
7007
+ return false;
7008
+ }
6915
7009
  }
6916
- function* optimize(path, input, scope = {
6917
- counter: 0,
6918
- visited: new Map(),
6919
- }) {
7010
+ function* optimize(path, input, config, rootDocument, startTime) {
6920
7011
  if (path.length > 2 && path.length > config.optimizedMinLength) {
6921
7012
  for (let i = 1; i < path.length - 1; i++) {
6922
- if (scope.counter > config.maxNumberOfTries) {
6923
- return; // Okay At least I tried!
7013
+ const elapsedTimeMs = new Date().getTime() - startTime.getTime();
7014
+ if (elapsedTimeMs > config.timeoutMs) {
7015
+ return;
6924
7016
  }
6925
- scope.counter += 1;
6926
7017
  const newPath = [...path];
6927
7018
  newPath.splice(i, 1);
6928
- const newPathKey = selector(newPath);
6929
- if (scope.visited.has(newPathKey)) {
6930
- return;
6931
- }
6932
- if (unique(newPath) && same(newPath, input)) {
7019
+ if (unique(newPath, rootDocument) &&
7020
+ rootDocument.querySelector(selector(newPath)) === input) {
6933
7021
  yield newPath;
6934
- scope.visited.set(newPathKey, true);
6935
- yield* optimize(newPath, input, scope);
7022
+ yield* optimize(newPath, input, config, rootDocument, startTime);
6936
7023
  }
6937
7024
  }
6938
7025
  }
6939
7026
  }
6940
- function same(path, input) {
6941
- return rootDocument.querySelector(selector(path)) === input;
6942
- }
6943
7027
 
6944
7028
  function _getSelector(target, document, options) {
6945
7029
  const selector = finder(target, {
6946
7030
  root: document.body,
6947
7031
  seedMinLength: 3,
6948
7032
  optimizedMinLength: options?.minSelectorDepth || 2,
6949
- threshold: options?.nthThreshold || 1000,
6950
- maxNumberOfTries: options?.maxOptimiseTries || 10000,
7033
+ maxNumberOfPathChecks: options?.maxOptimiseTries || 10000,
6951
7034
  });
6952
7035
  return selector;
6953
7036
  }
@@ -7214,6 +7297,11 @@ function Timing (app, opts) {
7214
7297
  if (shouldSkip) {
7215
7298
  return;
7216
7299
  }
7300
+ const failed = entry.responseEnd === 0
7301
+ || (entry.transferSize === 0 && entry.decodedBodySize === 0);
7302
+ if (failed) {
7303
+ app.send(ResourceTiming(entry.startTime + getTimeOrigin(), 0, 0, 0, 0, 0, entry.name, entry.initiatorType, 0, true));
7304
+ }
7217
7305
  app.send(ResourceTiming(entry.startTime + getTimeOrigin(), entry.duration, entry.responseStart && entry.startTime ? entry.responseStart - entry.startTime : 0, entry.transferSize > entry.encodedBodySize ? entry.transferSize - entry.encodedBodySize : 0, entry.encodedBodySize || 0, entry.decodedBodySize || 0, entry.name, entry.initiatorType, entry.transferSize,
7218
7306
  // @ts-ignore
7219
7307
  (entry.responseStatus && entry.responseStatus === 304) || entry.transferSize === 0));
@@ -8805,6 +8893,9 @@ function Network (app, opts = {}) {
8805
8893
  /* ====== modern way ====== */
8806
8894
  if (options.useProxy) {
8807
8895
  return createNetworkProxy(context, options.ignoreHeaders, setSessionTokenHeader, sanitize, (message) => {
8896
+ if (options.failuresOnly && message.status < 400) {
8897
+ return;
8898
+ }
8808
8899
  app.send(NetworkRequest(message.requestType, message.method, message.url, message.request, message.response, message.status, message.startTime + getTimeOrigin(), message.duration, message.responseSize));
8809
8900
  }, (url) => app.isServiceURL(url), { xhr: true, fetch: true, beacon: true }, options.tokenUrlMatcher);
8810
8901
  }
@@ -9061,11 +9152,8 @@ class API {
9061
9152
  };
9062
9153
  this.signalStartIssue = (reason, missingApi) => {
9063
9154
  const doNotTrack = this.checkDoNotTrack();
9064
- const req = new XMLHttpRequest();
9065
- const orig = this.options.ingestPoint || DEFAULT_INGEST_POINT;
9066
- req.open('POST', orig + '/v1/web/not-started');
9067
- req.send(JSON.stringify({
9068
- trackerVersion: '15.0.2',
9155
+ console.log("Tracker couldn't start due to:", JSON.stringify({
9156
+ trackerVersion: '15.0.5-beta.0',
9069
9157
  projectKey: this.options.projectKey,
9070
9158
  doNotTrack,
9071
9159
  reason: missingApi.length ? `missing api: ${missingApi.join(',')}` : reason,