clarity-js 0.8.12 → 0.8.13-beta

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 (91) hide show
  1. package/build/clarity.extended.js +1 -1
  2. package/build/clarity.insight.js +1 -1
  3. package/build/clarity.js +3952 -4179
  4. package/build/clarity.min.js +1 -1
  5. package/build/clarity.module.js +3952 -4179
  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 +28 -34
  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 +3 -3
  18. package/src/core/scrub.ts +20 -29
  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 +2 -2
  25. package/src/data/custom.ts +13 -8
  26. package/src/data/dimension.ts +7 -11
  27. package/src/data/encode.ts +30 -36
  28. package/src/data/envelope.ts +38 -38
  29. package/src/data/extract.ts +77 -86
  30. package/src/data/index.ts +6 -10
  31. package/src/data/limit.ts +1 -1
  32. package/src/data/metadata.ts +266 -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 +24 -35
  55. package/src/interaction/resize.ts +5 -5
  56. package/src/interaction/scroll.ts +11 -14
  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 +40 -60
  71. package/src/layout/schema.ts +8 -15
  72. package/src/layout/selector.ts +25 -47
  73. package/src/layout/style.ts +36 -44
  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 +29 -33
  86. package/types/diagnostic.d.ts +1 -1
  87. package/types/interaction.d.ts +4 -4
  88. package/types/layout.d.ts +21 -36
  89. package/types/performance.d.ts +5 -6
  90. package/.lintstagedrc.yml +0 -3
  91. package/biome.json +0 -43
@@ -1,4 +1,4 @@
1
- import { Event, Metric, type MetricData } from "@clarity-types/data";
1
+ import { Event, Metric, MetricData } from "@clarity-types/data";
2
2
  import encode from "./encode";
3
3
 
4
4
  export let data: MetricData = null;
@@ -16,24 +16,16 @@ export function stop(): void {
16
16
  }
17
17
 
18
18
  export function count(metric: Metric): void {
19
- if (!(metric in data)) {
20
- data[metric] = 0;
21
- }
22
- if (!(metric in updates)) {
23
- updates[metric] = 0;
24
- }
19
+ if (!(metric in data)) { data[metric] = 0; }
20
+ if (!(metric in updates)) { updates[metric] = 0; }
25
21
  data[metric]++;
26
22
  updates[metric]++;
27
23
  }
28
24
 
29
25
  export function sum(metric: Metric, value: number): void {
30
- if (value !== null) {
31
- if (!(metric in data)) {
32
- data[metric] = 0;
33
- }
34
- if (!(metric in updates)) {
35
- updates[metric] = 0;
36
- }
26
+ if (value !== null) {
27
+ if (!(metric in data)) { data[metric] = 0; }
28
+ if (!(metric in updates)) { updates[metric] = 0; }
37
29
  data[metric] += value;
38
30
  updates[metric] += value;
39
31
  }
@@ -41,10 +33,8 @@ export function sum(metric: Metric, value: number): void {
41
33
 
42
34
  export function max(metric: Metric, value: number): void {
43
35
  // Ensure that we do not process null or NaN values
44
- if (value !== null && Number.isNaN(value) === false) {
45
- if (!(metric in data)) {
46
- data[metric] = 0;
47
- }
36
+ if (value !== null && isNaN(value) === false) {
37
+ if (!(metric in data)) { data[metric] = 0; }
48
38
  if (value > data[metric] || data[metric] === 0) {
49
39
  updates[metric] = value;
50
40
  data[metric] = value;
package/src/data/ping.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Event, type PingData, Setting } from "@clarity-types/data";
1
+ import { Event, PingData, Setting } from "@clarity-types/data";
2
2
  import { suspend } from "@src/core";
3
3
  import { time } from "@src/core/time";
4
4
  import { clearTimeout, setTimeout } from "@src/core/timeout";
@@ -15,22 +15,18 @@ export function start(): void {
15
15
  }
16
16
 
17
17
  export function reset(): void {
18
- if (timeout) {
19
- clearTimeout(timeout);
20
- }
18
+ if (timeout) { clearTimeout(timeout); }
21
19
  timeout = setTimeout(ping, interval);
22
20
  last = time();
23
21
  }
24
22
 
25
23
  function ping(): void {
26
- const now = time();
24
+ let now = time();
27
25
  data = { gap: now - last };
28
26
  encode(Event.Ping);
29
27
  if (data.gap < Setting.PingTimeout) {
30
28
  timeout = setTimeout(ping, interval);
31
- } else {
32
- suspend();
33
- }
29
+ } else { suspend(); }
34
30
  }
35
31
 
36
32
  export function stop(): void {
@@ -1,30 +1,30 @@
1
- import type { ClaritySignal, SignalCallback } from "@clarity-types/data";
1
+ import { ClaritySignal, SignalCallback } from '@clarity-types/data';
2
2
 
3
3
  export let signalCallback: SignalCallback = null;
4
4
 
5
5
  export function signal(cb: SignalCallback): void {
6
- signalCallback = cb;
6
+ signalCallback = cb;
7
7
  }
8
8
 
9
9
  function parseSignals(signalsPayload: string): ClaritySignal[] {
10
- try {
11
- const parsedSignals: ClaritySignal[] = JSON.parse(signalsPayload);
12
- return parsedSignals;
13
- } catch {
14
- return [];
15
- }
10
+ try{
11
+ const parsedSignals: ClaritySignal[] = JSON.parse(signalsPayload);
12
+ return parsedSignals;
13
+ }catch{
14
+ return []
15
+ }
16
16
  }
17
17
 
18
18
  export function signalsEvent(signalsPayload: string) {
19
- try {
20
- if (!signalCallback) {
21
- return;
22
- }
23
- const signals = parseSignals(signalsPayload);
24
- for (const signal of signals) {
25
- signalCallback(signal);
26
- }
27
- } catch {
28
- //do nothing
19
+ try {
20
+ if (!signalCallback) {
21
+ return;
29
22
  }
23
+ const signals = parseSignals(signalsPayload);
24
+ signals.forEach((signal) => {
25
+ signalCallback(signal);
26
+ });
27
+ } catch {
28
+ //do nothing
29
+ }
30
30
  }
@@ -1,4 +1,4 @@
1
- import { Event, Setting, type SummaryData } from "@clarity-types/data";
1
+ import { Event, SummaryData, Setting } from "@clarity-types/data";
2
2
  import encode from "./encode";
3
3
 
4
4
  export let data: SummaryData = null;
@@ -15,15 +15,13 @@ export function track(event: Event, time: number): void {
15
15
  if (!(event in data)) {
16
16
  data[event] = [[time, 0]];
17
17
  } else {
18
- const e = data[event];
19
- const last = e[e.length - 1];
18
+ let e = data[event];
19
+ let last = e[e.length - 1];
20
20
  // Add a new entry only if the new event occurs after configured interval
21
21
  // Otherwise, extend the duration of the previous entry
22
22
  if (time - last[0] > Setting.SummaryInterval) {
23
23
  data[event].push([time, 0]);
24
- } else {
25
- last[1] = time - last[0];
26
- }
24
+ } else { last[1] = time - last[0]; }
27
25
  }
28
26
  }
29
27
 
package/src/data/token.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Token } from "@clarity-types/data";
1
+ import {Constant, Token} from "@clarity-types/data";
2
2
 
3
3
  // Following code takes an array of tokens and transforms it to optimize for repeating tokens and make it efficient to send over the wire
4
4
  // The way it works is that it iterate over all tokens and checks if the current token was already seen in the tokens array so far
@@ -6,20 +6,18 @@ import type { Token } from "@clarity-types/data";
6
6
  // E.g. If tokens array is: ["hello", "world", "coding", "language", "world", "language", "example"]
7
7
  // Then the resulting tokens array after following code execution would be: ["hello", "world", "coding", "language", [1, 3], "example"]
8
8
  // Where [1,3] points to tokens[1] => "world" and tokens[3] => "language"
9
- export default function (tokens: Token[]): Token[] {
10
- const output: Token[] = [];
11
- const lookup: { [key: string]: number } = {};
9
+ export default function(tokens: Token[]): Token[] {
10
+ let output: Token[] = [];
11
+ let lookup: {[key: string]: number} = {};
12
12
  let pointer = 0;
13
13
  let reference = null;
14
14
  for (let i = 0; i < tokens.length; i++) {
15
15
  // Only optimize for string values
16
- if (typeof tokens[i] === "string") {
17
- const token = tokens[i] as string;
18
- const index = lookup[token] || -1;
16
+ if (typeof tokens[i] === Constant.String) {
17
+ let token = tokens[i] as string;
18
+ let index = lookup[token] || -1;
19
19
  if (index >= 0) {
20
- if (reference) {
21
- reference.push(index);
22
- } else {
20
+ if (reference) { reference.push(index); } else {
23
21
  reference = [index];
24
22
  output.push(reference);
25
23
  pointer++;
@@ -1,4 +1,4 @@
1
- import { Constant, Event, type UpgradeData } from "@clarity-types/data";
1
+ import { Constant, Event, UpgradeData } from "@clarity-types/data";
2
2
  import * as core from "@src/core";
3
3
  import config from "@src/core/config";
4
4
  import encode from "@src/data/encode";
@@ -9,9 +9,7 @@ import * as style from "@src/layout/style";
9
9
  export let data: UpgradeData = null;
10
10
 
11
11
  export function start(): void {
12
- if (!config.lean && config.upgrade) {
13
- config.upgrade(Constant.Config);
14
- }
12
+ if (!config.lean && config.upgrade) { config.upgrade(Constant.Config); }
15
13
  data = null;
16
14
  }
17
15
 
@@ -30,9 +28,7 @@ export function upgrade(key: string): void {
30
28
  metadata.save();
31
29
 
32
30
  // Callback upgrade handler, if configured
33
- if (config.upgrade) {
34
- config.upgrade(key);
35
- }
31
+ if (config.upgrade) { config.upgrade(key); }
36
32
 
37
33
  encode(Event.Upgrade);
38
34
 
@@ -1,48 +1,34 @@
1
- import type { UploadCallback } from "@clarity-types/core";
2
- import {
3
- BooleanFlag,
4
- Check,
5
- Code,
6
- Constant,
7
- type EncodedPayload,
8
- Event,
9
- Metric,
10
- Setting,
11
- Severity,
12
- type Token,
13
- type Transit,
14
- type UploadData,
15
- XMLReadyState,
16
- } from "@clarity-types/data";
1
+ import { UploadCallback } from "@clarity-types/core";
2
+ import { BooleanFlag, Check, Code, Constant, EncodedPayload, Event, Metric, Setting, Severity, Token, Transit, UploadData, XMLReadyState } from "@clarity-types/data";
17
3
  import * as clarity from "@src/clarity";
18
4
  import config from "@src/core/config";
19
5
  import measure from "@src/core/measure";
20
- import { report } from "@src/core/report";
21
6
  import { time } from "@src/core/time";
22
7
  import { clearTimeout, setTimeout } from "@src/core/timeout";
23
8
  import compress from "@src/data/compress";
24
9
  import encode from "@src/data/encode";
25
10
  import * as envelope from "@src/data/envelope";
26
- import * as extract from "@src/data/extract";
27
11
  import * as data from "@src/data/index";
28
12
  import * as limit from "@src/data/limit";
29
13
  import * as metadata from "@src/data/metadata";
30
14
  import * as metric from "@src/data/metric";
31
15
  import * as ping from "@src/data/ping";
32
- import { signalsEvent } from "@src/data/signal";
33
16
  import * as internal from "@src/diagnostic/internal";
34
17
  import * as timeline from "@src/interaction/timeline";
35
18
  import * as region from "@src/layout/region";
19
+ import * as extract from "@src/data/extract";
36
20
  import * as style from "@src/layout/style";
21
+ import { report } from "@src/core/report";
22
+ import { signalsEvent } from "@src/data/signal";
37
23
 
38
- let discoverBytes = 0;
39
- let playbackBytes = 0;
24
+ let discoverBytes: number = 0;
25
+ let playbackBytes: number = 0;
40
26
  let playback: string[];
41
27
  let analysis: string[];
42
28
  let timeout: number = null;
43
29
  let transit: Transit;
44
30
  let active: boolean;
45
- let queuedTime = 0;
31
+ let queuedTime: number = 0;
46
32
  let leanLimit = false;
47
33
  export let track: UploadData;
48
34
 
@@ -58,11 +44,11 @@ export function start(): void {
58
44
  track = null;
59
45
  }
60
46
 
61
- export function queue(tokens: Token[], transmit = true): void {
47
+ export function queue(tokens: Token[], transmit: boolean = true): void {
62
48
  if (active) {
63
- const now = time();
64
- const type = tokens.length > 1 ? tokens[1] : null;
65
- const event = JSON.stringify(tokens);
49
+ let now = time();
50
+ let type = tokens.length > 1 ? tokens[1] : null;
51
+ let event = JSON.stringify(tokens);
66
52
 
67
53
  if (!config.lean) {
68
54
  leanLimit = false;
@@ -72,20 +58,15 @@ export function queue(tokens: Token[], transmit = true): void {
72
58
  }
73
59
 
74
60
  switch (type) {
75
- // biome-ignore lint/suspicious/noFallthroughSwitchClause: we want discover bytes to also count as playback bytes
76
61
  case Event.Discover:
77
- if (leanLimit) {
78
- break;
79
- }
62
+ if (leanLimit) { break; }
80
63
  discoverBytes += event.length;
81
64
  case Event.Box:
82
65
  case Event.Mutation:
83
66
  case Event.Snapshot:
84
67
  case Event.StyleSheetAdoption:
85
68
  case Event.StyleSheetUpdate:
86
- if (leanLimit) {
87
- break;
88
- }
69
+ if (leanLimit) { break; }
89
70
  playbackBytes += event.length;
90
71
  playback.push(event);
91
72
  break;
@@ -100,8 +81,8 @@ export function queue(tokens: Token[], transmit = true): void {
100
81
  // Following two checks are precautionary and act as a fail safe mechanism to get out of unexpected situations.
101
82
  // Check 1: If for any reason the upload hasn't happened after waiting for 2x the config.delay time,
102
83
  // reset the timer. This allows Clarity to attempt an upload again.
103
- const gap = delay();
104
- if (now - queuedTime > gap * 2) {
84
+ let gap = delay();
85
+ if (now - queuedTime > (gap * 2)) {
105
86
  clearTimeout(timeout);
106
87
  timeout = null;
107
88
  }
@@ -110,9 +91,7 @@ export function queue(tokens: Token[], transmit = true): void {
110
91
  // However, in certain scenarios - like metric calculation - which are triggered as part of an existing upload
111
92
  // We enrich the data going out with the existing upload. In these cases, call to upload comes with 'transmit' set to false.
112
93
  if (transmit && timeout === null) {
113
- if (type !== Event.Ping) {
114
- ping.reset();
115
- }
94
+ if (type !== Event.Ping) { ping.reset(); }
116
95
  timeout = setTimeout(upload, gap);
117
96
  queuedTime = now;
118
97
  limit.check(playbackBytes);
@@ -134,17 +113,14 @@ export function stop(): void {
134
113
  active = false;
135
114
  }
136
115
 
137
- async function upload(final = false): Promise<void> {
116
+ async function upload(final: boolean = false): Promise<void> {
138
117
  timeout = null;
139
118
 
140
119
  // Check if we can send playback bytes over the wire or not
141
120
  // For better instrumentation coverage, we send playback bytes from second sequence onwards
142
121
  // And, we only send playback metric when we are able to send the playback bytes back to server
143
- const sendPlaybackBytes =
144
- config.lean === false && playbackBytes > 0 && (playbackBytes < Setting.MaxFirstPayloadBytes || envelope.data.sequence > 0);
145
- if (sendPlaybackBytes) {
146
- metric.max(Metric.Playback, BooleanFlag.True);
147
- }
122
+ let sendPlaybackBytes = config.lean === false && playbackBytes > 0 && (playbackBytes < Setting.MaxFirstPayloadBytes || envelope.data.sequence > 0);
123
+ if (sendPlaybackBytes) { metric.max(Metric.Playback, BooleanFlag.True); }
148
124
 
149
125
  // CAUTION: Ensure "transmit" is set to false in the queue function for following events
150
126
  // Otherwise you run a risk of infinite loop.
@@ -157,18 +133,18 @@ async function upload(final = false): Promise<void> {
157
133
  // In real world tests, we noticed that certain third party scripts (e.g. https://www.npmjs.com/package/raven-js)
158
134
  // could inject function arguments for internal tracking (likely stack traces for script errors).
159
135
  // For these edge cases, we want to ensure that an injected object (e.g. {"key": "value"}) isn't mistaken to be true.
160
- const last = final === true;
161
- const e = JSON.stringify(envelope.envelope(last));
162
- const a = `[${analysis.join()}]`;
136
+ let last = final === true;
137
+ let e = JSON.stringify(envelope.envelope(last));
138
+ let a = `[${analysis.join()}]`;
163
139
 
164
- const p = sendPlaybackBytes ? `[${playback.join()}]` : Constant.Empty;
165
- const encoded: EncodedPayload = { e, a, p };
140
+ let p = sendPlaybackBytes ? `[${playback.join()}]` : Constant.Empty;
141
+ let encoded: EncodedPayload = {e, a, p};
166
142
 
167
143
  // Get the payload ready for sending over the wire
168
144
  // We also attempt to compress the payload if it is not the last payload and the browser supports it
169
145
  // In all other cases, we continue to send back string value
170
- const payload = stringify(encoded);
171
- const zipped = last ? null : await compress(payload);
146
+ let payload = stringify(encoded);
147
+ let zipped = last ? null : await compress(payload)
172
148
  metric.sum(Metric.TotalBytes, zipped ? zipped.length : payload.length);
173
149
  send(payload, zipped, envelope.data.sequence, last);
174
150
 
@@ -186,9 +162,9 @@ function stringify(encoded: EncodedPayload): string {
186
162
  return encoded.p.length > 0 ? `{"e":${encoded.e},"a":${encoded.a},"p":${encoded.p}}` : `{"e":${encoded.e},"a":${encoded.a}}`;
187
163
  }
188
164
 
189
- function send(payload: string, zipped: Uint8Array, sequence: number, beacon = false): void {
165
+ function send(payload: string, zipped: Uint8Array, sequence: number, beacon: boolean = false): void {
190
166
  // Upload data if a valid URL is defined in the config
191
- if (typeof config.upload === "string") {
167
+ if (typeof config.upload === Constant.String) {
192
168
  const url = config.upload as string;
193
169
  let dispatched = false;
194
170
 
@@ -200,12 +176,8 @@ function send(payload: string, zipped: Uint8Array, sequence: number, beacon = fa
200
176
  try {
201
177
  // Navigator needs to be bound to sendBeacon before it is used to avoid errors in some browsers
202
178
  dispatched = navigator.sendBeacon.bind(navigator)(url, payload);
203
- if (dispatched) {
204
- done(sequence);
205
- }
206
- } catch {
207
- /* do nothing - and we will automatically fallback to XHR below */
208
- }
179
+ if (dispatched) { done(sequence); }
180
+ } catch { /* do nothing - and we will automatically fallback to XHR below */ }
209
181
  }
210
182
 
211
183
  // Before initiating XHR upload, we check if the data has already been uploaded using sendBeacon
@@ -216,22 +188,12 @@ function send(payload: string, zipped: Uint8Array, sequence: number, beacon = fa
216
188
  if (dispatched === false) {
217
189
  // While tracking payload for retry, we only track string value of the payload to err on the safe side
218
190
  // Not all browsers support compression API and the support for it in supported browsers is still experimental
219
- if (sequence in transit) {
220
- transit[sequence].attempts++;
221
- } else {
222
- transit[sequence] = { data: payload, attempts: 1 };
223
- }
224
- const xhr = new XMLHttpRequest();
191
+ if (sequence in transit) { transit[sequence].attempts++; } else { transit[sequence] = { data: payload, attempts: 1 }; }
192
+ let xhr = new XMLHttpRequest();
225
193
  xhr.open("POST", url, true);
226
194
  xhr.timeout = Setting.UploadTimeout;
227
- xhr.ontimeout = () => {
228
- report(new Error(`${Constant.Timeout} : ${url}`));
229
- };
230
- if (sequence !== null) {
231
- xhr.onreadystatechange = (): void => {
232
- measure(check)(xhr, sequence);
233
- };
234
- }
195
+ xhr.ontimeout = () => { report(new Error(`${Constant.Timeout} : ${url}`)) };
196
+ if (sequence !== null) { xhr.onreadystatechange = (): void => { measure(check)(xhr, sequence); }; }
235
197
  xhr.withCredentials = true;
236
198
  if (zipped) {
237
199
  // If we do have valid compressed array, send it with appropriate HTTP headers so server can decode it appropriately
@@ -250,7 +212,7 @@ function send(payload: string, zipped: Uint8Array, sequence: number, beacon = fa
250
212
  }
251
213
 
252
214
  function check(xhr: XMLHttpRequest, sequence: number): void {
253
- const transitData = transit[sequence];
215
+ var transitData = transit[sequence];
254
216
  if (xhr && xhr.readyState === XMLReadyState.Done && transitData) {
255
217
  // Attempt send payload again (as configured in settings) if we do not receive a success (2XX) response code back from the server
256
218
  if ((xhr.status < 200 || xhr.status > 208) && transitData.attempts <= Setting.RetryLimit) {
@@ -264,9 +226,7 @@ function check(xhr: XMLHttpRequest, sequence: number): void {
264
226
  // 1: Browsers block upload because of content security policy violation
265
227
  // 2: Safari will terminate pending XHR requests with status code 0 if the user navigates away from the page
266
228
  // In any case, we switch the upload URL to fallback configuration (if available) before re-trying one more time
267
- if (xhr.status === 0) {
268
- config.upload = config.fallback ? config.fallback : config.upload;
269
- }
229
+ if (xhr.status === 0) { config.upload = config.fallback ? config.fallback : config.upload; }
270
230
  // In all other cases, re-attempt sending the same data
271
231
  // For retry we always fallback to string payload, even though we may have attempted
272
232
  // sending zipped payload earlier
@@ -275,13 +235,9 @@ function check(xhr: XMLHttpRequest, sequence: number): void {
275
235
  } else {
276
236
  track = { sequence, attempts: transitData.attempts, status: xhr.status };
277
237
  // Send back an event only if we were not successful in our first attempt
278
- if (transitData.attempts > 1) {
279
- encode(Event.Upload);
280
- }
238
+ if (transitData.attempts > 1) { encode(Event.Upload); }
281
239
  // Handle response if it was a 200 response with a valid body
282
- if (xhr.status === 200 && xhr.responseText) {
283
- response(xhr.responseText);
284
- }
240
+ if (xhr.status === 200 && xhr.responseText) { response(xhr.responseText); }
285
241
  // If we exhausted our retries then trigger Clarity's shutdown for this page since the data will be incomplete
286
242
  if (xhr.status === 0) {
287
243
  // And, right before we terminate the session, we will attempt one last time to see if we can use
@@ -290,9 +246,7 @@ function check(xhr: XMLHttpRequest, sequence: number): void {
290
246
  limit.trigger(Check.Retry);
291
247
  }
292
248
  // Signal that this request completed successfully
293
- if (xhr.status >= 200 && xhr.status <= 208) {
294
- done(sequence);
295
- }
249
+ if (xhr.status >= 200 && xhr.status <= 208) { done(sequence); }
296
250
  // Stop tracking this payload now that it's all done
297
251
  delete transit[sequence];
298
252
  }
@@ -310,14 +264,15 @@ function done(sequence: number): void {
310
264
  function delay(): number {
311
265
  // Progressively increase delay as we continue to send more payloads from the client to the server
312
266
  // If we are not uploading data to a server, and instead invoking UploadCallback, in that case keep returning configured value
313
- const gap = config.lean === false && discoverBytes > 0 ? Setting.MinUploadDelay : envelope.data.sequence * config.delay;
314
- return typeof config.upload === "string" ? Math.max(Math.min(gap, Setting.MaxUploadDelay), Setting.MinUploadDelay) : config.delay;
267
+ let gap = config.lean === false && discoverBytes > 0 ? Setting.MinUploadDelay : envelope.data.sequence * config.delay;
268
+ return typeof config.upload === Constant.String ? Math.max(Math.min(gap, Setting.MaxUploadDelay), Setting.MinUploadDelay) : config.delay;
315
269
  }
316
270
 
317
271
  function response(payload: string): void {
318
- const lines = payload && payload.length > 0 ? payload.split("\n") : [];
319
- for (const line of lines) {
320
- const parts = line && line.length > 0 ? line.split(/ (.*)/) : [Constant.Empty];
272
+ let lines = payload && payload.length > 0 ? payload.split("\n") : [];
273
+ for (var line of lines)
274
+ {
275
+ let parts = line && line.length > 0 ? line.split(/ (.*)/) : [Constant.Empty];
321
276
  switch (parts[0]) {
322
277
  case Constant.End:
323
278
  // Clear out session storage and end the session so we can start fresh the next time
@@ -329,19 +284,13 @@ function response(payload: string): void {
329
284
  break;
330
285
  case Constant.Action:
331
286
  // Invoke action callback, if configured and has a valid value
332
- if (config.action && parts.length > 1) {
333
- config.action(parts[1]);
334
- }
287
+ if (config.action && parts.length > 1) { config.action(parts[1]); }
335
288
  break;
336
289
  case Constant.Extract:
337
- if (parts.length > 1) {
338
- extract.trigger(parts[1]);
339
- }
290
+ if (parts.length > 1) { extract.trigger(parts[1]); }
340
291
  break;
341
292
  case Constant.Signal:
342
- if (parts.length > 1) {
343
- signalsEvent(parts[1]);
344
- }
293
+ if (parts.length > 1) { signalsEvent(parts[1]); }
345
294
  break;
346
295
  }
347
296
  }
@@ -1,4 +1,4 @@
1
- import { Constant, Event, type IdentityData, Setting, type VariableData } from "@clarity-types/data";
1
+ import { Constant, Event, IdentityData, Setting, VariableData } from "@clarity-types/data";
2
2
  import * as core from "@src/core";
3
3
  import { scrub } from "@src/core/scrub";
4
4
  import encode from "./encode";
@@ -10,28 +10,23 @@ export function start(): void {
10
10
  }
11
11
 
12
12
  export function set(variable: string, value: string | string[]): void {
13
- const values = typeof value === "string" ? [value as string] : (value as string[]);
13
+ let values = typeof value === Constant.String ? [value as string] : value as string[];
14
14
  log(variable, values);
15
15
  }
16
16
 
17
- export async function identify(
18
- userId: string,
19
- sessionId: string = null,
20
- pageId: string = null,
21
- userHint: string = null,
22
- ): Promise<IdentityData> {
23
- const output: IdentityData = { userId: await sha256(userId), userHint: userHint || redact(userId) };
17
+ export async function identify(userId: string, sessionId: string = null, pageId: string = null, userHint: string = null): Promise<IdentityData> {
18
+ let output: IdentityData = { userId: await sha256(userId), userHint: userHint || redact(userId) };
24
19
 
25
20
  // By default, hash custom userId using SHA256 algorithm on the client to preserve privacy
26
21
  log(Constant.UserId, [output.userId]);
27
-
22
+
28
23
  // Optional non-identifying name for the user
29
24
  // If name is not explicitly provided, we automatically generate a redacted version of the userId
30
25
  log(Constant.UserHint, [output.userHint]);
31
- log(Constant.UserType, [detect(userId)]);
26
+ log(Constant.UserType, [detect(userId)]);
32
27
 
33
28
  // Log sessionId and pageId if provided
34
- if (sessionId) {
29
+ if (sessionId) {
35
30
  log(Constant.SessionId, [sessionId]);
36
31
  output.sessionId = sessionId;
37
32
  }
@@ -44,12 +39,14 @@ export async function identify(
44
39
  }
45
40
 
46
41
  function log(variable: string, value: string[]): void {
47
- if (core.active() && variable && value && typeof variable === "string" && variable.length < 255) {
48
- const validValues = variable in data ? data[variable] : [];
42
+ if (core.active() &&
43
+ variable &&
44
+ value &&
45
+ typeof variable === Constant.String &&
46
+ variable.length < 255) {
47
+ let validValues = variable in data ? data[variable] : [];
49
48
  for (let i = 0; i < value.length; i++) {
50
- if (typeof value[i] === "string" && value[i].length < 255) {
51
- validValues.push(value[i]);
52
- }
49
+ if (typeof value[i] === Constant.String && value[i].length < 255) { validValues.push(value[i]); }
53
50
  }
54
51
  data[variable] = validValues;
55
52
  }
@@ -68,9 +65,8 @@ export function stop(): void {
68
65
  }
69
66
 
70
67
  function redact(input: string): string {
71
- return input && input.length >= Setting.WordLength
72
- ? `${input.substring(0, 2)}${scrub(input.substring(2), Constant.Asterix, Constant.Asterix)}`
73
- : scrub(input, Constant.Asterix, Constant.Asterix);
68
+ return input && input.length >= Setting.WordLength ?
69
+ `${input.substring(0,2)}${scrub(input.substring(2), Constant.Asterix, Constant.Asterix)}` : scrub(input, Constant.Asterix, Constant.Asterix);
74
70
  }
75
71
 
76
72
  async function sha256(input: string): Promise<string> {
@@ -78,14 +74,11 @@ async function sha256(input: string): Promise<string> {
78
74
  if (crypto && input) {
79
75
  // Reference: https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
80
76
  const buffer = await crypto.subtle.digest(Constant.SHA256, new TextEncoder().encode(input));
81
- return Array.prototype.map.call(new Uint8Array(buffer), (x) => `00${x.toString(16)}`.slice(-2)).join("");
82
- }
83
- return Constant.Empty;
84
- } catch {
85
- return Constant.Empty;
86
- }
77
+ return Array.prototype.map.call(new Uint8Array(buffer), (x: any) =>(('00'+x.toString(16)).slice(-2))).join('');
78
+ } else { return Constant.Empty; }
79
+ } catch { return Constant.Empty; }
87
80
  }
88
81
 
89
82
  function detect(input: string): string {
90
83
  return input && input.indexOf(Constant.At) > 0 ? Constant.Email : Constant.String;
91
- }
84
+ }
@@ -1,4 +1,4 @@
1
- import { Event, type Token } from "@clarity-types/data";
1
+ import { Event, Token } from "@clarity-types/data";
2
2
  import * as scrub from "@src/core/scrub";
3
3
  import { time } from "@src/core/time";
4
4
  import { queue } from "@src/data/upload";
@@ -7,7 +7,7 @@ import * as internal from "@src/diagnostic/internal";
7
7
  import * as script from "@src/diagnostic/script";
8
8
 
9
9
  export default async function (type: Event): Promise<void> {
10
- const tokens: Token[] = [time(), type];
10
+ let tokens: Token[] = [time(), type];
11
11
 
12
12
  switch (type) {
13
13
  case Event.ScriptError:
@@ -1,5 +1,5 @@
1
1
  import { BooleanFlag, Event, IframeStatus, Metric, Setting } from "@clarity-types/data";
2
- import type { FraudData } from "@clarity-types/diagnostic";
2
+ import { FraudData } from "@clarity-types/diagnostic";
3
3
  import config from "@src/core/config";
4
4
  import hash from "@src/core/hash";
5
5
  import * as metric from "@src/data/metric";
@@ -13,10 +13,11 @@ export function start(): void {
13
13
  metric.max(Metric.Automation, navigator.webdriver ? BooleanFlag.True : BooleanFlag.False);
14
14
  try {
15
15
  // some sites (unintentionally) overwrite the window.self property, so we also check for the main window object
16
- metric.max(Metric.Iframed, window.top === window.self || window.top === window ? IframeStatus.TopFrame : IframeStatus.Iframe);
16
+ metric.max(Metric.Iframed, window.top == window.self || window.top == window ? IframeStatus.TopFrame : IframeStatus.Iframe);
17
17
  } catch (ex) {
18
18
  metric.max(Metric.Iframed, IframeStatus.Unknown);
19
19
  }
20
+
20
21
  }
21
22
 
22
23
  export function check(id: number, target: number, input: string): void {
@@ -33,4 +34,4 @@ export function check(id: number, target: number, input: string): void {
33
34
 
34
35
  export function stop(): void {
35
36
  history = [];
36
- }
37
+ }