clarity-js 0.8.15 → 0.8.17

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";
@@ -15,6 +15,8 @@ export let data: Metadata = null;
15
15
  export let callbacks: MetadataCallbackOptions[] = [];
16
16
  export let electron = BooleanFlag.False;
17
17
  let rootDomain = null;
18
+ let consentStatus: ConsentState = null;
19
+ let defaultStatus: ConsentState = {ad_Storage: Constant.Denied, analytics_Storage: Constant.Denied};
18
20
 
19
21
  export function start(): void {
20
22
  rootDomain = null;
@@ -84,8 +86,12 @@ export function start(): void {
84
86
  }
85
87
 
86
88
  // Track consent config
87
- trackConsent.config(config.track);
88
-
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);
89
95
  // Track ids using a cookie if configuration allows it
90
96
  track(u);
91
97
  }
@@ -106,10 +112,11 @@ function userAgentData(): void {
106
112
  export function stop(): void {
107
113
  rootDomain = null;
108
114
  data = null;
115
+ consentStatus = null;
109
116
  callbacks.forEach(cb => { cb.called = false; });
110
117
  }
111
118
 
112
- 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 {
113
120
  let upgraded = config.lean ? BooleanFlag.False : BooleanFlag.True;
114
121
  let called = false;
115
122
  // if caller hasn't specified that they want to skip waiting for upgrade but we've already upgraded, we need to
@@ -117,11 +124,11 @@ export function metadata(cb: MetadataCallback, wait: boolean = true, recall: boo
117
124
  // we go through the upgrading flow.
118
125
  if (data && (upgraded || wait === false)) {
119
126
  // Immediately invoke the callback if the caller explicitly doesn't want to wait for the upgrade confirmation
120
- cb(data, !config.lean);
127
+ cb(data, !config.lean, consentInfo? consentStatus : undefined);
121
128
  called = true;
122
129
  }
123
130
  if (recall || !called) {
124
- callbacks.push({ callback: cb, wait, recall, called });
131
+ callbacks.push({ callback: cb, wait, recall, called, consentInfo });
125
132
  }
126
133
  }
127
134
 
@@ -129,8 +136,27 @@ export function id(): string {
129
136
  return data ? [data.userId, data.sessionId, data.pageNum].join(Constant.Dot) : Constant.Empty;
130
137
  }
131
138
 
132
- export function consent(status: boolean = true): void {
139
+ //TODO: Remove this function once consentv2 is fully released
140
+ export function consent(status = true): void {
133
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) {
134
160
  config.track = false;
135
161
  setCookie(Constant.SessionKey, Constant.Empty, -Number.MAX_VALUE);
136
162
  setCookie(Constant.CookieKey, Constant.Empty, -Number.MAX_VALUE);
@@ -143,10 +169,25 @@ export function consent(status: boolean = true): void {
143
169
  config.track = true;
144
170
  track(user(), BooleanFlag.True);
145
171
  save();
172
+ trackConsent.consentv2(consentData);
146
173
  trackConsent.consent();
147
174
  }
148
175
  }
149
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
+
150
191
  export function clear(): void {
151
192
  // Clear any stored information in the cookie that tracks session information so we can restart fresh the next time
152
193
  setCookie(Constant.SessionKey, Constant.Empty, 0);
@@ -162,9 +203,9 @@ function tab(): string {
162
203
  return id;
163
204
  }
164
205
 
165
- export function callback(): void {
206
+ export function callback(consentUpdate:boolean = false): void {
166
207
  let upgrade = config.lean ? BooleanFlag.False : BooleanFlag.True;
167
- processCallback(upgrade);
208
+ processCallback(upgrade, consentUpdate);
168
209
  }
169
210
 
170
211
  export function save(): void {
@@ -175,12 +216,16 @@ export function save(): void {
175
216
  setCookie(Constant.SessionKey, [data.sessionId, ts, data.pageNum, upgrade, upload].join(Constant.Pipe), Setting.SessionExpire);
176
217
  }
177
218
 
178
- function processCallback(upgrade: BooleanFlag) {
219
+ function processCallback(upgrade: BooleanFlag, consentUpdate: boolean = false): void {
179
220
  if (callbacks.length > 0) {
180
221
  for (let i = 0; i < callbacks.length; i++) {
181
222
  const cb = callbacks[i];
182
- if (cb.callback && !cb.called && (!cb.wait || upgrade)) {
183
- 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);
184
229
  cb.called = true;
185
230
  if (!cb.recall) {
186
231
  callbacks.splice(i, 1);
@@ -134,6 +134,10 @@ async function upload(final: boolean = false): Promise<void> {
134
134
  // could inject function arguments for internal tracking (likely stack traces for script errors).
135
135
  // For these edge cases, we want to ensure that an injected object (e.g. {"key": "value"}) isn't mistaken to be true.
136
136
  let last = final === true;
137
+
138
+ // In some cases envelope has null data because it's part of the shutdown process while there's one upload call queued which might introduce runtime error
139
+ if(!envelope.data) return;
140
+
137
141
  let e = JSON.stringify(envelope.envelope(last));
138
142
  let a = `[${analysis.join()}]`;
139
143
 
@@ -33,6 +33,10 @@ function recompute(event: UIEvent = null): void {
33
33
  let de = document.documentElement;
34
34
  let element = event ? target(event) : de;
35
35
 
36
+ // In some edge cases, it's possible for target to be null.
37
+ // In those cases, we cannot proceed with scroll event instrumentation.
38
+ if (!element) { return; }
39
+
36
40
  // If the target is a Document node, then identify corresponding documentElement and window for this document
37
41
  if (element && element.nodeType === Node.DOCUMENT_NODE) {
38
42
  let frame = iframe(element);
@@ -1,4 +1,4 @@
1
- import { Event, Metric } from "@clarity-types/data";
1
+ import { Event } 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";
@@ -99,10 +99,17 @@ export function stop(): void {
99
99
  if (observer) { observer.disconnect(); }
100
100
  observer = null;
101
101
  interaction.resetInteractions();
102
+ anchorCache = null;
102
103
  }
103
104
 
105
+ // Cached anchor element for optimal performance & memory management
106
+ let anchorCache: HTMLAnchorElement | null = null;
107
+
104
108
  function host(url: string): string {
105
- let a = document.createElement("a");
106
- a.href = url;
107
- return a.host;
109
+ if (!anchorCache) {
110
+ anchorCache = document.createElement("a");
111
+ }
112
+
113
+ anchorCache.href = url;
114
+ return anchorCache.host;
108
115
  }
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,
@@ -347,6 +349,8 @@ export const enum Constant {
347
349
  SHA256 = "SHA-256",
348
350
  Electron = "Electron",
349
351
  Caret = "^",
352
+ Granted = "granted",
353
+ Denied = "denied",
350
354
  }
351
355
 
352
356
  export const enum XMLReadyState {
@@ -357,6 +361,10 @@ export const enum XMLReadyState {
357
361
  Done = 4
358
362
  }
359
363
 
364
+ export const enum ConsentSource{
365
+ Implicit = 0,
366
+ API = 1
367
+ }
360
368
 
361
369
  /* Helper Interfaces */
362
370
 
@@ -514,4 +522,15 @@ export interface PerformanceEventTiming extends PerformanceEntry {
514
522
  export interface Interaction {
515
523
  id: number;
516
524
  latency: number;
517
- }
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;