clarity-js 0.7.27 → 0.7.31
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.
- package/build/clarity.extended.js +1 -1
- package/build/clarity.insight.js +1 -1
- package/build/clarity.js +89 -18
- package/build/clarity.min.js +1 -1
- package/build/clarity.module.js +89 -18
- package/build/clarity.performance.js +1 -1
- package/package.json +1 -1
- package/src/clarity.ts +2 -2
- package/src/core/config.ts +2 -1
- package/src/core/version.ts +1 -1
- package/src/data/baseline.ts +6 -3
- package/src/data/encode.ts +1 -0
- package/src/data/index.ts +1 -0
- package/src/data/signal.ts +30 -0
- package/src/data/upload.ts +4 -0
- package/src/insight/snapshot.ts +2 -0
- package/src/interaction/encode.ts +3 -1
- package/src/interaction/scroll.ts +28 -2
- package/src/layout/discover.ts +2 -2
- package/src/layout/index.ts +8 -1
- package/src/layout/mutation.ts +10 -10
- package/src/layout/node.ts +4 -4
- package/src/layout/style.ts +11 -6
- package/src/layout/traverse.ts +2 -2
- package/types/core.d.ts +1 -0
- package/types/data.d.ts +8 -0
- package/types/index.d.ts +1 -0
- package/types/interaction.d.ts +2 -0
package/src/layout/mutation.ts
CHANGED
|
@@ -129,19 +129,19 @@ async function process(): Promise<void> {
|
|
|
129
129
|
if (state === Task.Wait) { state = await task.suspend(timer); }
|
|
130
130
|
if (state === Task.Stop) { break; }
|
|
131
131
|
let target = mutation.target;
|
|
132
|
-
let type = track(mutation, timer, instance);
|
|
132
|
+
let type = track(mutation, timer, instance, record.time);
|
|
133
133
|
if (type && target && target.ownerDocument) { dom.parse(target.ownerDocument); }
|
|
134
134
|
if (type && target && target.nodeType == Node.DOCUMENT_FRAGMENT_NODE && (target as ShadowRoot).host) { dom.parse(target as ShadowRoot); }
|
|
135
135
|
switch (type) {
|
|
136
136
|
case Constant.Attributes:
|
|
137
|
-
processNode(target, Source.Attributes);
|
|
137
|
+
processNode(target, Source.Attributes, record.time);
|
|
138
138
|
break;
|
|
139
139
|
case Constant.CharacterData:
|
|
140
|
-
processNode(target, Source.CharacterData);
|
|
140
|
+
processNode(target, Source.CharacterData, record.time);
|
|
141
141
|
break;
|
|
142
142
|
case Constant.ChildList:
|
|
143
|
-
processNodeList(mutation.addedNodes, Source.ChildListAdd, timer);
|
|
144
|
-
processNodeList(mutation.removedNodes, Source.ChildListRemove, timer);
|
|
143
|
+
processNodeList(mutation.addedNodes, Source.ChildListAdd, timer, record.time);
|
|
144
|
+
processNodeList(mutation.removedNodes, Source.ChildListRemove, timer, record.time);
|
|
145
145
|
break;
|
|
146
146
|
case Constant.Suspend:
|
|
147
147
|
let value = dom.get(target);
|
|
@@ -156,7 +156,7 @@ async function process(): Promise<void> {
|
|
|
156
156
|
task.stop(timer);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
function track(m: MutationRecord, timer: Timer, instance: number): string {
|
|
159
|
+
function track(m: MutationRecord, timer: Timer, instance: number, timestamp: number): string {
|
|
160
160
|
let value = m.target ? dom.get(m.target.parentNode) : null;
|
|
161
161
|
// Check if the parent is already discovered and that the parent is not the document root
|
|
162
162
|
if (value && value.data.tag !== Constant.HTML) {
|
|
@@ -172,7 +172,7 @@ function track(m: MutationRecord, timer: Timer, instance: number): string {
|
|
|
172
172
|
history[key] = key in history ? history[key] : [0, instance];
|
|
173
173
|
let h = history[key];
|
|
174
174
|
// Lookup any pending nodes queued up for removal, and process them now if we suspended a mutation before
|
|
175
|
-
if (inactive === false && h[0] >= Setting.MutationSuspendThreshold) { processNodeList(h[2], Source.ChildListRemove, timer); }
|
|
175
|
+
if (inactive === false && h[0] >= Setting.MutationSuspendThreshold) { processNodeList(h[2], Source.ChildListRemove, timer, timestamp); }
|
|
176
176
|
// Update the counter
|
|
177
177
|
h[0] = inactive ? (h[1] === instance ? h[0] : h[0] + 1) : 1;
|
|
178
178
|
h[1] = instance;
|
|
@@ -195,16 +195,16 @@ function names(nodes: NodeList): string {
|
|
|
195
195
|
return output.join();
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
async function processNodeList(list: NodeList, source: Source, timer: Timer): Promise<void> {
|
|
198
|
+
async function processNodeList(list: NodeList, source: Source, timer: Timer, timestamp: number): Promise<void> {
|
|
199
199
|
let length = list ? list.length : 0;
|
|
200
200
|
for (let i = 0; i < length; i++) {
|
|
201
201
|
if (source === Source.ChildListAdd) {
|
|
202
|
-
traverse(list[i], timer, source);
|
|
202
|
+
traverse(list[i], timer, source, timestamp);
|
|
203
203
|
} else {
|
|
204
204
|
let state = task.state(timer);
|
|
205
205
|
if (state === Task.Wait) { state = await task.suspend(timer); }
|
|
206
206
|
if (state === Task.Stop) { break; }
|
|
207
|
-
processNode(list[i], source);
|
|
207
|
+
processNode(list[i], source, timestamp);
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
}
|
package/src/layout/node.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { electron } from "@src/data/metadata";
|
|
|
12
12
|
const IGNORE_ATTRIBUTES = ["title", "alt", "onload", "onfocus", "onerror", "data-drupal-form-submit-last"];
|
|
13
13
|
const newlineRegex = /[\r\n]+/g;
|
|
14
14
|
|
|
15
|
-
export default function (node: Node, source: Source): Node {
|
|
15
|
+
export default function (node: Node, source: Source, timestamp: number): Node {
|
|
16
16
|
let child: Node = null;
|
|
17
17
|
|
|
18
18
|
// Do not track this change if we are attempting to remove a node before discovering it
|
|
@@ -43,7 +43,7 @@ export default function (node: Node, source: Source): Node {
|
|
|
43
43
|
// We check for regions in the beginning when discovering document and
|
|
44
44
|
// later whenever there are new additions or modifications to DOM (mutations)
|
|
45
45
|
if (node === document) dom.parse(document);
|
|
46
|
-
checkDocumentStyles(node as Document);
|
|
46
|
+
checkDocumentStyles(node as Document, timestamp);
|
|
47
47
|
observe(node);
|
|
48
48
|
break;
|
|
49
49
|
case Node.DOCUMENT_FRAGMENT_NODE:
|
|
@@ -67,7 +67,7 @@ export default function (node: Node, source: Source): Node {
|
|
|
67
67
|
// the same way we observe real shadow DOM nodes (encapsulation provided by the browser).
|
|
68
68
|
dom[call](node, shadowRoot.host, { tag: Constant.PolyfillShadowDomTag, attributes: {} }, source);
|
|
69
69
|
}
|
|
70
|
-
checkDocumentStyles(node as Document);
|
|
70
|
+
checkDocumentStyles(node as Document, timestamp);
|
|
71
71
|
}
|
|
72
72
|
break;
|
|
73
73
|
case Node.TEXT_NODE:
|
|
@@ -187,7 +187,7 @@ export default function (node: Node, source: Source): Node {
|
|
|
187
187
|
case "SOURCE":
|
|
188
188
|
// Ignoring any base64 src attribute for media elements to prevent big unused tokens to be sent and shock the network
|
|
189
189
|
if (Constant.Src in attributes && attributes[Constant.Src].startsWith("data:")) {
|
|
190
|
-
|
|
190
|
+
attributes[Constant.Src] = "";
|
|
191
191
|
}
|
|
192
192
|
let mediaTag = { tag, attributes };
|
|
193
193
|
dom[call](node, parent, mediaTag, source);
|
package/src/layout/style.ts
CHANGED
|
@@ -14,6 +14,7 @@ let replaceSync: (text?: string) => void = null;
|
|
|
14
14
|
const styleSheetId = 'claritySheetId';
|
|
15
15
|
const styleSheetPageNum = 'claritySheetNum';
|
|
16
16
|
let styleSheetMap = {};
|
|
17
|
+
let styleTimeMap: {[key: string]: number} = {};
|
|
17
18
|
|
|
18
19
|
export function start(): void {
|
|
19
20
|
reset();
|
|
@@ -56,7 +57,8 @@ function bootStrapStyleSheet(styleSheet: CSSStyleSheet): void {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
export function checkDocumentStyles(documentNode: Document): void {
|
|
60
|
+
export function checkDocumentStyles(documentNode: Document, timestamp: number): void {
|
|
61
|
+
timestamp = timestamp || time();
|
|
60
62
|
if (!documentNode?.adoptedStyleSheets) {
|
|
61
63
|
// if we don't have adoptedStyledSheets on the Node passed to us, we can short circuit.
|
|
62
64
|
return;
|
|
@@ -69,8 +71,8 @@ export function checkDocumentStyles(documentNode: Document): void {
|
|
|
69
71
|
if (styleSheet[styleSheetPageNum] !== pageNum) {
|
|
70
72
|
styleSheet[styleSheetPageNum] = pageNum;
|
|
71
73
|
styleSheet[styleSheetId] = shortid();
|
|
72
|
-
trackStyleChange(
|
|
73
|
-
trackStyleChange(
|
|
74
|
+
trackStyleChange(timestamp, styleSheet[styleSheetId], StyleSheetOperation.Create);
|
|
75
|
+
trackStyleChange(timestamp, styleSheet[styleSheetId], StyleSheetOperation.ReplaceSync, getCssRules(styleSheet));
|
|
74
76
|
}
|
|
75
77
|
currentStyleSheets.push(styleSheet[styleSheetId]);
|
|
76
78
|
}
|
|
@@ -81,14 +83,16 @@ export function checkDocumentStyles(documentNode: Document): void {
|
|
|
81
83
|
}
|
|
82
84
|
if (!arraysEqual(currentStyleSheets, styleSheetMap[documentId])) {
|
|
83
85
|
// Using -1 to signify the root document node as we don't track that as part of our nodeMap
|
|
84
|
-
trackStyleAdoption(
|
|
86
|
+
trackStyleAdoption(timestamp, documentNode == document ? -1 : getId(documentNode), StyleSheetOperation.SetAdoptedStyles, currentStyleSheets);
|
|
85
87
|
styleSheetMap[documentId] = currentStyleSheets;
|
|
88
|
+
styleTimeMap[documentId] = timestamp;
|
|
86
89
|
}
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
export function compute(): void {
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
let ts = -1 in styleTimeMap ? styleTimeMap[-1] : null;
|
|
94
|
+
checkDocumentStyles(document, ts);
|
|
95
|
+
Object.keys(styleSheetMap).forEach((x) => checkDocumentStyles(getNode(parseInt(x, 10)) as Document, styleTimeMap[x]));
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
export function reset(): void {
|
|
@@ -97,6 +101,7 @@ export function reset(): void {
|
|
|
97
101
|
|
|
98
102
|
export function stop(): void {
|
|
99
103
|
styleSheetMap = {};
|
|
104
|
+
styleTimeMap = {};
|
|
100
105
|
reset();
|
|
101
106
|
}
|
|
102
107
|
|
package/src/layout/traverse.ts
CHANGED
|
@@ -3,7 +3,7 @@ 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): Promise<void> {
|
|
6
|
+
export default async function(root: Node, timer: Timer, source: Source, timestamp: number): Promise<void> {
|
|
7
7
|
let queue = [root];
|
|
8
8
|
while (queue.length > 0) {
|
|
9
9
|
let entry = queue.shift();
|
|
@@ -22,7 +22,7 @@ export default async function(root: Node, timer: Timer, source: Source): Promise
|
|
|
22
22
|
// Check if processing a node gives us a pointer to one of its sub nodes for traversal
|
|
23
23
|
// E.g. an element node may give us a pointer to traverse shadowDom if shadowRoot property is set
|
|
24
24
|
// Or, an iframe from the same origin could give a pointer to it's document for traversing contents of iframe.
|
|
25
|
-
let subnode = node(entry, source);
|
|
25
|
+
let subnode = node(entry, source, timestamp);
|
|
26
26
|
if (subnode) { queue.push(subnode); }
|
|
27
27
|
}
|
|
28
28
|
}
|
package/types/core.d.ts
CHANGED
package/types/data.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface MetadataCallbackOptions {
|
|
|
8
8
|
callback: MetadataCallback,
|
|
9
9
|
wait: boolean
|
|
10
10
|
}
|
|
11
|
+
export type SignalCallback = (data: ClaritySignal) => void
|
|
11
12
|
|
|
12
13
|
/* Enum */
|
|
13
14
|
export const enum Event {
|
|
@@ -278,6 +279,7 @@ export const enum Constant {
|
|
|
278
279
|
End = "END",
|
|
279
280
|
Upgrade = "UPGRADE",
|
|
280
281
|
Action = "ACTION",
|
|
282
|
+
Signal = "SIGNAL",
|
|
281
283
|
Extract = "EXTRACT",
|
|
282
284
|
UserHint = "userHint",
|
|
283
285
|
UserType = "userType",
|
|
@@ -389,6 +391,7 @@ export interface BaselineData {
|
|
|
389
391
|
pointerX: number;
|
|
390
392
|
pointerY: number;
|
|
391
393
|
activityTime: number;
|
|
394
|
+
scrollTime: number;
|
|
392
395
|
}
|
|
393
396
|
|
|
394
397
|
export interface IdentityData {
|
|
@@ -444,3 +447,8 @@ export interface UploadData {
|
|
|
444
447
|
attempts: number;
|
|
445
448
|
status: number;
|
|
446
449
|
}
|
|
450
|
+
|
|
451
|
+
export interface ClaritySignal {
|
|
452
|
+
type: string
|
|
453
|
+
value?: number
|
|
454
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ interface Clarity {
|
|
|
16
16
|
set: (variable: string, value: string | string[]) => void;
|
|
17
17
|
identify: (userId: string, sessionId?: string, pageId?: string, userHint?: string) => void;
|
|
18
18
|
metadata: (callback: Data.MetadataCallback, wait?: boolean) => void;
|
|
19
|
+
signal: (callback: Data.SignalCallback) => void;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
interface Selector {
|