clarity-js 0.8.11 → 0.8.13-beta

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 (90) hide show
  1. package/build/clarity.extended.js +1 -1
  2. package/build/clarity.insight.js +1 -1
  3. package/build/clarity.js +2964 -3181
  4. package/build/clarity.min.js +1 -1
  5. package/build/clarity.module.js +2964 -3181
  6. package/build/clarity.performance.js +1 -1
  7. package/package.json +69 -76
  8. package/rollup.config.ts +88 -84
  9. package/src/clarity.ts +28 -34
  10. package/src/core/config.ts +2 -2
  11. package/src/core/event.ts +32 -36
  12. package/src/core/hash.ts +6 -5
  13. package/src/core/history.ts +11 -10
  14. package/src/core/index.ts +11 -21
  15. package/src/core/measure.ts +5 -9
  16. package/src/core/report.ts +3 -3
  17. package/src/core/scrub.ts +20 -29
  18. package/src/core/task.ts +45 -73
  19. package/src/core/time.ts +3 -3
  20. package/src/core/timeout.ts +2 -2
  21. package/src/core/version.ts +1 -1
  22. package/src/data/baseline.ts +55 -60
  23. package/src/data/consent.ts +2 -2
  24. package/src/data/custom.ts +13 -8
  25. package/src/data/dimension.ts +7 -11
  26. package/src/data/encode.ts +30 -36
  27. package/src/data/envelope.ts +38 -38
  28. package/src/data/extract.ts +77 -86
  29. package/src/data/index.ts +6 -10
  30. package/src/data/limit.ts +1 -1
  31. package/src/data/metadata.ts +266 -305
  32. package/src/data/metric.ts +8 -18
  33. package/src/data/ping.ts +4 -8
  34. package/src/data/signal.ts +18 -18
  35. package/src/data/summary.ts +4 -6
  36. package/src/data/token.ts +8 -10
  37. package/src/data/upgrade.ts +3 -7
  38. package/src/data/upload.ts +49 -100
  39. package/src/data/variable.ts +20 -27
  40. package/src/diagnostic/encode.ts +2 -2
  41. package/src/diagnostic/fraud.ts +4 -3
  42. package/src/diagnostic/internal.ts +5 -11
  43. package/src/diagnostic/script.ts +8 -12
  44. package/src/global.ts +1 -1
  45. package/src/insight/blank.ts +4 -4
  46. package/src/insight/encode.ts +17 -23
  47. package/src/insight/snapshot.ts +37 -57
  48. package/src/interaction/change.ts +6 -9
  49. package/src/interaction/click.ts +28 -34
  50. package/src/interaction/clipboard.ts +2 -2
  51. package/src/interaction/encode.ts +31 -35
  52. package/src/interaction/input.ts +9 -11
  53. package/src/interaction/pointer.ts +24 -35
  54. package/src/interaction/resize.ts +5 -5
  55. package/src/interaction/scroll.ts +11 -14
  56. package/src/interaction/selection.ts +8 -12
  57. package/src/interaction/submit.ts +2 -2
  58. package/src/interaction/timeline.ts +9 -13
  59. package/src/interaction/unload.ts +1 -1
  60. package/src/interaction/visibility.ts +2 -2
  61. package/src/layout/animation.ts +41 -47
  62. package/src/layout/discover.ts +5 -5
  63. package/src/layout/document.ts +19 -31
  64. package/src/layout/dom.ts +91 -141
  65. package/src/layout/encode.ts +37 -52
  66. package/src/layout/mutation.ts +318 -321
  67. package/src/layout/node.ts +81 -104
  68. package/src/layout/offset.ts +6 -7
  69. package/src/layout/region.ts +40 -60
  70. package/src/layout/schema.ts +8 -15
  71. package/src/layout/selector.ts +25 -47
  72. package/src/layout/style.ts +36 -44
  73. package/src/layout/target.ts +10 -14
  74. package/src/layout/traverse.ts +11 -17
  75. package/src/performance/blank.ts +1 -1
  76. package/src/performance/encode.ts +4 -4
  77. package/src/performance/interaction.ts +70 -58
  78. package/src/performance/navigation.ts +2 -2
  79. package/src/performance/observer.ts +26 -59
  80. package/src/queue.ts +9 -16
  81. package/tsconfig.json +1 -1
  82. package/tslint.json +32 -25
  83. package/types/core.d.ts +13 -13
  84. package/types/data.d.ts +29 -32
  85. package/types/diagnostic.d.ts +1 -1
  86. package/types/interaction.d.ts +4 -4
  87. package/types/layout.d.ts +21 -36
  88. package/types/performance.d.ts +5 -6
  89. package/.lintstagedrc.yml +0 -3
  90. package/biome.json +0 -43
@@ -1,33 +1,25 @@
1
- import { Priority, Task, type Timer } from "@clarity-types/core";
1
+ import { Priority, Task, Timer } from "@clarity-types/core";
2
2
  import { Code, Event, Metric, Severity } from "@clarity-types/data";
3
- import {
4
- Constant,
5
- type IWindowWithOverrides,
6
- type MutationHistory,
7
- type MutationQueue,
8
- type MutationRecordWithTime,
9
- Setting,
10
- Source,
11
- } from "@clarity-types/layout";
3
+ import { Constant, MutationHistory, MutationRecordWithTime, MutationQueue, Setting, Source } from "@clarity-types/layout";
12
4
  import { FunctionNames } from "@clarity-types/performance";
13
- import * as core from "@src/core";
14
5
  import api from "@src/core/api";
15
- import config from "@src/core/config";
6
+ import * as core from "@src/core";
16
7
  import * as event from "@src/core/event";
17
8
  import measure from "@src/core/measure";
18
9
  import * as task from "@src/core/task";
19
10
  import { time } from "@src/core/time";
20
11
  import { clearTimeout, setTimeout } from "@src/core/timeout";
21
12
  import { id } from "@src/data/metadata";
22
- import * as metric from "@src/data/metric";
23
13
  import * as summary from "@src/data/summary";
24
14
  import * as internal from "@src/diagnostic/internal";
25
15
  import * as doc from "@src/layout/document";
26
16
  import * as dom from "@src/layout/dom";
17
+ import * as metric from "@src/data/metric";
27
18
  import encode from "@src/layout/encode";
28
19
  import * as region from "@src/layout/region";
29
20
  import traverse from "@src/layout/traverse";
30
21
  import processNode from "./node";
22
+ import config from "@src/core/config";
31
23
 
32
24
  let observers: Set<MutationObserver> = new Set();
33
25
  let mutations: MutationQueue[] = [];
@@ -43,377 +35,382 @@ let observedNodes: WeakMap<Node, MutationObserver> = new WeakMap<Node, MutationO
43
35
  const IGNORED_ATTRIBUTES = ["data-google-query-id", "data-load-complete", "data-google-container-id"];
44
36
 
45
37
  export function start(): void {
46
- start.dn = FunctionNames.MutationStart;
47
- observers = new Set();
48
- queue = [];
49
- timeout = null;
50
- activePeriod = 0;
51
- history = {};
52
- observedNodes = new WeakMap<Node, MutationObserver>();
53
- proxyStyleRules(window);
38
+ start.dn = FunctionNames.MutationStart;
39
+ observers = new Set();
40
+ queue = [];
41
+ timeout = null;
42
+ activePeriod = 0;
43
+ history = {};
44
+ observedNodes = new WeakMap<Node, MutationObserver>();
45
+ proxyStyleRules(window);
54
46
  }
55
47
 
56
48
  export function observe(node: Document | ShadowRoot): void {
57
- // Create a new observer for every time a new DOM tree (e.g. root document or shadowdom root) is discovered on the page
58
- // In the case of shadow dom, any mutations that happen within the shadow dom are not bubbled up to the host document
59
- // For this reason, we need to wire up mutations every time we see a new shadow dom.
60
- // Also, wrap it inside a try / catch. In certain browsers (e.g. legacy Edge), observer on shadow dom can throw errors
61
- try {
62
- const m = api(Constant.MutationObserver);
63
- const observer = m in window ? new window[m](measure(handle) as MutationCallback) : null;
64
- if (observer) {
65
- observer.observe(node, { attributes: true, childList: true, characterData: true, subtree: true });
66
- observedNodes.set(node, observer);
67
- observers.add(observer);
68
- }
69
- if (node instanceof Document && node.defaultView) {
70
- proxyStyleRules(node.defaultView);
71
- }
72
- } catch (e) {
73
- internal.log(Code.MutationObserver, Severity.Info, e ? e.name : null);
49
+ // Create a new observer for every time a new DOM tree (e.g. root document or shadowdom root) is discovered on the page
50
+ // In the case of shadow dom, any mutations that happen within the shadow dom are not bubbled up to the host document
51
+ // For this reason, we need to wire up mutations every time we see a new shadow dom.
52
+ // Also, wrap it inside a try / catch. In certain browsers (e.g. legacy Edge), observer on shadow dom can throw errors
53
+ try {
54
+
55
+ let m = api(Constant.MutationObserver);
56
+ let observer = m in window ? new window[m](measure(handle) as MutationCallback) : null;
57
+ if (observer) {
58
+ observer.observe(node, { attributes: true, childList: true, characterData: true, subtree: true });
59
+ observedNodes.set(node, observer);
60
+ observers.add(observer);
74
61
  }
62
+ if (node['defaultView']) {
63
+ proxyStyleRules(node['defaultView']);
64
+ }
65
+
66
+ } catch (e) {
67
+ internal.log(Code.MutationObserver, Severity.Info, e ? e.name : null);
68
+ }
75
69
  }
76
70
 
77
71
  export function monitor(frame: HTMLIFrameElement): void {
78
- // Bind to iframe's onload event so we get notified anytime there's an update to iframe content.
79
- // This includes cases where iframe location is updated without explicitly updating src attribute
80
- // E.g. iframe.contentWindow.location.href = "new-location";
81
- if (dom.has(frame) === false) {
82
- event.bind(frame, Constant.LoadEvent, generate.bind(this, frame, Constant.ChildList), true);
83
- }
72
+ // Bind to iframe's onload event so we get notified anytime there's an update to iframe content.
73
+ // This includes cases where iframe location is updated without explicitly updating src attribute
74
+ // E.g. iframe.contentWindow.location.href = "new-location";
75
+ if (dom.has(frame) === false) {
76
+ event.bind(frame, Constant.LoadEvent, generate.bind(this, frame, Constant.ChildList), true);
77
+ }
84
78
  }
85
79
 
86
80
  export function stop(): void {
87
- for (const observer of Array.from(observers)) {
88
- if (observer) {
89
- observer.disconnect();
90
- }
81
+ for (let observer of Array.from(observers)) {
82
+ if (observer) {
83
+ observer.disconnect();
91
84
  }
92
- observers = new Set();
93
- history = {};
94
- mutations = [];
95
- throttledMutations = {};
96
- queue = [];
97
- activePeriod = 0;
98
- timeout = null;
99
- observedNodes = new WeakMap();
85
+ }
86
+ observers = new Set();
87
+ history = {};
88
+ mutations = [];
89
+ throttledMutations = {};
90
+ queue = [];
91
+ activePeriod = 0;
92
+ timeout = null;
93
+ observedNodes = new WeakMap();
100
94
  }
101
95
 
102
96
  export function active(): void {
103
- activePeriod = time() + Setting.MutationActivePeriod;
97
+ activePeriod = time() + Setting.MutationActivePeriod;
104
98
  }
105
99
 
106
100
  export function disconnect(n: Node): void {
107
- const ob = observedNodes.get(n);
108
- if (ob) {
109
- ob.disconnect();
110
- observers.delete(ob);
111
- observedNodes.delete(n);
112
- }
101
+ const ob = observedNodes.get(n);
102
+ if (ob) {
103
+ ob.disconnect();
104
+ observers.delete(ob);
105
+ observedNodes.delete(n);
106
+ }
113
107
  }
114
108
 
115
109
  function handle(m: MutationRecord[]): void {
116
- handle.dn = FunctionNames.MutationHandle;
117
- // Queue up mutation records for asynchronous processing
118
- const now = time();
119
- summary.track(Event.Mutation, now);
120
- mutations.push({ time: now, mutations: m });
121
- task.schedule(process, Priority.High).then((): void => {
122
- setTimeout(doc.compute);
123
- measure(region.compute)();
124
- });
110
+ handle.dn = FunctionNames.MutationHandle;
111
+ // Queue up mutation records for asynchronous processing
112
+ let now = time();
113
+ summary.track(Event.Mutation, now);
114
+ mutations.push({ time: now, mutations: m });
115
+ task.schedule(process, Priority.High).then((): void => {
116
+ setTimeout(doc.compute);
117
+ measure(region.compute)();
118
+ });
125
119
  }
126
120
 
127
121
  async function processMutation(timer: Timer, mutation: MutationRecord, instance: number, timestamp: number): Promise<void> {
128
- let state = task.state(timer);
129
-
130
- if (state === Task.Wait) {
131
- state = await task.suspend(timer);
132
- }
133
- if (state === Task.Stop) {
134
- return;
135
- }
136
-
137
- const target = mutation.target;
138
- const type = config.throttleDom ? track(mutation, timer, instance, timestamp) : mutation.type;
139
-
140
- if (type && target && target.ownerDocument) {
141
- dom.parse(target.ownerDocument);
142
- }
143
-
144
- if (type && target && target.nodeType === Node.DOCUMENT_FRAGMENT_NODE && (target as ShadowRoot).host) {
145
- dom.parse(target as ShadowRoot);
146
- }
147
-
148
- switch (type) {
149
- case Constant.Attributes:
150
- if (IGNORED_ATTRIBUTES.indexOf(mutation.attributeName) < 0) {
151
- processNode(target, Source.Attributes, timestamp);
152
- }
153
- break;
154
- case Constant.CharacterData:
155
- processNode(target, Source.CharacterData, timestamp);
156
- break;
157
- case Constant.ChildList:
158
- processNodeList(mutation.addedNodes, Source.ChildListAdd, timer, timestamp);
159
- processNodeList(mutation.removedNodes, Source.ChildListRemove, timer, timestamp);
160
- break;
161
- default:
162
- break;
163
- }
122
+ let state = task.state(timer);
123
+
124
+ if (state === Task.Wait) {
125
+ state = await task.suspend(timer);
126
+ }
127
+ if (state === Task.Stop) {
128
+ return;
129
+ }
130
+
131
+ let target = mutation.target;
132
+ let type = config.throttleDom ? track(mutation, timer, instance, timestamp) : mutation.type;
133
+
134
+ if (type && target && target.ownerDocument) {
135
+ dom.parse(target.ownerDocument);
136
+ }
137
+
138
+ if (type && target && target.nodeType == Node.DOCUMENT_FRAGMENT_NODE && (target as ShadowRoot).host) {
139
+ dom.parse(target as ShadowRoot);
140
+ }
141
+
142
+ switch (type) {
143
+ case Constant.Attributes:
144
+ if (IGNORED_ATTRIBUTES.indexOf(mutation.attributeName) < 0) {
145
+ processNode(target, Source.Attributes, timestamp);
146
+ }
147
+ break;
148
+ case Constant.CharacterData:
149
+ processNode(target, Source.CharacterData, timestamp);
150
+ break;
151
+ case Constant.ChildList:
152
+ processNodeList(mutation.addedNodes, Source.ChildListAdd, timer, timestamp);
153
+ processNodeList(mutation.removedNodes, Source.ChildListRemove, timer, timestamp);
154
+ break;
155
+ case Constant.Throttle:
156
+ default:
157
+ break;
158
+ }
164
159
  }
165
160
  async function process(): Promise<void> {
166
- const timer: Timer = { id: id(), cost: Metric.LayoutCost };
167
- task.start(timer);
168
- while (mutations.length > 0) {
169
- const record = mutations.shift();
170
- const instance = time();
171
- for (const mutation of record.mutations) {
172
- await processMutation(timer, mutation, instance, record.time);
173
- }
174
- await encode(Event.Mutation, timer, record.time);
161
+ let timer: Timer = { id: id(), cost: Metric.LayoutCost };
162
+ task.start(timer);
163
+ while (mutations.length > 0) {
164
+ let record = mutations.shift();
165
+ let instance = time();
166
+ for (let mutation of record.mutations) {
167
+ await processMutation(timer, mutation, instance, record.time);
175
168
  }
169
+ await encode(Event.Mutation, timer, record.time);
170
+ }
176
171
 
177
- let processedMutations = false;
178
- for (const key of Object.keys(throttledMutations)) {
179
- const throttledMutationToProcess: MutationRecordWithTime = throttledMutations[key];
180
- delete throttledMutations[key];
181
- await processMutation(timer, throttledMutationToProcess.mutation, time(), throttledMutationToProcess.timestamp);
182
- processedMutations = true;
183
- }
172
+ let processedMutations = false;
173
+ for (var key of Object.keys(throttledMutations)) {
174
+ let throttledMutationToProcess: MutationRecordWithTime = throttledMutations[key];
175
+ delete throttledMutations[key];
176
+ await processMutation(timer, throttledMutationToProcess.mutation, time(), throttledMutationToProcess.timestamp);
177
+ processedMutations = true;
178
+ }
184
179
 
185
- if (Object.keys(throttledMutations).length > 0) {
186
- processThrottledMutations();
187
- }
180
+ if (Object.keys(throttledMutations).length > 0) {
181
+ processThrottledMutations();
182
+ }
188
183
 
189
- // ensure we encode the previously throttled mutations once we have finished them
190
- if (Object.keys(throttledMutations).length === 0 && processedMutations) {
191
- await encode(Event.Mutation, timer, time());
192
- }
184
+ // ensure we encode the previously throttled mutations once we have finished them
185
+ if (Object.keys(throttledMutations).length === 0 && processedMutations) {
186
+ await encode(Event.Mutation, timer, time());
187
+ }
193
188
 
194
- cleanHistory();
189
+ cleanHistory();
195
190
 
196
- task.stop(timer);
191
+ task.stop(timer);
197
192
  }
198
193
 
199
194
  function cleanHistory(): void {
200
- const now = time();
201
- if (Object.keys(history).length > Setting.MaxMutationHistoryCount) {
202
- history = {};
203
- metric.count(Metric.HistoryClear);
204
- }
195
+ let now = time();
196
+ if (Object.keys(history).length > Setting.MaxMutationHistoryCount) {
197
+ history = {};
198
+ metric.count(Metric.HistoryClear);
199
+ }
205
200
 
206
- for (const key of Object.keys(history)) {
207
- const h = history[key];
208
- if (now > h[1] + Setting.MaxMutationHistoryTime) {
209
- delete history[key];
210
- }
201
+ for (let key of Object.keys(history)) {
202
+ let h = history[key];
203
+ if (now > h[1] + Setting.MaxMutationHistoryTime) {
204
+ delete history[key];
211
205
  }
206
+ }
212
207
  }
213
208
 
214
209
  function track(m: MutationRecord, timer: Timer, instance: number, timestamp: number): string {
215
- const value = m.target ? dom.get(m.target.parentNode) : null;
216
-
217
- // Check if the parent is already discovered and that the parent is not the document root
218
- if (value && value.data.tag !== Constant.HTML) {
219
- // calculate inactive period based on the timestamp of the mutation not when the mutation is processed
220
- const inactive = timestamp > activePeriod;
221
-
222
- // Calculate critical period based on when mutation is processed
223
- const target = dom.get(m.target);
224
- const element = target?.selector ? target.selector.join() : m.target.nodeName;
225
- const parent = value.selector ? value.selector.join() : Constant.Empty;
226
-
227
- // We use selector, instead of id, to determine the key (signature for the mutation) because in some cases
228
- // repeated mutations can cause elements to be destroyed and then recreated as new DOM nodes
229
- // In those cases, IDs will change however the selector (which is relative to DOM xPath) remains the same
230
- const key = [parent, element, m.attributeName, names(m.addedNodes), names(m.removedNodes)].join();
231
-
232
- // Initialize an entry if it doesn't already exist
233
- history[key] = key in history ? history[key] : [0, instance];
234
- const h = history[key];
235
-
236
- // Lookup any pending nodes queued up for removal, and process them now if we suspended a mutation before
237
- if (inactive === false && h[0] >= Setting.MutationSuspendThreshold) {
238
- processNodeList(h[2], Source.ChildListRemove, timer, timestamp);
239
- }
210
+ let value = m.target ? dom.get(m.target.parentNode) : null;
211
+
212
+ // Check if the parent is already discovered and that the parent is not the document root
213
+ if (value && value.data.tag !== Constant.HTML) {
214
+ // calculate inactive period based on the timestamp of the mutation not when the mutation is processed
215
+ let inactive = timestamp > activePeriod;
216
+
217
+ // Calculate critical period based on when mutation is processed
218
+ let target = dom.get(m.target);
219
+ let element = target && target.selector ? target.selector.join() : m.target.nodeName;
220
+ let parent = value.selector ? value.selector.join() : Constant.Empty;
221
+
222
+ // We use selector, instead of id, to determine the key (signature for the mutation) because in some cases
223
+ // repeated mutations can cause elements to be destroyed and then recreated as new DOM nodes
224
+ // In those cases, IDs will change however the selector (which is relative to DOM xPath) remains the same
225
+ let key = [parent, element, m.attributeName, names(m.addedNodes), names(m.removedNodes)].join();
226
+
227
+ // Initialize an entry if it doesn't already exist
228
+ history[key] = key in history ? history[key] : [0, instance];
229
+ let h = history[key];
230
+
231
+ // Lookup any pending nodes queued up for removal, and process them now if we suspended a mutation before
232
+ if (inactive === false && h[0] >= Setting.MutationSuspendThreshold) {
233
+ processNodeList(h[2], Source.ChildListRemove, timer, timestamp);
234
+ }
240
235
 
241
- // Update the counter, do not reset counter if its critical period
242
- h[0] = inactive ? (h[1] === instance ? h[0] : h[0] + 1) : 1;
243
- h[1] = instance;
244
-
245
- // Return updated mutation type based on,
246
- // 1. if we have already hit the threshold or not
247
- // 2. if its a low priority mutation happening during critical time period
248
- if (h[0] >= Setting.MutationSuspendThreshold) {
249
- // Store a reference to removedNodes so we can process them later
250
- // when we resume mutations again on user interactions
251
- h[2] = m.removedNodes;
252
- if (instance > timestamp + Setting.MutationActivePeriod) {
253
- return m.type;
254
- }
255
-
256
- // we only store the most recent mutation for a given key if it is being throttled
257
- throttledMutations[key] = { mutation: m, timestamp };
258
-
259
- return Constant.Throttle;
260
- }
236
+ // Update the counter, do not reset counter if its critical period
237
+ h[0] = inactive ? (h[1] === instance ? h[0] : h[0] + 1) : 1;
238
+ h[1] = instance;
239
+
240
+ // Return updated mutation type based on,
241
+ // 1. if we have already hit the threshold or not
242
+ // 2. if its a low priority mutation happening during critical time period
243
+ if (h[0] >= Setting.MutationSuspendThreshold) {
244
+ // Store a reference to removedNodes so we can process them later
245
+ // when we resume mutations again on user interactions
246
+ h[2] = m.removedNodes;
247
+ if (instance > timestamp + Setting.MutationActivePeriod) {
248
+ return m.type;
249
+ }
250
+
251
+ // we only store the most recent mutation for a given key if it is being throttled
252
+ throttledMutations[key] = { mutation: m, timestamp };
253
+
254
+ return Constant.Throttle;
261
255
  }
262
- return m.type;
256
+ }
257
+ return m.type;
263
258
  }
264
259
 
265
260
  function names(nodes: NodeList): string {
266
- const output: string[] = [];
267
- for (let i = 0; nodes && i < nodes.length; i++) {
268
- output.push(nodes[i].nodeName);
269
- }
270
- return output.join();
261
+ let output: string[] = [];
262
+ for (let i = 0; nodes && i < nodes.length; i++) {
263
+ output.push(nodes[i].nodeName);
264
+ }
265
+ return output.join();
271
266
  }
272
267
 
273
268
  async function processNodeList(list: NodeList, source: Source, timer: Timer, timestamp: number): Promise<void> {
274
- const length = list ? list.length : 0;
275
- for (let i = 0; i < length; i++) {
276
- const node = list[i];
277
- if (source === Source.ChildListAdd) {
278
- traverse(node, timer, source, timestamp);
279
- } else {
280
- let state = task.state(timer);
281
- if (state === Task.Wait) {
282
- state = await task.suspend(timer);
283
- }
284
- if (state === Task.Stop) {
285
- break;
286
- }
287
- processNode(node, source, timestamp);
288
- }
269
+ let length = list ? list.length : 0;
270
+ for (let i = 0; i < length; i++) {
271
+ const node = list[i];
272
+ if (source === Source.ChildListAdd) {
273
+ traverse(node, timer, source, timestamp);
274
+ } else {
275
+ let state = task.state(timer);
276
+ if (state === Task.Wait) {
277
+ state = await task.suspend(timer);
278
+ }
279
+ if (state === Task.Stop) {
280
+ break;
281
+ }
282
+ processNode(node, source, timestamp);
289
283
  }
284
+ }
290
285
  }
291
286
 
292
287
  function processThrottledMutations(): void {
293
- if (throttleDelay) {
294
- clearTimeout(throttleDelay);
295
- }
296
- throttleDelay = setTimeout(() => {
297
- task.schedule(process, Priority.High);
298
- }, Setting.LookAhead);
288
+ if (throttleDelay) {
289
+ clearTimeout(throttleDelay);
290
+ }
291
+ throttleDelay = setTimeout(() => {
292
+ task.schedule(process, Priority.High);
293
+ }, Setting.LookAhead);
299
294
  }
300
295
 
301
296
  export function schedule(node: Node): Node {
302
- // Only schedule manual trigger for this node if it's not already in the queue
303
- if (queue.indexOf(node) < 0) {
304
- queue.push(node);
305
- }
306
-
307
- // Cancel any previous trigger before scheduling a new one.
308
- // It's common for a webpage to call multiple synchronous "insertRule" / "deleteRule" calls.
309
- // And in those cases we do not wish to monitor changes multiple times for the same node.
310
- if (timeout) {
311
- clearTimeout(timeout);
312
- }
313
- timeout = setTimeout(() => {
314
- trigger();
315
- }, Setting.LookAhead);
316
-
317
- return node;
297
+ // Only schedule manual trigger for this node if it's not already in the queue
298
+ if (queue.indexOf(node) < 0) {
299
+ queue.push(node);
300
+ }
301
+
302
+ // Cancel any previous trigger before scheduling a new one.
303
+ // It's common for a webpage to call multiple synchronous "insertRule" / "deleteRule" calls.
304
+ // And in those cases we do not wish to monitor changes multiple times for the same node.
305
+ if (timeout) {
306
+ clearTimeout(timeout);
307
+ }
308
+ timeout = setTimeout(() => {
309
+ trigger();
310
+ }, Setting.LookAhead);
311
+
312
+ return node;
318
313
  }
319
314
 
320
315
  function trigger(): void {
321
- for (const node of queue) {
322
- // Generate a mutation for this node only if it still exists
323
- if (node) {
324
- const shadowRoot = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
325
- // Skip re-processing shadowRoot if it was already discovered
326
- if (shadowRoot && dom.has(node)) {
327
- continue;
328
- }
329
- generate(node, shadowRoot ? Constant.ChildList : Constant.CharacterData);
330
- }
316
+ for (let node of queue) {
317
+ // Generate a mutation for this node only if it still exists
318
+ if (node) {
319
+ let shadowRoot = node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
320
+ // Skip re-processing shadowRoot if it was already discovered
321
+ if (shadowRoot && dom.has(node)) {
322
+ continue;
323
+ }
324
+ generate(node, shadowRoot ? Constant.ChildList : Constant.CharacterData);
331
325
  }
332
- queue = [];
326
+ }
327
+ queue = [];
333
328
  }
334
329
 
335
330
  function generate(target: Node, type: MutationRecordType): void {
336
- generate.dn = FunctionNames.MutationGenerate;
337
- measure(handle)([
338
- {
339
- addedNodes: [target],
340
- attributeName: null,
341
- attributeNamespace: null,
342
- nextSibling: null,
343
- oldValue: null,
344
- previousSibling: null,
345
- removedNodes: [],
346
- target,
347
- type,
348
- },
349
- ]);
331
+ generate.dn = FunctionNames.MutationGenerate;
332
+ measure(handle)([
333
+ {
334
+ addedNodes: [target],
335
+ attributeName: null,
336
+ attributeNamespace: null,
337
+ nextSibling: null,
338
+ oldValue: null,
339
+ previousSibling: null,
340
+ removedNodes: [],
341
+ target,
342
+ type,
343
+ },
344
+ ]);
350
345
  }
351
346
 
352
- function proxyStyleRules(win: IWindowWithOverrides): void {
353
- if (win === null || win === undefined) {
354
- return;
355
- }
356
-
357
- win.clarityOverrides = win.clarityOverrides || {};
358
-
359
- // Some popular open source libraries, like styled-components, optimize performance
360
- // by injecting CSS using insertRule API vs. appending text node. A side effect of
361
- // using javascript API is that it doesn't trigger DOM mutation and therefore we
362
- // need to override the insertRule API and listen for changes manually.
363
- if (win.clarityOverrides.InsertRule === undefined) {
364
- win.clarityOverrides.InsertRule = win.CSSStyleSheet.prototype.insertRule;
365
- win.CSSStyleSheet.prototype.insertRule = function (...args): number {
366
- if (core.active()) {
367
- schedule(this.ownerNode);
368
- }
369
- return win.clarityOverrides.InsertRule.apply(this, args);
370
- };
371
- }
372
-
373
- if ("CSSMediaRule" in win && win.clarityOverrides.MediaInsertRule === undefined) {
374
- win.clarityOverrides.MediaInsertRule = win.CSSMediaRule.prototype.insertRule;
375
- win.CSSMediaRule.prototype.insertRule = function (...args): number {
376
- if (core.active()) {
377
- schedule(this.parentStyleSheet.ownerNode);
378
- }
379
- return win.clarityOverrides.MediaInsertRule.apply(this, args);
380
- };
381
- }
382
-
383
- if (win.clarityOverrides.DeleteRule === undefined) {
384
- win.clarityOverrides.DeleteRule = win.CSSStyleSheet.prototype.deleteRule;
385
- win.CSSStyleSheet.prototype.deleteRule = function (...args): void {
386
- if (core.active()) {
387
- schedule(this.ownerNode);
388
- }
389
- win.clarityOverrides.DeleteRule.apply(this, args);
390
- };
391
- }
392
-
393
- if ("CSSMediaRule" in win && win.clarityOverrides.MediaDeleteRule === undefined) {
394
- win.clarityOverrides.MediaDeleteRule = win.CSSMediaRule.prototype.deleteRule;
395
- win.CSSMediaRule.prototype.deleteRule = function (...args): void {
396
- if (core.active()) {
397
- schedule(this.parentStyleSheet.ownerNode);
398
- }
399
- win.clarityOverrides.MediaDeleteRule.apply(this, args);
400
- };
401
- }
402
347
 
403
- // Add a hook to attachShadow API calls
404
- // In case we are unable to add a hook and browser throws an exception,
405
- // reset attachShadow variable and resume processing like before
406
- if (win.clarityOverrides.AttachShadow === undefined) {
407
- win.clarityOverrides.AttachShadow = win.Element.prototype.attachShadow;
408
- try {
409
- win.Element.prototype.attachShadow = function (...args): ShadowRoot {
410
- if (core.active()) {
411
- return schedule(win.clarityOverrides.AttachShadow.apply(this, args)) as ShadowRoot;
412
- }
413
- return win.clarityOverrides.AttachShadow.apply(this, args);
414
- };
415
- } catch {
416
- win.clarityOverrides.AttachShadow = null;
348
+ function proxyStyleRules(win: any): void {
349
+ if (win === null || win === undefined) {
350
+ return;
351
+ }
352
+
353
+ win.clarityOverrides = win.clarityOverrides || {};
354
+
355
+ // Some popular open source libraries, like styled-components, optimize performance
356
+ // by injecting CSS using insertRule API vs. appending text node. A side effect of
357
+ // using javascript API is that it doesn't trigger DOM mutation and therefore we
358
+ // need to override the insertRule API and listen for changes manually.
359
+ if (win.clarityOverrides.InsertRule === undefined) {
360
+ win.clarityOverrides.InsertRule = win.CSSStyleSheet.prototype.insertRule;
361
+ win.CSSStyleSheet.prototype.insertRule = function (): number {
362
+ if (core.active()) {
363
+ schedule(this.ownerNode);
364
+ }
365
+ return win.clarityOverrides.InsertRule.apply(this, arguments);
366
+ };
367
+ }
368
+
369
+ if ("CSSMediaRule" in win && win.clarityOverrides.MediaInsertRule === undefined) {
370
+ win.clarityOverrides.MediaInsertRule = win.CSSMediaRule.prototype.insertRule;
371
+ win.CSSMediaRule.prototype.insertRule = function (): number {
372
+ if (core.active()) {
373
+ schedule(this.parentStyleSheet.ownerNode);
374
+ }
375
+ return win.clarityOverrides.MediaInsertRule.apply(this, arguments);
376
+ };
377
+ }
378
+
379
+ if (win.clarityOverrides.DeleteRule === undefined) {
380
+ win.clarityOverrides.DeleteRule = win.CSSStyleSheet.prototype.deleteRule;
381
+ win.CSSStyleSheet.prototype.deleteRule = function (): void {
382
+ if (core.active()) {
383
+ schedule(this.ownerNode);
384
+ }
385
+ return win.clarityOverrides.DeleteRule.apply(this, arguments);
386
+ };
387
+ }
388
+
389
+ if ("CSSMediaRule" in win && win.clarityOverrides.MediaDeleteRule === undefined) {
390
+ win.clarityOverrides.MediaDeleteRule = win.CSSMediaRule.prototype.deleteRule;
391
+ win.CSSMediaRule.prototype.deleteRule = function (): void {
392
+ if (core.active()) {
393
+ schedule(this.parentStyleSheet.ownerNode);
394
+ }
395
+ return win.clarityOverrides.MediaDeleteRule.apply(this, arguments);
396
+ };
397
+ }
398
+
399
+ // Add a hook to attachShadow API calls
400
+ // In case we are unable to add a hook and browser throws an exception,
401
+ // reset attachShadow variable and resume processing like before
402
+ if (win.clarityOverrides.AttachShadow === undefined) {
403
+ win.clarityOverrides.AttachShadow = win.Element.prototype.attachShadow;
404
+ try {
405
+ win.Element.prototype.attachShadow = function (): ShadowRoot {
406
+ if (core.active()) {
407
+ return schedule(win.clarityOverrides.AttachShadow.apply(this, arguments)) as ShadowRoot;
408
+ } else {
409
+ return win.clarityOverrides.AttachShadow.apply(this, arguments);
417
410
  }
411
+ };
412
+ } catch {
413
+ win.clarityOverrides.AttachShadow = null;
418
414
  }
419
- }
415
+ }
416
+ }