clarity-js 0.7.56 → 0.7.57
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 +93 -50
- package/build/clarity.min.js +1 -1
- package/build/clarity.module.js +93 -50
- package/build/clarity.performance.js +1 -1
- package/package.json +1 -1
- package/src/core/config.ts +1 -0
- package/src/core/version.ts +1 -1
- package/src/data/metadata.ts +10 -3
- package/src/layout/animation.ts +8 -1
- package/src/layout/mutation.ts +61 -30
- package/types/core.d.ts +1 -0
- package/types/layout.d.ts +8 -2
package/src/layout/mutation.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Priority, Task, Timer } from "@clarity-types/core";
|
|
2
2
|
import { Code, Event, Metric, Severity } from "@clarity-types/data";
|
|
3
|
-
import { Constant, MutationHistory, MutationQueue, Setting, Source } from "@clarity-types/layout";
|
|
3
|
+
import { Constant, MutationHistory, MutationRecordWithTime, MutationQueue, Setting, Source } from "@clarity-types/layout";
|
|
4
4
|
import api from "@src/core/api";
|
|
5
5
|
import * as core from "@src/core";
|
|
6
6
|
import { bind } from "@src/core/event";
|
|
@@ -21,6 +21,7 @@ import config from "@src/core/config";
|
|
|
21
21
|
|
|
22
22
|
let observers: MutationObserver[] = [];
|
|
23
23
|
let mutations: MutationQueue[] = [];
|
|
24
|
+
let throttledMutations: { [key: number]: MutationRecordWithTime } = {};
|
|
24
25
|
let insertRule: (rule: string, index?: number) => number = null;
|
|
25
26
|
let deleteRule: (index?: number) => void = null;
|
|
26
27
|
let attachShadow: (init: ShadowRootInit) => ShadowRoot = null;
|
|
@@ -28,6 +29,7 @@ let mediaInsertRule: (rule: string, index?: number) => number = null;
|
|
|
28
29
|
let mediaDeleteRule: (index?: number) => void = null;
|
|
29
30
|
let queue: Node[] = [];
|
|
30
31
|
let timeout: number = null;
|
|
32
|
+
let throttleDelay: number = null;
|
|
31
33
|
let activePeriod = null;
|
|
32
34
|
let history: MutationHistory = {};
|
|
33
35
|
|
|
@@ -117,6 +119,7 @@ export function stop(): void {
|
|
|
117
119
|
observers = [];
|
|
118
120
|
history = {};
|
|
119
121
|
mutations = [];
|
|
122
|
+
throttledMutations = [];
|
|
120
123
|
queue = [];
|
|
121
124
|
activePeriod = 0;
|
|
122
125
|
timeout = null;
|
|
@@ -137,6 +140,30 @@ function handle(m: MutationRecord[]): void {
|
|
|
137
140
|
});
|
|
138
141
|
}
|
|
139
142
|
|
|
143
|
+
async function processMutation(timer: Timer, mutation: MutationRecord, instance: number, timestamp: number): Promise<void> {
|
|
144
|
+
let state = task.state(timer);
|
|
145
|
+
if (state === Task.Wait) { state = await task.suspend(timer); }
|
|
146
|
+
if (state === Task.Stop) { return; }
|
|
147
|
+
let target = mutation.target;
|
|
148
|
+
let type = config.throttleDom ? track(mutation, timer, instance, timestamp) : mutation.type;
|
|
149
|
+
if (type && target && target.ownerDocument) { dom.parse(target.ownerDocument); }
|
|
150
|
+
if (type && target && target.nodeType == Node.DOCUMENT_FRAGMENT_NODE && (target as ShadowRoot).host) { dom.parse(target as ShadowRoot); }
|
|
151
|
+
switch (type) {
|
|
152
|
+
case Constant.Attributes:
|
|
153
|
+
processNode(target, Source.Attributes, timestamp);
|
|
154
|
+
break;
|
|
155
|
+
case Constant.CharacterData:
|
|
156
|
+
processNode(target, Source.CharacterData, timestamp);
|
|
157
|
+
break;
|
|
158
|
+
case Constant.ChildList:
|
|
159
|
+
processNodeList(mutation.addedNodes, Source.ChildListAdd, timer, timestamp);
|
|
160
|
+
processNodeList(mutation.removedNodes, Source.ChildListRemove, timer, timestamp);
|
|
161
|
+
break;
|
|
162
|
+
case Constant.Throttle:
|
|
163
|
+
default:
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
140
167
|
async function process(): Promise<void> {
|
|
141
168
|
let timer: Timer = { id: id(), cost: Metric.LayoutCost };
|
|
142
169
|
task.start(timer);
|
|
@@ -144,34 +171,28 @@ async function process(): Promise<void> {
|
|
|
144
171
|
let record = mutations.shift();
|
|
145
172
|
let instance = time();
|
|
146
173
|
for (let mutation of record.mutations) {
|
|
147
|
-
|
|
148
|
-
if (state === Task.Wait) { state = await task.suspend(timer); }
|
|
149
|
-
if (state === Task.Stop) { break; }
|
|
150
|
-
let target = mutation.target;
|
|
151
|
-
let type = config.throttleDom ? track(mutation, timer, instance, record.time) : mutation.type;
|
|
152
|
-
if (type && target && target.ownerDocument) { dom.parse(target.ownerDocument); }
|
|
153
|
-
if (type && target && target.nodeType == Node.DOCUMENT_FRAGMENT_NODE && (target as ShadowRoot).host) { dom.parse(target as ShadowRoot); }
|
|
154
|
-
switch (type) {
|
|
155
|
-
case Constant.Attributes:
|
|
156
|
-
processNode(target, Source.Attributes, record.time);
|
|
157
|
-
break;
|
|
158
|
-
case Constant.CharacterData:
|
|
159
|
-
processNode(target, Source.CharacterData, record.time);
|
|
160
|
-
break;
|
|
161
|
-
case Constant.ChildList:
|
|
162
|
-
processNodeList(mutation.addedNodes, Source.ChildListAdd, timer, record.time);
|
|
163
|
-
processNodeList(mutation.removedNodes, Source.ChildListRemove, timer, record.time);
|
|
164
|
-
break;
|
|
165
|
-
case Constant.Suspend:
|
|
166
|
-
let value = dom.get(target);
|
|
167
|
-
if (value) { value.metadata.suspend = true; }
|
|
168
|
-
break;
|
|
169
|
-
default:
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
174
|
+
processMutation(timer, mutation, instance, record.time)
|
|
172
175
|
}
|
|
173
176
|
await encode(Event.Mutation, timer, record.time);
|
|
174
177
|
}
|
|
178
|
+
|
|
179
|
+
let processedMutations = false;
|
|
180
|
+
for (var key of Object.keys(throttledMutations)) {
|
|
181
|
+
let throttledMutationToProcess: MutationRecordWithTime = throttledMutations[key];
|
|
182
|
+
delete throttledMutations[key];
|
|
183
|
+
processMutation(timer, throttledMutationToProcess.mutation, time(), throttledMutationToProcess.timestamp);
|
|
184
|
+
processedMutations = true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (Object.keys(throttledMutations).length > 0) {
|
|
188
|
+
processThrottledMutations();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ensure we encode the previously throttled mutations once we have finished them
|
|
192
|
+
if (Object.keys(throttledMutations).length === 0 && processedMutations) {
|
|
193
|
+
await encode(Event.Mutation, timer, time());
|
|
194
|
+
}
|
|
195
|
+
|
|
175
196
|
task.stop(timer);
|
|
176
197
|
}
|
|
177
198
|
|
|
@@ -197,13 +218,16 @@ function track(m: MutationRecord, timer: Timer, instance: number, timestamp: num
|
|
|
197
218
|
h[0] = inactive ? (h[1] === instance ? h[0] : h[0] + 1) : 1;
|
|
198
219
|
h[1] = instance;
|
|
199
220
|
// Return updated mutation type based on if we have already hit the threshold or not
|
|
200
|
-
if (h[0]
|
|
221
|
+
if (h[0] >= Setting.MutationSuspendThreshold) {
|
|
201
222
|
// Store a reference to removedNodes so we can process them later
|
|
202
223
|
// when we resume mutations again on user interactions
|
|
203
224
|
h[2] = m.removedNodes;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
225
|
+
if (instance > timestamp + Setting.MutationActivePeriod) {
|
|
226
|
+
return m.type;
|
|
227
|
+
}
|
|
228
|
+
// we only store the most recent mutation for a given key if it is being throttled
|
|
229
|
+
throttledMutations[key] = {mutation: m, timestamp};
|
|
230
|
+
return Constant.Throttle;
|
|
207
231
|
}
|
|
208
232
|
}
|
|
209
233
|
return m.type;
|
|
@@ -229,6 +253,13 @@ async function processNodeList(list: NodeList, source: Source, timer: Timer, tim
|
|
|
229
253
|
}
|
|
230
254
|
}
|
|
231
255
|
|
|
256
|
+
function processThrottledMutations(): void {
|
|
257
|
+
if (throttleDelay) {
|
|
258
|
+
clearTimeout(throttleDelay);
|
|
259
|
+
}
|
|
260
|
+
throttleDelay = setTimeout(() => { task.schedule(process, Priority.High) }, Setting.LookAhead);
|
|
261
|
+
}
|
|
262
|
+
|
|
232
263
|
export function schedule(node: Node): Node {
|
|
233
264
|
// Only schedule manual trigger for this node if it's not already in the queue
|
|
234
265
|
if (queue.indexOf(node) < 0) { queue.push(node); }
|
package/types/core.d.ts
CHANGED
package/types/layout.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ export const enum AnimationOperation {
|
|
|
7
7
|
Play = 1,
|
|
8
8
|
Pause = 2,
|
|
9
9
|
Cancel = 3,
|
|
10
|
-
Finish = 4
|
|
10
|
+
Finish = 4,
|
|
11
|
+
CommitStyles = 5
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export const enum Source {
|
|
@@ -105,7 +106,7 @@ export const enum Constant {
|
|
|
105
106
|
ChildList = "childList",
|
|
106
107
|
Attributes = "attributes",
|
|
107
108
|
CharacterData = "characterData",
|
|
108
|
-
|
|
109
|
+
Throttle = "throttle",
|
|
109
110
|
LoadEvent = "load",
|
|
110
111
|
Pixel = "px",
|
|
111
112
|
BorderBox = "border-box",
|
|
@@ -217,6 +218,11 @@ export interface MutationQueue {
|
|
|
217
218
|
mutations: MutationRecord[];
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
export interface MutationRecordWithTime {
|
|
222
|
+
timestamp: number;
|
|
223
|
+
mutation: MutationRecord;
|
|
224
|
+
}
|
|
225
|
+
|
|
220
226
|
export interface MutationHistory {
|
|
221
227
|
[key: string]: [/* Count */ number, /* Instance */ number, /* Remove Nodes Buffer */ NodeList?];
|
|
222
228
|
}
|