clarity-js 0.8.12 → 0.8.14

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 (92) hide show
  1. package/build/clarity.extended.js +1 -1
  2. package/build/clarity.insight.js +1 -1
  3. package/build/clarity.js +4609 -4762
  4. package/build/clarity.min.js +1 -1
  5. package/build/clarity.module.js +4609 -4762
  6. package/build/clarity.performance.js +1 -1
  7. package/package.json +69 -76
  8. package/rollup.config.ts +88 -84
  9. package/src/clarity.ts +29 -35
  10. package/src/core/api.ts +1 -8
  11. package/src/core/config.ts +2 -2
  12. package/src/core/event.ts +32 -36
  13. package/src/core/hash.ts +6 -5
  14. package/src/core/history.ts +11 -10
  15. package/src/core/index.ts +11 -21
  16. package/src/core/measure.ts +5 -9
  17. package/src/core/report.ts +6 -10
  18. package/src/core/scrub.ts +27 -30
  19. package/src/core/task.ts +45 -73
  20. package/src/core/time.ts +3 -3
  21. package/src/core/timeout.ts +2 -2
  22. package/src/core/version.ts +1 -1
  23. package/src/data/baseline.ts +55 -60
  24. package/src/data/consent.ts +22 -4
  25. package/src/data/custom.ts +13 -8
  26. package/src/data/dimension.ts +7 -11
  27. package/src/data/encode.ts +38 -36
  28. package/src/data/envelope.ts +38 -38
  29. package/src/data/extract.ts +77 -86
  30. package/src/data/index.ts +9 -11
  31. package/src/data/limit.ts +1 -1
  32. package/src/data/metadata.ts +319 -305
  33. package/src/data/metric.ts +8 -18
  34. package/src/data/ping.ts +4 -8
  35. package/src/data/signal.ts +18 -18
  36. package/src/data/summary.ts +4 -6
  37. package/src/data/token.ts +8 -10
  38. package/src/data/upgrade.ts +3 -7
  39. package/src/data/upload.ts +49 -100
  40. package/src/data/variable.ts +20 -27
  41. package/src/diagnostic/encode.ts +2 -2
  42. package/src/diagnostic/fraud.ts +4 -3
  43. package/src/diagnostic/internal.ts +5 -11
  44. package/src/diagnostic/script.ts +8 -12
  45. package/src/global.ts +1 -1
  46. package/src/insight/blank.ts +4 -4
  47. package/src/insight/encode.ts +17 -23
  48. package/src/insight/snapshot.ts +37 -57
  49. package/src/interaction/change.ts +6 -9
  50. package/src/interaction/click.ts +28 -34
  51. package/src/interaction/clipboard.ts +2 -2
  52. package/src/interaction/encode.ts +31 -35
  53. package/src/interaction/input.ts +9 -11
  54. package/src/interaction/pointer.ts +30 -41
  55. package/src/interaction/resize.ts +5 -5
  56. package/src/interaction/scroll.ts +17 -20
  57. package/src/interaction/selection.ts +8 -12
  58. package/src/interaction/submit.ts +2 -2
  59. package/src/interaction/timeline.ts +9 -13
  60. package/src/interaction/unload.ts +1 -1
  61. package/src/interaction/visibility.ts +2 -2
  62. package/src/layout/animation.ts +41 -47
  63. package/src/layout/discover.ts +5 -5
  64. package/src/layout/document.ts +19 -31
  65. package/src/layout/dom.ts +91 -141
  66. package/src/layout/encode.ts +37 -52
  67. package/src/layout/mutation.ts +318 -321
  68. package/src/layout/node.ts +81 -104
  69. package/src/layout/offset.ts +6 -7
  70. package/src/layout/region.ts +43 -66
  71. package/src/layout/schema.ts +8 -15
  72. package/src/layout/selector.ts +25 -47
  73. package/src/layout/style.ts +37 -45
  74. package/src/layout/target.ts +10 -14
  75. package/src/layout/traverse.ts +11 -17
  76. package/src/performance/blank.ts +1 -1
  77. package/src/performance/encode.ts +4 -4
  78. package/src/performance/interaction.ts +70 -58
  79. package/src/performance/navigation.ts +2 -2
  80. package/src/performance/observer.ts +26 -59
  81. package/src/queue.ts +9 -16
  82. package/tsconfig.json +1 -1
  83. package/tslint.json +32 -25
  84. package/types/core.d.ts +13 -13
  85. package/types/data.d.ts +48 -32
  86. package/types/diagnostic.d.ts +1 -1
  87. package/types/index.d.ts +1 -0
  88. package/types/interaction.d.ts +4 -5
  89. package/types/layout.d.ts +21 -36
  90. package/types/performance.d.ts +5 -6
  91. package/.lintstagedrc.yml +0 -3
  92. package/biome.json +0 -43
@@ -1,5 +1,5 @@
1
1
  import { Character } from "../../types/data";
2
- import { Constant, Selector, type SelectorInput } from "../../types/layout";
2
+ import { Constant, Selector, SelectorInput } from "../../types/layout";
3
3
 
4
4
  const excludeClassNames = Constant.ExcludeClassNames.split(Constant.Comma);
5
5
  let selectorMap: { [selector: string]: number[] } = {};
@@ -9,9 +9,9 @@ export function reset(): void {
9
9
  }
10
10
 
11
11
  export function get(input: SelectorInput, type: Selector): string {
12
- const a = input.attributes;
12
+ let a = input.attributes;
13
13
  let prefix = input.prefix ? input.prefix[type] : null;
14
- const suffix = type === Selector.Alpha ? `${Constant.Tilde}${input.position - 1}` : `:nth-of-type(${input.position})`;
14
+ let suffix = type === Selector.Alpha ? `${Constant.Tilde}${input.position-1}` : `:nth-of-type(${input.position})`;
15
15
  switch (input.tag) {
16
16
  case "STYLE":
17
17
  case "TITLE":
@@ -22,37 +22,24 @@ export function get(input: SelectorInput, type: Selector): string {
22
22
  return Constant.Empty;
23
23
  case "HTML":
24
24
  return Constant.HTML;
25
- default: {
26
- if (prefix === null) {
27
- return Constant.Empty;
28
- }
25
+ default:
26
+ if (prefix === null) { return Constant.Empty; }
29
27
  prefix = `${prefix}${Constant.Separator}`;
30
28
  input.tag = input.tag.indexOf(Constant.SvgPrefix) === 0 ? input.tag.substr(Constant.SvgPrefix.length) : input.tag;
31
29
  let selector = `${prefix}${input.tag}${suffix}`;
32
- const id = Constant.Id in a && a[Constant.Id].length > 0 ? a[Constant.Id] : null;
33
- const classes =
34
- input.tag !== Constant.BodyTag && Constant.Class in a && a[Constant.Class].length > 0
35
- ? a[Constant.Class]
36
- .trim()
37
- .split(/\s+/)
38
- .filter((c) => filter(c))
39
- .join(Constant.Period)
40
- : null;
30
+ let id = Constant.Id in a && a[Constant.Id].length > 0 ? a[Constant.Id] : null;
31
+ let classes = input.tag !== Constant.BodyTag && Constant.Class in a && a[Constant.Class].length > 0 ? a[Constant.Class].trim().split(/\s+/).filter(c => filter(c)).join(Constant.Period) : null;
41
32
  if (classes && classes.length > 0) {
42
33
  if (type === Selector.Alpha) {
43
34
  // In Alpha mode, update selector to use class names, with relative positioning within the parent id container.
44
35
  // If the node has valid class name(s) then drop relative positioning within the parent path to keep things simple.
45
- const key = `${getDomPath(prefix)}${input.tag}${Constant.Dot}${classes}`;
46
- if (!(key in selectorMap)) {
47
- selectorMap[key] = [];
48
- }
49
- if (selectorMap[key].indexOf(input.id) < 0) {
50
- selectorMap[key].push(input.id);
51
- }
36
+ let key = `${getDomPath(prefix)}${input.tag}${Constant.Dot}${classes}`;
37
+ if (!(key in selectorMap)) { selectorMap[key] = []; }
38
+ if (selectorMap[key].indexOf(input.id) < 0) { selectorMap[key].push(input.id); }
52
39
  selector = `${key}${Constant.Tilde}${selectorMap[key].indexOf(input.id)}`;
53
40
  } else {
54
41
  // In Beta mode, we continue to look at query selectors in context of the full page
55
- selector = `${prefix}${input.tag}.${classes}${suffix}`;
42
+ selector = `${prefix}${input.tag}.${classes}${suffix}`
56
43
  }
57
44
  }
58
45
  // Update selector to use "id" field when available. There are two exceptions:
@@ -60,45 +47,36 @@ export function get(input: SelectorInput, type: Selector): string {
60
47
  // (2) if "id" appears inside a shadow DOM, in which case we continue to prefix up to shadow DOM to prevent conflicts
61
48
  selector = id && filter(id) ? `${getDomPrefix(prefix)}${Constant.Hash}${id}` : selector;
62
49
  return selector;
63
- }
64
50
  }
65
51
  }
66
52
 
67
53
  function getDomPrefix(prefix: string): string {
68
- const shadowDomStart = prefix.lastIndexOf(Constant.ShadowDomTag);
69
- const iframeDomStart = prefix.lastIndexOf(`${Constant.IFramePrefix}${Constant.HTML}`);
70
- const domStart = Math.max(shadowDomStart, iframeDomStart);
71
-
72
- if (domStart < 0) {
73
- return Constant.Empty;
74
- }
54
+ const shadowDomStart = prefix.lastIndexOf(Constant.ShadowDomTag);
55
+ const iframeDomStart = prefix.lastIndexOf(`${Constant.IFramePrefix}${Constant.HTML}`);
56
+ const domStart = Math.max(shadowDomStart, iframeDomStart);
57
+
58
+ if (domStart < 0) { return Constant.Empty; }
75
59
 
76
- return prefix.substring(0, prefix.indexOf(Constant.Separator, domStart) + 1);
60
+ return prefix.substring(0, prefix.indexOf(Constant.Separator, domStart) + 1);
77
61
  }
78
62
 
79
63
  function getDomPath(input: string): string {
80
- const parts = input.split(Constant.Separator);
64
+ let parts = input.split(Constant.Separator);
81
65
  for (let i = 0; i < parts.length; i++) {
82
- const tIndex = parts[i].indexOf(Constant.Tilde);
83
- const dIndex = parts[i].indexOf(Constant.Dot);
84
- parts[i] = parts[i].substring(0, dIndex > 0 ? dIndex : tIndex > 0 ? tIndex : parts[i].length);
66
+ let tIndex = parts[i].indexOf(Constant.Tilde);
67
+ let dIndex = parts[i].indexOf(Constant.Dot);
68
+ parts[i] = parts[i].substring(0, dIndex > 0 ? dIndex : (tIndex > 0 ? tIndex : parts[i].length));
85
69
  }
86
70
  return parts.join(Constant.Separator);
87
71
  }
88
72
 
89
73
  // Check if the given input string has digits or excluded class names
90
74
  function filter(value: string): boolean {
91
- if (!value) {
92
- return false;
93
- } // Do not process empty strings
94
- if (excludeClassNames.some((x) => value.toLowerCase().indexOf(x) >= 0)) {
95
- return false;
96
- }
75
+ if (!value) { return false; } // Do not process empty strings
76
+ if (excludeClassNames.some(x => value.toLowerCase().indexOf(x) >= 0)) { return false; }
97
77
  for (let i = 0; i < value.length; i++) {
98
- const c = value.charCodeAt(i);
99
- if (c >= Character.Zero && c <= Character.Nine) {
100
- return false;
101
- }
78
+ let c = value.charCodeAt(i);
79
+ if (c >= Character.Zero && c <= Character.Nine) { return false };
102
80
  }
103
81
  return true;
104
82
  }
@@ -1,70 +1,67 @@
1
- import { Event } from "@clarity-types/data";
2
- import { type IWindowWithOverrides, StyleSheetOperation, type StyleSheetState } from "@clarity-types/layout";
3
- import * as core from "@src/core";
4
- import config from "@src/core/config";
1
+ import { Event, Metric } from "@clarity-types/data";
2
+ import { StyleSheetOperation, StyleSheetState } from "@clarity-types/layout";
5
3
  import { time } from "@src/core/time";
6
4
  import { shortid } from "@src/data/metadata";
7
- import { getId } from "@src/layout/dom";
8
5
  import encode from "@src/layout/encode";
6
+ import { getId } from "@src/layout/dom";
7
+ import * as core from "@src/core";
8
+ import config from "@src/core/config";
9
9
  import { getCssRules } from "./node";
10
10
 
11
11
  export let sheetUpdateState: StyleSheetState[] = [];
12
12
  export let sheetAdoptionState: StyleSheetState[] = [];
13
- const styleSheetId = "claritySheetId";
13
+ const styleSheetId = 'claritySheetId';
14
14
  let styleSheetMap = {};
15
- let styleTimeMap: { [key: string]: number } = {};
15
+ let styleTimeMap: {[key: string]: number} = {};
16
16
  let documentNodes = [];
17
17
  let createdSheetIds = [];
18
18
 
19
- function proxyStyleRules(win: IWindowWithOverrides) {
19
+ function proxyStyleRules(win: any) {
20
20
  if ((config.lean && config.lite) || win === null || win === undefined) {
21
21
  return;
22
22
  }
23
-
23
+
24
24
  win.clarityOverrides = win.clarityOverrides || {};
25
25
 
26
- if (win.CSSStyleSheet?.prototype) {
27
- if (win.clarityOverrides.replace === undefined) {
28
- win.clarityOverrides.replace = win.CSSStyleSheet.prototype.replace;
29
- win.CSSStyleSheet.prototype.replace = function (...args): Promise<CSSStyleSheet> {
26
+ if (win['CSSStyleSheet'] && win.CSSStyleSheet.prototype) {
27
+ if (win.clarityOverrides.replace === undefined) {
28
+ win.clarityOverrides.replace = win.CSSStyleSheet.prototype.replace;
29
+ win.CSSStyleSheet.prototype.replace = function(): Promise<CSSStyleSheet> {
30
30
  if (core.active()) {
31
31
  // if we haven't seen this stylesheet on this page yet, wait until the checkDocumentStyles has found it
32
32
  // and attached the sheet to a document. This way the timestamp of the style sheet creation will align
33
33
  // to when it is used in the document rather than potentially being misaligned during the traverse process.
34
34
  if (createdSheetIds.indexOf(this[styleSheetId]) > -1) {
35
- trackStyleChange(time(), this[styleSheetId], StyleSheetOperation.Replace, args[0]);
35
+ trackStyleChange(time(), this[styleSheetId], StyleSheetOperation.Replace, arguments[0]);
36
36
  }
37
37
  }
38
- return win.clarityOverrides.replace.apply(this, args);
38
+ return win.clarityOverrides.replace.apply(this, arguments);
39
39
  };
40
40
  }
41
41
 
42
- if (win.clarityOverrides.replaceSync === undefined) {
43
- win.clarityOverrides.replaceSync = win.CSSStyleSheet.prototype.replaceSync;
44
- win.CSSStyleSheet.prototype.replaceSync = function (...args): void {
42
+ if (win.clarityOverrides.replaceSync === undefined) {
43
+ win.clarityOverrides.replaceSync = win.CSSStyleSheet.prototype.replaceSync;
44
+ win.CSSStyleSheet.prototype.replaceSync = function(): void {
45
45
  if (core.active()) {
46
46
  // if we haven't seen this stylesheet on this page yet, wait until the checkDocumentStyles has found it
47
47
  // and attached the sheet to a document. This way the timestamp of the style sheet creation will align
48
48
  // to when it is used in the document rather than potentially being misaligned during the traverse process.
49
49
  if (createdSheetIds.indexOf(this[styleSheetId]) > -1) {
50
- trackStyleChange(time(), this[styleSheetId], StyleSheetOperation.ReplaceSync, args[0]);
51
- }
50
+ trackStyleChange(time(), this[styleSheetId], StyleSheetOperation.ReplaceSync, arguments[0]);
51
+ }
52
52
  }
53
- win.clarityOverrides.replaceSync.apply(this, args);
54
- return;
53
+ return win.clarityOverrides.replaceSync.apply(this, arguments);
55
54
  };
56
55
  }
57
- }
56
+ }
58
57
  }
59
58
 
60
59
  export function start(): void {
61
60
  proxyStyleRules(window);
62
61
  }
63
62
 
64
- export function checkDocumentStyles(documentNode: Document, cachedTimestamp: number): void {
65
- if (config.lean && config.lite) {
66
- return;
67
- }
63
+ export function checkDocumentStyles(documentNode: Document, timestamp: number): void {
64
+ if (config.lean && config.lite) { return; }
68
65
 
69
66
  if (documentNodes.indexOf(documentNode) === -1) {
70
67
  documentNodes.push(documentNode);
@@ -72,13 +69,13 @@ export function checkDocumentStyles(documentNode: Document, cachedTimestamp: num
72
69
  proxyStyleRules(documentNode.defaultView);
73
70
  }
74
71
  }
75
- const timestamp = cachedTimestamp || time();
72
+ timestamp = timestamp || time();
76
73
  if (!documentNode?.adoptedStyleSheets) {
77
74
  // if we don't have adoptedStyledSheets on the Node passed to us, we can short circuit.
78
75
  return;
79
76
  }
80
- const currentStyleSheets: string[] = [];
81
- for (const styleSheet of documentNode.adoptedStyleSheets) {
77
+ let currentStyleSheets: string[] = [];
78
+ for (var styleSheet of documentNode.adoptedStyleSheets) {
82
79
  // If we haven't seen this style sheet on this page yet, we create a reference to it for the visualizer.
83
80
  // For SPA or times in which Clarity restarts on a given page, our visualizer would lose context
84
81
  // on the previously created style sheet for page N-1.
@@ -93,27 +90,22 @@ export function checkDocumentStyles(documentNode: Document, cachedTimestamp: num
93
90
  currentStyleSheets.push(styleSheet[styleSheetId]);
94
91
  }
95
92
 
96
- const documentId = getId(documentNode, true);
93
+ let documentId = getId(documentNode, true);
97
94
  if (!styleSheetMap[documentId]) {
98
95
  styleSheetMap[documentId] = [];
99
96
  }
100
97
  if (!arraysEqual(currentStyleSheets, styleSheetMap[documentId])) {
101
98
  // Using -1 to signify the root document node as we don't track that as part of our nodeMap
102
- trackStyleAdoption(
103
- timestamp,
104
- documentNode === document ? -1 : getId(documentNode),
105
- StyleSheetOperation.SetAdoptedStyles,
106
- currentStyleSheets,
107
- );
99
+ trackStyleAdoption(timestamp, documentNode == document ? -1 : getId(documentNode), StyleSheetOperation.SetAdoptedStyles, currentStyleSheets);
108
100
  styleSheetMap[documentId] = currentStyleSheets;
109
101
  styleTimeMap[documentId] = timestamp;
110
102
  }
111
103
  }
112
104
 
113
105
  export function compute(): void {
114
- for (const documentNode of documentNodes) {
115
- const docId = documentNode === document ? -1 : getId(documentNode);
116
- const ts = docId in styleTimeMap ? styleTimeMap[docId] : null;
106
+ for (var documentNode of documentNodes) {
107
+ var docId = documentNode == document ? -1 : getId(documentNode);
108
+ let ts = docId in styleTimeMap ? styleTimeMap[docId] : null;
117
109
  checkDocumentStyles(documentNode, ts);
118
110
  }
119
111
  }
@@ -138,8 +130,8 @@ function trackStyleChange(time: number, id: string, operation: StyleSheetOperati
138
130
  data: {
139
131
  id,
140
132
  operation,
141
- cssRules,
142
- },
133
+ cssRules
134
+ }
143
135
  });
144
136
 
145
137
  encode(Event.StyleSheetUpdate);
@@ -152,8 +144,8 @@ function trackStyleAdoption(time: number, id: number, operation: StyleSheetOpera
152
144
  data: {
153
145
  id,
154
146
  operation,
155
- newIds,
156
- },
147
+ newIds
148
+ }
157
149
  });
158
150
 
159
151
  encode(Event.StyleSheetAdoption);
@@ -165,4 +157,4 @@ function arraysEqual(a: string[], b: string[]): boolean {
165
157
  }
166
158
 
167
159
  return a.every((value, index) => value === b[index]);
168
- }
160
+ }
@@ -1,34 +1,30 @@
1
1
  import { Privacy } from "@clarity-types/core";
2
- import type { Event } from "@clarity-types/data";
3
- import type { TargetMetadata } from "@clarity-types/layout";
2
+ import { Event } from "@clarity-types/data";
3
+ import { TargetMetadata } from "@clarity-types/layout";
4
4
  import * as fraud from "@src/diagnostic/fraud";
5
+ import * as region from "@src/layout/region";
5
6
  import * as dom from "@src/layout/dom";
6
7
  import * as mutation from "@src/layout/mutation";
7
- import * as region from "@src/layout/region";
8
8
 
9
9
  export function target(evt: UIEvent): Node {
10
- const path = evt.composed && evt.composedPath ? evt.composedPath() : null;
11
- const node = (path && path.length > 0 ? path[0] : evt.target) as Node;
10
+ let path = evt.composed && evt.composedPath ? evt.composedPath() : null;
11
+ let node = (path && path.length > 0 ? path[0] : evt.target) as Node;
12
12
  mutation.active(); // Mark active periods of time so mutations can continue uninterrupted
13
13
  return node && node.nodeType === Node.DOCUMENT_NODE ? (node as Document).documentElement : node;
14
14
  }
15
15
 
16
16
  export function metadata(node: Node, event: Event, text: string = null): TargetMetadata {
17
17
  // If the node is null, we return a reserved value for id: 0. Valid assignment of id begins from 1+.
18
- const output: TargetMetadata = { id: 0, hash: null, privacy: Privacy.Text };
18
+ let output: TargetMetadata = { id: 0, hash: null, privacy: Privacy.Text };
19
19
  if (node) {
20
- const value = dom.get(node);
20
+ let value = dom.get(node);
21
21
  if (value !== null) {
22
- const metadata = value.metadata;
22
+ let metadata = value.metadata;
23
23
  output.id = value.id;
24
24
  output.hash = value.hash;
25
25
  output.privacy = metadata.privacy;
26
- if (value.region) {
27
- region.track(value.region, event);
28
- }
29
- if (metadata.fraud) {
30
- fraud.check(metadata.fraud, value.id, text || value.data.value);
31
- }
26
+ if (value.region) { region.track(value.region, event); }
27
+ if (metadata.fraud) { fraud.check(metadata.fraud, value.id, text || value.data.value); }
32
28
  }
33
29
  }
34
30
 
@@ -1,34 +1,28 @@
1
- import { Task, type Timer } from "@clarity-types/core";
2
- import type { Source } from "@clarity-types/layout";
1
+ import { Task, Timer } from "@clarity-types/core";
2
+ import { Source } from "@clarity-types/layout";
3
3
  import * as task from "@src/core/task";
4
4
  import node from "@src/layout/node";
5
5
 
6
- export default async function (root: Node, timer: Timer, source: Source, timestamp: number): Promise<void> {
7
- const queue = [root];
6
+ export default async function(root: Node, timer: Timer, source: Source, timestamp: number): Promise<void> {
7
+ let queue = [root];
8
8
  while (queue.length > 0) {
9
- const entry = queue.shift();
9
+ let entry = queue.shift();
10
10
  let next = entry.firstChild;
11
11
 
12
12
  while (next) {
13
13
  queue.push(next);
14
14
  next = next.nextSibling;
15
15
  }
16
-
16
+
17
17
  // Check the status of current task to see if we should yield before continuing
18
18
  let state = task.state(timer);
19
- if (state === Task.Wait) {
20
- state = await task.suspend(timer);
21
- }
22
- if (state === Task.Stop) {
23
- break;
24
- }
19
+ if (state === Task.Wait) { state = await task.suspend(timer); }
20
+ if (state === Task.Stop) { break; }
25
21
 
26
22
  // Check if processing a node gives us a pointer to one of its sub nodes for traversal
27
23
  // E.g. an element node may give us a pointer to traverse shadowDom if shadowRoot property is set
28
24
  // Or, an iframe from the same origin could give a pointer to it's document for traversing contents of iframe.
29
- const subnode = node(entry, source, timestamp);
30
- if (subnode) {
31
- queue.push(subnode);
32
- }
25
+ let subnode = node(entry, source, timestamp);
26
+ if (subnode) { queue.push(subnode); }
33
27
  }
34
- }
28
+ }
@@ -1,6 +1,6 @@
1
1
  export * from "@src/insight/blank";
2
2
 
3
- export const keys = [];
3
+ export let keys = [];
4
4
 
5
5
  /* Intentionally blank module with empty code */
6
6
  export function hashText(): void {}
@@ -1,11 +1,11 @@
1
- import { Event, type Token } from "@clarity-types/data";
1
+ import {Event, Token} from "@clarity-types/data";
2
2
  import { time } from "@src/core/time";
3
3
  import { queue } from "@src/data/upload";
4
4
  import * as navigation from "@src/performance/navigation";
5
5
 
6
- export default async function (type: Event): Promise<void> {
7
- const t = time();
8
- const tokens: Token[] = [t, type];
6
+ export default async function(type: Event): Promise<void> {
7
+ let t = time();
8
+ let tokens: Token[] = [t, type];
9
9
  switch (type) {
10
10
  case Event.Navigation:
11
11
  tokens.push(navigation.data.fetchStart);
@@ -1,8 +1,8 @@
1
- import type { Interaction, PerformanceEventTiming } from "@clarity-types/data";
1
+ import { PerformanceEventTiming, Interaction } from '@clarity-types/data';
2
2
 
3
3
  // Estimate variables to keep track of interactions
4
4
  let interactionCountEstimate = 0;
5
- let minKnownInteractionId = Number.POSITIVE_INFINITY;
5
+ let minKnownInteractionId = Infinity;
6
6
  let maxKnownInteractionId = 0;
7
7
 
8
8
  let prevInteractionCount = 0; // Used to track interaction count between pages
@@ -21,25 +21,33 @@ const longestInteractionMap: Map<number, Interaction> = new Map();
21
21
  * Dividing by 7 helps approximate the interaction count more accurately, since interaction IDs are spread out across a large range.
22
22
  */
23
23
  const countInteractions = (entry: PerformanceEventTiming) => {
24
- if ("interactionCount" in performance) {
25
- interactionCountEstimate = performance.interactionCount as number;
26
- return;
27
- }
28
-
29
- if (entry.interactionId) {
30
- minKnownInteractionId = Math.min(minKnownInteractionId, entry.interactionId);
31
- maxKnownInteractionId = Math.max(maxKnownInteractionId, entry.interactionId);
32
-
33
- interactionCountEstimate = maxKnownInteractionId ? (maxKnownInteractionId - minKnownInteractionId) / 7 + 1 : 0;
34
- }
24
+ if ('interactionCount' in performance) {
25
+ interactionCountEstimate = performance.interactionCount as number;
26
+ return;
27
+ }
28
+
29
+ if (entry.interactionId) {
30
+ minKnownInteractionId = Math.min(
31
+ minKnownInteractionId,
32
+ entry.interactionId
33
+ );
34
+ maxKnownInteractionId = Math.max(
35
+ maxKnownInteractionId,
36
+ entry.interactionId
37
+ );
38
+
39
+ interactionCountEstimate = maxKnownInteractionId
40
+ ? (maxKnownInteractionId - minKnownInteractionId) / 7 + 1
41
+ : 0;
42
+ }
35
43
  };
36
44
 
37
45
  const getInteractionCount = () => {
38
- return interactionCountEstimate || 0;
46
+ return interactionCountEstimate || 0;
39
47
  };
40
48
 
41
49
  const getInteractionCountForNavigation = () => {
42
- return getInteractionCount() - prevInteractionCount;
50
+ return getInteractionCount() - prevInteractionCount;
43
51
  };
44
52
 
45
53
  /**
@@ -47,67 +55,71 @@ const getInteractionCountForNavigation = () => {
47
55
  * the candidate interaction based on the current interaction count.
48
56
  * Dividing by 50 is a heuristic to estimate the 98th percentile (P98) interaction.
49
57
  * This assumes one out of every 50 interactions represents the P98 interaction.
50
- * By dividing the total interaction count by 50, we get an index to approximate
58
+ * By dividing the total interaction count by 50, we get an index to approximate
51
59
  * the slowest 2% of interactions, helping identify a likely P98 candidate.
52
60
  */
53
61
  export const estimateP98LongestInteraction = () => {
54
- if (!longestInteractionList.length) {
55
- return -1;
56
- }
62
+ if(!longestInteractionList.length){
63
+ return -1;
64
+ }
57
65
 
58
- const candidateInteractionIndex = Math.min(longestInteractionList.length - 1, Math.floor(getInteractionCountForNavigation() / 50));
66
+ const candidateInteractionIndex = Math.min(
67
+ longestInteractionList.length - 1,
68
+ Math.floor(getInteractionCountForNavigation() / 50)
69
+ );
59
70
 
60
- return longestInteractionList[candidateInteractionIndex].latency;
71
+ return longestInteractionList[candidateInteractionIndex].latency;
61
72
  };
62
73
 
63
74
  /**
64
75
  * Resets the interaction tracking, usually called after navigation to a new page.
65
76
  */
66
77
  export const resetInteractions = () => {
67
- prevInteractionCount = getInteractionCount();
68
- longestInteractionList.length = 0;
69
- longestInteractionMap.clear();
78
+ prevInteractionCount = getInteractionCount();
79
+ longestInteractionList.length = 0;
80
+ longestInteractionMap.clear();
70
81
  };
71
82
 
72
83
  /**
73
84
  * Processes a PerformanceEventTiming entry by updating the longest interaction list.
74
85
  */
75
86
  export const processInteractionEntry = (entry: PerformanceEventTiming) => {
76
- // Ignore entries with 0 interactionId or very short durations
77
- if (!entry.interactionId || entry.duration < DEFAULT_DURATION_THRESHOLD) {
78
- return;
87
+ // Ignore entries with 0 interactionId or very short durations
88
+ if (!entry.interactionId || entry.duration < DEFAULT_DURATION_THRESHOLD) {
89
+ return;
90
+ }
91
+
92
+ countInteractions(entry);
93
+
94
+ const minLongestInteraction =
95
+ longestInteractionList[longestInteractionList.length - 1];
96
+
97
+ const existingInteraction = longestInteractionMap.get(entry.interactionId!);
98
+
99
+ // Either update existing, add new, or replace shortest interaction if necessary
100
+ if (
101
+ existingInteraction ||
102
+ longestInteractionList.length < MAX_INTERACTIONS_TO_CONSIDER ||
103
+ entry.duration > minLongestInteraction?.latency
104
+ ) {
105
+ if (!existingInteraction) {
106
+ const interaction = {
107
+ id: entry.interactionId,
108
+ latency: entry.duration,
109
+ };
110
+ longestInteractionMap.set(interaction.id, interaction);
111
+ longestInteractionList.push(interaction);
112
+ } else if (entry.duration > existingInteraction.latency) {
113
+ existingInteraction.latency = entry.duration;
79
114
  }
80
115
 
81
- countInteractions(entry);
82
-
83
- const minLongestInteraction = longestInteractionList[longestInteractionList.length - 1];
84
-
85
- const existingInteraction = longestInteractionMap.get(entry.interactionId);
86
-
87
- // Either update existing, add new, or replace shortest interaction if necessary
88
- if (
89
- existingInteraction ||
90
- longestInteractionList.length < MAX_INTERACTIONS_TO_CONSIDER ||
91
- entry.duration > minLongestInteraction?.latency
92
- ) {
93
- if (!existingInteraction) {
94
- const interaction = {
95
- id: entry.interactionId,
96
- latency: entry.duration,
97
- };
98
- longestInteractionMap.set(interaction.id, interaction);
99
- longestInteractionList.push(interaction);
100
- } else if (entry.duration > existingInteraction.latency) {
101
- existingInteraction.latency = entry.duration;
102
- }
103
-
104
- longestInteractionList.sort((a, b) => b.latency - a.latency);
105
-
106
- // Trim the list to the maximum number of interactions to consider
107
- if (longestInteractionList.length > MAX_INTERACTIONS_TO_CONSIDER) {
108
- for (const i of longestInteractionList.splice(MAX_INTERACTIONS_TO_CONSIDER)) {
109
- longestInteractionMap.delete(i.id);
110
- }
111
- }
116
+ longestInteractionList.sort((a, b) => b.latency - a.latency);
117
+
118
+ // Trim the list to the maximum number of interactions to consider
119
+ if (longestInteractionList.length > MAX_INTERACTIONS_TO_CONSIDER) {
120
+ longestInteractionList
121
+ .splice(MAX_INTERACTIONS_TO_CONSIDER)
122
+ .forEach((i) => longestInteractionMap.delete(i.id));
112
123
  }
124
+ }
113
125
  };
@@ -1,5 +1,5 @@
1
1
  import { Event } from "@clarity-types/data";
2
- import type { NavigationData } from "@clarity-types/performance";
2
+ import { NavigationData } from "@clarity-types/performance";
3
3
  import encode from "./encode";
4
4
 
5
5
  export let data: NavigationData = null;
@@ -25,7 +25,7 @@ export function compute(entry: PerformanceNavigationTiming): void {
25
25
  type: entry.type,
26
26
  protocol: entry.nextHopProtocol,
27
27
  encodedSize: entry.encodedBodySize ? entry.encodedBodySize : 0,
28
- decodedSize: entry.decodedBodySize ? entry.decodedBodySize : 0,
28
+ decodedSize: entry.decodedBodySize ? entry.decodedBodySize : 0
29
29
  };
30
30
  encode(Event.Navigation);
31
31
  }