spy-client 2.1.8 → 2.1.9

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 (86) hide show
  1. package/README.md +54 -11
  2. package/dist/spy-client.esm.js +14 -9
  3. package/dist/spy-client.iife.js +14 -9
  4. package/dist/spy-client.iife.min.js +1 -1
  5. package/dist/spy-client.js +14 -9
  6. package/dist/spy-client.min.js +1 -1
  7. package/dist/spy-client.mjs +14 -9
  8. package/dist/spy-head.js +15 -10
  9. package/dist/spy-head.min.js +1 -1
  10. package/package.json +1 -1
  11. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/base.css +0 -224
  12. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/basicSpec.ts.html +0 -740
  13. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/block-navigation.js +0 -79
  14. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/checkSpec.ts.html +0 -479
  15. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/favicon.png +0 -0
  16. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/headSpec.ts.html +0 -695
  17. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/index.html +0 -171
  18. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/markSpec.ts.html +0 -284
  19. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/metricSpec.ts.html +0 -977
  20. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/prettify.css +0 -1
  21. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/prettify.js +0 -2
  22. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/sort-arrow-sprite.png +0 -0
  23. package/coverage/Chrome 92.0.4515.159 (Mac OS 10.15.7)/html/sorter.js +0 -170
  24. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/base.css +0 -224
  25. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/basicSpec.ts.html +0 -740
  26. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/block-navigation.js +0 -79
  27. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/checkSpec.ts.html +0 -479
  28. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/favicon.png +0 -0
  29. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/headSpec.ts.html +0 -695
  30. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/index.html +0 -171
  31. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/markSpec.ts.html +0 -284
  32. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/metricSpec.ts.html +0 -977
  33. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/prettify.css +0 -1
  34. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/prettify.js +0 -2
  35. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/sort-arrow-sprite.png +0 -0
  36. package/coverage/Chrome 95.0.4638.69 (Mac OS 10.15.7)/html/sorter.js +0 -170
  37. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/base.css +0 -224
  38. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/basicSpec.ts.html +0 -740
  39. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/block-navigation.js +0 -79
  40. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/checkSpec.ts.html +0 -479
  41. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/favicon.png +0 -0
  42. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/headSpec.ts.html +0 -695
  43. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/index.html +0 -171
  44. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/markSpec.ts.html +0 -284
  45. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/metricSpec.ts.html +0 -977
  46. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/prettify.css +0 -1
  47. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/prettify.js +0 -2
  48. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/sort-arrow-sprite.png +0 -0
  49. package/coverage/Chrome 96.0.4664.110 (Mac OS 10.15.7)/html/sorter.js +0 -170
  50. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/base.css +0 -224
  51. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/basicSpec.ts.html +0 -740
  52. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/block-navigation.js +0 -79
  53. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/checkSpec.ts.html +0 -479
  54. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/favicon.png +0 -0
  55. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/headSpec.ts.html +0 -695
  56. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/index.html +0 -171
  57. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/markSpec.ts.html +0 -284
  58. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/metricSpec.ts.html +0 -977
  59. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/prettify.css +0 -1
  60. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/prettify.js +0 -2
  61. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/sort-arrow-sprite.png +0 -0
  62. package/coverage/Chrome 96.0.4664.55 (Mac OS 10.15.7)/html/sorter.js +0 -170
  63. package/src/head/base.ts +0 -69
  64. package/src/head/error.ts +0 -118
  65. package/src/head/observer.ts +0 -40
  66. package/src/head/readme.md +0 -1
  67. package/src/head/whitescreen.ts +0 -105
  68. package/src/lib/data.ts +0 -14
  69. package/src/lib/huffman.ts +0 -307
  70. package/src/lib/interface.ts +0 -331
  71. package/src/lib/spyHeadInterface.ts +0 -40
  72. package/src/lib/util.ts +0 -101
  73. package/src/module/fid.ts +0 -56
  74. package/src/module/layoutShift.ts +0 -71
  75. package/src/module/lcp.ts +0 -63
  76. package/src/module/longtask.ts +0 -193
  77. package/src/module/memory.ts +0 -43
  78. package/src/module/navigatorInfo.ts +0 -61
  79. package/src/module/resource.ts +0 -433
  80. package/src/module/timing.ts +0 -75
  81. package/src/module/tti.ts +0 -139
  82. package/src/spy-client-basic.ts +0 -389
  83. package/src/spy-client.ts +0 -198
  84. package/src/spy-head.ts +0 -31
  85. package/src/spy-local-cache.ts +0 -478
  86. package/src/types/globals.d.ts +0 -93
@@ -1,478 +0,0 @@
1
- import {huffmanEncode, huffmanDecode} from './lib/huffman';
2
-
3
- interface Option {
4
- defaultTrigger: boolean;
5
- compress: 'huffman' | 'lzw' | 'no';
6
- key: string;
7
- maxRecordLen: number;
8
- interval: number;
9
- onFlush?: (res: any) => void;
10
- onSave?: (res: any) => void;
11
- onAdd?: (res: any) => boolean;
12
- storage: 'indexedDB' | 'localstorage' | 'empty';
13
- }
14
-
15
- class Storage {
16
- static isSupport: () => boolean;
17
-
18
- set(key: string, value: any) {
19
-
20
- }
21
-
22
- get(key: string, cb: any) {
23
-
24
- }
25
-
26
- rm(key: string) {
27
-
28
- }
29
- }
30
-
31
- class LS extends Storage {
32
- static isSupport() {
33
- return !!window.localStorage;
34
- }
35
-
36
- set(key: string, value: any) {
37
- try {
38
- localStorage.setItem(key, value);
39
- }
40
- catch (e) {
41
- console.error(e);
42
- }
43
- return;
44
- }
45
-
46
- get(key: string, cb: any) {
47
- let res = null;
48
- try {
49
- res = localStorage.getItem(key);
50
- }
51
- catch (e) {
52
- console.error(e);
53
- }
54
- if (cb) {
55
- cb(res);
56
- }
57
- return;
58
- }
59
-
60
- rm(key: string) {
61
- try {
62
- localStorage.removeItem(key);
63
- }
64
- catch (e) {
65
- console.error(e);
66
- }
67
- return;
68
- }
69
-
70
- }
71
-
72
- class IndexedDB extends Storage {
73
- private readonly databaseName = 'spyLC';
74
- private db: IDBDatabase | null = null;
75
- private readonly setQueue: any[] = [];
76
- private readonly getQueue: any[] = [];
77
- constructor() {
78
- super();
79
-
80
- let request = window.indexedDB.open(this.databaseName);
81
-
82
- request.onupgradeneeded = event => {
83
- // @ts-ignore ts默认的EventTarget类型有问题,先ignore掉
84
- this.db = event.target && event.target.result;
85
- if (this.db && !this.db.objectStoreNames.contains(this.databaseName)) {
86
- this.db.createObjectStore(this.databaseName, {keyPath: 'key'});
87
- }
88
- this.runQueueTask();
89
- };
90
-
91
- request.onsuccess = () => {
92
- this.db = request.result;
93
- this.runQueueTask();
94
- };
95
-
96
- }
97
- static isSupport() {
98
- return !!window.indexedDB;
99
- }
100
-
101
- set(key: string, value: any) {
102
- // 因为indexDB是异步初始化的,如果set的时候未初始化,先缓存之后再执行set
103
- if (!this.db) {
104
- this.setQueue.push({
105
- key,
106
- value,
107
- });
108
- return;
109
- }
110
- this.db.transaction([this.databaseName], 'readwrite')
111
- .objectStore(this.databaseName)
112
- .add({
113
- key,
114
- value,
115
- });
116
-
117
- }
118
-
119
- get(key: string, cb: any) {
120
- // 因为indexDB是异步初始化的,如果get的时候未初始化,先缓存之后再执行get
121
- if (!this.db) {
122
- this.getQueue.push({
123
- key,
124
- cb,
125
- });
126
- return;
127
- }
128
- const transaction = this.db.transaction([this.databaseName]);
129
- const objectStore = transaction.objectStore(this.databaseName);
130
- const request = objectStore.get(key);
131
-
132
- request.onsuccess = () => {
133
- const result = request.result ? request.result : {value: ''};
134
- cb(result.value);
135
- };
136
- }
137
-
138
- private runQueueTask() {
139
- this.setQueue.forEach(ele => {
140
- this.set(ele.key, ele.value);
141
- });
142
- this.getQueue.forEach(ele => {
143
- this.get(ele.key, ele.cb);
144
- });
145
- }
146
- }
147
-
148
- function assign(...args: any[]) {
149
- const __assign = Object.assign || function __assign(t: any) {
150
- for (let s, i = 1, n = arguments.length; i < n; i++) {
151
- // eslint-disable-next-line prefer-rest-params
152
- s = arguments[i];
153
- for (const p in s) {
154
- if (Object.prototype.hasOwnProperty.call(s, p)) {
155
- t[p] = s[p];
156
- }
157
- }
158
- }
159
- return t;
160
- };
161
- return __assign.apply(this, args);
162
- };
163
-
164
- function utf8Encode(text: string) {
165
- let result = '';
166
- for (let n = 0; n < text.length; n++) {
167
- const c = text.charCodeAt(n);
168
- if (c < 128) {
169
- result += String.fromCharCode(c);
170
- }
171
- else if (c > 127 && c < 2048) {
172
- result += String.fromCharCode((c >> 6) | 192);
173
- result += String.fromCharCode((c & 63) | 128);
174
- }
175
- else {
176
- result += String.fromCharCode((c >> 12) | 224);
177
- result += String.fromCharCode(((c >> 6) & 63) | 128);
178
- result += String.fromCharCode((c & 63) | 128);
179
- }
180
- }
181
- return result;
182
- // return window.btoa(result);
183
- }
184
-
185
- function utf8Decode(text: string) {
186
- // text = window.atob(text);
187
- let result = '';
188
- let i = 0;
189
- let c1 = 0;
190
- let c2 = 0;
191
- let c3 = 0;
192
- while (i < text.length) {
193
- c1 = text.charCodeAt(i);
194
- if (c1 < 128) {
195
- result += String.fromCharCode(c1);
196
- i++;
197
- }
198
- else if (c1 > 191 && c1 < 224) {
199
- c2 = text.charCodeAt(i + 1);
200
- result += String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
201
- i += 2;
202
- }
203
- else {
204
- c2 = text.charCodeAt(i + 1);
205
- c3 = text.charCodeAt(i + 2);
206
- result += String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
207
- i += 3;
208
- }
209
- }
210
- return result;
211
- }
212
-
213
-
214
- function lzw(text: string, deCompress = false) {
215
- if (!text) {
216
- return '';
217
- };
218
- if (!deCompress) {
219
- text = utf8Encode(text);
220
- }
221
-
222
- let dict: any = {};
223
- let out: any[] = [];
224
- let prefix = text.charAt(0);
225
- let curChar = prefix;
226
- let oldPrefix = curChar;
227
- let idx = 256;
228
- let i;
229
- let c;
230
- let d;
231
- let g = function () {
232
- out.push(prefix.length > 1 ? String.fromCharCode(dict[prefix]) : prefix);
233
- };
234
- if (deCompress) {
235
- out.push(prefix);
236
- }
237
- for (i = 1, c, d; i < text.length; i++) {
238
- c = text.charAt(i);
239
- if (deCompress) {
240
- d = text.charCodeAt(i);
241
- prefix = d < 256 ? c : dict[d] || (prefix + curChar);
242
- out.push(prefix);
243
- curChar = prefix.charAt(0);
244
- dict[idx++] = oldPrefix + curChar;
245
- oldPrefix = prefix;
246
- }
247
- else {
248
- if (dict.hasOwnProperty(prefix + c)) {
249
- prefix += c;
250
- }
251
- else {
252
- g();
253
- dict[prefix + c] = idx++;
254
- prefix = c;
255
- }
256
- }
257
- }
258
- if (!deCompress) {
259
- g();
260
- }
261
-
262
- let ret = out.join('');
263
- if (deCompress) {
264
- ret = utf8Decode(ret);
265
- }
266
-
267
- return ret;
268
- }
269
-
270
-
271
- export default class SpyLocalCache {
272
- private readonly option: Option;
273
- private storage: Storage;
274
- private timer: ReturnType<typeof setTimeout>;
275
- private tmpList: string[] = [];
276
- constructor(option: any = {}) {
277
- this.option = assign({
278
- defaultTrigger: true,
279
- compress: 'lzw', // huffman lzw no
280
- key: 'SpyLocalCache',
281
- interval: 500,
282
- maxRecordLen: 30,
283
- onFlush: () => {},
284
- onSave: () => {},
285
- onAdd: () => {
286
- return true;
287
- },
288
- storage: IndexedDB.isSupport()
289
- ? 'indexedDB'
290
- : LS.isSupport()
291
- ? 'localstorage'
292
- : 'empty',
293
- }, option);
294
-
295
- this.load = this.load.bind(this);
296
-
297
- this.init();
298
- }
299
-
300
- init() {
301
- if (this.option.storage === 'indexedDB') {
302
- this.storage = new IndexedDB();
303
- }
304
- else if (this.option.storage === 'localstorage') {
305
- this.storage = new LS();
306
- }
307
- else {
308
- this.storage = new Storage();
309
- }
310
-
311
- if (document.readyState === 'complete') {
312
- this.load();
313
- }
314
- else {
315
- window.addEventListener('load', this.load);
316
- }
317
- }
318
-
319
- load() {
320
- if (location.search.indexOf('_FlushLogLocalCache=1') > -1 && this.option.defaultTrigger) {
321
- this.flushLog();
322
- }
323
- }
324
-
325
- addLog(info: any) {
326
- if (this.option.onAdd) {
327
- if (!this.option.onAdd(info)) {
328
- return;
329
- }
330
- }
331
-
332
- info = JSON.stringify(info);
333
-
334
- this.tmpList.push(info as string);
335
-
336
- // 控制写日志频率
337
- if (this.timer) {
338
- clearTimeout(this.timer);
339
- }
340
- this.timer = setTimeout(() => {
341
- this.save();
342
- }, this.option.interval);
343
- }
344
-
345
- getData(cb: any) {
346
- try {
347
- this.storage.get(this.option.key, (encodeStr: string) => {
348
- if (encodeStr) {
349
- this.storage.get(this.option.key + 'Codes', (codes: any) => {
350
- let res: any[] = [];
351
- try {
352
- if (codes) {
353
- codes = JSON.parse(codes);
354
- }
355
- const str = this.unzip(encodeStr, codes);
356
- res = str.split('\n');
357
- }
358
- catch (e) {
359
- console.error(e);
360
- }
361
- cb(res);
362
- });
363
- }
364
- else {
365
- cb([]);
366
- }
367
- });
368
-
369
- }
370
- catch (e) {
371
- console.error(e);
372
- cb([]);
373
- }
374
- }
375
-
376
- save() {
377
- const st = Date.now();
378
- this.getData((list: any[]) => {
379
- // 只保留最近的maxRecordLen条日志
380
- const reserveLen = this.option.maxRecordLen - 1;
381
- const originList = list.length > reserveLen ? list.slice(list.length - reserveLen, list.length) : list;
382
- let newList = originList.concat(this.tmpList);
383
- let content = newList.join('\n');
384
- let error: Error | null = null;
385
- let len = 0;
386
- try {
387
- let data = this.zip(content);
388
- let codesStr = '';
389
- if (data.codes) {
390
- codesStr = JSON.stringify(data.codes);
391
- this.storage.set(this.option.key + 'Codes', codesStr);
392
- }
393
- else {
394
- this.storage.rm(this.option.key + 'Codes');
395
- }
396
-
397
- this.storage.set(this.option.key, data.result);
398
- len = data.result.length;
399
- }
400
- catch (e) {
401
- error = e;
402
- console.error(e);
403
- }
404
-
405
- this.tmpList = [];
406
-
407
- this.option.onSave && this.option.onSave({
408
- cost: Date.now() - st,
409
- length: len / 1024,
410
- list: newList,
411
- error: error,
412
- });
413
- });
414
- }
415
-
416
- flushLog() {
417
- this.getData((list: any[]) => {
418
-
419
- // 先解析来自存储的数据,若失败,说明格式有问题,清空存储
420
- try {
421
- for (let index = 0; index < list.length; index++) {
422
- list[index] = JSON.parse(list[index]);
423
- }
424
- }
425
- catch (e) {
426
- list = [];
427
- this.storage.rm(this.option.key);
428
- console.error(e);
429
- }
430
- // 未落盘的数据也加上
431
- for (let index = 0; index < this.tmpList.length; index++) {
432
- list.push(JSON.parse(this.tmpList[index]));
433
- }
434
- this.option.onFlush && this.option.onFlush(list);
435
- });
436
- }
437
-
438
- zip(str: string) {
439
- if (this.option.compress === 'lzw') {
440
- return {
441
- codes: null,
442
- result: lzw(str),
443
- };
444
- }
445
- else if (this.option.compress === 'huffman') {
446
- return huffmanEncode(str);
447
- }
448
- return {
449
- codes: null,
450
- result: str,
451
- };
452
- }
453
-
454
- unzip(str: string, codes?: any) {
455
- if (this.option.compress === 'lzw') {
456
- return lzw(str, true);
457
- }
458
- else if (this.option.compress === 'huffman') {
459
- return huffmanDecode(codes, str);
460
- }
461
- return str;
462
- }
463
- }
464
-
465
- // For test
466
- // let content = JSON.stringify({
467
- // type: 3,
468
- // fm: 'disp',
469
- // data: [{"base":{"size":{"doc":{"w":360,"h":4875},"wind":{"w":360,"h":640},"scr":{"w":360,"h":640}},"vsb":"visible","num":16},"t":1629773746698,"path":"/s"}],
470
- // qid: 10991431029479106376,
471
- // did: '8dd09c47c7bc90c9fd7274f0ad2c581e',
472
- // q: '刘德华',
473
- // t: 1629773746698,
474
- // });
475
- // const compressText = lzw(content);
476
- // const deCompressText = lzw(compressText, true);
477
- // console.log('compressText', compressText, compressText.length);
478
- // console.log('deCompressText', deCompressText, deCompressText.length);
@@ -1,93 +0,0 @@
1
- /**
2
- * @file 全局声明
3
- * @author kaivean
4
- */
5
-
6
- interface Window {
7
- __spyHead: any;
8
- __spyclientConf: any;
9
- PerformanceObserver: any;
10
- requestIdleCallback?: (callback: () => void, options?: any) => void;
11
- }
12
-
13
- interface Event {
14
- // MSMediaKeyMessageEvent 继承 Event,并且有message属性 且是 Uint8Array,会有冲突。这里设置为any
15
- message?: any;
16
- lineno?: number;
17
- line?: number;
18
- colno?: number;
19
- column?: number;
20
- error?: any;
21
- filename: any;
22
- sourceURL: any;
23
- errorCharacter?: number;
24
- }
25
-
26
- interface Connection {
27
- downlink?: number;
28
- effectiveType?: '2g' | '3g' | '4g' | 'slow-2g';
29
- onchange?: () => void;
30
- rtt?: number;
31
- saveData?: boolean;
32
- }
33
-
34
- interface Navigator {
35
- deviceMemory: number;
36
- hardwareConcurrency: number;
37
- connection: Connection;
38
- }
39
-
40
- interface MemoryInfo {
41
- totalJSHeapSize: number;
42
- usedJSHeapSize: number;
43
- jsHeapSizeLimit: number;
44
- }
45
-
46
- interface Performance {
47
- memory?: MemoryInfo;
48
- }
49
-
50
- interface PerformanceTiming {
51
- domFirstPaint?: number;
52
- domFirstScreenPaint?: number;
53
- }
54
-
55
- type PerformanceObserverType =
56
- | 'first-input'
57
- | 'largest-contentful-paint'
58
- | 'layout-shift'
59
- | 'longtask'
60
- | 'measure'
61
- | 'navigation'
62
- | 'paint'
63
- | 'resource';
64
-
65
-
66
- type PerformanceEntryInitiatorType =
67
- | 'beacon'
68
- | 'css'
69
- | 'fetch'
70
- | 'img'
71
- | 'other'
72
- | 'script'
73
- | 'xmlhttprequest';
74
-
75
-
76
- interface PerformanceEntry {
77
- decodedBodySize?: number;
78
- // duration: number;
79
- // entryType: PerformanceObserverType;
80
- initiatorType?: PerformanceEntryInitiatorType;
81
- loadTime: number;
82
- // name: string;
83
- renderTime: number;
84
- // startTime: number;
85
- // hadRecentInput?: boolean;
86
- value?: number;
87
- hadRecentInput: boolean;
88
- attribution: Array<{containerSrc: string}>;
89
- }
90
-
91
- interface PerformanceObserver {
92
- takeRecords: () => PerformanceEntryList;
93
- }