spy-client 2.1.15 → 2.2.1

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 (44) hide show
  1. package/.travis.yml +7 -4
  2. package/AGENTS.md +132 -0
  3. package/README.md +27 -19
  4. package/dist/head/base.d.ts +27 -26
  5. package/dist/head/error.d.ts +6 -6
  6. package/dist/head/observer.d.ts +5 -5
  7. package/dist/head/whitescreen.d.ts +6 -6
  8. package/dist/lib/data.d.ts +6 -6
  9. package/dist/lib/huffman.d.ts +9 -9
  10. package/dist/lib/interface.d.ts +209 -209
  11. package/dist/lib/spyHeadInterface.d.ts +36 -36
  12. package/dist/lib/util.d.ts +22 -22
  13. package/dist/module/fid.d.ts +15 -15
  14. package/dist/module/layoutShift.d.ts +18 -18
  15. package/dist/module/lcp.d.ts +17 -17
  16. package/dist/module/longtask.d.ts +46 -46
  17. package/dist/module/memory.d.ts +11 -11
  18. package/dist/module/navigatorInfo.d.ts +9 -9
  19. package/dist/module/resource.d.ts +39 -39
  20. package/dist/module/timing.d.ts +11 -11
  21. package/dist/module/tti.d.ts +19 -19
  22. package/dist/spy-client-basic.d.ts +118 -118
  23. package/dist/spy-client-basic.esm.js +298 -298
  24. package/dist/spy-client-basic.iife.js +298 -298
  25. package/dist/spy-client-basic.iife.min.js +1 -1
  26. package/dist/spy-client-basic.js +298 -298
  27. package/dist/spy-client-basic.min.js +1 -1
  28. package/dist/spy-client-basic.mjs +298 -298
  29. package/dist/spy-client.d.ts +32 -32
  30. package/dist/spy-client.esm.js +1430 -1423
  31. package/dist/spy-client.iife.js +1430 -1423
  32. package/dist/spy-client.iife.min.js +1 -1
  33. package/dist/spy-client.js +1430 -1423
  34. package/dist/spy-client.min.js +1 -1
  35. package/dist/spy-client.mjs +1430 -1423
  36. package/dist/spy-head.d.ts +6 -6
  37. package/dist/spy-head.js +359 -358
  38. package/dist/spy-head.min.js +1 -1
  39. package/dist/spy-local-cache.d.ts +18 -18
  40. package/dist/spy-local-cache.js +718 -711
  41. package/dist/spy-local-cache.min.js +1 -1
  42. package/package.json +26 -73
  43. package/test-results/.last-run.json +4 -0
  44. package/karma.conf.js +0 -111
@@ -1,7 +1,7 @@
1
1
  var SpyClient = (function () {
2
2
  'use strict';
3
3
 
4
- /*! *****************************************************************************
4
+ /******************************************************************************
5
5
  Copyright (c) Microsoft Corporation.
6
6
 
7
7
  Permission to use, copy, modify, and/or distribute this software for any
@@ -15,16 +15,18 @@ var SpyClient = (function () {
15
15
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16
16
  PERFORMANCE OF THIS SOFTWARE.
17
17
  ***************************************************************************** */
18
- /* global Reflect, Promise */
18
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
19
19
 
20
20
  var extendStatics = function(d, b) {
21
21
  extendStatics = Object.setPrototypeOf ||
22
22
  ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
23
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
23
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
24
24
  return extendStatics(d, b);
25
25
  };
26
26
 
27
27
  function __extends(d, b) {
28
+ if (typeof b !== "function" && b !== null)
29
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
28
30
  extendStatics(d, b);
29
31
  function __() { this.constructor = d; }
30
32
  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
@@ -39,1454 +41,1459 @@ var SpyClient = (function () {
39
41
  return t;
40
42
  };
41
43
  return __assign.apply(this, arguments);
44
+ };
45
+
46
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
47
+ var e = new Error(message);
48
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
42
49
  };
43
50
 
44
- /**
45
- * @file utils
46
- * @author kaivean
47
- */
48
- function assign() {
49
- var args = [];
50
- for (var _i = 0; _i < arguments.length; _i++) {
51
- args[_i] = arguments[_i];
52
- }
53
- var __assign = Object.assign || function __assign(t) {
54
- for (var s = void 0, i = 1, n = arguments.length; i < n; i++) {
55
- // eslint-disable-next-line prefer-rest-params
56
- s = arguments[i];
57
- for (var p in s) {
58
- if (Object.prototype.hasOwnProperty.call(s, p)) {
59
- t[p] = s[p];
60
- }
61
- }
62
- }
63
- return t;
64
- };
65
- return __assign.apply(this, args);
66
- }
67
- function getUrlInfoFromURL(url) {
68
- if (URL && url) {
69
- try {
70
- var obj = new URL(url);
71
- if (obj.host !== undefined) {
72
- return {
73
- protocol: obj.protocol,
74
- host: obj.host,
75
- pathname: obj.pathname,
76
- ext: '',
77
- };
78
- }
79
- }
80
- catch (e) {
81
- console.error(e);
82
- }
83
- }
84
- return;
85
- }
86
- function getUrlInfo(url) {
87
- var info = getUrlInfoFromURL(url);
88
- if (!info) {
89
- var parser = document.createElement('a');
90
- parser.href = url;
91
- info = {
92
- protocol: parser.protocol,
93
- host: parser.host || location.host,
94
- pathname: parser.pathname,
95
- ext: '',
96
- };
97
- }
98
- var split = info.pathname.split('.');
99
- info.ext = split[split.length - 1];
100
- return info;
101
- }
102
- function f$1(n) {
103
- return +n.toFixed(1);
104
- }
105
- function getResTiming(t) {
106
- return {
107
- wait: f$1(t.domainLookupStart - (t.navigationStart || t.fetchStart || t.startTime)),
108
- dns: f$1(t.domainLookupEnd - t.domainLookupStart),
109
- connect: f$1(t.connectEnd - t.connectStart),
110
- req: f$1(t.responseStart - t.requestStart),
111
- res: f$1(t.responseEnd - t.responseStart),
112
- };
113
- }
114
- function getxpath(el) {
115
- if (!el) {
116
- return { xpath: '' };
117
- }
118
- var xpath = [];
119
- while (el && el.nodeType === 1 && el !== el.parentNode) {
120
- var t = el.tagName.toLowerCase();
121
- if (el.getAttribute('id')) {
122
- t += '[#' + el.getAttribute('id') + ']';
123
- }
124
- else if (el.classList && el.classList.length) {
125
- t += '[.' + el.classList[el.classList.length - 1] + ']';
126
- }
127
- xpath.push(t);
128
- if (el === document.body) {
129
- break;
130
- }
131
- el = el.parentNode; // 修复缺陷检查
132
- }
133
- return {
134
- xpath: xpath.join('<'),
135
- };
51
+ /**
52
+ * @file utils
53
+ * @author kaivean
54
+ */
55
+ function assign() {
56
+ var args = [];
57
+ for (var _i = 0; _i < arguments.length; _i++) {
58
+ args[_i] = arguments[_i];
59
+ }
60
+ var __assign = Object.assign || function __assign(t) {
61
+ for (var s = void 0, i = 1, n = arguments.length; i < n; i++) {
62
+ // eslint-disable-next-line prefer-rest-params
63
+ s = arguments[i];
64
+ for (var p in s) {
65
+ if (Object.prototype.hasOwnProperty.call(s, p)) {
66
+ t[p] = s[p];
67
+ }
68
+ }
69
+ }
70
+ return t;
71
+ };
72
+ return __assign.apply(null, args);
73
+ }
74
+ function getUrlInfoFromURL(url) {
75
+ if (URL && url) {
76
+ try {
77
+ var obj = new URL(url);
78
+ if (obj.host !== undefined) {
79
+ return {
80
+ protocol: obj.protocol,
81
+ host: obj.host,
82
+ pathname: obj.pathname,
83
+ ext: '',
84
+ };
85
+ }
86
+ }
87
+ catch (e) {
88
+ console.error(e);
89
+ }
90
+ }
91
+ return;
92
+ }
93
+ function getUrlInfo(url) {
94
+ var info = getUrlInfoFromURL(url);
95
+ if (!info) {
96
+ var parser = document.createElement('a');
97
+ parser.href = url;
98
+ info = {
99
+ protocol: parser.protocol,
100
+ host: parser.host || location.host,
101
+ pathname: parser.pathname,
102
+ ext: '',
103
+ };
104
+ }
105
+ var split = info.pathname.split('.');
106
+ info.ext = split[split.length - 1];
107
+ return info;
108
+ }
109
+ function f$1(n) {
110
+ return +n.toFixed(1);
111
+ }
112
+ function getResTiming(t) {
113
+ return {
114
+ wait: f$1(t.domainLookupStart - (t.navigationStart || t.fetchStart || t.startTime)),
115
+ dns: f$1(t.domainLookupEnd - t.domainLookupStart),
116
+ connect: f$1(t.connectEnd - t.connectStart),
117
+ req: f$1(t.responseStart - t.requestStart),
118
+ res: f$1(t.responseEnd - t.responseStart),
119
+ };
120
+ }
121
+ function getxpath(el) {
122
+ if (!el) {
123
+ return { xpath: '' };
124
+ }
125
+ var xpath = [];
126
+ while (el && el.nodeType === 1 && el !== el.parentNode) {
127
+ var t = el.tagName.toLowerCase();
128
+ if (el.getAttribute('id')) {
129
+ t += '[#' + el.getAttribute('id') + ']';
130
+ }
131
+ else if (el.classList && el.classList.length) {
132
+ t += '[.' + el.classList[el.classList.length - 1] + ']';
133
+ }
134
+ xpath.push(t);
135
+ if (el === document.body) {
136
+ break;
137
+ }
138
+ el = el.parentNode; // 修复缺陷检查
139
+ }
140
+ return {
141
+ xpath: xpath.join('<'),
142
+ };
136
143
  }
137
144
 
138
- /**
139
- * @file SpyClient
140
- * @author kaivean
141
- */
142
- var defaultLogServer = 'https://sp1.baidu.com/5b1ZeDe5KgQFm2e88IuM_a/mwb2.gif?';
143
- // 基础版本兼容非浏览器环境
144
- var ver = navigator && navigator.userAgent ? navigator.userAgent.toLowerCase().match(/cpu iphone os (.*?)_/) : '';
145
- var isLtIos14 = ver && ver[2] && (+ver[2] < 14);
146
- function err(msg) {
147
- console.error("[SpyClient_log]".concat(msg));
148
- // throw new Error(msg);
149
- }
150
- function stringify(obj) {
151
- return Object.keys(obj).map(function (key) {
152
- var value = obj[key];
153
- if (typeof value === 'undefined') {
154
- value = '';
155
- }
156
- else if (typeof value !== 'string') {
157
- value = JSON.stringify(value);
158
- }
159
- return encodeURIComponent(key) + '=' + encodeURIComponent(value);
160
- }).join('&');
161
- }
162
- function isArray(arr) {
163
- return Object.prototype.toString.call(arr) === '[object Array]';
164
- }
165
- var SpyClient$1 = /** @class */ (function () {
166
- function SpyClient(option) {
167
- this.sample = {};
168
- this.markCache = {};
169
- if (!option.pid) {
170
- throw new Error('pid is required');
171
- }
172
- this.option = {
173
- pid: option.pid,
174
- lid: option.lid,
175
- check: option.check !== false,
176
- sample: option.sample,
177
- localCache: option.localCache,
178
- logServer: option.logServer || defaultLogServer,
179
- };
180
- }
181
- SpyClient.prototype.handle = function (logItem) {
182
- if (!this.check(logItem)) {
183
- return;
184
- }
185
- logItem = assign({
186
- pid: this.option.pid,
187
- lid: this.option.lid,
188
- ts: Date.now(),
189
- group: 'common',
190
- }, logItem);
191
- if (this.option.localCache) {
192
- this.option.localCache.addLog(logItem);
193
- }
194
- // 当前api设置了抽样,
195
- if (typeof logItem.sample === 'number') {
196
- if (Math.random() > logItem.sample) {
197
- return;
198
- }
199
- }
200
- else if (typeof this.option.sample === 'number' && Math.random() > this.option.sample) { // 否则,用全局抽样
201
- return;
202
- }
203
- delete logItem.sample;
204
- return logItem;
205
- };
206
- SpyClient.prototype.send = function (data, post) {
207
- if (post === void 0) { post = false; }
208
- var logItems = isArray(data) ? data : [data];
209
- var postData = [];
210
- for (var _i = 0, logItems_1 = logItems; _i < logItems_1.length; _i++) {
211
- var logItem = logItems_1[_i];
212
- logItem = this.handle(logItem);
213
- if (!logItem) {
214
- continue;
215
- }
216
- postData.push(logItem);
217
- // 期望通过post方式上传日志
218
- if (post) {
219
- continue;
220
- }
221
- var url = this.option.logServer + stringify(logItem);
222
- this.request(url);
223
- }
224
- if (post) {
225
- this.sendPost(postData);
226
- }
227
- };
228
- SpyClient.prototype.check = function (query) {
229
- if (!this.option.check) {
230
- return true;
231
- }
232
- var types = ['perf', 'except', 'dist', 'count'];
233
- if (types.indexOf(query.type) === -1) {
234
- err('type only is one of ' + types.join(', '));
235
- return false;
236
- }
237
- if (query.group && query.group.length > 30) {
238
- err('group length execeeds 30');
239
- return false;
240
- }
241
- var simpleReg = /^[a-zA-Z0-9-_]{0,30}$/;
242
- if (query.type === 'except') {
243
- if (!(typeof query.info.msg === 'string' && query.info.msg.length)) {
244
- err('info.msg field must be not empty and is String');
245
- return false;
246
- }
247
- }
248
- else {
249
- for (var _i = 0, _a = Object.keys(query.info); _i < _a.length; _i++) {
250
- var infoKey = _a[_i];
251
- if (!simpleReg.test(infoKey)) {
252
- err("info.".concat(infoKey, " is unexpected. ")
253
- + 'Length must be not more than 30. '
254
- + 'Supported chars: a-zA-Z0-9-_');
255
- return false;
256
- }
257
- var infoVal = query.info[infoKey];
258
- if (query.type === 'dist') {
259
- if (infoVal.length > 30) {
260
- err("info.".concat(infoKey, " value length execeeds 30 when type == 'dist'"));
261
- return false;
262
- }
263
- }
264
- else if (typeof infoVal !== 'number') {
265
- err("info.".concat(infoKey, " value must be number"));
266
- return false;
267
- }
268
- }
269
- }
270
- if (query.dim) {
271
- for (var _b = 0, _c = Object.keys(query.dim); _b < _c.length; _b++) {
272
- var dimKey = _c[_b];
273
- if (!simpleReg.test(dimKey)) {
274
- err("dim key [".concat(dimKey, "] is unexpected. ")
275
- + 'Length must be not more than 30. '
276
- + 'Supported chars: a-zA-Z0-9-_');
277
- return false;
278
- }
279
- var dimVal = query.dim[dimKey];
280
- if (!/^[a-zA-Z0-9\-_\*\.\s\/#\+@\&\u4e00-\u9fa5]{0,30}$/.test(dimVal)) {
281
- err("dim.".concat(dimKey, " value [").concat(dimVal, "] is unexpected. ")
282
- + 'Length must be not more than 30. '
283
- + 'Supported chars: a-zA-Z0-9-_*. /#+@& and Chinese');
284
- return false;
285
- }
286
- }
287
- }
288
- return true;
289
- };
290
- /**
291
- *
292
- * @param option 配置
293
- */
294
- SpyClient.prototype.sendPerf = function (option) {
295
- this.send(assign({
296
- type: 'perf',
297
- }, option));
298
- };
299
- /**
300
- *
301
- * @param option 错误配置项
302
- */
303
- SpyClient.prototype.sendExcept = function (option) {
304
- this.send(assign({
305
- type: 'except',
306
- }, option));
307
- };
308
- /**
309
- *
310
- * @param option 配置
311
- */
312
- SpyClient.prototype.sendDist = function (option) {
313
- this.send(assign({
314
- type: 'dist',
315
- }, option));
316
- };
317
- /**
318
- *
319
- * @param option 配置
320
- */
321
- SpyClient.prototype.sendCount = function (option) {
322
- this.send(assign({
323
- type: 'count',
324
- }, option));
325
- };
326
- /**
327
- *
328
- * @param e 错误实例
329
- * @param option 错误配置项
330
- */
331
- SpyClient.prototype.sendExceptForError = function (e, option) {
332
- var newOpt = assign({}, option);
333
- newOpt.info = assign({}, option.info || {}, {
334
- msg: e.message,
335
- stack: e.stack,
336
- });
337
- this.sendExcept(newOpt);
338
- };
339
- SpyClient.prototype.startMark = function (sign) {
340
- this.markCache[sign] = {
341
- start: Date.now(),
342
- };
343
- };
344
- SpyClient.prototype.endMark = function (sign) {
345
- if (this.markCache[sign]) {
346
- this.markCache[sign].total = Date.now() - this.markCache[sign].start;
347
- return this.markCache[sign].total;
348
- }
349
- return 0;
350
- };
351
- SpyClient.prototype.clearMark = function (sign) {
352
- if (this.markCache[sign]) {
353
- delete this.markCache[sign];
354
- }
355
- };
356
- SpyClient.prototype.getAllMark = function () {
357
- var ret = {};
358
- for (var _i = 0, _a = Object.keys(this.markCache); _i < _a.length; _i++) {
359
- var sign = _a[_i];
360
- ret[sign] = this.markCache[sign].total;
361
- }
362
- return ret;
363
- };
364
- SpyClient.prototype.clearAllMark = function () {
365
- this.markCache = {};
366
- };
367
- /**
368
- * sendPost能以post请求发送,可以承接多个日志项,上报内容size可以提升,但没有前端校验
369
- * send(data, true) 也能以post请求发送,但是会有严格校验
370
- * 要求:发送的多条日志必须是同一个pid,同一类型type,同一分组group
371
- * @param data 日志项或者日志项数组
372
- */
373
- SpyClient.prototype.sendPost = function (data) {
374
- var logItems = isArray(data) ? data : [data];
375
- var first = logItems[0];
376
- // 需要在页面暴漏出来pid等基本信息,方式调试查看基本信息与兼容目前的监控
377
- var query = {
378
- pid: first.pid,
379
- type: first.type,
380
- group: first.group,
381
- };
382
- var url = this.option.logServer + stringify(query);
383
- this.request(url, data);
384
- };
385
- // 有data时,意味着要用post发送请求
386
- SpyClient.prototype.request = function (url, data) {
387
- if (!(!isLtIos14
388
- && navigator
389
- && navigator.sendBeacon
390
- && navigator.sendBeacon(url, data ? JSON.stringify(data) : undefined))) {
391
- if (data) {
392
- this.fetch(url, data);
393
- }
394
- else {
395
- (new Image()).src = url;
396
- }
397
- }
398
- };
399
- SpyClient.prototype.fetch = function (url, data) {
400
- if (!fetch) {
401
- err('Global fetch method doesn\'t exist');
402
- return;
403
- }
404
- fetch(url, {
405
- method: 'POST',
406
- credentials: 'include',
407
- headers: {
408
- 'Content-Type': 'application/json',
409
- },
410
- body: JSON.stringify(data),
411
- });
412
- };
413
- return SpyClient;
145
+ /**
146
+ * @file SpyClient
147
+ * @author kaivean
148
+ */
149
+ var defaultLogServer = 'https://sp1.baidu.com/5b1ZeDe5KgQFm2e88IuM_a/mwb2.gif?';
150
+ // 基础版本兼容非浏览器环境
151
+ var ver = navigator && navigator.userAgent ? navigator.userAgent.toLowerCase().match(/cpu iphone os (.*?)_/) : '';
152
+ var isLtIos14 = ver && ver[2] && (+ver[2] < 14);
153
+ function err(msg) {
154
+ console.error("[SpyClient_log]".concat(msg));
155
+ // throw new Error(msg);
156
+ }
157
+ function stringify(obj) {
158
+ return Object.keys(obj).map(function (key) {
159
+ var value = obj[key];
160
+ if (typeof value === 'undefined') {
161
+ value = '';
162
+ }
163
+ else if (typeof value !== 'string') {
164
+ value = JSON.stringify(value);
165
+ }
166
+ return encodeURIComponent(key) + '=' + encodeURIComponent(value);
167
+ }).join('&');
168
+ }
169
+ function isArray(arr) {
170
+ return Object.prototype.toString.call(arr) === '[object Array]';
171
+ }
172
+ var SpyClient$1 = /** @class */ (function () {
173
+ function SpyClient(option) {
174
+ this.sample = {};
175
+ this.markCache = {};
176
+ if (!option.pid) {
177
+ throw new Error('pid is required');
178
+ }
179
+ this.option = {
180
+ pid: option.pid,
181
+ lid: option.lid,
182
+ check: option.check !== false,
183
+ sample: option.sample,
184
+ localCache: option.localCache,
185
+ logServer: option.logServer || defaultLogServer,
186
+ };
187
+ }
188
+ SpyClient.prototype.handle = function (logItem) {
189
+ if (!this.check(logItem)) {
190
+ return;
191
+ }
192
+ logItem = assign({
193
+ pid: this.option.pid,
194
+ lid: this.option.lid,
195
+ ts: Date.now(),
196
+ group: 'common',
197
+ }, logItem);
198
+ if (this.option.localCache) {
199
+ this.option.localCache.addLog(logItem);
200
+ }
201
+ // 当前api设置了抽样,
202
+ if (typeof logItem.sample === 'number') {
203
+ if (Math.random() > logItem.sample) {
204
+ return;
205
+ }
206
+ }
207
+ else if (typeof this.option.sample === 'number' && Math.random() > this.option.sample) { // 否则,用全局抽样
208
+ return;
209
+ }
210
+ delete logItem.sample;
211
+ return logItem;
212
+ };
213
+ SpyClient.prototype.send = function (data, post) {
214
+ if (post === void 0) { post = false; }
215
+ var logItems = isArray(data) ? data : [data];
216
+ var postData = [];
217
+ for (var _i = 0, logItems_1 = logItems; _i < logItems_1.length; _i++) {
218
+ var logItem = logItems_1[_i];
219
+ logItem = this.handle(logItem);
220
+ if (!logItem) {
221
+ continue;
222
+ }
223
+ postData.push(logItem);
224
+ // 期望通过post方式上传日志
225
+ if (post) {
226
+ continue;
227
+ }
228
+ var url = this.option.logServer + stringify(logItem);
229
+ this.request(url);
230
+ }
231
+ if (post) {
232
+ this.sendPost(postData);
233
+ }
234
+ };
235
+ SpyClient.prototype.check = function (query) {
236
+ if (!this.option.check) {
237
+ return true;
238
+ }
239
+ var types = ['perf', 'except', 'dist', 'count'];
240
+ if (types.indexOf(query.type) === -1) {
241
+ err('type only is one of ' + types.join(', '));
242
+ return false;
243
+ }
244
+ if (query.group && query.group.length > 30) {
245
+ err('group length execeeds 30');
246
+ return false;
247
+ }
248
+ var simpleReg = /^[a-zA-Z0-9-_]{0,30}$/;
249
+ if (query.type === 'except') {
250
+ if (!(typeof query.info.msg === 'string' && query.info.msg.length)) {
251
+ err('info.msg field must be not empty and is String');
252
+ return false;
253
+ }
254
+ }
255
+ else {
256
+ for (var _i = 0, _a = Object.keys(query.info); _i < _a.length; _i++) {
257
+ var infoKey = _a[_i];
258
+ if (!simpleReg.test(infoKey)) {
259
+ err("info.".concat(infoKey, " is unexpected. ")
260
+ + 'Length must be not more than 30. '
261
+ + 'Supported chars: a-zA-Z0-9-_');
262
+ return false;
263
+ }
264
+ var infoVal = query.info[infoKey];
265
+ if (query.type === 'dist') {
266
+ if (infoVal.length > 30) {
267
+ err("info.".concat(infoKey, " value length execeeds 30 when type == 'dist'"));
268
+ return false;
269
+ }
270
+ }
271
+ else if (typeof infoVal !== 'number') {
272
+ err("info.".concat(infoKey, " value must be number"));
273
+ return false;
274
+ }
275
+ }
276
+ }
277
+ if (query.dim) {
278
+ for (var _b = 0, _c = Object.keys(query.dim); _b < _c.length; _b++) {
279
+ var dimKey = _c[_b];
280
+ if (!simpleReg.test(dimKey)) {
281
+ err("dim key [".concat(dimKey, "] is unexpected. ")
282
+ + 'Length must be not more than 30. '
283
+ + 'Supported chars: a-zA-Z0-9-_');
284
+ return false;
285
+ }
286
+ var dimVal = query.dim[dimKey];
287
+ if (!/^[a-zA-Z0-9\-_\*\.\s\/#\+@\&\u4e00-\u9fa5]{0,30}$/.test(dimVal)) {
288
+ err("dim.".concat(dimKey, " value [").concat(dimVal, "] is unexpected. ")
289
+ + 'Length must be not more than 30. '
290
+ + 'Supported chars: a-zA-Z0-9-_*. /#+@& and Chinese');
291
+ return false;
292
+ }
293
+ }
294
+ }
295
+ return true;
296
+ };
297
+ /**
298
+ *
299
+ * @param option 配置
300
+ */
301
+ SpyClient.prototype.sendPerf = function (option) {
302
+ this.send(assign({
303
+ type: 'perf',
304
+ }, option));
305
+ };
306
+ /**
307
+ *
308
+ * @param option 错误配置项
309
+ */
310
+ SpyClient.prototype.sendExcept = function (option) {
311
+ this.send(assign({
312
+ type: 'except',
313
+ }, option));
314
+ };
315
+ /**
316
+ *
317
+ * @param option 配置
318
+ */
319
+ SpyClient.prototype.sendDist = function (option) {
320
+ this.send(assign({
321
+ type: 'dist',
322
+ }, option));
323
+ };
324
+ /**
325
+ *
326
+ * @param option 配置
327
+ */
328
+ SpyClient.prototype.sendCount = function (option) {
329
+ this.send(assign({
330
+ type: 'count',
331
+ }, option));
332
+ };
333
+ /**
334
+ *
335
+ * @param e 错误实例
336
+ * @param option 错误配置项
337
+ */
338
+ SpyClient.prototype.sendExceptForError = function (e, option) {
339
+ var newOpt = assign({}, option);
340
+ newOpt.info = assign({}, option.info || {}, {
341
+ msg: e.message,
342
+ stack: e.stack,
343
+ });
344
+ this.sendExcept(newOpt);
345
+ };
346
+ SpyClient.prototype.startMark = function (sign) {
347
+ this.markCache[sign] = {
348
+ start: Date.now(),
349
+ };
350
+ };
351
+ SpyClient.prototype.endMark = function (sign) {
352
+ if (this.markCache[sign]) {
353
+ this.markCache[sign].total = Date.now() - this.markCache[sign].start;
354
+ return this.markCache[sign].total;
355
+ }
356
+ return 0;
357
+ };
358
+ SpyClient.prototype.clearMark = function (sign) {
359
+ if (this.markCache[sign]) {
360
+ delete this.markCache[sign];
361
+ }
362
+ };
363
+ SpyClient.prototype.getAllMark = function () {
364
+ var ret = {};
365
+ for (var _i = 0, _a = Object.keys(this.markCache); _i < _a.length; _i++) {
366
+ var sign = _a[_i];
367
+ ret[sign] = this.markCache[sign].total;
368
+ }
369
+ return ret;
370
+ };
371
+ SpyClient.prototype.clearAllMark = function () {
372
+ this.markCache = {};
373
+ };
374
+ /**
375
+ * sendPost能以post请求发送,可以承接多个日志项,上报内容size可以提升,但没有前端校验
376
+ * send(data, true) 也能以post请求发送,但是会有严格校验
377
+ * 要求:发送的多条日志必须是同一个pid,同一类型type,同一分组group
378
+ * @param data 日志项或者日志项数组
379
+ */
380
+ SpyClient.prototype.sendPost = function (data) {
381
+ var logItems = isArray(data) ? data : [data];
382
+ var first = logItems[0];
383
+ // 需要在页面暴漏出来pid等基本信息,方式调试查看基本信息与兼容目前的监控
384
+ var query = {
385
+ pid: first.pid,
386
+ type: first.type,
387
+ group: first.group,
388
+ };
389
+ var url = this.option.logServer + stringify(query);
390
+ this.request(url, data);
391
+ };
392
+ // 有data时,意味着要用post发送请求
393
+ SpyClient.prototype.request = function (url, data) {
394
+ if (!(!isLtIos14
395
+ && navigator
396
+ && navigator.sendBeacon
397
+ && navigator.sendBeacon(url, data ? JSON.stringify(data) : undefined))) {
398
+ if (data) {
399
+ this.fetch(url, data);
400
+ }
401
+ else {
402
+ (new Image()).src = url;
403
+ }
404
+ }
405
+ };
406
+ SpyClient.prototype.fetch = function (url, data) {
407
+ if (!fetch) {
408
+ err('Global fetch method doesn\'t exist');
409
+ return;
410
+ }
411
+ fetch(url, {
412
+ method: 'POST',
413
+ credentials: 'include',
414
+ headers: {
415
+ 'Content-Type': 'application/json',
416
+ },
417
+ body: JSON.stringify(data),
418
+ });
419
+ };
420
+ return SpyClient;
414
421
  }());
415
422
 
416
- /**
417
- * @file FID
418
- * @author kaivean
419
- */
420
- var spyclient$3 = window.__spyHead;
421
- var entryType$3 = 'first-input';
422
- var FID = /** @class */ (function () {
423
- function FID() {
424
- var _this = this;
425
- this.observer = null;
426
- if (!window.PerformanceObserver) {
427
- return;
428
- }
429
- if (spyclient$3 && spyclient$3.entryMap && spyclient$3.entryMap[entryType$3]) {
430
- this.handle(spyclient$3.entryMap[entryType$3]);
431
- }
432
- try {
433
- this.observer = new PerformanceObserver(function (list) {
434
- _this.handle(list.getEntries());
435
- });
436
- this.observer.observe({ entryTypes: [entryType$3] });
437
- }
438
- catch (e) { }
439
- }
440
- FID.prototype.listenFID = function (cb) {
441
- this.cb = cb;
442
- this.callCB();
443
- };
444
- FID.prototype.callCB = function () {
445
- if (this.finalValue !== undefined && this.cb) {
446
- this.cb({ fid: this.finalValue });
447
- }
448
- };
449
- FID.prototype.destroy = function () {
450
- this.observer && this.observer.disconnect();
451
- this.observer = null;
452
- };
453
- FID.prototype.handle = function (entries) {
454
- var lastEntry = entries.pop();
455
- if (lastEntry) {
456
- this.finalValue = lastEntry.duration;
457
- this.callCB();
458
- this.destroy();
459
- }
460
- };
461
- return FID;
423
+ /**
424
+ * @file FID
425
+ * @author kaivean
426
+ */
427
+ var spyclient$3 = window.__spyHead;
428
+ var entryType$3 = 'first-input';
429
+ var FID = /** @class */ (function () {
430
+ function FID() {
431
+ var _this = this;
432
+ this.observer = null;
433
+ if (!window.PerformanceObserver) {
434
+ return;
435
+ }
436
+ if (spyclient$3 && spyclient$3.entryMap && spyclient$3.entryMap[entryType$3]) {
437
+ this.handle(spyclient$3.entryMap[entryType$3]);
438
+ }
439
+ try {
440
+ this.observer = new PerformanceObserver(function (list) {
441
+ _this.handle(list.getEntries());
442
+ });
443
+ this.observer.observe({ entryTypes: [entryType$3] });
444
+ }
445
+ catch (e) { }
446
+ }
447
+ FID.prototype.listenFID = function (cb) {
448
+ this.cb = cb;
449
+ this.callCB();
450
+ };
451
+ FID.prototype.callCB = function () {
452
+ if (this.finalValue !== undefined && this.cb) {
453
+ this.cb({ fid: this.finalValue });
454
+ }
455
+ };
456
+ FID.prototype.destroy = function () {
457
+ this.observer && this.observer.disconnect();
458
+ this.observer = null;
459
+ };
460
+ FID.prototype.handle = function (entries) {
461
+ var lastEntry = entries.pop();
462
+ if (lastEntry) {
463
+ this.finalValue = lastEntry.duration;
464
+ this.callCB();
465
+ this.destroy();
466
+ }
467
+ };
468
+ return FID;
462
469
  }());
463
470
 
464
- /**
465
- * @file LayoutShift
466
- * @author kaivean
467
- */
468
- var spyclient$2 = window.__spyHead;
469
- var entryType$2 = 'layout-shift';
470
- var LayoutShift = /** @class */ (function () {
471
- function LayoutShift() {
472
- var _this = this;
473
- this.observer = null;
474
- this.onceLeave = false;
475
- if (!window.PerformanceObserver) {
476
- return;
477
- }
478
- // 先消费之前的
479
- if (spyclient$2 && spyclient$2.entryMap && spyclient$2.entryMap[entryType$2]) {
480
- this.handle(spyclient$2.entryMap[entryType$2]);
481
- }
482
- try {
483
- this.observer = new PerformanceObserver(function (list) {
484
- _this.handle(list.getEntries());
485
- });
486
- this.observer.observe({ entryTypes: [entryType$2] });
487
- }
488
- catch (e) { }
489
- }
490
- LayoutShift.prototype.listenLayoutShift = function (cb) {
491
- this.cb = cb;
492
- };
493
- LayoutShift.prototype.leave = function () {
494
- if (!this.onceLeave) {
495
- this.onceLeave = true;
496
- this.observer && this.observer.takeRecords && this.observer.takeRecords();
497
- this.finalValue = this.value;
498
- this.callCB();
499
- this.destroy();
500
- }
501
- };
502
- LayoutShift.prototype.callCB = function () {
503
- if (this.finalValue !== undefined && this.cb) {
504
- this.cb({ layoutShift: this.finalValue });
505
- }
506
- };
507
- LayoutShift.prototype.destroy = function () {
508
- this.observer && this.observer.disconnect();
509
- this.observer = null;
510
- };
511
- LayoutShift.prototype.handle = function (entries) {
512
- var _this = this;
513
- entries.map(function (entry) {
514
- // hadRecentInput 如果过去500毫秒内有用户输入,则返回 true, 刚进入页面内500ms,不会认为是layoutShift
515
- if (!entry.hadRecentInput) {
516
- _this.value = (_this.value || 0) + (entry.value || 0);
517
- }
518
- });
519
- };
520
- return LayoutShift;
471
+ /**
472
+ * @file LayoutShift
473
+ * @author kaivean
474
+ */
475
+ var spyclient$2 = window.__spyHead;
476
+ var entryType$2 = 'layout-shift';
477
+ var LayoutShift = /** @class */ (function () {
478
+ function LayoutShift() {
479
+ var _this = this;
480
+ this.observer = null;
481
+ this.onceLeave = false;
482
+ if (!window.PerformanceObserver) {
483
+ return;
484
+ }
485
+ // 先消费之前的
486
+ if (spyclient$2 && spyclient$2.entryMap && spyclient$2.entryMap[entryType$2]) {
487
+ this.handle(spyclient$2.entryMap[entryType$2]);
488
+ }
489
+ try {
490
+ this.observer = new PerformanceObserver(function (list) {
491
+ _this.handle(list.getEntries());
492
+ });
493
+ this.observer.observe({ entryTypes: [entryType$2] });
494
+ }
495
+ catch (e) { }
496
+ }
497
+ LayoutShift.prototype.listenLayoutShift = function (cb) {
498
+ this.cb = cb;
499
+ };
500
+ LayoutShift.prototype.leave = function () {
501
+ if (!this.onceLeave) {
502
+ this.onceLeave = true;
503
+ this.observer && this.observer.takeRecords && this.observer.takeRecords();
504
+ this.finalValue = this.value;
505
+ this.callCB();
506
+ this.destroy();
507
+ }
508
+ };
509
+ LayoutShift.prototype.callCB = function () {
510
+ if (this.finalValue !== undefined && this.cb) {
511
+ this.cb({ layoutShift: this.finalValue });
512
+ }
513
+ };
514
+ LayoutShift.prototype.destroy = function () {
515
+ this.observer && this.observer.disconnect();
516
+ this.observer = null;
517
+ };
518
+ LayoutShift.prototype.handle = function (entries) {
519
+ var _this = this;
520
+ entries.map(function (entry) {
521
+ // hadRecentInput 如果过去500毫秒内有用户输入,则返回 true, 刚进入页面内500ms,不会认为是layoutShift
522
+ if (!entry.hadRecentInput) {
523
+ _this.value = (_this.value || 0) + (entry.value || 0);
524
+ }
525
+ });
526
+ };
527
+ return LayoutShift;
521
528
  }());
522
529
 
523
- /**
524
- * @file 简单的公共数据模块,内部模块之间数据交换
525
- * @author kaivean
526
- */
527
- var data = {};
528
- function setData(key, value) {
529
- data[key] = value;
530
- }
531
- function getData(key) {
532
- return data[key];
530
+ /**
531
+ * @file 简单的公共数据模块,内部模块之间数据交换
532
+ * @author kaivean
533
+ */
534
+ var data = {};
535
+ function setData(key, value) {
536
+ data[key] = value;
537
+ }
538
+ function getData(key) {
539
+ return data[key];
533
540
  }
534
541
 
535
- /**
536
- * @file LCP
537
- * @author kaivean
538
- */
539
- var spyclient$1 = window.__spyHead;
540
- var entryType$1 = 'largest-contentful-paint';
541
- var LCP = /** @class */ (function () {
542
- function LCP() {
543
- var _this = this;
544
- this.observer = null;
545
- if (!window.PerformanceObserver) {
546
- return;
547
- }
548
- if (spyclient$1 && spyclient$1.entryMap && spyclient$1.entryMap[entryType$1]) {
549
- this.handle(spyclient$1.entryMap[entryType$1]);
550
- }
551
- try {
552
- // 仅在 img,image,svg,video,css url, block element with text nodes
553
- this.observer = new PerformanceObserver(function (list) {
554
- _this.handle(list.getEntries());
555
- });
556
- this.observer.observe({ entryTypes: [entryType$1] });
557
- }
558
- catch (e) { }
559
- }
560
- LCP.prototype.listenLCP = function (cb) {
561
- this.cb = cb;
562
- this.callCB();
563
- };
564
- LCP.prototype.callCB = function () {
565
- if (this.finalValue && this.cb) {
566
- this.cb({ lcp: this.finalValue });
567
- }
568
- };
569
- LCP.prototype.load = function () {
570
- this.observer && this.observer.takeRecords && this.observer.takeRecords();
571
- this.finalValue = this.value;
572
- setData('lcp', this.value);
573
- this.callCB();
574
- this.destroy();
575
- };
576
- LCP.prototype.destroy = function () {
577
- this.observer && this.observer.disconnect();
578
- this.observer = null;
579
- };
580
- LCP.prototype.handle = function (entries) {
581
- var _this = this;
582
- entries.map(function (entry) {
583
- _this.value = entry.renderTime || entry.loadTime;
584
- });
585
- };
586
- return LCP;
542
+ /**
543
+ * @file LCP
544
+ * @author kaivean
545
+ */
546
+ var spyclient$1 = window.__spyHead;
547
+ var entryType$1 = 'largest-contentful-paint';
548
+ var LCP = /** @class */ (function () {
549
+ function LCP() {
550
+ var _this = this;
551
+ this.observer = null;
552
+ if (!window.PerformanceObserver) {
553
+ return;
554
+ }
555
+ if (spyclient$1 && spyclient$1.entryMap && spyclient$1.entryMap[entryType$1]) {
556
+ this.handle(spyclient$1.entryMap[entryType$1]);
557
+ }
558
+ try {
559
+ // 仅在 img,image,svg,video,css url, block element with text nodes
560
+ this.observer = new PerformanceObserver(function (list) {
561
+ _this.handle(list.getEntries());
562
+ });
563
+ this.observer.observe({ entryTypes: [entryType$1] });
564
+ }
565
+ catch (e) { }
566
+ }
567
+ LCP.prototype.listenLCP = function (cb) {
568
+ this.cb = cb;
569
+ this.callCB();
570
+ };
571
+ LCP.prototype.callCB = function () {
572
+ if (this.finalValue && this.cb) {
573
+ this.cb({ lcp: this.finalValue });
574
+ }
575
+ };
576
+ LCP.prototype.load = function () {
577
+ this.observer && this.observer.takeRecords && this.observer.takeRecords();
578
+ this.finalValue = this.value;
579
+ setData('lcp', this.value);
580
+ this.callCB();
581
+ this.destroy();
582
+ };
583
+ LCP.prototype.destroy = function () {
584
+ this.observer && this.observer.disconnect();
585
+ this.observer = null;
586
+ };
587
+ LCP.prototype.handle = function (entries) {
588
+ var _this = this;
589
+ entries.map(function (entry) {
590
+ _this.value = entry.renderTime || entry.loadTime || 0;
591
+ });
592
+ };
593
+ return LCP;
587
594
  }());
588
595
 
589
- function filterExcludeLogGifs(entry) {
590
- return entry.name.indexOf('.gif?') < 0;
591
- }
592
- var TTI = /** @class */ (function () {
593
- function TTI() {
594
- this.lastLongTask = 0;
595
- this.observer = null;
596
- this.interval = 5000;
597
- this.filterRequest = filterExcludeLogGifs;
598
- this.observerCallback = this.observerCallback.bind(this);
599
- this.ttiCheck = this.ttiCheck.bind(this);
600
- }
601
- TTI.prototype.check = function () {
602
- return window.PerformanceObserver && performance && performance.timing && performance.timing.navigationStart;
603
- };
604
- TTI.prototype.listenTTI = function (cb, option) {
605
- if (!this.check()) {
606
- return;
607
- }
608
- // 监听获取longtask,持续20s
609
- this.observeLongtask(20000);
610
- this.cb = cb;
611
- option = option || {};
612
- if (option.interval) {
613
- this.interval = option.interval;
614
- }
615
- if (option.filterRequest) {
616
- this.filterRequest = option.filterRequest;
617
- }
618
- };
619
- TTI.prototype.load = function () {
620
- if (!this.check()) {
621
- return;
622
- }
623
- // 每 5 秒检查一次是否符合TTI要求
624
- this.ttiTimer = setInterval(this.ttiCheck, this.interval);
625
- };
626
- TTI.prototype.ttiCheck = function () {
627
- if (!this.cb) {
628
- return;
629
- }
630
- var now = performance.now();
631
- if (now - this.lastLongTask < this.interval) {
632
- // debug('tti 没有达成条件,上次 longTask 离现在差' + (now - this.lastLongTask) + 'ms');
633
- return;
634
- }
635
- var networkSilence = this.getNetworkSilenceAt();
636
- if (networkSilence === false) {
637
- // debug('tti 没有达成条件,网络没有静默');
638
- return;
639
- }
640
- if (now - networkSilence < this.interval) {
641
- // debug('tti 没有达成条件,上次网络请求离现在差' + (now - networkSilence) + 'ms');
642
- return;
643
- }
644
- // debug('tti 达成条件');
645
- clearTimeout(this.ttiTimer);
646
- var tti = this.lastLongTask;
647
- // 没有longtask,就用dom ready时间作为tti,默认是认为dom ready已经绑定好时间,可以交换了
648
- if (!tti) {
649
- tti = performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart;
650
- }
651
- // 停止 long task 获取
652
- this.stopObserveLongTask();
653
- this.cb({
654
- tti: tti,
655
- });
656
- };
657
- TTI.prototype.observerCallback = function (list) {
658
- var entries = list.getEntries();
659
- var lastEntry = entries[entries.length - 1];
660
- // debug('long task (no more than ' + lastEntry.duration + 'ms) detected');
661
- // 最新的longTask完成时间
662
- this.lastLongTask = lastEntry.startTime + lastEntry.duration;
663
- };
664
- TTI.prototype.stopObserveLongTask = function () {
665
- if (this.observer) {
666
- this.observer.disconnect();
667
- this.observer = null;
668
- }
669
- if (this.stopLongTaskTimeoutId) {
670
- clearTimeout(this.stopLongTaskTimeoutId);
671
- }
672
- };
673
- TTI.prototype.getNetworkSilenceAt = function () {
674
- // const now = performance.now();
675
- var resources = performance.getEntriesByType('resource')
676
- .filter(this.filterRequest);
677
- var lastResourceEnd = 0;
678
- for (var _i = 0, resources_1 = resources; _i < resources_1.length; _i++) {
679
- var item = resources_1[_i];
680
- // 还没有responseEnd字段,说明网络请求还未结束
681
- if (!item.responseEnd) {
682
- return false;
683
- }
684
- // 取responseEnd最大值
685
- if (item.responseEnd > lastResourceEnd) {
686
- lastResourceEnd = item.responseEnd;
687
- }
688
- }
689
- return lastResourceEnd;
690
- };
691
- TTI.prototype.observeLongtask = function (timeout) {
692
- this.stopObserveLongTask();
693
- try {
694
- this.observer = new PerformanceObserver(this.observerCallback);
695
- this.observer.observe({
696
- entryTypes: ['longtask'],
697
- });
698
- this.stopLongTaskTimeoutId = setTimeout(this.stopObserveLongTask, timeout);
699
- }
700
- catch (e) { }
701
- };
702
- return TTI;
596
+ function filterExcludeLogGifs(entry) {
597
+ return entry.name.indexOf('.gif?') < 0;
598
+ }
599
+ var TTI = /** @class */ (function () {
600
+ function TTI() {
601
+ this.lastLongTask = 0;
602
+ this.observer = null;
603
+ this.interval = 5000;
604
+ this.filterRequest = filterExcludeLogGifs;
605
+ this.observerCallback = this.observerCallback.bind(this);
606
+ this.ttiCheck = this.ttiCheck.bind(this);
607
+ }
608
+ TTI.prototype.check = function () {
609
+ return window.PerformanceObserver && performance && performance.timing && performance.timing.navigationStart;
610
+ };
611
+ TTI.prototype.listenTTI = function (cb, option) {
612
+ if (!this.check()) {
613
+ return;
614
+ }
615
+ // 监听获取longtask,持续20s
616
+ this.observeLongtask(20000);
617
+ this.cb = cb;
618
+ option = option || {};
619
+ if (option.interval) {
620
+ this.interval = option.interval;
621
+ }
622
+ if (option.filterRequest) {
623
+ this.filterRequest = option.filterRequest;
624
+ }
625
+ };
626
+ TTI.prototype.load = function () {
627
+ if (!this.check()) {
628
+ return;
629
+ }
630
+ // 每 5 秒检查一次是否符合TTI要求
631
+ this.ttiTimer = setInterval(this.ttiCheck, this.interval);
632
+ };
633
+ TTI.prototype.ttiCheck = function () {
634
+ if (!this.cb) {
635
+ return;
636
+ }
637
+ var now = performance.now();
638
+ if (now - this.lastLongTask < this.interval) {
639
+ // debug('tti 没有达成条件,上次 longTask 离现在差' + (now - this.lastLongTask) + 'ms');
640
+ return;
641
+ }
642
+ var networkSilence = this.getNetworkSilenceAt();
643
+ if (networkSilence === false) {
644
+ // debug('tti 没有达成条件,网络没有静默');
645
+ return;
646
+ }
647
+ if (now - networkSilence < this.interval) {
648
+ // debug('tti 没有达成条件,上次网络请求离现在差' + (now - networkSilence) + 'ms');
649
+ return;
650
+ }
651
+ // debug('tti 达成条件');
652
+ clearTimeout(this.ttiTimer);
653
+ var tti = this.lastLongTask;
654
+ // 没有longtask,就用dom ready时间作为tti,默认是认为dom ready已经绑定好时间,可以交换了
655
+ if (!tti) {
656
+ tti = performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart;
657
+ }
658
+ // 停止 long task 获取
659
+ this.stopObserveLongTask();
660
+ this.cb({
661
+ tti: tti,
662
+ });
663
+ };
664
+ TTI.prototype.observerCallback = function (list) {
665
+ var entries = list.getEntries();
666
+ var lastEntry = entries[entries.length - 1];
667
+ // debug('long task (no more than ' + lastEntry.duration + 'ms) detected');
668
+ // 最新的longTask完成时间
669
+ this.lastLongTask = lastEntry.startTime + lastEntry.duration;
670
+ };
671
+ TTI.prototype.stopObserveLongTask = function () {
672
+ if (this.observer) {
673
+ this.observer.disconnect();
674
+ this.observer = null;
675
+ }
676
+ if (this.stopLongTaskTimeoutId) {
677
+ clearTimeout(this.stopLongTaskTimeoutId);
678
+ }
679
+ };
680
+ TTI.prototype.getNetworkSilenceAt = function () {
681
+ // const now = performance.now();
682
+ var resources = performance.getEntriesByType('resource')
683
+ .filter(this.filterRequest);
684
+ var lastResourceEnd = 0;
685
+ for (var _i = 0, resources_1 = resources; _i < resources_1.length; _i++) {
686
+ var item = resources_1[_i];
687
+ // 还没有responseEnd字段,说明网络请求还未结束
688
+ if (!item.responseEnd) {
689
+ return false;
690
+ }
691
+ // 取responseEnd最大值
692
+ if (item.responseEnd > lastResourceEnd) {
693
+ lastResourceEnd = item.responseEnd;
694
+ }
695
+ }
696
+ return lastResourceEnd;
697
+ };
698
+ TTI.prototype.observeLongtask = function (timeout) {
699
+ this.stopObserveLongTask();
700
+ try {
701
+ this.observer = new PerformanceObserver(this.observerCallback);
702
+ this.observer.observe({
703
+ entryTypes: ['longtask'],
704
+ });
705
+ this.stopLongTaskTimeoutId = setTimeout(this.stopObserveLongTask, timeout);
706
+ }
707
+ catch (e) { }
708
+ };
709
+ return TTI;
703
710
  }());
704
711
 
705
- /**
706
- * @file LCP
707
- * @author kaivean
708
- */
709
- var Timing = /** @class */ (function () {
710
- function Timing() {
711
- }
712
- // constructor() {}
713
- Timing.prototype.listenTiming = function (cb) {
714
- if (!window.performance || !window.performance.timing) {
715
- // debug('no performance.timing api');
716
- return;
717
- }
718
- this.cb = cb;
719
- };
720
- Timing.prototype.load = function () {
721
- var _this = this;
722
- setTimeout(function () {
723
- _this.cb && _this.cb(_this.getMetric());
724
- }, 500);
725
- };
726
- Timing.prototype.getMetric = function () {
727
- var metric = {};
728
- var timing = window.performance.timing;
729
- var startTime = timing.navigationStart;
730
- // 基础信息
731
- metric.dns = timing.domainLookupEnd - timing.domainLookupStart;
732
- metric.tcp = timing.connectEnd - timing.connectStart;
733
- metric.request = timing.responseStart - timing.requestStart;
734
- metric.response = timing.responseEnd - timing.responseStart;
735
- // 这是从页面部分数据返回,浏览器开始解析doc元素到最底部的script脚本解析执行完成
736
- // 脚本里触发的异步或绑定了更靠后的事件,不再纳入范围内
737
- metric.parseHtml = timing.domInteractive - timing.domLoading;
738
- // 很多事件绑定是在domContentLoaded事件里的,所以等其结束,一般页面元素的事件绑定好了,用户可以正确交互
739
- // 当然存在在该加载事件之后绑定元素事件情况,但不再此考虑范围内
740
- metric.domReady = timing.domContentLoadedEventEnd - startTime;
741
- metric.loadEventHandle = timing.loadEventEnd - timing.loadEventStart;
742
- // 基本该做的都做完,资源也都加载完成了
743
- // 当然在onload事件处理函数里启动了异步方法,不再纳入范围内
744
- metric.load = timing.loadEventEnd - startTime;
745
- // 浏览器首次绘制与首次内容绘制, ios低版本无getEntriesByType api
746
- if (performance.getEntriesByType) {
747
- var paintEntries = performance.getEntriesByType('paint');
748
- if (paintEntries && paintEntries.length) {
749
- paintEntries.forEach(function (_a) {
750
- var name = _a.name, duration = _a.duration, startTime = _a.startTime;
751
- var time = Math.ceil(duration + startTime);
752
- if (name === 'first-paint') {
753
- metric.fp = time;
754
- }
755
- if (name === 'first-contentful-paint') {
756
- metric.fcp = time;
757
- }
758
- });
759
- }
760
- }
761
- // 端首次绘制与首屏
762
- if (timing.domFirstPaint) {
763
- metric.t7FirstPaint = timing.domFirstPaint - startTime;
764
- }
765
- if (timing.domFirstScreenPaint) {
766
- metric.t7FirstScreen = timing.domFirstScreenPaint - startTime;
767
- }
768
- return metric;
769
- };
770
- return Timing;
712
+ /**
713
+ * @file LCP
714
+ * @author kaivean
715
+ */
716
+ var Timing = /** @class */ (function () {
717
+ function Timing() {
718
+ }
719
+ // constructor() {}
720
+ Timing.prototype.listenTiming = function (cb) {
721
+ if (!window.performance || !window.performance.timing) {
722
+ // debug('no performance.timing api');
723
+ return;
724
+ }
725
+ this.cb = cb;
726
+ };
727
+ Timing.prototype.load = function () {
728
+ var _this = this;
729
+ setTimeout(function () {
730
+ _this.cb && _this.cb(_this.getMetric());
731
+ }, 500);
732
+ };
733
+ Timing.prototype.getMetric = function () {
734
+ var metric = {};
735
+ var timing = window.performance.timing;
736
+ var startTime = timing.navigationStart;
737
+ // 基础信息
738
+ metric.dns = timing.domainLookupEnd - timing.domainLookupStart;
739
+ metric.tcp = timing.connectEnd - timing.connectStart;
740
+ metric.request = timing.responseStart - timing.requestStart;
741
+ metric.response = timing.responseEnd - timing.responseStart;
742
+ // 这是从页面部分数据返回,浏览器开始解析doc元素到最底部的script脚本解析执行完成
743
+ // 脚本里触发的异步或绑定了更靠后的事件,不再纳入范围内
744
+ metric.parseHtml = timing.domInteractive - timing.domLoading;
745
+ // 很多事件绑定是在domContentLoaded事件里的,所以等其结束,一般页面元素的事件绑定好了,用户可以正确交互
746
+ // 当然存在在该加载事件之后绑定元素事件情况,但不再此考虑范围内
747
+ metric.domReady = timing.domContentLoadedEventEnd - startTime;
748
+ metric.loadEventHandle = timing.loadEventEnd - timing.loadEventStart;
749
+ // 基本该做的都做完,资源也都加载完成了
750
+ // 当然在onload事件处理函数里启动了异步方法,不再纳入范围内
751
+ metric.load = timing.loadEventEnd - startTime;
752
+ // 浏览器首次绘制与首次内容绘制, ios低版本无getEntriesByType api
753
+ if (performance.getEntriesByType) {
754
+ var paintEntries = performance.getEntriesByType('paint');
755
+ if (paintEntries && paintEntries.length) {
756
+ paintEntries.forEach(function (_a) {
757
+ var name = _a.name, duration = _a.duration, startTime = _a.startTime;
758
+ var time = Math.ceil(duration + startTime);
759
+ if (name === 'first-paint') {
760
+ metric.fp = time;
761
+ }
762
+ if (name === 'first-contentful-paint') {
763
+ metric.fcp = time;
764
+ }
765
+ });
766
+ }
767
+ }
768
+ // 端首次绘制与首屏
769
+ if (timing.domFirstPaint) {
770
+ metric.t7FirstPaint = timing.domFirstPaint - startTime;
771
+ }
772
+ if (timing.domFirstScreenPaint) {
773
+ metric.t7FirstScreen = timing.domFirstScreenPaint - startTime;
774
+ }
775
+ return metric;
776
+ };
777
+ return Timing;
771
778
  }());
772
779
 
773
- var ResType;
774
- (function (ResType) {
775
- ResType["JS"] = "js";
776
- ResType["CSS"] = "css";
777
- ResType["IMG"] = "img";
778
- ResType["FONT"] = "font";
780
+ var ResType;
781
+ (function (ResType) {
782
+ ResType["JS"] = "js";
783
+ ResType["CSS"] = "css";
784
+ ResType["IMG"] = "img";
785
+ ResType["FONT"] = "font";
779
786
  })(ResType || (ResType = {}));
780
787
 
781
- // 字体
782
- // svg使用场景比较灵活,可能作为一种字体格式,也可能当做图片使用
783
- // 但因woff在所有现代浏览器中都支持,在css中使用iconfont时
784
- // 往往优先加载woff格式字体文件,所以这里将svg划分为图片一类
785
- var FontsTypes;
786
- (function (FontsTypes) {
787
- FontsTypes[FontsTypes["ttf"] = 0] = "ttf";
788
- FontsTypes[FontsTypes["eot"] = 1] = "eot";
789
- FontsTypes[FontsTypes["woff"] = 2] = "woff";
790
- FontsTypes[FontsTypes["woff2"] = 3] = "woff2";
791
- })(FontsTypes || (FontsTypes = {}));
792
- // 忽略图片
793
- // 下述图片路径往往是用来进行日志统计,不是正常图片
794
- var ignoreDefaultPaths = [
795
- '/mwb2.gif',
796
- ];
797
- function ignorePath(url, paths) {
798
- for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) {
799
- var path = paths_1[_i];
800
- if (url.indexOf(path) > -1) {
801
- return true;
802
- }
803
- }
804
- return false;
805
- }
806
- function f(n) {
807
- return +n.toFixed(1);
808
- }
809
- var Resource = /** @class */ (function () {
810
- function Resource() {
811
- this.trigger = 'load';
812
- this.jsList = [];
813
- this.cssList = [];
814
- this.imgList = [];
815
- this.fontList = [];
816
- this.hostList = {};
817
- this.bigImgList = {};
818
- this.httpResList = {};
819
- this.slowList = {};
820
- this.resOption = { ignorePaths: [], trigger: 'load' };
821
- this.bigImgOption = { ignorePaths: [], maxSize: 150 * 1024, trigger: 'load' };
822
- this.httpResOption = { ignorePaths: [], trigger: 'load' };
823
- this.slowOption = { ignorePaths: [], trigger: 'load', threshold: 1000 };
824
- }
825
- Resource.prototype.check = function () {
826
- return performance && performance.getEntriesByType;
827
- };
828
- Resource.prototype.listenResource = function (cb, option) {
829
- if (option === void 0) { option = {}; }
830
- if (!this.check()) {
831
- return;
832
- }
833
- this.resOption = assign(this.resOption, option);
834
- this.trigger = this.resOption.trigger;
835
- this.cb = cb;
836
- };
837
- Resource.prototype.listenBigImg = function (cb, option) {
838
- if (option === void 0) { option = {}; }
839
- if (!this.check()) {
840
- return;
841
- }
842
- this.bigImgOption = assign(this.bigImgOption, option);
843
- this.trigger = this.bigImgOption.trigger;
844
- this.bigImgCB = cb;
845
- };
846
- Resource.prototype.listenHttpResource = function (cb, option) {
847
- if (option === void 0) { option = {}; }
848
- if (!this.check()) {
849
- return;
850
- }
851
- this.httpResOption = assign(this.httpResOption, option);
852
- this.trigger = this.httpResOption.trigger;
853
- this.httpResCB = cb;
854
- };
855
- Resource.prototype.listenSlowResource = function (cb, option) {
856
- if (option === void 0) { option = {}; }
857
- if (!this.check()) {
858
- return;
859
- }
860
- this.slowOption = assign(this.slowOption, option);
861
- this.trigger = this.slowOption.trigger;
862
- this.slowResCB = cb;
863
- };
864
- Resource.prototype.report = function () {
865
- var _this = this;
866
- var metricRes = this.getMetric();
867
- if (metricRes) {
868
- var metric = metricRes.metric, hostMetric = metricRes.hostMetric;
869
- this.cb && this.cb(metric, hostMetric);
870
- }
871
- if (this.bigImgCB) {
872
- // 发送大图监控数据
873
- if (typeof window.requestIdleCallback === 'function' && Object.keys(this.bigImgList).length) {
874
- window.requestIdleCallback(function () {
875
- for (var _i = 0, _a = Object.keys(_this.bigImgList); _i < _a.length; _i++) {
876
- var host = _a[_i];
877
- for (var _b = 0, _c = _this.bigImgList[host]; _b < _c.length; _b++) {
878
- var entry = _c[_b];
879
- var timing = entry.timing;
880
- var type = entry.type;
881
- try {
882
- var img = document.body.querySelector('img[src="' + timing.name + '"]');
883
- _this.bigImgCB({
884
- msg: timing.name,
885
- dur: timing.duration,
886
- xpath: getxpath(img).xpath,
887
- host: host,
888
- type: type,
889
- });
890
- }
891
- catch (e) {
892
- console.error(e);
893
- }
894
- }
895
- }
896
- });
897
- }
898
- }
899
- if (this.httpResCB) {
900
- // 发送http资源监控数据
901
- if (typeof window.requestIdleCallback === 'function' && Object.keys(this.httpResList).length) {
902
- window.requestIdleCallback(function () {
903
- for (var _i = 0, _a = Object.keys(_this.httpResList); _i < _a.length; _i++) {
904
- var host = _a[_i];
905
- for (var _b = 0, _c = _this.httpResList[host]; _b < _c.length; _b++) {
906
- var entry = _c[_b];
907
- var timing = entry.timing;
908
- var type = entry.type;
909
- try {
910
- var img = document.body.querySelector('[src="' + timing.name + '"]');
911
- _this.httpResCB({
912
- msg: timing.name,
913
- dur: timing.duration,
914
- xpath: getxpath(img).xpath,
915
- host: host,
916
- type: type,
917
- });
918
- }
919
- catch (e) {
920
- console.error(e);
921
- }
922
- }
923
- }
924
- });
925
- }
926
- }
927
- if (this.slowResCB) {
928
- // 发送慢资源监控数据
929
- if (typeof window.requestIdleCallback === 'function' && Object.keys(this.slowList).length) {
930
- window.requestIdleCallback(function () {
931
- for (var _i = 0, _a = Object.keys(_this.slowList); _i < _a.length; _i++) {
932
- var host = _a[_i];
933
- for (var _b = 0, _c = _this.slowList[host]; _b < _c.length; _b++) {
934
- var entry = _c[_b];
935
- var timing = entry.timing;
936
- var type = entry.type;
937
- try {
938
- var img = document.body.querySelector('[src="' + timing.name + '"]');
939
- var info = getResTiming(timing);
940
- _this.slowResCB(__assign(__assign({}, info), { dur: timing.duration, msg: timing.name, xpath: getxpath(img).xpath, host: host, type: type }));
941
- }
942
- catch (e) {
943
- console.error(e);
944
- }
945
- }
946
- }
947
- });
948
- }
949
- }
950
- };
951
- Resource.prototype.load = function () {
952
- var _this = this;
953
- if (this.trigger !== 'load') {
954
- return;
955
- }
956
- if (!this.check()) {
957
- return;
958
- }
959
- setTimeout(function () {
960
- _this.report();
961
- }, 500);
962
- };
963
- Resource.prototype.leave = function () {
964
- if (this.trigger !== 'leave') {
965
- return;
966
- }
967
- this.report();
968
- };
969
- Resource.prototype.push = function (type, list, timing, urlInfo) {
970
- if (!ignorePath(urlInfo.pathname, this.resOption.ignorePaths || [])) {
971
- list.push(timing);
972
- this.pushWithHost(type, this.hostList, timing, urlInfo);
973
- }
974
- if (!ignorePath(urlInfo.pathname, this.slowOption.ignorePaths || [])) {
975
- if (timing.duration > this.slowOption.threshold) {
976
- this.pushWithHost(type, this.slowList, timing, urlInfo);
977
- }
978
- }
979
- };
980
- Resource.prototype.pushWithHost = function (type, list, timing, urlInfo) {
981
- var host = urlInfo.host;
982
- if (!list[host]) {
983
- list[host] = [];
984
- }
985
- list[host].push({
986
- timing: timing,
987
- type: type,
988
- });
989
- };
990
- Resource.prototype.collectHttpResInHttps = function (type, timing, urlInfo) {
991
- if (location.protocol === 'https:'
992
- && timing.name.indexOf('http://') === 0
993
- && !ignorePath(urlInfo.pathname, this.httpResOption.ignorePaths || [])) {
994
- this.pushWithHost(type, this.httpResList, timing, urlInfo);
995
- }
996
- };
997
- // 有些jsonp也属于script,这里只统计js后缀的文件
998
- Resource.prototype.addScript = function (timing, urlInfo) {
999
- if (urlInfo.ext === 'js') {
1000
- if (timing.decodedBodySize !== 0) {
1001
- this.push('js', this.jsList, timing, urlInfo);
1002
- }
1003
- }
1004
- };
1005
- // 暂时将css文件或代码块发起的请求归位三类(主要为这两类)
1006
- // 1、加载字体
1007
- // 2、加载背景图(图片不容易区分,有的没有明确后缀名)
1008
- // 3、光标文件(后缀为.cur,这里也划分为图片)
1009
- // (svg当做图片,前述已说明)
1010
- Resource.prototype.addResFromCss = function (timing, urlInfo) {
1011
- if (urlInfo.ext && FontsTypes.hasOwnProperty(urlInfo.ext)) {
1012
- this.push('font', this.fontList, timing, urlInfo);
1013
- }
1014
- else {
1015
- this.addImg(timing, urlInfo);
1016
- }
1017
- };
1018
- // link一般加载css资源
1019
- // 也可以通过preload可以预下载一些资源
1020
- // 这里只统计js类型的preload
1021
- Resource.prototype.addLink = function (timing, urlInfo) {
1022
- if (urlInfo.ext === 'css') {
1023
- this.push('css', this.cssList, timing, urlInfo);
1024
- }
1025
- // preload as script
1026
- else if (urlInfo.ext === 'js') {
1027
- this.push('js', this.jsList, timing, urlInfo);
1028
- }
1029
- };
1030
- Resource.prototype.addImg = function (timing, urlInfo) {
1031
- this.push('img', this.imgList, timing, urlInfo);
1032
- // 大于指定size的图片采集
1033
- if (timing.decodedBodySize > (this.bigImgOption.maxSize || 0)
1034
- && !ignorePath(urlInfo.pathname, this.bigImgOption.ignorePaths || [])) {
1035
- this.pushWithHost('img', this.bigImgList, timing, urlInfo);
1036
- }
1037
- };
1038
- Resource.prototype.handleTimings = function (list) {
1039
- var len = list.length;
1040
- for (var i = 0; i < len; i++) {
1041
- var timing = list[i];
1042
- var urlInfo = getUrlInfo(timing.name);
1043
- if (ignorePath(urlInfo.pathname, ignoreDefaultPaths)) {
1044
- continue;
1045
- }
1046
- switch (list[i].initiatorType) {
1047
- case 'script':
1048
- this.addScript(timing, urlInfo);
1049
- break;
1050
- case 'css':
1051
- this.addResFromCss(timing, urlInfo);
1052
- break;
1053
- case 'img':
1054
- this.addImg(timing, urlInfo);
1055
- break;
1056
- case 'link':
1057
- this.addLink(timing, urlInfo);
1058
- break;
1059
- case 'audio':
1060
- this.collectHttpResInHttps('audio', timing, urlInfo);
1061
- break;
1062
- case 'video':
1063
- this.collectHttpResInHttps('video', timing, urlInfo);
1064
- break;
1065
- }
1066
- }
1067
- };
1068
- Resource.prototype.getNumAndSize = function (type, list) {
1069
- var obj = {};
1070
- var num = type + 'Num';
1071
- var size = type + 'Size';
1072
- var transferSize = type + 'TransferSize';
1073
- var cacheRate = type + 'CacheRate';
1074
- var duration = type + 'Duration';
1075
- obj[num] = 0;
1076
- obj[size] = 0;
1077
- obj[transferSize] = 0;
1078
- var totalDurationTime = 0;
1079
- list.forEach(function (timing) {
1080
- obj[num]++;
1081
- obj[size] += (timing.decodedBodySize / 1024);
1082
- obj[transferSize] += (timing.transferSize / 1024);
1083
- totalDurationTime += timing.duration;
1084
- });
1085
- obj[duration] = f(obj[num] > 0 ? totalDurationTime / obj[num] : 0);
1086
- if (obj[size]) {
1087
- var diff = obj[size] - obj[transferSize];
1088
- obj[cacheRate] = f(diff >= 0 ? 100 * diff / obj[size] : 0);
1089
- }
1090
- obj[size] = f(obj[size]);
1091
- obj[transferSize] = f(obj[transferSize]);
1092
- return obj;
1093
- };
1094
- Resource.prototype.getMetric = function () {
1095
- // 原来代码
1096
- var mainPageTiming = performance.getEntriesByType('navigation')[0];
1097
- var resourceTimings = performance.getEntriesByType('resource');
1098
- if (mainPageTiming && resourceTimings && resourceTimings.length) {
1099
- this.handleTimings(resourceTimings);
1100
- var metric = __assign(__assign(__assign(__assign({}, this.getNumAndSize(ResType.JS, this.jsList)), this.getNumAndSize(ResType.CSS, this.cssList)), this.getNumAndSize(ResType.IMG, this.imgList)), this.getNumAndSize(ResType.FONT, this.fontList));
1101
- // 主文档大小
1102
- var pageTiming = mainPageTiming;
1103
- metric.docSize = f(pageTiming.decodedBodySize / 1024);
1104
- metric.docTransferSize = f(pageTiming.transferSize / 1024);
1105
- metric.headerSize = f((pageTiming.transferSize - pageTiming.encodedBodySize || 0) / 1024);
1106
- metric.allSize = f(metric.docSize
1107
- + metric.jsSize
1108
- + metric.cssSize
1109
- + metric.imgSize
1110
- + metric.fontSize);
1111
- metric.allTransferSize = f(metric.docTransferSize
1112
- + metric.jsTransferSize
1113
- + metric.cssTransferSize
1114
- + metric.imgTransferSize
1115
- + metric.fontTransferSize);
1116
- var hostMetric = {};
1117
- for (var _i = 0, _a = Object.keys(this.hostList); _i < _a.length; _i++) {
1118
- var host = _a[_i];
1119
- var timings = this.hostList[host].map(function (item) { return item.timing; });
1120
- hostMetric[host] = this.getNumAndSize('host', timings);
1121
- }
1122
- return { metric: metric, hostMetric: hostMetric };
1123
- }
1124
- return;
1125
- };
1126
- return Resource;
788
+ // 字体
789
+ // svg使用场景比较灵活,可能作为一种字体格式,也可能当做图片使用
790
+ // 但因woff在所有现代浏览器中都支持,在css中使用iconfont时
791
+ // 往往优先加载woff格式字体文件,所以这里将svg划分为图片一类
792
+ var FontsTypes;
793
+ (function (FontsTypes) {
794
+ FontsTypes[FontsTypes["ttf"] = 0] = "ttf";
795
+ FontsTypes[FontsTypes["eot"] = 1] = "eot";
796
+ FontsTypes[FontsTypes["woff"] = 2] = "woff";
797
+ FontsTypes[FontsTypes["woff2"] = 3] = "woff2";
798
+ })(FontsTypes || (FontsTypes = {}));
799
+ // 忽略图片
800
+ // 下述图片路径往往是用来进行日志统计,不是正常图片
801
+ var ignoreDefaultPaths = [
802
+ '/mwb2.gif',
803
+ ];
804
+ function ignorePath(url, paths) {
805
+ for (var _i = 0, paths_1 = paths; _i < paths_1.length; _i++) {
806
+ var path = paths_1[_i];
807
+ if (url.indexOf(path) > -1) {
808
+ return true;
809
+ }
810
+ }
811
+ return false;
812
+ }
813
+ function f(n) {
814
+ return +n.toFixed(1);
815
+ }
816
+ var Resource = /** @class */ (function () {
817
+ function Resource() {
818
+ this.trigger = 'load';
819
+ this.jsList = [];
820
+ this.cssList = [];
821
+ this.imgList = [];
822
+ this.fontList = [];
823
+ this.hostList = {};
824
+ this.bigImgList = {};
825
+ this.httpResList = {};
826
+ this.slowList = {};
827
+ this.resOption = { ignorePaths: [], trigger: 'load' };
828
+ this.bigImgOption = { ignorePaths: [], maxSize: 150 * 1024, trigger: 'load' };
829
+ this.httpResOption = { ignorePaths: [], trigger: 'load' };
830
+ this.slowOption = { ignorePaths: [], trigger: 'load', threshold: 1000 };
831
+ }
832
+ Resource.prototype.check = function () {
833
+ return performance && performance.getEntriesByType;
834
+ };
835
+ Resource.prototype.listenResource = function (cb, option) {
836
+ if (option === void 0) { option = {}; }
837
+ if (!this.check()) {
838
+ return;
839
+ }
840
+ this.resOption = assign(this.resOption, option);
841
+ this.trigger = this.resOption.trigger;
842
+ this.cb = cb;
843
+ };
844
+ Resource.prototype.listenBigImg = function (cb, option) {
845
+ if (option === void 0) { option = {}; }
846
+ if (!this.check()) {
847
+ return;
848
+ }
849
+ this.bigImgOption = assign(this.bigImgOption, option);
850
+ this.trigger = this.bigImgOption.trigger;
851
+ this.bigImgCB = cb;
852
+ };
853
+ Resource.prototype.listenHttpResource = function (cb, option) {
854
+ if (option === void 0) { option = {}; }
855
+ if (!this.check()) {
856
+ return;
857
+ }
858
+ this.httpResOption = assign(this.httpResOption, option);
859
+ this.trigger = this.httpResOption.trigger;
860
+ this.httpResCB = cb;
861
+ };
862
+ Resource.prototype.listenSlowResource = function (cb, option) {
863
+ if (option === void 0) { option = {}; }
864
+ if (!this.check()) {
865
+ return;
866
+ }
867
+ this.slowOption = assign(this.slowOption, option);
868
+ this.trigger = this.slowOption.trigger;
869
+ this.slowResCB = cb;
870
+ };
871
+ Resource.prototype.report = function () {
872
+ var _this = this;
873
+ var metricRes = this.getMetric();
874
+ if (metricRes) {
875
+ var metric = metricRes.metric, hostMetric = metricRes.hostMetric;
876
+ this.cb && this.cb(metric, hostMetric);
877
+ }
878
+ if (this.bigImgCB) {
879
+ // 发送大图监控数据
880
+ if (typeof window.requestIdleCallback === 'function' && Object.keys(this.bigImgList).length) {
881
+ window.requestIdleCallback(function () {
882
+ for (var _i = 0, _a = Object.keys(_this.bigImgList); _i < _a.length; _i++) {
883
+ var host = _a[_i];
884
+ for (var _b = 0, _c = _this.bigImgList[host]; _b < _c.length; _b++) {
885
+ var entry = _c[_b];
886
+ var timing = entry.timing;
887
+ var type = entry.type;
888
+ try {
889
+ var img = document.body.querySelector('img[src="' + timing.name + '"]');
890
+ _this.bigImgCB({
891
+ msg: timing.name,
892
+ dur: timing.duration,
893
+ xpath: getxpath(img).xpath,
894
+ host: host,
895
+ type: type,
896
+ });
897
+ }
898
+ catch (e) {
899
+ console.error(e);
900
+ }
901
+ }
902
+ }
903
+ });
904
+ }
905
+ }
906
+ if (this.httpResCB) {
907
+ // 发送http资源监控数据
908
+ if (typeof window.requestIdleCallback === 'function' && Object.keys(this.httpResList).length) {
909
+ window.requestIdleCallback(function () {
910
+ for (var _i = 0, _a = Object.keys(_this.httpResList); _i < _a.length; _i++) {
911
+ var host = _a[_i];
912
+ for (var _b = 0, _c = _this.httpResList[host]; _b < _c.length; _b++) {
913
+ var entry = _c[_b];
914
+ var timing = entry.timing;
915
+ var type = entry.type;
916
+ try {
917
+ var img = document.body.querySelector('[src="' + timing.name + '"]');
918
+ _this.httpResCB({
919
+ msg: timing.name,
920
+ dur: timing.duration,
921
+ xpath: getxpath(img).xpath,
922
+ host: host,
923
+ type: type,
924
+ });
925
+ }
926
+ catch (e) {
927
+ console.error(e);
928
+ }
929
+ }
930
+ }
931
+ });
932
+ }
933
+ }
934
+ if (this.slowResCB) {
935
+ // 发送慢资源监控数据
936
+ if (typeof window.requestIdleCallback === 'function' && Object.keys(this.slowList).length) {
937
+ window.requestIdleCallback(function () {
938
+ for (var _i = 0, _a = Object.keys(_this.slowList); _i < _a.length; _i++) {
939
+ var host = _a[_i];
940
+ for (var _b = 0, _c = _this.slowList[host]; _b < _c.length; _b++) {
941
+ var entry = _c[_b];
942
+ var timing = entry.timing;
943
+ var type = entry.type;
944
+ try {
945
+ var img = document.body.querySelector('[src="' + timing.name + '"]');
946
+ var info = getResTiming(timing);
947
+ _this.slowResCB(__assign(__assign({}, info), { dur: timing.duration, msg: timing.name, xpath: getxpath(img).xpath, host: host, type: type }));
948
+ }
949
+ catch (e) {
950
+ console.error(e);
951
+ }
952
+ }
953
+ }
954
+ });
955
+ }
956
+ }
957
+ };
958
+ Resource.prototype.load = function () {
959
+ var _this = this;
960
+ if (this.trigger !== 'load') {
961
+ return;
962
+ }
963
+ if (!this.check()) {
964
+ return;
965
+ }
966
+ setTimeout(function () {
967
+ _this.report();
968
+ }, 500);
969
+ };
970
+ Resource.prototype.leave = function () {
971
+ if (this.trigger !== 'leave') {
972
+ return;
973
+ }
974
+ this.report();
975
+ };
976
+ Resource.prototype.push = function (type, list, timing, urlInfo) {
977
+ if (!ignorePath(urlInfo.pathname, this.resOption.ignorePaths || [])) {
978
+ list.push(timing);
979
+ this.pushWithHost(type, this.hostList, timing, urlInfo);
980
+ }
981
+ if (!ignorePath(urlInfo.pathname, this.slowOption.ignorePaths || [])) {
982
+ if (timing.duration > this.slowOption.threshold) {
983
+ this.pushWithHost(type, this.slowList, timing, urlInfo);
984
+ }
985
+ }
986
+ };
987
+ Resource.prototype.pushWithHost = function (type, list, timing, urlInfo) {
988
+ var host = urlInfo.host;
989
+ if (!list[host]) {
990
+ list[host] = [];
991
+ }
992
+ list[host].push({
993
+ timing: timing,
994
+ type: type,
995
+ });
996
+ };
997
+ Resource.prototype.collectHttpResInHttps = function (type, timing, urlInfo) {
998
+ if (location.protocol === 'https:'
999
+ && timing.name.indexOf('http://') === 0
1000
+ && !ignorePath(urlInfo.pathname, this.httpResOption.ignorePaths || [])) {
1001
+ this.pushWithHost(type, this.httpResList, timing, urlInfo);
1002
+ }
1003
+ };
1004
+ // 有些jsonp也属于script,这里只统计js后缀的文件
1005
+ Resource.prototype.addScript = function (timing, urlInfo) {
1006
+ if (urlInfo.ext === 'js') {
1007
+ if (timing.decodedBodySize !== 0) {
1008
+ this.push('js', this.jsList, timing, urlInfo);
1009
+ }
1010
+ }
1011
+ };
1012
+ // 暂时将css文件或代码块发起的请求归位三类(主要为这两类)
1013
+ // 1、加载字体
1014
+ // 2、加载背景图(图片不容易区分,有的没有明确后缀名)
1015
+ // 3、光标文件(后缀为.cur,这里也划分为图片)
1016
+ // (svg当做图片,前述已说明)
1017
+ Resource.prototype.addResFromCss = function (timing, urlInfo) {
1018
+ if (urlInfo.ext && FontsTypes.hasOwnProperty(urlInfo.ext)) {
1019
+ this.push('font', this.fontList, timing, urlInfo);
1020
+ }
1021
+ else {
1022
+ this.addImg(timing, urlInfo);
1023
+ }
1024
+ };
1025
+ // link一般加载css资源
1026
+ // 也可以通过preload可以预下载一些资源
1027
+ // 这里只统计js类型的preload
1028
+ Resource.prototype.addLink = function (timing, urlInfo) {
1029
+ if (urlInfo.ext === 'css') {
1030
+ this.push('css', this.cssList, timing, urlInfo);
1031
+ }
1032
+ // preload as script
1033
+ else if (urlInfo.ext === 'js') {
1034
+ this.push('js', this.jsList, timing, urlInfo);
1035
+ }
1036
+ };
1037
+ Resource.prototype.addImg = function (timing, urlInfo) {
1038
+ this.push('img', this.imgList, timing, urlInfo);
1039
+ // 大于指定size的图片采集
1040
+ if (timing.decodedBodySize > (this.bigImgOption.maxSize || 0)
1041
+ && !ignorePath(urlInfo.pathname, this.bigImgOption.ignorePaths || [])) {
1042
+ this.pushWithHost('img', this.bigImgList, timing, urlInfo);
1043
+ }
1044
+ };
1045
+ Resource.prototype.handleTimings = function (list) {
1046
+ var len = list.length;
1047
+ for (var i = 0; i < len; i++) {
1048
+ var timing = list[i];
1049
+ var urlInfo = getUrlInfo(timing.name);
1050
+ if (ignorePath(urlInfo.pathname, ignoreDefaultPaths)) {
1051
+ continue;
1052
+ }
1053
+ switch (list[i].initiatorType) {
1054
+ case 'script':
1055
+ this.addScript(timing, urlInfo);
1056
+ break;
1057
+ case 'css':
1058
+ this.addResFromCss(timing, urlInfo);
1059
+ break;
1060
+ case 'img':
1061
+ this.addImg(timing, urlInfo);
1062
+ break;
1063
+ case 'link':
1064
+ this.addLink(timing, urlInfo);
1065
+ break;
1066
+ case 'audio':
1067
+ this.collectHttpResInHttps('audio', timing, urlInfo);
1068
+ break;
1069
+ case 'video':
1070
+ this.collectHttpResInHttps('video', timing, urlInfo);
1071
+ break;
1072
+ }
1073
+ }
1074
+ };
1075
+ Resource.prototype.getNumAndSize = function (type, list) {
1076
+ var obj = {};
1077
+ var num = type + 'Num';
1078
+ var size = type + 'Size';
1079
+ var transferSize = type + 'TransferSize';
1080
+ var cacheRate = type + 'CacheRate';
1081
+ var duration = type + 'Duration';
1082
+ obj[num] = 0;
1083
+ obj[size] = 0;
1084
+ obj[transferSize] = 0;
1085
+ var totalDurationTime = 0;
1086
+ list.forEach(function (timing) {
1087
+ obj[num]++;
1088
+ obj[size] += (timing.decodedBodySize / 1024);
1089
+ obj[transferSize] += (timing.transferSize / 1024);
1090
+ totalDurationTime += timing.duration;
1091
+ });
1092
+ obj[duration] = f(obj[num] > 0 ? totalDurationTime / obj[num] : 0);
1093
+ if (obj[size]) {
1094
+ var diff = obj[size] - obj[transferSize];
1095
+ obj[cacheRate] = f(diff >= 0 ? 100 * diff / obj[size] : 0);
1096
+ }
1097
+ obj[size] = f(obj[size]);
1098
+ obj[transferSize] = f(obj[transferSize]);
1099
+ return obj;
1100
+ };
1101
+ Resource.prototype.getMetric = function () {
1102
+ // 原来代码
1103
+ var mainPageTiming = performance.getEntriesByType('navigation')[0];
1104
+ var resourceTimings = performance.getEntriesByType('resource');
1105
+ if (mainPageTiming && resourceTimings && resourceTimings.length) {
1106
+ this.handleTimings(resourceTimings);
1107
+ var metric = __assign(__assign(__assign(__assign({}, this.getNumAndSize(ResType.JS, this.jsList)), this.getNumAndSize(ResType.CSS, this.cssList)), this.getNumAndSize(ResType.IMG, this.imgList)), this.getNumAndSize(ResType.FONT, this.fontList));
1108
+ // 主文档大小
1109
+ var pageTiming = mainPageTiming;
1110
+ metric.docSize = f(pageTiming.decodedBodySize / 1024);
1111
+ metric.docTransferSize = f(pageTiming.transferSize / 1024);
1112
+ metric.headerSize = f((pageTiming.transferSize - pageTiming.encodedBodySize || 0) / 1024);
1113
+ metric.allSize = f(metric.docSize
1114
+ + metric.jsSize
1115
+ + metric.cssSize
1116
+ + metric.imgSize
1117
+ + metric.fontSize);
1118
+ metric.allTransferSize = f(metric.docTransferSize
1119
+ + metric.jsTransferSize
1120
+ + metric.cssTransferSize
1121
+ + metric.imgTransferSize
1122
+ + metric.fontTransferSize);
1123
+ var hostMetric = {};
1124
+ for (var _i = 0, _a = Object.keys(this.hostList); _i < _a.length; _i++) {
1125
+ var host = _a[_i];
1126
+ var timings = this.hostList[host].map(function (item) { return item.timing; });
1127
+ hostMetric[host] = this.getNumAndSize('host', timings);
1128
+ }
1129
+ return { metric: metric, hostMetric: hostMetric };
1130
+ }
1131
+ return;
1132
+ };
1133
+ return Resource;
1127
1134
  }());
1128
1135
 
1129
- /**
1130
- * @file Memory
1131
- * @author kaivean
1132
- */
1133
- var initMemory = null;
1134
- if (window.performance && window.performance.memory) {
1135
- initMemory = window.performance.memory;
1136
- }
1137
- var Memory = /** @class */ (function () {
1138
- function Memory() {
1139
- this.onceLeave = false;
1140
- }
1141
- // constructor() {}
1142
- Memory.prototype.listenMemory = function (cb) {
1143
- this.cb = cb;
1144
- };
1145
- Memory.prototype.leave = function () {
1146
- if (!this.onceLeave && initMemory) {
1147
- this.onceLeave = true;
1148
- var curMemory = window.performance.memory;
1149
- // fix 早期浏览器的memroy api 值不更新的问题,将此情况排除
1150
- if (curMemory.usedJSHeapSize === initMemory.usedJSHeapSize
1151
- && curMemory.totalJSHeapSize === initMemory.totalJSHeapSize) {
1152
- return;
1153
- }
1154
- var memory = window.performance.memory;
1155
- this.cb && this.cb({
1156
- usedJSHeapSize: memory.usedJSHeapSize / 1024,
1157
- totalJSHeapSize: memory.totalJSHeapSize / 1024,
1158
- jsHeapSizeLimit: memory.jsHeapSizeLimit / 1024,
1159
- usedJSHeapRate: 100 * memory.usedJSHeapSize / memory.totalJSHeapSize,
1160
- });
1161
- }
1162
- };
1163
- return Memory;
1136
+ /**
1137
+ * @file Memory
1138
+ * @author kaivean
1139
+ */
1140
+ var initMemory = null;
1141
+ if (window.performance && window.performance.memory) {
1142
+ initMemory = window.performance.memory;
1143
+ }
1144
+ var Memory = /** @class */ (function () {
1145
+ function Memory() {
1146
+ this.onceLeave = false;
1147
+ }
1148
+ // constructor() {}
1149
+ Memory.prototype.listenMemory = function (cb) {
1150
+ this.cb = cb;
1151
+ };
1152
+ Memory.prototype.leave = function () {
1153
+ if (!this.onceLeave && initMemory) {
1154
+ this.onceLeave = true;
1155
+ var curMemory = window.performance.memory;
1156
+ // fix 早期浏览器的memroy api 值不更新的问题,将此情况排除
1157
+ if (curMemory.usedJSHeapSize === initMemory.usedJSHeapSize
1158
+ && curMemory.totalJSHeapSize === initMemory.totalJSHeapSize) {
1159
+ return;
1160
+ }
1161
+ var memory = window.performance.memory;
1162
+ this.cb && this.cb({
1163
+ usedJSHeapSize: memory.usedJSHeapSize / 1024,
1164
+ totalJSHeapSize: memory.totalJSHeapSize / 1024,
1165
+ jsHeapSizeLimit: memory.jsHeapSizeLimit / 1024,
1166
+ usedJSHeapRate: 100 * memory.usedJSHeapSize / memory.totalJSHeapSize,
1167
+ });
1168
+ }
1169
+ };
1170
+ return Memory;
1164
1171
  }());
1165
1172
 
1166
- /**
1167
- * @file NavigatorInfo
1168
- * @author kaivean
1169
- */
1170
- var NavigatorInfo = /** @class */ (function () {
1171
- function NavigatorInfo() {
1172
- }
1173
- // private cb: NavigatorInfoCB;
1174
- NavigatorInfo.prototype.getNavigatorInfo = function () {
1175
- // this.cb = cb;
1176
- var ret = {};
1177
- var dataConnection = navigator.connection;
1178
- if (typeof dataConnection === 'object') {
1179
- ret = {
1180
- downlink: dataConnection.downlink,
1181
- effectiveType: dataConnection.effectiveType,
1182
- rtt: dataConnection.rtt,
1183
- saveData: !!dataConnection.saveData, // 数据节约模式
1184
- };
1185
- }
1186
- // 内存 G
1187
- if (navigator.deviceMemory) {
1188
- ret.deviceMemory = navigator.deviceMemory;
1189
- }
1190
- // 核数
1191
- if (navigator.hardwareConcurrency) {
1192
- ret.hardwareConcurrency = navigator.deviceMemory;
1193
- }
1194
- return ret;
1195
- };
1196
- NavigatorInfo.prototype.load = function () { };
1197
- return NavigatorInfo;
1173
+ /**
1174
+ * @file NavigatorInfo
1175
+ * @author kaivean
1176
+ */
1177
+ var NavigatorInfo = /** @class */ (function () {
1178
+ function NavigatorInfo() {
1179
+ }
1180
+ // private cb: NavigatorInfoCB;
1181
+ NavigatorInfo.prototype.getNavigatorInfo = function () {
1182
+ // this.cb = cb;
1183
+ var ret = {};
1184
+ var dataConnection = navigator.connection;
1185
+ if (typeof dataConnection === 'object') {
1186
+ ret = {
1187
+ downlink: dataConnection.downlink, // 网站下载速度 M/s
1188
+ effectiveType: dataConnection.effectiveType, // 网络类型
1189
+ rtt: dataConnection.rtt, // 网络往返时间 ms
1190
+ saveData: !!dataConnection.saveData, // 数据节约模式
1191
+ };
1192
+ }
1193
+ // 内存 G
1194
+ if (navigator.deviceMemory) {
1195
+ ret.deviceMemory = navigator.deviceMemory;
1196
+ }
1197
+ // 核数
1198
+ if (navigator.hardwareConcurrency) {
1199
+ ret.hardwareConcurrency = navigator.deviceMemory;
1200
+ }
1201
+ return ret;
1202
+ };
1203
+ NavigatorInfo.prototype.load = function () { };
1204
+ return NavigatorInfo;
1198
1205
  }());
1199
1206
 
1200
- /**
1201
- * @file Longtask
1202
- * @author kaivean
1203
- */
1204
- var spyclient = window.__spyHead;
1205
- var entryType = 'longtask';
1206
- var Longtask = /** @class */ (function () {
1207
- function Longtask() {
1208
- var _this = this;
1209
- this.lts = [];
1210
- this.observer = null;
1211
- this.onceLeave = false;
1212
- if (spyclient && spyclient.entryMap && spyclient.entryMap[entryType]) {
1213
- this.lts = this.lts.concat(spyclient.entryMap[entryType]);
1214
- }
1215
- try {
1216
- this.observer = new PerformanceObserver(function (list) {
1217
- _this.lts = _this.lts.concat(list.getEntries());
1218
- });
1219
- // buffered 兼容性太差
1220
- this.observer.observe({ entryTypes: [entryType] });
1221
- }
1222
- catch (e) { }
1223
- }
1224
- Longtask.prototype.check = function () {
1225
- return window.PerformanceObserver && performance && performance.timing && performance.timing.navigationStart;
1226
- };
1227
- Longtask.prototype.listenFSPLongTask = function (cb) {
1228
- if (!this.check()) {
1229
- return;
1230
- }
1231
- this.fspCB = cb;
1232
- };
1233
- Longtask.prototype.listenLCPLongTask = function (cb) {
1234
- if (!this.check()) {
1235
- return;
1236
- }
1237
- this.lcpCB = cb;
1238
- };
1239
- Longtask.prototype.listenLoadLongTask = function (cb) {
1240
- if (!this.check()) {
1241
- return;
1242
- }
1243
- this.loadCB = cb;
1244
- };
1245
- Longtask.prototype.listenPageLongTask = function (cb) {
1246
- if (!this.check()) {
1247
- return;
1248
- }
1249
- this.pageCB = cb;
1250
- };
1251
- Longtask.prototype.load = function () {
1252
- var _this = this;
1253
- if (!this.check()) {
1254
- return;
1255
- }
1256
- var data = this.getStatData(Date.now());
1257
- // lcp的值存入data模块也是在load期间,这里延迟一会再获取
1258
- setTimeout(function () {
1259
- _this.loadCB && _this.loadCB({
1260
- loadLongtaskTime: data.time,
1261
- loadTBT: data.tbt,
1262
- loadTotalTime: data.totalTime,
1263
- loadLongtaskRate: data.rate,
1264
- loadLongtaskNum: data.num,
1265
- });
1266
- if (performance.timing && performance.timing.domFirstScreenPaint) {
1267
- var data_1 = _this.getStatData(performance.timing.domFirstScreenPaint);
1268
- _this.fspCB && _this.fspCB({
1269
- fspLongtaskTime: data_1.time,
1270
- fspTBT: data_1.tbt,
1271
- fspTotalTime: data_1.totalTime,
1272
- fspLongtaskRate: data_1.rate,
1273
- fspLongtaskNum: data_1.num,
1274
- });
1275
- }
1276
- if (getData('lcp')) {
1277
- var data_2 = _this.getStatData(performance.timing.navigationStart + getData('lcp'));
1278
- _this.lcpCB && _this.lcpCB({
1279
- lcpLongtaskTime: data_2.time,
1280
- lcpTBT: data_2.tbt,
1281
- lcpTotalTime: data_2.totalTime,
1282
- lcpLongtaskRate: data_2.rate,
1283
- lcpLongtaskNum: data_2.num,
1284
- });
1285
- }
1286
- }, 200);
1287
- };
1288
- Longtask.prototype.leave = function () {
1289
- if (!this.onceLeave) {
1290
- this.onceLeave = true;
1291
- var data = this.getStatData(Date.now());
1292
- this.pageCB && this.pageCB({
1293
- pageLongtaskTime: data.time,
1294
- pageTBT: data.tbt,
1295
- pageTotalTime: data.totalTime,
1296
- pageLongtaskRate: data.rate,
1297
- pageLongtaskNum: data.num,
1298
- pageIframeLongtaskTime: data.iframeTime,
1299
- pageIframeLongtaskRate: data.iframeRate,
1300
- pageIframeLongtaskNum: data.iframeNum,
1301
- });
1302
- }
1303
- };
1304
- Longtask.prototype.destroy = function () {
1305
- this.lts = [];
1306
- this.observer && this.observer.disconnect();
1307
- this.observer = null;
1308
- };
1309
- Longtask.prototype.getStatData = function (finalTime) {
1310
- var navigationStart = performance.timing.navigationStart;
1311
- var time = 0;
1312
- // Total Blocking Time
1313
- var tbt = 0;
1314
- var num = 0;
1315
- var iframeTime = 0;
1316
- var iframeNum = 0;
1317
- var iframeLongtasks = {};
1318
- for (var index = 0; index < this.lts.length; index++) {
1319
- var item = this.lts[index];
1320
- var duration = item.duration;
1321
- var end = navigationStart + item.startTime + duration;
1322
- if (end < finalTime) {
1323
- time += duration;
1324
- // 多出来的时间
1325
- tbt += (duration - 50 > 0 ? duration - 50 : 0);
1326
- num++;
1327
- if (item.attribution && item.attribution[0]) {
1328
- var containerSrc = item.attribution[0].containerSrc;
1329
- if (containerSrc && containerSrc !== location.href) {
1330
- if (!iframeLongtasks[containerSrc]) {
1331
- iframeLongtasks[containerSrc] = [];
1332
- }
1333
- iframeLongtasks[containerSrc].push(duration);
1334
- iframeTime += duration;
1335
- iframeNum++;
1336
- }
1337
- }
1338
- }
1339
- }
1340
- var totalTime = finalTime - navigationStart;
1341
- return {
1342
- num: num,
1343
- time: time,
1344
- tbt: tbt,
1345
- totalTime: totalTime,
1346
- rate: 100 * time / totalTime,
1347
- iframeTime: iframeTime,
1348
- iframeNum: iframeNum,
1349
- iframeRate: 100 * iframeTime / totalTime,
1350
- iframeLongtasks: iframeLongtasks,
1351
- };
1352
- };
1353
- return Longtask;
1207
+ /**
1208
+ * @file Longtask
1209
+ * @author kaivean
1210
+ */
1211
+ var spyclient = window.__spyHead;
1212
+ var entryType = 'longtask';
1213
+ var Longtask = /** @class */ (function () {
1214
+ function Longtask() {
1215
+ var _this = this;
1216
+ this.lts = [];
1217
+ this.observer = null;
1218
+ this.onceLeave = false;
1219
+ if (spyclient && spyclient.entryMap && spyclient.entryMap[entryType]) {
1220
+ this.lts = this.lts.concat(spyclient.entryMap[entryType]);
1221
+ }
1222
+ try {
1223
+ this.observer = new PerformanceObserver(function (list) {
1224
+ _this.lts = _this.lts.concat(list.getEntries());
1225
+ });
1226
+ // buffered 兼容性太差
1227
+ this.observer.observe({ entryTypes: [entryType] });
1228
+ }
1229
+ catch (e) { }
1230
+ }
1231
+ Longtask.prototype.check = function () {
1232
+ return window.PerformanceObserver && performance && performance.timing && performance.timing.navigationStart;
1233
+ };
1234
+ Longtask.prototype.listenFSPLongTask = function (cb) {
1235
+ if (!this.check()) {
1236
+ return;
1237
+ }
1238
+ this.fspCB = cb;
1239
+ };
1240
+ Longtask.prototype.listenLCPLongTask = function (cb) {
1241
+ if (!this.check()) {
1242
+ return;
1243
+ }
1244
+ this.lcpCB = cb;
1245
+ };
1246
+ Longtask.prototype.listenLoadLongTask = function (cb) {
1247
+ if (!this.check()) {
1248
+ return;
1249
+ }
1250
+ this.loadCB = cb;
1251
+ };
1252
+ Longtask.prototype.listenPageLongTask = function (cb) {
1253
+ if (!this.check()) {
1254
+ return;
1255
+ }
1256
+ this.pageCB = cb;
1257
+ };
1258
+ Longtask.prototype.load = function () {
1259
+ var _this = this;
1260
+ if (!this.check()) {
1261
+ return;
1262
+ }
1263
+ var data = this.getStatData(Date.now());
1264
+ // lcp的值存入data模块也是在load期间,这里延迟一会再获取
1265
+ setTimeout(function () {
1266
+ _this.loadCB && _this.loadCB({
1267
+ loadLongtaskTime: data.time,
1268
+ loadTBT: data.tbt,
1269
+ loadTotalTime: data.totalTime,
1270
+ loadLongtaskRate: data.rate,
1271
+ loadLongtaskNum: data.num,
1272
+ });
1273
+ if (performance.timing && performance.timing.domFirstScreenPaint) {
1274
+ var data_1 = _this.getStatData(performance.timing.domFirstScreenPaint);
1275
+ _this.fspCB && _this.fspCB({
1276
+ fspLongtaskTime: data_1.time,
1277
+ fspTBT: data_1.tbt,
1278
+ fspTotalTime: data_1.totalTime,
1279
+ fspLongtaskRate: data_1.rate,
1280
+ fspLongtaskNum: data_1.num,
1281
+ });
1282
+ }
1283
+ if (getData('lcp')) {
1284
+ var data_2 = _this.getStatData(performance.timing.navigationStart + getData('lcp'));
1285
+ _this.lcpCB && _this.lcpCB({
1286
+ lcpLongtaskTime: data_2.time,
1287
+ lcpTBT: data_2.tbt,
1288
+ lcpTotalTime: data_2.totalTime,
1289
+ lcpLongtaskRate: data_2.rate,
1290
+ lcpLongtaskNum: data_2.num,
1291
+ });
1292
+ }
1293
+ }, 200);
1294
+ };
1295
+ Longtask.prototype.leave = function () {
1296
+ if (!this.onceLeave) {
1297
+ this.onceLeave = true;
1298
+ var data = this.getStatData(Date.now());
1299
+ this.pageCB && this.pageCB({
1300
+ pageLongtaskTime: data.time,
1301
+ pageTBT: data.tbt,
1302
+ pageTotalTime: data.totalTime,
1303
+ pageLongtaskRate: data.rate,
1304
+ pageLongtaskNum: data.num,
1305
+ pageIframeLongtaskTime: data.iframeTime,
1306
+ pageIframeLongtaskRate: data.iframeRate,
1307
+ pageIframeLongtaskNum: data.iframeNum,
1308
+ });
1309
+ }
1310
+ };
1311
+ Longtask.prototype.destroy = function () {
1312
+ this.lts = [];
1313
+ this.observer && this.observer.disconnect();
1314
+ this.observer = null;
1315
+ };
1316
+ Longtask.prototype.getStatData = function (finalTime) {
1317
+ var navigationStart = performance.timing.navigationStart;
1318
+ var time = 0;
1319
+ // Total Blocking Time
1320
+ var tbt = 0;
1321
+ var num = 0;
1322
+ var iframeTime = 0;
1323
+ var iframeNum = 0;
1324
+ var iframeLongtasks = {};
1325
+ for (var index = 0; index < this.lts.length; index++) {
1326
+ var item = this.lts[index];
1327
+ var duration = item.duration;
1328
+ var end = navigationStart + item.startTime + duration;
1329
+ if (end < finalTime) {
1330
+ time += duration;
1331
+ // 多出来的时间
1332
+ tbt += (duration - 50 > 0 ? duration - 50 : 0);
1333
+ num++;
1334
+ if (item.attribution && item.attribution[0]) {
1335
+ var containerSrc = item.attribution[0].containerSrc;
1336
+ if (containerSrc && containerSrc !== location.href) {
1337
+ if (!iframeLongtasks[containerSrc]) {
1338
+ iframeLongtasks[containerSrc] = [];
1339
+ }
1340
+ iframeLongtasks[containerSrc].push(duration);
1341
+ iframeTime += duration;
1342
+ iframeNum++;
1343
+ }
1344
+ }
1345
+ }
1346
+ }
1347
+ var totalTime = finalTime - navigationStart;
1348
+ return {
1349
+ num: num,
1350
+ time: time,
1351
+ tbt: tbt,
1352
+ totalTime: totalTime,
1353
+ rate: 100 * time / totalTime,
1354
+ iframeTime: iframeTime,
1355
+ iframeNum: iframeNum,
1356
+ iframeRate: 100 * iframeTime / totalTime,
1357
+ iframeLongtasks: iframeLongtasks,
1358
+ };
1359
+ };
1360
+ return Longtask;
1354
1361
  }());
1355
1362
 
1356
- /**
1357
- * @file SpyClient
1358
- * @author kaivean
1359
- */
1360
- var SpyClient = /** @class */ (function (_super) {
1361
- __extends(SpyClient, _super);
1362
- function SpyClient(option) {
1363
- var _this = _super.call(this, option) || this;
1364
- _this.modules = [];
1365
- _this.register(new FID());
1366
- _this.register(new LCP());
1367
- _this.register(new LayoutShift());
1368
- _this.register(new TTI());
1369
- _this.register(new Timing());
1370
- _this.register(new Resource());
1371
- _this.register(new Memory());
1372
- _this.register(new NavigatorInfo());
1373
- _this.register(new Longtask());
1374
- _this.visibilitychangeCB = _this.visibilitychangeCB.bind(_this);
1375
- _this.load = _this.load.bind(_this);
1376
- _this.leave = _this.leave.bind(_this);
1377
- if (document.readyState === 'complete') {
1378
- _this.load();
1379
- }
1380
- else {
1381
- window.addEventListener('load', _this.load);
1382
- }
1383
- document.addEventListener('visibilitychange', _this.visibilitychangeCB);
1384
- window.addEventListener('beforeunload', _this.leave, false);
1385
- window.addEventListener('unload', _this.leave, false);
1386
- _this.handleHead();
1387
- return _this;
1388
- }
1389
- SpyClient.prototype.handleHead = function () {
1390
- var _this = this;
1391
- if (this.option.localCache) {
1392
- var spyHead = window.__spyHead;
1393
- if (spyHead && spyHead.winerrors) {
1394
- for (var index = 0; index < spyHead.winerrors.length; index++) {
1395
- var obj = spyHead.winerrors[index];
1396
- this.option.localCache.addLog(obj);
1397
- }
1398
- // Head发送的异常,也保存一份到本地,主要是JS错误和资源加载异常
1399
- spyHead.interceptor = function (obj) {
1400
- _this.option.localCache.addLog(obj);
1401
- };
1402
- }
1403
- }
1404
- };
1405
- SpyClient.prototype.listenFID = function (cb) {
1406
- this.invoke('listenFID', cb);
1407
- };
1408
- SpyClient.prototype.listenLayoutShift = function (cb) {
1409
- this.invoke('listenLayoutShift', cb);
1410
- };
1411
- SpyClient.prototype.listenLCP = function (cb) {
1412
- this.invoke('listenLCP', cb);
1413
- };
1414
- SpyClient.prototype.listenFSPLongTask = function (cb) {
1415
- this.invoke('listenFSPLongTask', cb);
1416
- };
1417
- SpyClient.prototype.listenLCPLongTask = function (cb) {
1418
- this.invoke('listenLCPLongTask', cb);
1419
- };
1420
- SpyClient.prototype.listenLoadLongTask = function (cb) {
1421
- this.invoke('listenLoadLongTask', cb);
1422
- };
1423
- SpyClient.prototype.listenPageLongTask = function (cb) {
1424
- this.invoke('listenPageLongTask', cb);
1425
- };
1426
- SpyClient.prototype.listenMemory = function (cb) {
1427
- this.invoke('listenMemory', cb);
1428
- };
1429
- SpyClient.prototype.getNavigatorInfo = function () {
1430
- return this.invoke('getNavigatorInfo');
1431
- };
1432
- SpyClient.prototype.listenResource = function (cb, option) {
1433
- this.invoke('listenResource', cb, option);
1434
- };
1435
- SpyClient.prototype.listenBigImg = function (cb, option) {
1436
- this.invoke('listenBigImg', cb, option);
1437
- };
1438
- SpyClient.prototype.listenHttpResource = function (cb, option) {
1439
- this.invoke('listenHttpResource', cb, option);
1440
- };
1441
- SpyClient.prototype.listenSlowResource = function (cb, option) {
1442
- this.invoke('listenSlowResource', cb, option);
1443
- };
1444
- SpyClient.prototype.listenTiming = function (cb) {
1445
- this.invoke('listenTiming', cb);
1446
- };
1447
- SpyClient.prototype.listenTTI = function (cb, option) {
1448
- this.invoke('listenTTI', cb, option);
1449
- };
1450
- SpyClient.prototype.invoke = function (name, cb, option) {
1451
- for (var index = 0; index < this.modules.length; index++) {
1452
- var mod = this.modules[index];
1453
- if (typeof mod[name] === 'function') {
1454
- return mod[name].apply(mod, [cb, option]);
1455
- }
1456
- }
1457
- console.error('no method', name);
1458
- };
1459
- SpyClient.prototype.register = function (mod) {
1460
- this.modules.push(mod);
1461
- };
1462
- SpyClient.prototype.load = function () {
1463
- for (var index = 0; index < this.modules.length; index++) {
1464
- var mod = this.modules[index];
1465
- mod.load && mod.load();
1466
- }
1467
- };
1468
- SpyClient.prototype.leave = function () {
1469
- for (var index = 0; index < this.modules.length; index++) {
1470
- var mod = this.modules[index];
1471
- mod.leave && mod.leave();
1472
- }
1473
- };
1474
- SpyClient.prototype.destroy = function () {
1475
- for (var index = 0; index < this.modules.length; index++) {
1476
- var mod = this.modules[index];
1477
- mod.destroy && mod.destroy();
1478
- }
1479
- document.removeEventListener('visibilitychange', this.visibilitychangeCB);
1480
- window.removeEventListener('load', this.load);
1481
- window.removeEventListener('beforeunload', this.leave);
1482
- window.removeEventListener('unload', this.destroy);
1483
- };
1484
- SpyClient.prototype.visibilitychangeCB = function () {
1485
- if (document.visibilityState === 'hidden') {
1486
- this.leave();
1487
- }
1488
- };
1489
- return SpyClient;
1363
+ /**
1364
+ * @file SpyClient
1365
+ * @author kaivean
1366
+ */
1367
+ var SpyClient = /** @class */ (function (_super) {
1368
+ __extends(SpyClient, _super);
1369
+ function SpyClient(option) {
1370
+ var _this = _super.call(this, option) || this;
1371
+ _this.modules = [];
1372
+ _this.register(new FID());
1373
+ _this.register(new LCP());
1374
+ _this.register(new LayoutShift());
1375
+ _this.register(new TTI());
1376
+ _this.register(new Timing());
1377
+ _this.register(new Resource());
1378
+ _this.register(new Memory());
1379
+ _this.register(new NavigatorInfo());
1380
+ _this.register(new Longtask());
1381
+ _this.visibilitychangeCB = _this.visibilitychangeCB.bind(_this);
1382
+ _this.load = _this.load.bind(_this);
1383
+ _this.leave = _this.leave.bind(_this);
1384
+ if (document.readyState === 'complete') {
1385
+ _this.load();
1386
+ }
1387
+ else {
1388
+ window.addEventListener('load', _this.load);
1389
+ }
1390
+ document.addEventListener('visibilitychange', _this.visibilitychangeCB);
1391
+ window.addEventListener('beforeunload', _this.leave, false);
1392
+ window.addEventListener('unload', _this.leave, false);
1393
+ _this.handleHead();
1394
+ return _this;
1395
+ }
1396
+ SpyClient.prototype.handleHead = function () {
1397
+ var _this = this;
1398
+ if (this.option.localCache) {
1399
+ var spyHead = window.__spyHead;
1400
+ if (spyHead && spyHead.winerrors) {
1401
+ for (var index = 0; index < spyHead.winerrors.length; index++) {
1402
+ var obj = spyHead.winerrors[index];
1403
+ this.option.localCache.addLog(obj);
1404
+ }
1405
+ // Head发送的异常,也保存一份到本地,主要是JS错误和资源加载异常
1406
+ spyHead.interceptor = function (obj) {
1407
+ _this.option.localCache.addLog(obj);
1408
+ };
1409
+ }
1410
+ }
1411
+ };
1412
+ SpyClient.prototype.listenFID = function (cb) {
1413
+ this.invoke('listenFID', cb);
1414
+ };
1415
+ SpyClient.prototype.listenLayoutShift = function (cb) {
1416
+ this.invoke('listenLayoutShift', cb);
1417
+ };
1418
+ SpyClient.prototype.listenLCP = function (cb) {
1419
+ this.invoke('listenLCP', cb);
1420
+ };
1421
+ SpyClient.prototype.listenFSPLongTask = function (cb) {
1422
+ this.invoke('listenFSPLongTask', cb);
1423
+ };
1424
+ SpyClient.prototype.listenLCPLongTask = function (cb) {
1425
+ this.invoke('listenLCPLongTask', cb);
1426
+ };
1427
+ SpyClient.prototype.listenLoadLongTask = function (cb) {
1428
+ this.invoke('listenLoadLongTask', cb);
1429
+ };
1430
+ SpyClient.prototype.listenPageLongTask = function (cb) {
1431
+ this.invoke('listenPageLongTask', cb);
1432
+ };
1433
+ SpyClient.prototype.listenMemory = function (cb) {
1434
+ this.invoke('listenMemory', cb);
1435
+ };
1436
+ SpyClient.prototype.getNavigatorInfo = function () {
1437
+ return this.invoke('getNavigatorInfo');
1438
+ };
1439
+ SpyClient.prototype.listenResource = function (cb, option) {
1440
+ this.invoke('listenResource', cb, option);
1441
+ };
1442
+ SpyClient.prototype.listenBigImg = function (cb, option) {
1443
+ this.invoke('listenBigImg', cb, option);
1444
+ };
1445
+ SpyClient.prototype.listenHttpResource = function (cb, option) {
1446
+ this.invoke('listenHttpResource', cb, option);
1447
+ };
1448
+ SpyClient.prototype.listenSlowResource = function (cb, option) {
1449
+ this.invoke('listenSlowResource', cb, option);
1450
+ };
1451
+ SpyClient.prototype.listenTiming = function (cb) {
1452
+ this.invoke('listenTiming', cb);
1453
+ };
1454
+ SpyClient.prototype.listenTTI = function (cb, option) {
1455
+ this.invoke('listenTTI', cb, option);
1456
+ };
1457
+ SpyClient.prototype.invoke = function (name, cb, option) {
1458
+ for (var index = 0; index < this.modules.length; index++) {
1459
+ var mod = this.modules[index];
1460
+ if (typeof mod[name] === 'function') {
1461
+ return mod[name].apply(mod, [cb, option]);
1462
+ }
1463
+ }
1464
+ console.error('no method', name);
1465
+ };
1466
+ SpyClient.prototype.register = function (mod) {
1467
+ this.modules.push(mod);
1468
+ };
1469
+ SpyClient.prototype.load = function () {
1470
+ for (var index = 0; index < this.modules.length; index++) {
1471
+ var mod = this.modules[index];
1472
+ mod.load && mod.load();
1473
+ }
1474
+ };
1475
+ SpyClient.prototype.leave = function () {
1476
+ for (var index = 0; index < this.modules.length; index++) {
1477
+ var mod = this.modules[index];
1478
+ mod.leave && mod.leave();
1479
+ }
1480
+ };
1481
+ SpyClient.prototype.destroy = function () {
1482
+ for (var index = 0; index < this.modules.length; index++) {
1483
+ var mod = this.modules[index];
1484
+ mod.destroy && mod.destroy();
1485
+ }
1486
+ document.removeEventListener('visibilitychange', this.visibilitychangeCB);
1487
+ window.removeEventListener('load', this.load);
1488
+ window.removeEventListener('beforeunload', this.leave);
1489
+ window.removeEventListener('unload', this.destroy);
1490
+ };
1491
+ SpyClient.prototype.visibilitychangeCB = function () {
1492
+ if (document.visibilityState === 'hidden') {
1493
+ this.leave();
1494
+ }
1495
+ };
1496
+ return SpyClient;
1490
1497
  }(SpyClient$1));
1491
1498
 
1492
1499
  return SpyClient;