clarity-js 0.8.1 → 0.8.2

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 (102) hide show
  1. package/README.md +26 -26
  2. package/build/clarity.extended.js +1 -1
  3. package/build/clarity.insight.js +1 -1
  4. package/build/clarity.js +5537 -5537
  5. package/build/clarity.min.js +1 -1
  6. package/build/clarity.module.js +5537 -5537
  7. package/build/clarity.performance.js +1 -1
  8. package/package.json +70 -70
  9. package/rollup.config.ts +98 -98
  10. package/src/clarity.ts +60 -60
  11. package/src/core/api.ts +8 -8
  12. package/src/core/config.ts +32 -32
  13. package/src/core/copy.ts +3 -3
  14. package/src/core/event.ts +53 -53
  15. package/src/core/hash.ts +19 -19
  16. package/src/core/history.ts +73 -73
  17. package/src/core/index.ts +83 -83
  18. package/src/core/measure.ts +19 -19
  19. package/src/core/report.ts +28 -28
  20. package/src/core/scrub.ts +196 -196
  21. package/src/core/task.ts +180 -180
  22. package/src/core/time.ts +19 -19
  23. package/src/core/timeout.ts +10 -10
  24. package/src/core/version.ts +2 -2
  25. package/src/data/baseline.ts +155 -155
  26. package/src/data/compress.ts +31 -31
  27. package/src/data/consent.ts +20 -20
  28. package/src/data/custom.ts +23 -23
  29. package/src/data/dimension.ts +53 -53
  30. package/src/data/encode.ts +140 -140
  31. package/src/data/envelope.ts +53 -53
  32. package/src/data/extract.ts +211 -211
  33. package/src/data/index.ts +46 -46
  34. package/src/data/limit.ts +44 -44
  35. package/src/data/metadata.ts +356 -356
  36. package/src/data/metric.ts +51 -51
  37. package/src/data/ping.ts +36 -36
  38. package/src/data/signal.ts +30 -30
  39. package/src/data/summary.ts +34 -34
  40. package/src/data/token.ts +39 -39
  41. package/src/data/upgrade.ts +37 -37
  42. package/src/data/upload.ts +283 -283
  43. package/src/data/variable.ts +83 -83
  44. package/src/diagnostic/encode.ts +40 -40
  45. package/src/diagnostic/fraud.ts +36 -36
  46. package/src/diagnostic/index.ts +15 -15
  47. package/src/diagnostic/internal.ts +28 -28
  48. package/src/diagnostic/script.ts +37 -37
  49. package/src/global.ts +6 -6
  50. package/src/index.ts +9 -9
  51. package/src/insight/blank.ts +14 -14
  52. package/src/insight/encode.ts +60 -60
  53. package/src/insight/snapshot.ts +114 -114
  54. package/src/interaction/change.ts +40 -40
  55. package/src/interaction/click.ts +162 -162
  56. package/src/interaction/clipboard.ts +34 -34
  57. package/src/interaction/encode.ts +193 -193
  58. package/src/interaction/index.ts +59 -59
  59. package/src/interaction/input.ts +59 -59
  60. package/src/interaction/pointer.ts +139 -139
  61. package/src/interaction/resize.ts +47 -47
  62. package/src/interaction/scroll.ts +124 -124
  63. package/src/interaction/selection.ts +68 -68
  64. package/src/interaction/submit.ts +32 -32
  65. package/src/interaction/timeline.ts +65 -65
  66. package/src/interaction/unload.ts +28 -28
  67. package/src/interaction/visibility.ts +26 -26
  68. package/src/layout/animation.ts +133 -133
  69. package/src/layout/discover.ts +31 -31
  70. package/src/layout/document.ts +48 -48
  71. package/src/layout/dom.ts +437 -437
  72. package/src/layout/encode.ts +147 -147
  73. package/src/layout/index.ts +41 -41
  74. package/src/layout/mutation.ts +409 -409
  75. package/src/layout/node.ts +292 -292
  76. package/src/layout/offset.ts +19 -19
  77. package/src/layout/region.ts +153 -153
  78. package/src/layout/schema.ts +63 -63
  79. package/src/layout/selector.ts +82 -82
  80. package/src/layout/style.ts +150 -150
  81. package/src/layout/target.ts +32 -32
  82. package/src/layout/traverse.ts +27 -27
  83. package/src/performance/blank.ts +7 -7
  84. package/src/performance/encode.ts +31 -31
  85. package/src/performance/index.ts +14 -14
  86. package/src/performance/interaction.ts +125 -125
  87. package/src/performance/navigation.ts +31 -31
  88. package/src/performance/observer.ts +108 -108
  89. package/src/queue.ts +33 -33
  90. package/test/core.test.ts +139 -139
  91. package/test/helper.ts +162 -162
  92. package/test/html/core.html +27 -27
  93. package/test/tsconfig.test.json +5 -5
  94. package/tsconfig.json +21 -21
  95. package/tslint.json +32 -32
  96. package/types/core.d.ts +153 -153
  97. package/types/data.d.ts +510 -510
  98. package/types/diagnostic.d.ts +24 -24
  99. package/types/index.d.ts +39 -39
  100. package/types/interaction.d.ts +165 -165
  101. package/types/layout.d.ts +272 -272
  102. package/types/performance.d.ts +64 -64
package/src/core/task.ts CHANGED
@@ -1,180 +1,180 @@
1
- import { AsyncTask, Priority, RequestIdleCallbackDeadline, RequestIdleCallbackOptions, Task, Timer } from "@clarity-types/core";
2
- import { TaskFunction, TaskResolve, Tasks } from "@clarity-types/core";
3
- import { Code, Metric, Setting, Severity } from "@clarity-types/data";
4
- import * as metadata from "@src/data/metadata";
5
- import * as metric from "@src/data/metric";
6
- import * as internal from "@src/diagnostic/internal";
7
-
8
- // Track the start time to be able to compute duration at the end of the task
9
- const idleTimeout = 5000;
10
- let tracker: Tasks = {};
11
- let queuedTasks: AsyncTask[] = [];
12
- let activeTask: AsyncTask = null;
13
- let pauseTask: Promise<void> = null;
14
- let resumeResolve: TaskResolve = null;
15
-
16
- export function pause(): void {
17
- if (pauseTask === null) {
18
- pauseTask = new Promise<void>((resolve: TaskResolve): void => {
19
- resumeResolve = resolve;
20
- });
21
- }
22
- }
23
-
24
- export function resume(): void {
25
- if (pauseTask) {
26
- resumeResolve();
27
- pauseTask = null;
28
- if (activeTask === null) { run(); }
29
- }
30
- }
31
-
32
- export function reset(): void {
33
- tracker = {};
34
- queuedTasks = [];
35
- activeTask = null;
36
- pauseTask = null;
37
- }
38
-
39
- export async function schedule(task: TaskFunction, priority: Priority = Priority.Normal): Promise<void> {
40
- // If this task is already scheduled, skip it
41
- for (let q of queuedTasks) {
42
- if (q.task === task) {
43
- return;
44
- }
45
- }
46
-
47
- let promise = new Promise<void>((resolve: TaskResolve): void => {
48
- let insert = priority === Priority.High ? "unshift" : "push";
49
- // Queue this task for asynchronous execution later
50
- // We also store a unique page identifier (id) along with the task to ensure
51
- // ensure that we do not accidentally execute this task in context of a different page
52
- queuedTasks[insert]({ task, resolve, id: metadata.id() });
53
- });
54
-
55
- // If there is no active task running, and Clarity is not in pause state,
56
- // invoke the first task in the queue synchronously. This ensures that we don't yield the thread during unload event
57
- if (activeTask === null && pauseTask === null) { run(); }
58
-
59
- return promise;
60
- }
61
-
62
- function run(): void {
63
- let entry = queuedTasks.shift();
64
- if (entry) {
65
- activeTask = entry;
66
- entry.task().then((): void => {
67
- // Bail out if the context in which this task was operating is different from the current page
68
- // An example scenario where task could span across pages is Single Page Applications (SPA)
69
- // A task that started on page #1, but completes on page #2
70
- if (entry.id !== metadata.id()) { return; }
71
- entry.resolve();
72
- activeTask = null; // Reset active task back to null now that the promise is resolved
73
- run();
74
- }).catch((error: Error): void => {
75
- // If one of the scheduled tasks failed, log, recover and continue processing rest of the tasks
76
- if (entry.id !== metadata.id()) { return; }
77
- if (error) { internal.log(Code.RunTask, Severity.Warning, error.name, error.message, error.stack); }
78
- activeTask = null;
79
- run();
80
- });
81
- }
82
- }
83
-
84
- export function state(timer: Timer): Task {
85
- let id = key(timer);
86
- if (id in tracker) {
87
- let elapsed = performance.now() - tracker[id].start;
88
- return (elapsed > tracker[id].yield) ? Task.Wait : Task.Run;
89
- }
90
- // If this task is no longer being tracked, send stop message to the caller
91
- return Task.Stop;
92
- }
93
-
94
- export function start(timer: Timer): void {
95
- tracker[key(timer)] = { start: performance.now(), calls: 0, yield: Setting.LongTask };
96
- }
97
-
98
- function restart(timer: Timer): void {
99
- let id = key(timer);
100
- if (tracker && tracker[id]) {
101
- let c = tracker[id].calls;
102
- let y = tracker[id].yield;
103
- start(timer);
104
- tracker[id].calls = c + 1;
105
- tracker[id].yield = y;
106
- }
107
- }
108
-
109
- export function stop(timer: Timer): void {
110
- let end = performance.now();
111
- let id = key(timer);
112
- let duration = end - tracker[id].start;
113
- metric.sum(timer.cost, duration);
114
- metric.count(Metric.InvokeCount);
115
-
116
- // For the first execution, which is synchronous, time is automatically counted towards TotalDuration.
117
- // However, for subsequent asynchronous runs, we need to manually update TotalDuration metric.
118
- if (tracker[id].calls > 0) { metric.sum(Metric.TotalCost, duration); }
119
- }
120
-
121
- export async function suspend(timer: Timer): Promise<Task> {
122
- // Suspend and yield the thread only if the task is still being tracked
123
- // It's possible that Clarity is wrapping up instrumentation on a page and we are still in the middle of an async task.
124
- // In that case, we do not wish to continue yielding thread.
125
- // Instead, we will turn async task into a sync task and maximize our chances of getting some data back.
126
- let id = key(timer);
127
- if (id in tracker) {
128
- stop(timer);
129
- tracker[id].yield = (await wait()).timeRemaining();
130
- restart(timer);
131
- }
132
- // After we are done with suspending task, ensure that we are still operating in the right context
133
- // If the task is still being tracked, continue running the task, otherwise ask caller to stop execution
134
- return id in tracker ? Task.Run : Task.Stop;
135
- }
136
-
137
- function key(timer: Timer): string {
138
- return `${timer.id}.${timer.cost}`;
139
- }
140
-
141
- async function wait(): Promise<RequestIdleCallbackDeadline> {
142
- if (pauseTask) { await pauseTask; }
143
- return new Promise<RequestIdleCallbackDeadline>((resolve: (deadline: RequestIdleCallbackDeadline) => void): void => {
144
- requestIdleCallback(resolve, { timeout: idleTimeout });
145
- });
146
- }
147
-
148
- // Use native implementation of requestIdleCallback if it exists.
149
- // Otherwise, fall back to a custom implementation using requestAnimationFrame & MessageChannel.
150
- // While it's not possible to build a perfect polyfill given the nature of this API, the following code attempts to get close.
151
- // Background context: requestAnimationFrame invokes the js code right before: style, layout and paint computation within the frame.
152
- // This means, that any code that runs as part of requestAnimationFrame will by default be blocking in nature. Not what we want.
153
- // For non-blocking behavior, We need to know when browser has finished painting. This can be accomplished in two different ways (hacks):
154
- // (1) Use MessageChannel to pass the message, and browser will receive the message right after paint event has occured.
155
- // (2) Use setTimeout call within requestAnimationFrame. This also works, but there's a risk that browser may throttle setTimeout calls.
156
- // Given this information, we are currently using (1) from above. More information on (2) as well as some additional context is below:
157
- // https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Performance_best_practices_for_Firefox_fe_engineers
158
- function requestIdleCallbackPolyfill(callback: (deadline: RequestIdleCallbackDeadline) => void, options: RequestIdleCallbackOptions): void {
159
- const startTime = performance.now();
160
- const channel = new MessageChannel();
161
- const incoming = channel.port1;
162
- const outgoing = channel.port2;
163
- incoming.onmessage = (event: MessageEvent): void => {
164
- let currentTime = performance.now();
165
- let elapsed = currentTime - startTime;
166
- let duration = currentTime - event.data;
167
- if (duration > Setting.LongTask && elapsed < options.timeout) {
168
- requestAnimationFrame((): void => { outgoing.postMessage(currentTime); });
169
- } else {
170
- let didTimeout = elapsed > options.timeout;
171
- callback({
172
- didTimeout,
173
- timeRemaining: (): number => didTimeout ? Setting.LongTask : Math.max(0, Setting.LongTask - duration)
174
- });
175
- }
176
- };
177
- requestAnimationFrame((): void => { outgoing.postMessage(performance.now()); });
178
- }
179
-
180
- let requestIdleCallback = window["requestIdleCallback"] || requestIdleCallbackPolyfill;
1
+ import { AsyncTask, Priority, RequestIdleCallbackDeadline, RequestIdleCallbackOptions, Task, Timer } from "@clarity-types/core";
2
+ import { TaskFunction, TaskResolve, Tasks } from "@clarity-types/core";
3
+ import { Code, Metric, Setting, Severity } from "@clarity-types/data";
4
+ import * as metadata from "@src/data/metadata";
5
+ import * as metric from "@src/data/metric";
6
+ import * as internal from "@src/diagnostic/internal";
7
+
8
+ // Track the start time to be able to compute duration at the end of the task
9
+ const idleTimeout = 5000;
10
+ let tracker: Tasks = {};
11
+ let queuedTasks: AsyncTask[] = [];
12
+ let activeTask: AsyncTask = null;
13
+ let pauseTask: Promise<void> = null;
14
+ let resumeResolve: TaskResolve = null;
15
+
16
+ export function pause(): void {
17
+ if (pauseTask === null) {
18
+ pauseTask = new Promise<void>((resolve: TaskResolve): void => {
19
+ resumeResolve = resolve;
20
+ });
21
+ }
22
+ }
23
+
24
+ export function resume(): void {
25
+ if (pauseTask) {
26
+ resumeResolve();
27
+ pauseTask = null;
28
+ if (activeTask === null) { run(); }
29
+ }
30
+ }
31
+
32
+ export function reset(): void {
33
+ tracker = {};
34
+ queuedTasks = [];
35
+ activeTask = null;
36
+ pauseTask = null;
37
+ }
38
+
39
+ export async function schedule(task: TaskFunction, priority: Priority = Priority.Normal): Promise<void> {
40
+ // If this task is already scheduled, skip it
41
+ for (let q of queuedTasks) {
42
+ if (q.task === task) {
43
+ return;
44
+ }
45
+ }
46
+
47
+ let promise = new Promise<void>((resolve: TaskResolve): void => {
48
+ let insert = priority === Priority.High ? "unshift" : "push";
49
+ // Queue this task for asynchronous execution later
50
+ // We also store a unique page identifier (id) along with the task to ensure
51
+ // ensure that we do not accidentally execute this task in context of a different page
52
+ queuedTasks[insert]({ task, resolve, id: metadata.id() });
53
+ });
54
+
55
+ // If there is no active task running, and Clarity is not in pause state,
56
+ // invoke the first task in the queue synchronously. This ensures that we don't yield the thread during unload event
57
+ if (activeTask === null && pauseTask === null) { run(); }
58
+
59
+ return promise;
60
+ }
61
+
62
+ function run(): void {
63
+ let entry = queuedTasks.shift();
64
+ if (entry) {
65
+ activeTask = entry;
66
+ entry.task().then((): void => {
67
+ // Bail out if the context in which this task was operating is different from the current page
68
+ // An example scenario where task could span across pages is Single Page Applications (SPA)
69
+ // A task that started on page #1, but completes on page #2
70
+ if (entry.id !== metadata.id()) { return; }
71
+ entry.resolve();
72
+ activeTask = null; // Reset active task back to null now that the promise is resolved
73
+ run();
74
+ }).catch((error: Error): void => {
75
+ // If one of the scheduled tasks failed, log, recover and continue processing rest of the tasks
76
+ if (entry.id !== metadata.id()) { return; }
77
+ if (error) { internal.log(Code.RunTask, Severity.Warning, error.name, error.message, error.stack); }
78
+ activeTask = null;
79
+ run();
80
+ });
81
+ }
82
+ }
83
+
84
+ export function state(timer: Timer): Task {
85
+ let id = key(timer);
86
+ if (id in tracker) {
87
+ let elapsed = performance.now() - tracker[id].start;
88
+ return (elapsed > tracker[id].yield) ? Task.Wait : Task.Run;
89
+ }
90
+ // If this task is no longer being tracked, send stop message to the caller
91
+ return Task.Stop;
92
+ }
93
+
94
+ export function start(timer: Timer): void {
95
+ tracker[key(timer)] = { start: performance.now(), calls: 0, yield: Setting.LongTask };
96
+ }
97
+
98
+ function restart(timer: Timer): void {
99
+ let id = key(timer);
100
+ if (tracker && tracker[id]) {
101
+ let c = tracker[id].calls;
102
+ let y = tracker[id].yield;
103
+ start(timer);
104
+ tracker[id].calls = c + 1;
105
+ tracker[id].yield = y;
106
+ }
107
+ }
108
+
109
+ export function stop(timer: Timer): void {
110
+ let end = performance.now();
111
+ let id = key(timer);
112
+ let duration = end - tracker[id].start;
113
+ metric.sum(timer.cost, duration);
114
+ metric.count(Metric.InvokeCount);
115
+
116
+ // For the first execution, which is synchronous, time is automatically counted towards TotalDuration.
117
+ // However, for subsequent asynchronous runs, we need to manually update TotalDuration metric.
118
+ if (tracker[id].calls > 0) { metric.sum(Metric.TotalCost, duration); }
119
+ }
120
+
121
+ export async function suspend(timer: Timer): Promise<Task> {
122
+ // Suspend and yield the thread only if the task is still being tracked
123
+ // It's possible that Clarity is wrapping up instrumentation on a page and we are still in the middle of an async task.
124
+ // In that case, we do not wish to continue yielding thread.
125
+ // Instead, we will turn async task into a sync task and maximize our chances of getting some data back.
126
+ let id = key(timer);
127
+ if (id in tracker) {
128
+ stop(timer);
129
+ tracker[id].yield = (await wait()).timeRemaining();
130
+ restart(timer);
131
+ }
132
+ // After we are done with suspending task, ensure that we are still operating in the right context
133
+ // If the task is still being tracked, continue running the task, otherwise ask caller to stop execution
134
+ return id in tracker ? Task.Run : Task.Stop;
135
+ }
136
+
137
+ function key(timer: Timer): string {
138
+ return `${timer.id}.${timer.cost}`;
139
+ }
140
+
141
+ async function wait(): Promise<RequestIdleCallbackDeadline> {
142
+ if (pauseTask) { await pauseTask; }
143
+ return new Promise<RequestIdleCallbackDeadline>((resolve: (deadline: RequestIdleCallbackDeadline) => void): void => {
144
+ requestIdleCallback(resolve, { timeout: idleTimeout });
145
+ });
146
+ }
147
+
148
+ // Use native implementation of requestIdleCallback if it exists.
149
+ // Otherwise, fall back to a custom implementation using requestAnimationFrame & MessageChannel.
150
+ // While it's not possible to build a perfect polyfill given the nature of this API, the following code attempts to get close.
151
+ // Background context: requestAnimationFrame invokes the js code right before: style, layout and paint computation within the frame.
152
+ // This means, that any code that runs as part of requestAnimationFrame will by default be blocking in nature. Not what we want.
153
+ // For non-blocking behavior, We need to know when browser has finished painting. This can be accomplished in two different ways (hacks):
154
+ // (1) Use MessageChannel to pass the message, and browser will receive the message right after paint event has occured.
155
+ // (2) Use setTimeout call within requestAnimationFrame. This also works, but there's a risk that browser may throttle setTimeout calls.
156
+ // Given this information, we are currently using (1) from above. More information on (2) as well as some additional context is below:
157
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Performance_best_practices_for_Firefox_fe_engineers
158
+ function requestIdleCallbackPolyfill(callback: (deadline: RequestIdleCallbackDeadline) => void, options: RequestIdleCallbackOptions): void {
159
+ const startTime = performance.now();
160
+ const channel = new MessageChannel();
161
+ const incoming = channel.port1;
162
+ const outgoing = channel.port2;
163
+ incoming.onmessage = (event: MessageEvent): void => {
164
+ let currentTime = performance.now();
165
+ let elapsed = currentTime - startTime;
166
+ let duration = currentTime - event.data;
167
+ if (duration > Setting.LongTask && elapsed < options.timeout) {
168
+ requestAnimationFrame((): void => { outgoing.postMessage(currentTime); });
169
+ } else {
170
+ let didTimeout = elapsed > options.timeout;
171
+ callback({
172
+ didTimeout,
173
+ timeRemaining: (): number => didTimeout ? Setting.LongTask : Math.max(0, Setting.LongTask - duration)
174
+ });
175
+ }
176
+ };
177
+ requestAnimationFrame((): void => { outgoing.postMessage(performance.now()); });
178
+ }
179
+
180
+ let requestIdleCallback = window["requestIdleCallback"] || requestIdleCallbackPolyfill;
package/src/core/time.ts CHANGED
@@ -1,19 +1,19 @@
1
- let startTime = 0;
2
-
3
- export function start(): void {
4
- startTime = performance.now() + performance.timeOrigin;
5
- }
6
-
7
- // event.timestamp is number of milliseconds elapsed since the document was loaded
8
- // since iframes can be loaded later the event timestamp is not the same as performance.now()
9
- // converting everything to absolute time by adding timeorigin of the event view
10
- // to synchronize times before calculating the difference with start time
11
- export function time(event: UIEvent | PageTransitionEvent = null): number {
12
- let ts = event && event.timeStamp > 0 ? event.timeStamp : performance.now();
13
- let origin = event && (event as UIEvent).view ? (event as UIEvent).view.performance.timeOrigin : performance.timeOrigin;
14
- return Math.max(Math.round(ts + origin - startTime), 0);
15
- }
16
-
17
- export function stop(): void {
18
- startTime = 0;
19
- }
1
+ let startTime = 0;
2
+
3
+ export function start(): void {
4
+ startTime = performance.now() + performance.timeOrigin;
5
+ }
6
+
7
+ // event.timestamp is number of milliseconds elapsed since the document was loaded
8
+ // since iframes can be loaded later the event timestamp is not the same as performance.now()
9
+ // converting everything to absolute time by adding timeorigin of the event view
10
+ // to synchronize times before calculating the difference with start time
11
+ export function time(event: UIEvent | PageTransitionEvent = null): number {
12
+ let ts = event && event.timeStamp > 0 ? event.timeStamp : performance.now();
13
+ let origin = event && (event as UIEvent).view ? (event as UIEvent).view.performance.timeOrigin : performance.timeOrigin;
14
+ return Math.max(Math.round(ts + origin - startTime), 0);
15
+ }
16
+
17
+ export function stop(): void {
18
+ startTime = 0;
19
+ }
@@ -1,10 +1,10 @@
1
- import { Event } from "@clarity-types/data";
2
- import measure from "./measure";
3
-
4
- export function setTimeout(handler: (event?: Event | boolean) => void, timeout?: number, event?: Event): number {
5
- return window.setTimeout(measure(handler), timeout, event);
6
- }
7
-
8
- export function clearTimeout(handle: number): void {
9
- return window.clearTimeout(handle);
10
- }
1
+ import { Event } from "@clarity-types/data";
2
+ import measure from "./measure";
3
+
4
+ export function setTimeout(handler: (event?: Event | boolean) => void, timeout?: number, event?: Event): number {
5
+ return window.setTimeout(measure(handler), timeout, event);
6
+ }
7
+
8
+ export function clearTimeout(handle: number): void {
9
+ return window.clearTimeout(handle);
10
+ }
@@ -1,2 +1,2 @@
1
- let version = "0.8.1";
2
- export default version;
1
+ let version = "0.8.2";
2
+ export default version;