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.
@@ -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
- trackConsent.config(config.track);
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
- export function consent(status: boolean = true): void {
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 (cb.callback && !cb.called && (!cb.wait || upgrade)) {
175
- cb.callback(data, !config.lean);
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
- const dx = last.data.x - current.data.x;
128
- const dy = last.data.y - current.data.y;
129
- const distance = Math.sqrt(dx * dx + dy * dy);
130
- const gap = current.time - last.time;
131
- const match = current.data.target === last.data.target;
132
- const sameId = current.data.id !== undefined ? current.data.id === last.data.id : true;
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
- const frame = iframe(root);
26
- const node = frame ? frame.contentWindow : root === document ? window : root;
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
- const dx = last.data.x - current.data.x;
106
- const dy = last.data.y - current.data.y;
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 {
@@ -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
- const id = target ? dom.getId(target) : null;
94
- const data =
95
- id in regions
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;
@@ -1,4 +1,4 @@
1
- import { Event } from "@clarity-types/data";
1
+ import { Event, Metric } from "@clarity-types/data";
2
2
  import { StyleSheetOperation, StyleSheetState } from "@clarity-types/layout";
3
3
  import { time } from "@src/core/time";
4
4
  import { shortid } from "@src/data/metadata";
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;
@@ -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