clarity-js 0.8.13-beta → 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.
- package/build/clarity.extended.js +1 -1
- package/build/clarity.insight.js +1 -1
- package/build/clarity.js +104 -30
- package/build/clarity.min.js +1 -1
- package/build/clarity.module.js +104 -30
- package/build/clarity.performance.js +1 -1
- package/package.json +1 -1
- package/src/clarity.ts +1 -1
- package/src/core/report.ts +3 -7
- package/src/core/scrub.ts +7 -1
- package/src/core/version.ts +1 -1
- package/src/data/consent.ts +21 -3
- package/src/data/encode.ts +8 -0
- package/src/data/index.ts +3 -1
- package/src/data/metadata.ts +65 -12
- package/src/interaction/pointer.ts +6 -6
- package/src/interaction/scroll.ts +6 -6
- package/src/layout/region.ts +3 -6
- package/src/layout/style.ts +1 -1
- package/types/data.d.ts +24 -4
- package/types/index.d.ts +1 -0
- package/types/interaction.d.ts +0 -1
package/src/data/metadata.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Time } from "@clarity-types/core";
|
|
2
|
-
import { BooleanFlag, Constant, Dimension, Metadata, MetadataCallback, MetadataCallbackOptions, Metric, Session, User, Setting } from "@clarity-types/data";
|
|
2
|
+
import { BooleanFlag, Constant, Dimension, Metadata, MetadataCallback, MetadataCallbackOptions, Metric, Session, User, Setting, ConsentState, ConsentSource, ConsentData } from "@clarity-types/data";
|
|
3
3
|
import * as clarity from "@src/clarity";
|
|
4
4
|
import * as core from "@src/core";
|
|
5
5
|
import config from "@src/core/config";
|
|
@@ -9,11 +9,14 @@ import * as dimension from "@src/data/dimension";
|
|
|
9
9
|
import * as metric from "@src/data/metric";
|
|
10
10
|
import { set } from "@src/data/variable";
|
|
11
11
|
import * as trackConsent from "@src/data/consent";
|
|
12
|
+
import { Constant as CoreConstant } from "@clarity-types/core";
|
|
12
13
|
|
|
13
14
|
export let data: Metadata = null;
|
|
14
15
|
export let callbacks: MetadataCallbackOptions[] = [];
|
|
15
16
|
export let electron = BooleanFlag.False;
|
|
16
17
|
let rootDomain = null;
|
|
18
|
+
let consentStatus: ConsentState = null;
|
|
19
|
+
let defaultStatus: ConsentState = {ad_Storage: Constant.Denied, analytics_Storage: Constant.Denied};
|
|
17
20
|
|
|
18
21
|
export function start(): void {
|
|
19
22
|
rootDomain = null;
|
|
@@ -54,6 +57,13 @@ export function start(): void {
|
|
|
54
57
|
metric.max(Metric.Playback, BooleanFlag.False);
|
|
55
58
|
metric.max(Metric.Electron, electron);
|
|
56
59
|
|
|
60
|
+
const zone = (window as any)?.[CoreConstant.Zone];
|
|
61
|
+
const isZone = zone && CoreConstant.Symbol in zone;
|
|
62
|
+
|
|
63
|
+
if (isZone) {
|
|
64
|
+
metric.max(Metric.AngularZone, BooleanFlag.True);
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
// Capture navigator specific dimensions
|
|
58
68
|
if (navigator) {
|
|
59
69
|
dimension.log(Dimension.Language, navigator.language);
|
|
@@ -76,8 +86,12 @@ export function start(): void {
|
|
|
76
86
|
}
|
|
77
87
|
|
|
78
88
|
// Track consent config
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
consentStatus = {
|
|
90
|
+
ad_Storage: config.track ? Constant.Granted : Constant.Denied,
|
|
91
|
+
analytics_Storage: config.track ? Constant.Granted : Constant.Denied,
|
|
92
|
+
}
|
|
93
|
+
const consent = getConsentData(consentStatus, ConsentSource.Implicit);
|
|
94
|
+
trackConsent.config(consent);
|
|
81
95
|
// Track ids using a cookie if configuration allows it
|
|
82
96
|
track(u);
|
|
83
97
|
}
|
|
@@ -98,10 +112,11 @@ function userAgentData(): void {
|
|
|
98
112
|
export function stop(): void {
|
|
99
113
|
rootDomain = null;
|
|
100
114
|
data = null;
|
|
115
|
+
consentStatus = null;
|
|
101
116
|
callbacks.forEach(cb => { cb.called = false; });
|
|
102
117
|
}
|
|
103
118
|
|
|
104
|
-
export function metadata(cb: MetadataCallback, wait: boolean = true, recall: boolean = false): void {
|
|
119
|
+
export function metadata(cb: MetadataCallback, wait: boolean = true, recall: boolean = false, consentInfo: boolean = false): void {
|
|
105
120
|
let upgraded = config.lean ? BooleanFlag.False : BooleanFlag.True;
|
|
106
121
|
let called = false;
|
|
107
122
|
// if caller hasn't specified that they want to skip waiting for upgrade but we've already upgraded, we need to
|
|
@@ -109,11 +124,11 @@ export function metadata(cb: MetadataCallback, wait: boolean = true, recall: boo
|
|
|
109
124
|
// we go through the upgrading flow.
|
|
110
125
|
if (data && (upgraded || wait === false)) {
|
|
111
126
|
// Immediately invoke the callback if the caller explicitly doesn't want to wait for the upgrade confirmation
|
|
112
|
-
cb(data, !config.lean);
|
|
127
|
+
cb(data, !config.lean, consentInfo? consentStatus : undefined);
|
|
113
128
|
called = true;
|
|
114
129
|
}
|
|
115
130
|
if (recall || !called) {
|
|
116
|
-
callbacks.push({ callback: cb, wait, recall, called });
|
|
131
|
+
callbacks.push({ callback: cb, wait, recall, called, consentInfo });
|
|
117
132
|
}
|
|
118
133
|
}
|
|
119
134
|
|
|
@@ -121,8 +136,27 @@ export function id(): string {
|
|
|
121
136
|
return data ? [data.userId, data.sessionId, data.pageNum].join(Constant.Dot) : Constant.Empty;
|
|
122
137
|
}
|
|
123
138
|
|
|
124
|
-
|
|
139
|
+
//TODO: Remove this function once consentv2 is fully released
|
|
140
|
+
export function consent(status = true): void {
|
|
125
141
|
if (!status) {
|
|
142
|
+
consentv2();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
consentv2({ ad_Storage: Constant.Granted, analytics_Storage: Constant.Granted });
|
|
147
|
+
trackConsent.consent();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function consentv2(consentState: ConsentState = defaultStatus, source: number = ConsentSource.API): void {
|
|
151
|
+
consentStatus = {
|
|
152
|
+
ad_Storage: normalizeConsent(consentState.ad_Storage),
|
|
153
|
+
analytics_Storage: normalizeConsent(consentState.analytics_Storage)
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
callback(true);
|
|
157
|
+
const consentData = getConsentData(consentStatus, source);
|
|
158
|
+
|
|
159
|
+
if (!consentData.analytics_Storage) {
|
|
126
160
|
config.track = false;
|
|
127
161
|
setCookie(Constant.SessionKey, Constant.Empty, -Number.MAX_VALUE);
|
|
128
162
|
setCookie(Constant.CookieKey, Constant.Empty, -Number.MAX_VALUE);
|
|
@@ -135,10 +169,25 @@ export function consent(status: boolean = true): void {
|
|
|
135
169
|
config.track = true;
|
|
136
170
|
track(user(), BooleanFlag.True);
|
|
137
171
|
save();
|
|
172
|
+
trackConsent.consentv2(consentData);
|
|
138
173
|
trackConsent.consent();
|
|
139
174
|
}
|
|
140
175
|
}
|
|
141
176
|
|
|
177
|
+
function getConsentData(consentState: ConsentState, source : ConsentSource): ConsentData {
|
|
178
|
+
let consent: ConsentData = {
|
|
179
|
+
source: source,
|
|
180
|
+
ad_Storage: consentState.ad_Storage === Constant.Granted ? BooleanFlag.True : BooleanFlag.False,
|
|
181
|
+
analytics_Storage: consentState.analytics_Storage === Constant.Granted ? BooleanFlag.True : BooleanFlag.False,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return consent;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function normalizeConsent(value: unknown): string {
|
|
188
|
+
return typeof value === 'string' ? value.toLowerCase() : Constant.Denied;
|
|
189
|
+
}
|
|
190
|
+
|
|
142
191
|
export function clear(): void {
|
|
143
192
|
// Clear any stored information in the cookie that tracks session information so we can restart fresh the next time
|
|
144
193
|
setCookie(Constant.SessionKey, Constant.Empty, 0);
|
|
@@ -154,9 +203,9 @@ function tab(): string {
|
|
|
154
203
|
return id;
|
|
155
204
|
}
|
|
156
205
|
|
|
157
|
-
export function callback(): void {
|
|
206
|
+
export function callback(consentUpdate:boolean = false): void {
|
|
158
207
|
let upgrade = config.lean ? BooleanFlag.False : BooleanFlag.True;
|
|
159
|
-
processCallback(upgrade);
|
|
208
|
+
processCallback(upgrade, consentUpdate);
|
|
160
209
|
}
|
|
161
210
|
|
|
162
211
|
export function save(): void {
|
|
@@ -167,12 +216,16 @@ export function save(): void {
|
|
|
167
216
|
setCookie(Constant.SessionKey, [data.sessionId, ts, data.pageNum, upgrade, upload].join(Constant.Pipe), Setting.SessionExpire);
|
|
168
217
|
}
|
|
169
218
|
|
|
170
|
-
function processCallback(upgrade: BooleanFlag) {
|
|
219
|
+
function processCallback(upgrade: BooleanFlag, consentUpdate: boolean = false): void {
|
|
171
220
|
if (callbacks.length > 0) {
|
|
172
221
|
for (let i = 0; i < callbacks.length; i++) {
|
|
173
222
|
const cb = callbacks[i];
|
|
174
|
-
if (
|
|
175
|
-
cb.callback
|
|
223
|
+
if (
|
|
224
|
+
cb.callback &&
|
|
225
|
+
((!cb.called && !consentUpdate) || (cb.consentInfo && consentUpdate)) && //If consentUpdate is true, we only call the callback if it has consentInfo
|
|
226
|
+
(!cb.wait || upgrade)
|
|
227
|
+
) {
|
|
228
|
+
cb.callback(data, !config.lean, cb.consentInfo ? consentStatus : undefined);
|
|
176
229
|
cb.called = true;
|
|
177
230
|
if (!cb.recall) {
|
|
178
231
|
callbacks.splice(i, 1);
|
|
@@ -124,12 +124,12 @@ export function reset(): void {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
function similar(last: PointerState, current: PointerState): boolean {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
127
|
+
let dx = last.data.x - current.data.x;
|
|
128
|
+
let dy = last.data.y - current.data.y;
|
|
129
|
+
let distance = Math.sqrt(dx * dx + dy * dy);
|
|
130
|
+
let gap = current.time - last.time;
|
|
131
|
+
let match = current.data.target === last.data.target;
|
|
132
|
+
let sameId = current.data.id !== undefined ? current.data.id === last.data.id : true;
|
|
133
133
|
return current.event === last.event && match && distance < Setting.Distance && gap < Setting.PointerInterval && sameId;
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -6,10 +6,10 @@ import { schedule } from "@src/core/task";
|
|
|
6
6
|
import { time } from "@src/core/time";
|
|
7
7
|
import { clearTimeout, setTimeout } from "@src/core/timeout";
|
|
8
8
|
import throttle from "@src/core/throttle";
|
|
9
|
-
import * as dimension from "@src/data/dimension";
|
|
10
9
|
import { iframe } from "@src/layout/dom";
|
|
11
10
|
import { target, metadata } from "@src/layout/target";
|
|
12
11
|
import encode from "./encode";
|
|
12
|
+
import * as dimension from "@src/data/dimension";
|
|
13
13
|
|
|
14
14
|
export let state: ScrollState[] = [];
|
|
15
15
|
let initialTop: Node = null;
|
|
@@ -22,8 +22,8 @@ export function start(): void {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export function observe(root: Node): void {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
let frame = iframe(root);
|
|
26
|
+
let node = frame ? frame.contentWindow : (root === document ? window : root);
|
|
27
27
|
bind(node, "scroll", throttledRecompute, true);
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -102,9 +102,9 @@ function process(event: Event): void {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
function similar(last: ScrollState, current: ScrollState): boolean {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return dx * dx + dy * dy < Setting.Distance * Setting.Distance && current.time - last.time < Setting.ScrollInterval;
|
|
105
|
+
let dx = last.data.x - current.data.x;
|
|
106
|
+
let dy = last.data.y - current.data.y;
|
|
107
|
+
return (dx * dx + dy * dy < Setting.Distance * Setting.Distance) && (current.time - last.time < Setting.ScrollInterval);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
export function compute(): void {
|
package/src/layout/region.ts
CHANGED
|
@@ -90,12 +90,9 @@ function handler(entries: IntersectionObserverEntry[]): void {
|
|
|
90
90
|
// like search box - one for desktop, and another for mobile. In those cases, CSS media queries determine which one should be visible.
|
|
91
91
|
// Also, if these regions ever become non-zero width or height (through AJAX, user action or orientation change) - we will automatically start monitoring them from that point onwards
|
|
92
92
|
if (regionMap.has(target) && rect.width + rect.height > 0 && viewport && viewport.width > 0 && viewport.height > 0) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
? regions[id]
|
|
97
|
-
: { id, name: regionMap.get(target), interaction: InteractionState.None, visibility: RegionVisibility.Rendered };
|
|
98
|
-
|
|
93
|
+
let id = target ? dom.getId(target) : null;
|
|
94
|
+
let data = id in regions ? regions[id] : { id, name: regionMap.get(target), interaction: InteractionState.None, visibility: RegionVisibility.Rendered };
|
|
95
|
+
|
|
99
96
|
// For regions that have relatively smaller area, we look at intersection ratio and see the overlap relative to element's area
|
|
100
97
|
// However, for larger regions, area of regions could be bigger than viewport and therefore comparison is relative to visible area
|
|
101
98
|
let viewportRatio = overlap ? (overlap.width * overlap.height * 1.0) / (viewport.width * viewport.height) : 0;
|
package/src/layout/style.ts
CHANGED
package/types/data.d.ts
CHANGED
|
@@ -3,12 +3,13 @@ export type Target = (number | Node);
|
|
|
3
3
|
export type Token = (string | number | number[] | string[] | (string | number)[]);
|
|
4
4
|
export type DecodedToken = (any | any[]);
|
|
5
5
|
|
|
6
|
-
export type MetadataCallback = (data: Metadata, playback: boolean) => void;
|
|
6
|
+
export type MetadataCallback = (data: Metadata, playback: boolean, consentStatus?: ConsentState) => void;
|
|
7
7
|
export interface MetadataCallbackOptions {
|
|
8
8
|
callback: MetadataCallback,
|
|
9
9
|
wait: boolean,
|
|
10
10
|
recall: boolean,
|
|
11
|
-
called: boolean
|
|
11
|
+
called: boolean,
|
|
12
|
+
consentInfo: boolean
|
|
12
13
|
}
|
|
13
14
|
export type SignalCallback = (data: ClaritySignal) => void
|
|
14
15
|
|
|
@@ -71,6 +72,7 @@ export const enum Event {
|
|
|
71
72
|
Animation = 44,
|
|
72
73
|
StyleSheetAdoption = 45,
|
|
73
74
|
StyleSheetUpdate = 46,
|
|
75
|
+
Consent = 47,
|
|
74
76
|
|
|
75
77
|
// Apps specific events
|
|
76
78
|
WebViewDiscover = 100,
|
|
@@ -131,7 +133,8 @@ export const enum Metric {
|
|
|
131
133
|
* @deprecated Move it to dimension as it'll report only last value
|
|
132
134
|
*/
|
|
133
135
|
InteractionNextPaint = 37,
|
|
134
|
-
HistoryClear = 38
|
|
136
|
+
HistoryClear = 38,
|
|
137
|
+
AngularZone = 39,
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
export const enum Dimension {
|
|
@@ -346,6 +349,8 @@ export const enum Constant {
|
|
|
346
349
|
SHA256 = "SHA-256",
|
|
347
350
|
Electron = "Electron",
|
|
348
351
|
Caret = "^",
|
|
352
|
+
Granted = "granted",
|
|
353
|
+
Denied = "denied",
|
|
349
354
|
}
|
|
350
355
|
|
|
351
356
|
export const enum XMLReadyState {
|
|
@@ -356,6 +361,10 @@ export const enum XMLReadyState {
|
|
|
356
361
|
Done = 4
|
|
357
362
|
}
|
|
358
363
|
|
|
364
|
+
export const enum ConsentSource{
|
|
365
|
+
Implicit = 0,
|
|
366
|
+
API = 1
|
|
367
|
+
}
|
|
359
368
|
|
|
360
369
|
/* Helper Interfaces */
|
|
361
370
|
|
|
@@ -513,4 +522,15 @@ export interface PerformanceEventTiming extends PerformanceEntry {
|
|
|
513
522
|
export interface Interaction {
|
|
514
523
|
id: number;
|
|
515
524
|
latency: number;
|
|
516
|
-
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export interface ConsentState {
|
|
528
|
+
ad_Storage?: string;
|
|
529
|
+
analytics_Storage?: string;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export interface ConsentData {
|
|
533
|
+
source: ConsentSource;
|
|
534
|
+
ad_Storage: BooleanFlag;
|
|
535
|
+
analytics_Storage: BooleanFlag;
|
|
536
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ interface Clarity {
|
|
|
12
12
|
resume: () => void;
|
|
13
13
|
upgrade: (key: string) => void;
|
|
14
14
|
consent: () => void;
|
|
15
|
+
consentv2: () => void;
|
|
15
16
|
event: (name: string, value: string) => void;
|
|
16
17
|
set: (variable: string, value: string | string[]) => void;
|
|
17
18
|
identify: (userId: string, sessionId?: string, pageId?: string, userHint?: string) => void;
|
package/types/interaction.d.ts
CHANGED
|
@@ -18,7 +18,6 @@ export const enum Setting {
|
|
|
18
18
|
ScrollInterval = 50, // 25 milliseconds
|
|
19
19
|
PointerInterval = 25, // 25 milliseconds
|
|
20
20
|
Throttle = 25, // 25 milliseconds
|
|
21
|
-
// biome-ignore lint/style/useLiteralEnumMembers: Time.Second is a const enum, it is compiled to a number
|
|
22
21
|
TimelineSpan = 2 * Time.Second, // 2 seconds
|
|
23
22
|
}
|
|
24
23
|
|