monitor-track 1.11.0 → 1.12.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.
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- var MonitorTrack = (function (axios, uuid, uaParserJs, rrweb, ErrorStackParser, html2canvas) {
1
+ var MonitorTrack = (function (uuid, ErrorStackParser, html2canvas, rrweb, axios, uaParserJs) {
2
2
  'use strict';
3
3
 
4
4
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -21,10 +21,37 @@ var MonitorTrack = (function (axios, uuid, uaParserJs, rrweb, ErrorStackParser,
21
21
  return Object.freeze(n);
22
22
  }
23
23
 
24
- var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
25
- var rrweb__namespace = /*#__PURE__*/_interopNamespace(rrweb);
26
24
  var ErrorStackParser__default = /*#__PURE__*/_interopDefaultLegacy(ErrorStackParser);
27
25
  var html2canvas__default = /*#__PURE__*/_interopDefaultLegacy(html2canvas);
26
+ var rrweb__namespace = /*#__PURE__*/_interopNamespace(rrweb);
27
+ var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
28
+
29
+ /** */
30
+ /**
31
+ * @description config配置项默认值
32
+ */
33
+ var Config = {
34
+ reportUrl: '',
35
+ projectID: '',
36
+ maxLength: 1000,
37
+ spa: false,
38
+ hash: false,
39
+ enableBehavior: true,
40
+ enableError: true,
41
+ enableVisualTrack: false,
42
+ ignore: {
43
+ urls: [],
44
+ errors: [],
45
+ apis: []
46
+ }
47
+ };
48
+ /**
49
+ * @description 设置config配置项
50
+ * @param config 配置项
51
+ */
52
+ function setConfig(config) {
53
+ Object.assign(Config, config);
54
+ }
28
55
 
29
56
  /******************************************************************************
30
57
  Copyright (c) Microsoft Corporation.
@@ -95,1428 +122,1445 @@ var MonitorTrack = (function (axios, uuid, uaParserJs, rrweb, ErrorStackParser,
95
122
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
96
123
  };
97
124
 
98
- var monitorTrackId = 'monitor-track-id';
99
- var monitorTrackSessionId = 'monitor-track-session-id';
125
+ var version = "1.12.1";
100
126
 
101
- /** */
102
- /**
103
- * @description config配置项默认值
104
- */
105
- var Config = {
106
- reportUrl: '',
107
- projectID: '',
108
- maxLength: 1000,
109
- spa: false,
110
- hash: false,
111
- enableBehavior: true,
112
- enableError: true,
113
- enableVisualTrack: false,
114
- ignore: {
115
- urls: [],
116
- errors: [],
117
- apis: []
118
- }
119
- };
120
- /**
121
- * @description 设置config配置项
122
- * @param config 配置项
123
- */
124
- function setConfig(config) {
125
- Object.assign(Config, config);
127
+ var eventsMatrix = [[]];
128
+ rrweb__namespace.record({
129
+ emit: function (event, isCheckout) {
130
+ // isCheckout 是一个标识,告诉你重新制作了快照
131
+ if (isCheckout) {
132
+ eventsMatrix.push([]);
133
+ }
134
+ if (eventsMatrix.length > 2) {
135
+ eventsMatrix.shift();
136
+ }
137
+ var lastEvents = eventsMatrix[eventsMatrix.length - 1];
138
+ lastEvents.push(event);
139
+ },
140
+ // 每30秒重新制作快照
141
+ checkoutEveryNms: 30 * 1000
142
+ });
143
+ /**
144
+ * @description 错误事件触发后的操作
145
+ */
146
+ function handleError(event) {
147
+ setReportValue('type', 'error');
148
+ if (event.type === 'error') {
149
+ if (event instanceof ErrorEvent) {
150
+ setCaughtError(event);
151
+ }
152
+ else {
153
+ setResourceError(event);
154
+ }
155
+ }
156
+ else if (event.type === 'unhandledrejection') {
157
+ setPromiseError(event);
158
+ }
126
159
  }
127
-
128
- var eventsMatrix = [[]];
129
- rrweb__namespace.record({
130
- emit: function (event, isCheckout) {
131
- // isCheckout 是一个标识,告诉你重新制作了快照
132
- if (isCheckout) {
133
- eventsMatrix.push([]);
134
- }
135
- if (eventsMatrix.length > 2) {
136
- eventsMatrix.shift();
137
- }
138
- var lastEvents = eventsMatrix[eventsMatrix.length - 1];
139
- lastEvents.push(event);
140
- },
141
- // 每30秒重新制作快照
142
- checkoutEveryNms: 30 * 1000
143
- });
144
- /**
145
- * @description 错误事件触发后的操作
146
- */
147
- function handleError(event) {
148
- setReportValue('type', 'error');
149
- if (event.type === 'error') {
150
- if (event instanceof ErrorEvent) {
151
- setCaughtError(event);
152
- }
153
- else {
154
- setResourceError(event);
155
- }
156
- }
157
- else if (event.type === 'unhandledrejection') {
158
- setPromiseError(event);
159
- }
160
- }
161
- /**
162
- * @description 设置错误信息
163
- */
164
- function setCaughtError(event) {
165
- var _a, _b;
166
- if (ignoreError(event.message)) {
167
- return;
168
- }
169
- var filename = event.filename, line = event.lineno, column = event.colno, functionName = '', stackFrame = JSON.stringify([]);
170
- if (event.error && event.error.stack) {
171
- var error = ErrorStackParser__default["default"].parse(event.error);
172
- stackFrame = JSON.stringify(error);
173
- var info = error.find(function (item) { return item.fileName && !/node_modules|vendor|bundle/.test(item.fileName); });
174
- if (info) {
175
- filename = info.fileName;
176
- line = Number(info.lineNumber);
177
- column = Number(info.columnNumber);
178
- functionName = info.functionName || '';
179
- }
180
- }
181
- setReportValue('error', {
182
- subType: 'js',
183
- line: line,
184
- column: column,
185
- message: event.message,
186
- filename: filename,
187
- functionName: functionName,
188
- stack: ((_b = (_a = event === null || event === void 0 ? void 0 : event.error) === null || _a === void 0 ? void 0 : _a.stack) === null || _b === void 0 ? void 0 : _b.substring(0, Config.maxLength)) || '',
189
- stackFrame: stackFrame,
190
- events: getUserEvents(false)
191
- });
192
- report(getReport());
193
- }
194
- /**
195
- * @description 设置异步错误信息
196
- */
197
- function setPromiseError(event) {
198
- var message = event.reason;
199
- if (event.reason && typeof event.reason === 'object') {
200
- message = JSON.stringify(event.reason);
201
- if (message === '{}') {
202
- message = event.reason.stack;
203
- }
204
- }
205
- if (ignoreError(message)) {
206
- return;
207
- }
208
- setReportValue('error', {
209
- subType: 'async',
210
- message: message,
211
- events: getUserEvents(false)
212
- });
213
- report(getReport());
214
- }
215
- /**
216
- * @description 设置资源错误信息
217
- */
218
- function setResourceError(event) {
219
- var target = event.target;
220
- if (ignoreError(target.outerHTML)) {
221
- return;
222
- }
223
- setReportValue('error', {
224
- subType: 'resource',
225
- message: target.outerHTML,
226
- filename: target.src || target.currentSrc,
227
- stack: target.localName.toUpperCase().substring(0, Config.maxLength),
228
- events: getUserEvents(false)
229
- });
230
- report(getReport());
231
- }
232
- /**
233
- * 忽略的error
234
- * @param errorMsg 错误信息
235
- */
236
- function ignoreError(errorMsg) {
237
- var _a;
238
- var errors = ((_a = Config === null || Config === void 0 ? void 0 : Config.ignore) === null || _a === void 0 ? void 0 : _a.errors) || [];
239
- var existIgnoreError = errors.findIndex(function (item) {
240
- if (typeof item === 'string') {
241
- return item === errorMsg;
242
- }
243
- else if (Object.prototype.toString.call(item) === '[object Object]') {
244
- if (item.regExp && typeof item.input === 'string') {
245
- var regex = new RegExp(item.input, 'g');
246
- return regex.test(errorMsg);
247
- }
248
- return false;
249
- }
250
- else {
251
- return false;
252
- }
253
- });
254
- return existIgnoreError > -1;
255
- }
256
- /**
257
- * 获取用户最近得操作,视频录像时间应当在30秒到60秒之间
258
- */
259
- var lastGetTime = Date.now(); //缓存操作录像,防止段时间内大量获取录像数据导滞内存溢出
260
- function getUserEvents(notUseCache) {
261
- var events;
262
- if (Date.now() - lastGetTime > 2000 || notUseCache) {
263
- lastGetTime = Date.now();
264
- if (eventsMatrix.length >= 2) {
265
- var finalVideoData = eventsMatrix[eventsMatrix.length - 1];
266
- events = JSON.stringify(eventsMatrix[eventsMatrix.length - 2].concat(finalVideoData));
267
- if (events.length > 1024 * 1024 * 15 && JSON.stringify(finalVideoData).length > 1024 * 1024) {
268
- //如果录像数据量太大且最后那个分片的数据量不算小,那么缩短上报的录像时长
269
- events = JSON.stringify(eventsMatrix[eventsMatrix.length - 1]);
270
- }
271
- }
272
- else {
273
- events = JSON.stringify(eventsMatrix);
274
- }
275
- }
276
- return events;
277
- }
278
- function getFullScreenShoot(filename) {
279
- var name = filename || uuid.v4();
280
- return html2canvas__default["default"](document.body).then(function (canvas) {
281
- var _a;
282
- var urlData = canvas.toDataURL('image/png', 1);
283
- var bytes = window.atob(urlData.split(',')[1]);
284
- var mime = (_a = urlData.split(',')[0].match(/:(.*?);/)) === null || _a === void 0 ? void 0 : _a[1];
285
- var ab = new ArrayBuffer(bytes.length);
286
- var ia = new Uint8Array(ab);
287
- for (var i = 0; i < bytes.length; i++) {
288
- ia[i] = bytes.charCodeAt(i);
289
- }
290
- var file = new File([ab], "".concat(name, ".png"), { type: mime });
291
- return file;
292
- });
160
+ /**
161
+ * @description 设置错误信息
162
+ */
163
+ function setCaughtError(event) {
164
+ var _a, _b;
165
+ if (ignoreError(event.message)) {
166
+ return;
167
+ }
168
+ var filename = event.filename, line = event.lineno, column = event.colno, functionName = '', stackFrame = JSON.stringify([]);
169
+ if (event.error && event.error.stack) {
170
+ var error = ErrorStackParser__default["default"].parse(event.error);
171
+ stackFrame = JSON.stringify(error);
172
+ var info = error.find(function (item) { return item.fileName && !/node_modules|vendor|bundle/.test(item.fileName); });
173
+ if (info) {
174
+ filename = info.fileName;
175
+ line = Number(info.lineNumber);
176
+ column = Number(info.columnNumber);
177
+ functionName = info.functionName || '';
178
+ }
179
+ }
180
+ setReportValue('error', {
181
+ subType: 'js',
182
+ line: line,
183
+ column: column,
184
+ message: event.message,
185
+ filename: filename,
186
+ functionName: functionName,
187
+ stack: ((_b = (_a = event === null || event === void 0 ? void 0 : event.error) === null || _a === void 0 ? void 0 : _a.stack) === null || _b === void 0 ? void 0 : _b.substring(0, Config.maxLength)) || '',
188
+ stackFrame: stackFrame,
189
+ events: getUserEvents(false)
190
+ });
191
+ report(getReport());
293
192
  }
294
-
295
- /** 上报数据映射 */
296
- var reportMap = {
297
- /** SDK 版本 */
298
- version: '_v',
299
- /** 项目ID */
300
- projectID: 'pid',
301
- /** location.host */
302
- host: 'host',
303
- /** 用户uuid */
304
- uuid: '_id',
305
- /** 上报日志的时间戳 */
306
- time: 'time',
307
- /** 源页面 */
308
- originPage: 'o',
309
- /** 当前页面 */
310
- page: 'p',
311
- /** 自定义payload */
312
- customPayload: 'cP',
313
- /** 信息类型 */
314
- type: 't',
315
- /** viewport的宽高 */
316
- viewport: 'vp',
317
- /** 屏幕宽高 */
318
- screen: 'sc',
319
- /** 页面title */
320
- pageTitle: 'pt',
321
- /** 页面referrer */
322
- referrer: 'ref',
323
- /** 页面编码 */
324
- charset: 'char',
325
- /** language */
326
- language: 'lan',
327
- /** vendor, 浏览器厂商 */
328
- navigatorVendor: 'nV',
329
- /** 网络连接类型 */
330
- connectionType: 'cT',
331
- /** 浏览器名称 */
332
- browserName: 'bN',
333
- /** 浏览器版本 */
334
- browserVersion: 'bV',
335
- /** 浏览器引擎名称 */
336
- engineName: 'enN',
337
- /** 浏览器引擎版本 */
338
- engineVersion: 'enV',
339
- /** 操作系统名称 */
340
- osName: 'osN',
341
- /** 操作系统版本 */
342
- osVersion: 'osV',
343
- pv: {
344
- /** 子类型 */
345
- subType: 'suT',
346
- /** 停留时间 */
347
- stayTime: 'sT'
348
- },
349
- error: {
350
- /** 子类型 */
351
- subType: 'suT',
352
- /** 错误行 */
353
- line: 'line',
354
- /** 错误列 */
355
- column: 'col',
356
- /** 错误信息 */
357
- message: 'msg',
358
- /** 错误文件 */
359
- filename: 'fil',
360
- /** 错误函数名 */
361
- functionName: 'fn',
362
- /** 错误栈信息 */
363
- stack: '_st',
364
- /** 错误帧 */
365
- stackFrame: '_sf',
366
- /** 视频录制数据 */
367
- events: 'evt',
368
- /** 页面截图 */
369
- picture: 'pic'
370
- },
371
- ua: {
372
- /** 子类型 */
373
- subType: 'suT',
374
- /** x轴坐标 */
375
- x: 'x',
376
- /** y轴坐标 */
377
- y: 'y',
378
- /** 元素路径 */
379
- path: 'path'
380
- },
381
- /** 性能数据 */
382
- perf: {
383
- navigation: {
384
- /**
385
- *一个无符号短整型,表示是如何导航到这个页面的。可能的值如下:
386
- TYPE_NAVIGATE (0)
387
- 当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在url中直接输入地址,type值为0
388
- TYPE_RELOAD (1)
389
- 点击刷新页面按钮或者通过Location.reload()方法显示的页面,type值为1
390
- The page was accessed by clicking the Reload button or via the Location.reload() method.
391
- TYPE_BACK_FORWARD (2)
392
- 页面通过历史记录和前进后退访问时。type值为2
393
- TYPE_RESERVED (255)
394
- 任何其他方式,type值为255
395
- */
396
- type: 't',
397
- /** 无符号短整型,表示在到达这个页面之前重定向了多少次。 */
398
- redirectCount: 'rc'
399
- },
400
- memory: {
401
- /** 上下文内可用堆的最大体积,以字节计算。 */
402
- jsHeapSizeLimit: 'hsl',
403
- /** 已分配的堆体积,以字节计算。 */
404
- totalJSHeapSize: 'ths',
405
- /** 当前 JS 堆活跃段(segment)的体积,以字节计算。 */
406
- usedJSHeapSize: 'uhs'
407
- },
408
- /** 返回性能测量开始时的时间的高精度时间戳。 */
409
- timeOrigin: 'to',
410
- timing: {
411
- /** 是一个无符号long long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。 */
412
- connectEnd: 'ce',
413
- /** 是一个无符号long long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值 */
414
- connectStart: 'cs',
415
- /** 是一个无符号long long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange (en-US) 被触发时的Unix毫秒时间戳。 */
416
- domComplete: 'dc',
417
- /** 是一个无符号long long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。 */
418
- domContentLoadedEventEnd: 'scLee',
419
- /** 是一个无符号long long 型的毫秒数,返回当解析器发送DOMContentLoaded (en-US) 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。 */
420
- domContentLoadedEventStart: 'dcLes',
421
- /** 是一个无符号long long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange (en-US)事件触发时)的Unix毫秒时间戳。 */
422
- domInteractive: 'di',
423
- /** 是一个无符号long long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange (en-US)事件触发时)的Unix毫秒时间戳。 */
424
- domLoading: 'dl',
425
- /** 是一个无符号long long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。 */
426
- domainLookupEnd: 'dle',
427
- /** 是一个无符号long long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。 */
428
- domainLookupStart: 'dls',
429
- /** 是一个无符号long long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。 */
430
- fetchStart: 'fs',
431
- /** 是一个无符号long long 型的毫秒数,返回当load (en-US)事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0. */
432
- loadEventEnd: 'lee',
433
- /** 是一个无符号long long 型的毫秒数,返回该文档下,load (en-US)事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。 */
434
- loadEventStart: 'les',
435
- /** 是一个无符号long long 型的毫秒数,表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同 */
436
- navigationStart: 'ns',
437
- /** 是一个无符号long long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0. */
438
- redirectEnd: 're',
439
- /** 是一个无符号long long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0. */
440
- redirectStart: 'redS',
441
- /** 是一个无符号long long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。 */
442
- requestStart: 'reqS',
443
- /** 是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。 */
444
- responseEnd: 'resE',
445
- /** 是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。 */
446
- responseStart: 'resS',
447
- /** 是一个无符号long long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。 */
448
- secureConnectionStart: 'scs',
449
- /** 是一个无符号long long 型的毫秒数,表征了unload (en-US)事件处理完成时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0. */
450
- unloadEventEnd: 'uee',
451
- /** 是一个无符号long long 型的毫秒数,表征了unload (en-US)事件抛出时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0. */
452
- unloadEventStart: 'ees'
453
- }
454
- },
455
- /** api请求相关 */
456
- req: {
457
- /** 请求类型,xhr或fetch */
458
- requestType: 'rT',
459
- /** 请求地址 */
460
- responseURL: 'url',
461
- /** 请求结果 */
462
- status: 'stat',
463
- /** 请求耗时 */
464
- loadTime: 'lT',
465
- /** 请求状态 */
466
- statusText: 'sT',
467
- /** 上报原因, slow或failed */
468
- reason: 're',
469
- /** 上报时带上的详细信息 */
470
- detail: 'de'
471
- },
472
- vD: {
473
- /** 埋点名称 */
474
- trackName: 'tN',
475
- /** 埋点参数 */
476
- params: 'params'
477
- },
478
- manualReport: 'mR',
479
- lifeCycleId: 'li',
480
- sessionId: 'sI'
481
- };
482
- var xhrFunc = function (filename, userId, asyncUpdateId, file, res, callback, rest) {
483
- var formData = new FormData();
484
- for (var i in rest) {
485
- if (Object.prototype.hasOwnProperty.call(rest, i)) {
486
- formData.append(i, rest[i]);
487
- }
488
- }
489
- formData.append('filename', filename);
490
- formData.append('asyncUpdateId', asyncUpdateId); //用于上传到oss后异步更新到db的id
491
- formData.append('userId', userId);
492
- formData.append('files', file);
493
- var xhr = new XMLHttpRequest();
494
- xhr.addEventListener('error', function () { return res(); }, false);
495
- xhr.open('POST', Config.reportUrl);
496
- xhr.onreadystatechange = function () {
497
- var _a;
498
- if (xhr.readyState === 4) {
499
- if (xhr.status === 200) {
500
- try {
501
- var response = xhr.responseText;
502
- var resp = JSON.parse(response);
503
- callback((_a = resp.result) === null || _a === void 0 ? void 0 : _a.response);
504
- }
505
- catch (_err) {
506
- //
507
- }
508
- res();
509
- }
510
- else {
511
- res();
512
- }
513
- }
514
- };
515
- xhr.send(formData);
516
- };
517
- var ERROR_MESSAGE_MAP = {};
518
- function reportFunc(data) {
519
- var _a, _b, _c;
520
- var payload = convertToSchema(data, reportMap);
521
- payload.time = new Date().getTime(); // 设置上报时间
522
- var picturePromise = Promise.resolve();
523
- var hasErrorEvent = false;
524
- var filename = uuid.v4();
525
- if (data.type === 'error') {
526
- var message_1 = ((_a = data.error) === null || _a === void 0 ? void 0 : _a.message) || 'default';
527
- if (((_b = data.error) === null || _b === void 0 ? void 0 : _b.subType) === 'resource') {
528
- message_1 = 'resource';
529
- }
530
- if (ERROR_MESSAGE_MAP[message_1]) ;
531
- else {
532
- ERROR_MESSAGE_MAP[message_1] = true;
533
- setTimeout(function () {
534
- //节流效果,每隔3秒上报一次重复的错误录像和错误截图
535
- delete ERROR_MESSAGE_MAP[message_1];
536
- }, 3000);
537
- var p_1 = payload.p, pid_1 = payload.pid, host_1 = payload.host, bN_1 = payload.bN, pt_1 = payload.pt;
538
- if ((_c = data.error) === null || _c === void 0 ? void 0 : _c.events) {
539
- hasErrorEvent = true;
540
- var errorEvent_1 = data.error.events;
541
- var delayTime = Math.ceil(Math.random() * 10000);
542
- setTimeout(function () {
543
- var blob = new Blob([errorEvent_1], { type: 'text/json' });
544
- return new Promise(function (res) {
545
- xhrFunc("".concat(filename, ".json"), data.uuid, filename, blob, res, function () { }, { p: p_1, pid: pid_1, host: host_1, bN: bN_1, pt: pt_1 });
546
- });
547
- }, delayTime < 3500 ? 3500 : delayTime);
548
- }
549
- picturePromise = getFullScreenShoot(filename)
550
- .then(function (file) {
551
- return new Promise(function (res) {
552
- xhrFunc(file.name, data.uuid, filename, file, res, function (result) {
553
- payload.error.pic = result;
554
- }, { p: p_1, pid: pid_1, host: host_1, bN: bN_1, pt: pt_1 });
555
- });
556
- })["catch"](function (err) {
557
- payload.error.picError = (err === null || err === void 0 ? void 0 : err.stack) || (err === null || err === void 0 ? void 0 : err.toString());
558
- });
559
- }
560
- }
561
- return picturePromise
562
- .then(function () {
563
- var _a;
564
- if ((_a = payload.error) === null || _a === void 0 ? void 0 : _a.evt)
565
- delete payload.error.evt;
566
- if (window && window.navigator && typeof window.navigator.sendBeacon === 'function') {
567
- var formData_1 = new FormData();
568
- Object.keys(payload).forEach(function (key) {
569
- var value = payload[key];
570
- if (value !== null && value !== undefined) {
571
- if (typeof value === 'object') {
572
- value = JSON.stringify(value);
573
- }
574
- formData_1.append(key, value);
575
- }
576
- });
577
- //eventErrorFilename,将错误录像的文件名附在错误上报的埋点里,便于将错误录像上报回调地址异步的更新到db
578
- if (hasErrorEvent)
579
- formData_1.append('eef', filename);
580
- window.navigator.sendBeacon(Config.reportUrl, formData_1);
581
- }
582
- else {
583
- if (hasErrorEvent)
584
- payload.eef = filename;
585
- new Image().src = "".concat(Config.reportUrl, "?").concat(serialize(payload));
586
- }
587
- })["catch"](function (_err) {
588
- //
589
- });
590
- }
591
- // 上报
592
- function report(data) {
593
- return new Promise(function (res) {
594
- //在帧的空闲时间上报
595
- var dataCopy = JSON.parse(JSON.stringify(data));
596
- if (typeof window.requestIdleCallback === 'function') {
597
- window.requestIdleCallback(function () { return reportFunc(dataCopy).then(res); }, { timeout: 2000 });
598
- }
599
- else {
600
- setTimeout(function () { return reportFunc(dataCopy).then(res); }, 0);
601
- }
602
- });
603
- }
604
- /**
605
- * @description 将数据转换成最终提交的数据,主要目的是简化数据的key的长度,从而降低荷载大小
606
- * @param data 要转换的数据
607
- * @param map 要转换的数据的字段映射
608
- * @param redundancyData 冗余数据
609
- */
610
- function convertToSchema(data, map, redundancyData) {
611
- var reportData = {};
612
- if (redundancyData) {
613
- map = __assign(__assign({}, map), redundancyData);
614
- }
615
- for (var _i = 0, _a = Object.entries(map); _i < _a.length; _i++) {
616
- var result = _a[_i];
617
- var _b = result, key = _b[0], mapKey = _b[1];
618
- if (key === 'manualReport') {
619
- reportData[mapKey] = data[key];
620
- }
621
- else {
622
- //字段值为数组的场景,直接转换
623
- if (typeof data[key] === 'object' && !Array.isArray(data[key])) {
624
- if (data[key]) {
625
- if (redundancyData && Object.prototype.hasOwnProperty.call(redundancyData, key)) {
626
- reportData[key] = data[key];
627
- }
628
- else {
629
- reportData[key] = convertToSchema(data[key], map[key]);
630
- }
631
- }
632
- else {
633
- reportData[key] = data[key];
634
- }
635
- }
636
- else {
637
- // 排除undefined
638
- if (data[key] !== undefined) {
639
- reportData[mapKey] = data[key];
640
- }
641
- }
642
- }
643
- }
644
- return reportData;
193
+ /**
194
+ * @description 设置异步错误信息
195
+ */
196
+ function setPromiseError(event) {
197
+ var message = event.reason;
198
+ if (event.reason && typeof event.reason === 'object') {
199
+ message = JSON.stringify(event.reason);
200
+ if (message === '{}') {
201
+ message = event.reason.stack;
202
+ }
203
+ }
204
+ if (ignoreError(message)) {
205
+ return;
206
+ }
207
+ setReportValue('error', {
208
+ subType: 'async',
209
+ message: message,
210
+ events: getUserEvents(false)
211
+ });
212
+ report(getReport());
213
+ }
214
+ /**
215
+ * @description 设置资源错误信息
216
+ */
217
+ function setResourceError(event) {
218
+ var target = event.target;
219
+ if (ignoreError(target.outerHTML)) {
220
+ return;
221
+ }
222
+ setReportValue('error', {
223
+ subType: 'resource',
224
+ message: target.outerHTML,
225
+ filename: target.src || target.currentSrc,
226
+ stack: target.localName.toUpperCase().substring(0, Config.maxLength),
227
+ events: getUserEvents(false)
228
+ });
229
+ report(getReport());
230
+ }
231
+ /**
232
+ * 忽略的error
233
+ * @param errorMsg 错误信息
234
+ */
235
+ function ignoreError(errorMsg) {
236
+ var _a;
237
+ var errors = ((_a = Config === null || Config === void 0 ? void 0 : Config.ignore) === null || _a === void 0 ? void 0 : _a.errors) || [];
238
+ var existIgnoreError = errors.findIndex(function (item) {
239
+ if (typeof item === 'string') {
240
+ return item === errorMsg;
241
+ }
242
+ else if (Object.prototype.toString.call(item) === '[object Object]') {
243
+ if (item.regExp && typeof item.input === 'string') {
244
+ var regex = new RegExp(item.input, 'g');
245
+ return regex.test(errorMsg);
246
+ }
247
+ return false;
248
+ }
249
+ else {
250
+ return false;
251
+ }
252
+ });
253
+ return existIgnoreError > -1;
254
+ }
255
+ /**
256
+ * 获取用户最近得操作,视频录像时间应当在30秒到60秒之间
257
+ */
258
+ var lastGetTime = Date.now(); //缓存操作录像,防止段时间内大量获取录像数据导滞内存溢出
259
+ function getUserEvents(notUseCache) {
260
+ var events;
261
+ if (Date.now() - lastGetTime > 2000 || notUseCache) {
262
+ lastGetTime = Date.now();
263
+ if (eventsMatrix.length >= 2) {
264
+ var finalVideoData = eventsMatrix[eventsMatrix.length - 1];
265
+ events = JSON.stringify(eventsMatrix[eventsMatrix.length - 2].concat(finalVideoData));
266
+ if (events.length > 1024 * 1024 * 15 && JSON.stringify(finalVideoData).length > 1024 * 1024) {
267
+ //如果录像数据量太大且最后那个分片的数据量不算小,那么缩短上报的录像时长
268
+ events = JSON.stringify(eventsMatrix[eventsMatrix.length - 1]);
269
+ }
270
+ }
271
+ else {
272
+ events = JSON.stringify(eventsMatrix);
273
+ }
274
+ }
275
+ return events;
276
+ }
277
+ function getFullScreenShoot(filename) {
278
+ var name = filename || uuid.v4();
279
+ return html2canvas__default["default"](document.body).then(function (canvas) {
280
+ var _a;
281
+ var urlData = canvas.toDataURL('image/png', 1);
282
+ var bytes = window.atob(urlData.split(',')[1]);
283
+ var mime = (_a = urlData.split(',')[0].match(/:(.*?);/)) === null || _a === void 0 ? void 0 : _a[1];
284
+ var ab = new ArrayBuffer(bytes.length);
285
+ var ia = new Uint8Array(ab);
286
+ for (var i = 0; i < bytes.length; i++) {
287
+ ia[i] = bytes.charCodeAt(i);
288
+ }
289
+ var file = new File([ab], "".concat(name, ".png"), { type: mime });
290
+ return file;
291
+ });
645
292
  }
646
293
 
647
- /** 路由栈数组,存储路由变化信息 */
648
- var routerStack = [];
649
- /** 路由触发的时间 */
650
- var time = 0;
651
- /**
652
- * @description 派发pushState, replaceState 的监听
653
- * @param type
654
- */
655
- function _history(type) {
656
- var origin = history[type];
657
- return function () {
658
- // @ts-ignore
659
- // eslint-disable-next-line prefer-rest-params
660
- var r = origin.apply(this, arguments);
661
- var e = new Event(type);
662
- // @ts-ignore
663
- // eslint-disable-next-line prefer-rest-params
664
- e.arguments = arguments;
665
- window.dispatchEvent(e);
666
- return r;
667
- };
668
- }
669
- /**
670
- * @description 处理history变化
671
- * @param e history事件
672
- */
673
- function handleHistoryChange(__e) {
674
- visualTrackFunc();
675
- setReport();
676
- }
677
- /**
678
- * 设置信息并触发上报
679
- */
680
- function setReport() {
681
- var pageUrl = location.href;
682
- /**
683
- * 此判断的逻辑是因为spa且hash模式下,路由搜索参数更改也会触发handleHistoryChange事件
684
- * 所以路由栈中的信息只保存无搜索参数的地址
685
- */
686
- if (Config.hash && Config.spa) {
687
- var currentUrlSplit = location.href.split('#');
688
- var currentUrlPostfix = '';
689
- if (currentUrlSplit[1]) {
690
- var index = currentUrlSplit[1].indexOf('?');
691
- if (index > -1) {
692
- currentUrlPostfix = currentUrlSplit[1].substring(0, index);
693
- }
694
- else {
695
- currentUrlPostfix = currentUrlSplit[1];
696
- }
697
- }
698
- var currentUrlResult = "".concat(currentUrlSplit[0], "#").concat(currentUrlPostfix);
699
- pageUrl = currentUrlResult;
700
- }
701
- var _a = setRouteStack(pageUrl), originPage = _a.originPage, page = _a.page;
702
- // 愿页面等于当前页面,说明无路由变更。只有spa且hash模式下才会出现
703
- if (originPage === page) {
704
- return;
705
- }
706
- var currentTime = new Date().getTime();
707
- // 页面停留时间
708
- var stayTime = currentTime - time;
709
- // 设置触发路由变化的时间
710
- time = currentTime;
711
- // 设置信息
712
- setReportValue('originPage', originPage);
713
- setReportValue('page', page);
714
- setReportValue('type', 'pv');
715
- setReportValue('pv', { stayTime: stayTime });
716
- // 上报数据
717
- report(getReport());
718
- }
719
- function setPVTime() {
720
- time = new Date().getTime();
721
- }
722
- /**
723
- * @description 添加路由栈信息
724
- * @param page 页面信息
725
- */
726
- function setRouteStack(page) {
727
- if (typeof page === 'string') {
728
- routerStack.push(page);
729
- }
730
- else {
731
- routerStack = routerStack.concat(page);
732
- }
733
- // 路由栈只需保留两个来作为源页面和当前页面
734
- routerStack = routerStack.slice(-2);
735
- return {
736
- originPage: routerStack[0],
737
- page: routerStack[1] || routerStack[0]
738
- };
294
+ var monitorTrackId = 'monitor-track-id';
295
+ var monitorTrackSessionId = 'monitor-track-session-id';
296
+
297
+ /**
298
+ * @description 点击事件触发后的操作
299
+ */
300
+ function handleClick(event) {
301
+ var target = event.target;
302
+ if (target.nodeName === 'INPUT' || target.nodeName === 'TEXTAREA') {
303
+ return;
304
+ }
305
+ var path = getElmPath(target);
306
+ if (path) {
307
+ setReportValue('type', 'ua');
308
+ setReportValue('ua', {
309
+ subType: 'ui.click',
310
+ x: event.x,
311
+ y: event.y,
312
+ path: path
313
+ });
314
+ report(getReport());
315
+ }
316
+ }
317
+ /**
318
+ * @description 点击触发失焦后的操作
319
+ */
320
+ function handleBlur(event) {
321
+ var target = event.target;
322
+ if (target.nodeName !== 'INPUT' && target.nodeName !== 'TEXTAREA') {
323
+ return;
324
+ }
325
+ var path = getElmPath(target);
326
+ if (path) {
327
+ setReportValue('type', 'ua');
328
+ setReportValue('ua', {
329
+ subType: 'ui.blur',
330
+ x: event.x,
331
+ y: event.y,
332
+ path: path,
333
+ value: target.value
334
+ });
335
+ report(getReport());
336
+ }
337
+ }
338
+ /**
339
+ * @description 滚动的操作
340
+ */
341
+ var timeout$1;
342
+ function handleScroll(_event) {
343
+ clearTimeout(timeout$1);
344
+ timeout$1 = setTimeout(function () {
345
+ setReportValue('type', 'ua');
346
+ setReportValue('ua', {
347
+ subType: 'ui.scroll'
348
+ });
349
+ report(getReport());
350
+ }, 1000);
739
351
  }
740
352
 
741
- var version = "1.11.0";
353
+ var parser = new uaParserJs.UAParser();
354
+ /**
355
+ * @description 获取uuid
356
+ */
357
+ function getUid() {
358
+ var uid = localStorage.getItem(monitorTrackId) || '';
359
+ if (!uid) {
360
+ uid = uuid.v4();
361
+ localStorage.setItem(monitorTrackId, uid);
362
+ }
363
+ return uid;
364
+ }
365
+ /**
366
+ * @description 获取session id
367
+ */
368
+ function getSessionId() {
369
+ var sessionId = sessionStorage.getItem(monitorTrackSessionId);
370
+ if (sessionId) {
371
+ return sessionId;
372
+ }
373
+ else {
374
+ var id = uuid.v4();
375
+ sessionStorage.setItem(monitorTrackSessionId, id);
376
+ return id;
377
+ }
378
+ }
379
+ /**
380
+ * @description 获取浏览器信息
381
+ */
382
+ function getNavigator() {
383
+ var _a;
384
+ var uaResult = parser.getResult();
385
+ return {
386
+ language: navigator.language,
387
+ navigatorVendor: navigator.vendor,
388
+ connectionType: ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.connection) === null || _a === void 0 ? void 0 : _a.effectiveType) || '2g',
389
+ browserName: uaResult.browser.name || '',
390
+ browserVersion: uaResult.browser.version || '',
391
+ engineName: uaResult.engine.name || '',
392
+ engineVersion: uaResult.engine.version || '',
393
+ osName: uaResult.os.name || '',
394
+ osVersion: uaResult.os.version || ''
395
+ };
396
+ }
397
+ /**
398
+ * @description 获取viewport的宽高
399
+ */
400
+ function getViewport() {
401
+ var w = document.documentElement.clientWidth || document.body.clientWidth;
402
+ var h = document.documentElement.clientHeight || document.body.clientHeight;
403
+ return "".concat(w, " x ").concat(h);
404
+ }
405
+ /**
406
+ * @description 获取元素路径,最多保留5层
407
+ * @param e
408
+ */
409
+ var getElmPath = function (target) {
410
+ if (!target || target.nodeType !== 1) {
411
+ return '';
412
+ }
413
+ var ret = [];
414
+ // 层数,最多5层
415
+ var deepLength = 0;
416
+ // 元素
417
+ var elm = '';
418
+ if (typeof target.innerText === 'string') {
419
+ ret.push("(".concat(target.innerText.substr(0, 50), ")"));
420
+ }
421
+ for (var t = target || null; t && deepLength++ < 5 && !((elm = normalTarget(t)) === 'html');) {
422
+ // eslint-disable-next-line no-sequences
423
+ ret.push(elm), (t = t.parentNode);
424
+ }
425
+ return ret.reverse().join(' > ');
426
+ };
427
+ /**
428
+ * @description 处理html node
429
+ * @param e
430
+ */
431
+ var normalTarget = function (target) {
432
+ var t, n, r, a, i;
433
+ var o = [];
434
+ if (!target || !target.tagName) {
435
+ return '';
436
+ }
437
+ o.push(target.tagName.toLowerCase());
438
+ if (target.id) {
439
+ o.push('#'.concat(target.id));
440
+ }
441
+ if ((t = target.className) && Object.prototype.toString.call(t) === '[object String]') {
442
+ for (n = t.split(/\s+/), i = 0; i < n.length; i++) {
443
+ // className包含active的不加入路径
444
+ if (n[i].indexOf('active') < 0)
445
+ o.push('.'.concat(n[i]));
446
+ }
447
+ }
448
+ var s = ['type', 'name', 'title', 'alt'];
449
+ for (i = 0; i < s.length; i++) {
450
+ r = s[i];
451
+ if ((a = target.getAttribute(r))) {
452
+ o.push('['.concat(r, '="').concat(a, '"]'));
453
+ }
454
+ }
455
+ return o.join('');
456
+ };
457
+ /**
458
+ * @description 序列化对象
459
+ * @param obj
460
+ */
461
+ function serialize(obj) {
462
+ var str = [];
463
+ for (var p in obj) {
464
+ if (Object.prototype.hasOwnProperty.call(obj, p) && typeof obj[p] !== 'undefined') {
465
+ var value = typeof obj[p] === 'object' ? JSON.stringify(obj[p]) : obj[p];
466
+ str.push(encodeURIComponent(p) + '=' + encodeURIComponent(value));
467
+ }
468
+ }
469
+ return str.join('&');
470
+ }
471
+ /**
472
+ * @description 注册事件监听
473
+ * @param event 事件
474
+ * @param fn 回调方法
475
+ */
476
+ var on = function (event, fn) {
477
+ if (window.addEventListener) {
478
+ window.addEventListener(event, fn, true);
479
+ }
480
+ else {
481
+ window.attachEvent("on".concat(event), fn);
482
+ }
483
+ };
484
+ /**
485
+ * @description 移除事件监听
486
+ * @param event 事件
487
+ * @param fn 回调方法
488
+ */
489
+ var off = function (event, fn) {
490
+ if (window.removeEventListener) {
491
+ return window.removeEventListener(event, fn);
492
+ }
493
+ else {
494
+ return window.detachEvent("on".concat(event), fn);
495
+ }
496
+ };
497
+ var formatTrackElementXPath = function (xPath) {
498
+ var result = [];
499
+ var array = xPath.split('/');
500
+ array.forEach(function (item) {
501
+ if (!['svg', 'path', 'g', 'image', 'text', 'line', 'rect', 'polygon', 'circle', 'ellipse'].some(function (child) {
502
+ if (item.toLowerCase().split('[')[0] === child) {
503
+ result.push("/*[name()=\"".concat(child, "\"]"));
504
+ return true;
505
+ }
506
+ else {
507
+ return false;
508
+ }
509
+ })) {
510
+ result.push(item);
511
+ }
512
+ });
513
+ return result.join('/');
514
+ };
515
+ var formatElementXPath = function (info) {
516
+ var _a;
517
+ var elementXPathValue = '';
518
+ var elementXPath = info.elementXPath;
519
+ if (elementXPath) {
520
+ var xpathElement = document.evaluate(formatTrackElementXPath(elementXPath), document).iterateNext();
521
+ elementXPathValue = xpathElement ? (_a = (xpathElement.textContent || xpathElement.value)) === null || _a === void 0 ? void 0 : _a.trim() : '';
522
+ }
523
+ return {
524
+ elementXPath: elementXPath,
525
+ elementXPathValue: elementXPathValue
526
+ };
527
+ };
528
+ var existTrackListenEvent = {};
529
+ var elements = [];
530
+ var timeout;
531
+ var visualTrackFunc = function () {
532
+ if (Config.enableVisualTrack) {
533
+ if (timeout)
534
+ clearTimeout(timeout);
535
+ timeout = setTimeout(function () {
536
+ timeout = null;
537
+ var url = location.href.split('?')[0];
538
+ axios__default["default"].get("".concat(Config.reportUrl.replace('/s/r', ''), "/visual/get_data_list?url=").concat(encodeURIComponent(url))).then(function (res) {
539
+ var _a, _b;
540
+ var listData = (_b = (_a = res.data.result) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.dataList;
541
+ if (Array.isArray(listData)) {
542
+ try {
543
+ listData.forEach(function (item) {
544
+ if (item.trackType === 'mount') {
545
+ visualReportEvent(item);
546
+ }
547
+ else {
548
+ var element = document.evaluate(formatTrackElementXPath(item.trackElementXPath), document).iterateNext();
549
+ var key = url + item.trackElementXPath;
550
+ if (element) {
551
+ if (!existTrackListenEvent[key]) {
552
+ existTrackListenEvent[key] = true;
553
+ elements.push(element);
554
+ //@ts-ignore
555
+ element.setAttribute('visual-track-data-attr', JSON.stringify(item));
556
+ element.addEventListener('click', clickEventFunc);
557
+ }
558
+ }
559
+ else {
560
+ var func_1 = function () {
561
+ var _a;
562
+ var _b = getNavigator(), browserName = _b.browserName, browserVersion = _b.browserVersion;
563
+ var payload = {
564
+ projectID: Config.projectID,
565
+ version: version,
566
+ url: url,
567
+ trackElementXPath: item.trackElementXPath,
568
+ trackNameInfo: item.trackNameInfo,
569
+ params: item.params,
570
+ customPayload: Config.customPayload,
571
+ type: 'visual_track_xpath_not_found',
572
+ browser: "".concat(browserName, " - ").concat(browserVersion)
573
+ };
574
+ if (typeof ((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.sendBeacon) === 'function') {
575
+ var formData = new FormData();
576
+ for (var i in payload) {
577
+ if (Object.prototype.hasOwnProperty.call(payload, i)) {
578
+ var value = payload[i];
579
+ formData.append(i, value && typeof value === 'object' ? JSON.stringify(value) : value);
580
+ }
581
+ }
582
+ window.navigator.sendBeacon(Config.reportUrl, formData);
583
+ }
584
+ else {
585
+ new Image().src = "".concat(Config.reportUrl, "?").concat(serialize(payload));
586
+ }
587
+ };
588
+ if (typeof window.requestIdleCallback === 'function') {
589
+ window.requestIdleCallback(function () { return func_1(); }, { timeout: 2000 });
590
+ }
591
+ else {
592
+ setTimeout(function () { return func_1(); }, 0);
593
+ }
594
+ }
595
+ }
596
+ });
597
+ }
598
+ catch (err) {
599
+ // eslint-disable-next-line no-console
600
+ console.log('visual track evaluate err', err);
601
+ }
602
+ }
603
+ });
604
+ }, 1000);
605
+ }
606
+ };
607
+ var visualReportEvent = function (item) {
608
+ var _a;
609
+ var _b = formatElementXPath(item.trackNameInfo), elementXPath = _b.elementXPath, elementXPathValue = _b.elementXPathValue;
610
+ var visualTrackData = {
611
+ trackType: item.trackType,
612
+ trackName: elementXPath ? elementXPathValue : item.trackNameInfo.name,
613
+ params: (_a = item.params) === null || _a === void 0 ? void 0 : _a.map(function (child) {
614
+ var _a = formatElementXPath(child), elementXPath = _a.elementXPath, elementXPathValue = _a.elementXPathValue;
615
+ return {
616
+ name: child.name,
617
+ value: elementXPath ? elementXPathValue : child.value
618
+ };
619
+ })
620
+ };
621
+ setReportValue('type', 'visual');
622
+ setReportValue('vD', visualTrackData);
623
+ report(getReport());
624
+ setReportValue('vD', undefined);
625
+ };
626
+ var clickEventFunc = function (e) {
627
+ //@ts-ignore
628
+ var item = JSON.parse(e.target.getAttribute('visual-track-data-attr'));
629
+ visualReportEvent(item);
630
+ };
631
+ var initWindowObjectFunction = function () {
632
+ if (typeof window.manualReportTrackFunc === 'undefined') {
633
+ window.manualReportTrackFunc = function (data) {
634
+ if (Object.prototype.toString.call(data) !== '[object Object]') {
635
+ // eslint-disable-next-line no-console
636
+ console.warn('manualReportTrackFunc参数必须是一个对象!');
637
+ }
638
+ else {
639
+ setReportValue('type', 'manual');
640
+ setReportValue('manualReport', data);
641
+ report(getReport());
642
+ setReportValue('manualReport', undefined);
643
+ }
644
+ };
645
+ }
646
+ if (typeof window.getFullScreenShootFunc === 'undefined') {
647
+ //下载页面截图
648
+ window.getFullScreenShootFunc = function (filename) { return __awaiter(void 0, void 0, void 0, function () {
649
+ var file, url, tagA;
650
+ return __generator(this, function (_a) {
651
+ switch (_a.label) {
652
+ case 0: return [4 /*yield*/, getFullScreenShoot(filename)];
653
+ case 1:
654
+ file = _a.sent();
655
+ url = window.URL.createObjectURL(file);
656
+ tagA = document.createElement('a');
657
+ tagA.setAttribute('href', url);
658
+ tagA.setAttribute('download', file.name);
659
+ tagA.setAttribute('target', '_blank');
660
+ document.body.appendChild(tagA);
661
+ tagA.click();
662
+ document.body.removeChild(tagA);
663
+ return [2 /*return*/];
664
+ }
665
+ });
666
+ }); };
667
+ }
668
+ if (typeof window.getRRWebUserEventsCaptureFunc === 'undefined') {
669
+ window.getRRWebUserEventsCaptureFunc = function () {
670
+ setReportValue('type', 'error');
671
+ setReportValue('error', {
672
+ subType: 'manual',
673
+ message: 'manual trigger',
674
+ events: getUserEvents(true)
675
+ });
676
+ report(getReport());
677
+ };
678
+ }
679
+ };
680
+ var prewHref = '';
681
+ var handleLocationChange = function (e) {
682
+ var curHref = location.href;
683
+ // href中`?`后面的值改变,不触发后续动作
684
+ if (prewHref.split('?')[0] === curHref.split('?')[0]) {
685
+ return;
686
+ }
687
+ prewHref = curHref;
688
+ handleHistoryChange();
689
+ elements.forEach(function (element) {
690
+ element === null || element === void 0 ? void 0 : element.removeEventListener('click', clickEventFunc);
691
+ });
692
+ //清空可视化埋点之前的数据
693
+ existTrackListenEvent = {};
694
+ elements.length = 0;
695
+ visualTrackFunc();
696
+ };
742
697
 
743
- // 上报数据
744
- var Report = {
745
- uuid: '',
746
- projectID: '',
747
- host: '',
748
- originPage: '',
749
- page: '',
750
- time: 0,
751
- browserName: '',
752
- browserVersion: '',
753
- engineName: '',
754
- engineVersion: '',
755
- language: '',
756
- navigatorVendor: '',
757
- connectionType: '2g',
758
- osName: '',
759
- osVersion: '',
760
- type: 'init',
761
- viewport: '',
762
- screen: '',
763
- version: '',
764
- charset: '',
765
- pageTitle: '',
766
- referrer: '',
767
- pv: null,
768
- ua: null,
769
- error: null,
770
- dpr: 1,
771
- perf: null,
772
- vD: undefined,
773
- manualReport: undefined,
774
- lifeCycleId: uuid.v4(),
775
- sessionId: getSessionId()
776
- };
777
- /**
778
- * @description 初始化上报数据并上报
779
- */
780
- function initReport() {
781
- if (document.readyState === 'complete') {
782
- initReportFunc();
783
- }
784
- else {
785
- // 注意:这里不要使用window.onload,因为一个项目window.onload只能用一次,如果这里用了就会影响宿主项目的功能
786
- window.addEventListener('load', initReportFunc);
787
- }
788
- }
789
- function initReportFunc() {
790
- setRouteStack([location.href, location.href]);
791
- setPVTime();
792
- Report = __assign(__assign({}, getReport()), {
793
- page: location.href,
794
- originPage: location.href,
795
- type: 'init',
796
- perf: performance,
797
- sessionId: getSessionId(),
798
- lifeCycleId: uuid.v4()
799
- });
800
- report(Report);
801
- }
802
- /**
803
- * @description 设置Report的值
804
- * @param key Report key
805
- * @param value Report 值
806
- */
807
- function setReportValue(key, value) {
808
- if (Object.prototype.hasOwnProperty.call(Report, key)) {
809
- if (['pv', 'ua', 'error', 'request'].includes(key)) {
810
- Report = __assign(__assign({}, Report), {
811
- pv: null,
812
- ua: null,
813
- error: null,
814
- req: null,
815
- manualReport: undefined,
816
- perf: null
817
- });
818
- }
819
- Report[key] = value;
820
- }
821
- }
822
- /**
823
- * @description 获取上报数据
824
- */
825
- function getReport() {
826
- var nav = getNavigator();
827
- var viewport = getViewport();
828
- var uuid = localStorage.getItem('username') || getUid();
829
- return __assign(__assign(__assign({}, Report), nav), {
830
- version: version,
831
- projectID: Config.projectID,
832
- host: location.host,
833
- uuid: uuid,
834
- viewport: viewport,
835
- screen: "".concat(screen.width, " x ").concat(screen.height),
836
- pageTitle: document.title,
837
- referrer: document.referrer,
838
- charset: document.charset,
839
- customPayload: Config.customPayload,
840
- dpr: window.devicePixelRatio
841
- });
842
- }
843
- function ajaxEventTrigger(event) {
844
- var ajaxEvent = new CustomEvent(event, {
845
- detail: this
846
- });
847
- window.dispatchEvent(ajaxEvent);
848
- }
849
- var OldXHR = window.XMLHttpRequest;
850
- function newXHR() {
851
- var realXHR = new OldXHR();
852
- realXHR.addEventListener('loadstart', function () {
853
- ajaxEventTrigger.call(this, 'ajaxLoadStart');
854
- }, false);
855
- realXHR.addEventListener('loadend', function () {
856
- ajaxEventTrigger.call(this, 'ajaxLoadEnd');
857
- }, false);
858
- // 此处的捕获的异常会连日志接口也一起捕获,如果日志上报接口异常了,就会导致死循环了。
859
- realXHR.onerror = function (e) {
860
- // eslint-disable-next-line no-console
861
- console.warn('realXHR.onerror, e', e);
862
- };
863
- return realXHR;
864
- }
865
- /**
866
- * 页面接口请求监控
867
- */
868
- var tempUrlInfo = {};
869
- var recordXMLHttpRequestLog = function (XMLHttpRequestTimeout) {
870
- XMLHttpRequestTimeout = typeof XMLHttpRequestTimeout === 'number' ? XMLHttpRequestTimeout : 1000;
871
- var timeRecordArray = [];
872
- window.__XMLHttpRequest__ = window.XMLHttpRequest;
873
- window.XMLHttpRequest = newXHR;
874
- window.addEventListener('ajaxLoadStart', function (e) {
875
- var tempObj = {
876
- timeStamp: new Date().getTime(),
877
- event: e
878
- };
879
- timeRecordArray.push(tempObj);
880
- });
881
- window.addEventListener('ajaxLoadEnd', function () {
882
- var timeRecordArrayCopy = [].concat(timeRecordArray);
883
- var _loop_1 = function (i) {
884
- if (timeRecordArrayCopy[i].event.detail && timeRecordArrayCopy[i].event.detail.status > 0) {
885
- var currentTime = new Date().getTime();
886
- var _a = timeRecordArrayCopy[i].event.detail, responseURL_1 = _a.responseURL, status_1 = _a.status, statusText = _a.statusText, timeStamp = _a.timeStamp;
887
- var previousTime = timeStamp || timeRecordArrayCopy[i].timeStamp;
888
- var loadTime = currentTime - previousTime;
889
- var request = {
890
- requestType: 'xhr',
891
- responseURL: responseURL_1,
892
- status: status_1,
893
- loadTime: loadTime,
894
- statusText: statusText,
895
- reason: '',
896
- detail: ''
897
- };
898
- if (loadTime && loadTime > XMLHttpRequestTimeout) {
899
- request.reason = 'slow';
900
- request.detail = "request is too slow, XMLHttpRequestTimeout: ".concat(XMLHttpRequestTimeout);
901
- }
902
- else if (status_1 && status_1 >= 300 && statusText && statusText.toLowerCase() !== 'ok') {
903
- request.reason = 'failed';
904
- request.detail = "request is failed, status: ".concat(status_1, ", statusText: ").concat(statusText);
905
- }
906
- else ;
907
- if (request.reason) {
908
- if (!tempUrlInfo[responseURL_1]) {
909
- tempUrlInfo[responseURL_1] = true;
910
- setTimeout(function () {
911
- delete tempUrlInfo[responseURL_1];
912
- }, 10);
913
- setReportValue('error', null);
914
- report(__assign(__assign({}, getReport()), {
915
- page: location.href,
916
- originPage: location.href,
917
- type: 'request',
918
- req: request
919
- }));
920
- }
921
- }
922
- }
923
- // 当前请求成功后就在数组中移除掉
924
- timeRecordArray.splice(i, 1);
925
- };
926
- for (var i = 0; i < timeRecordArrayCopy.length; i++) {
927
- _loop_1(i);
928
- }
929
- });
930
- };
931
- var hackFetch = function (XMLHttpRequestTimeout) {
932
- XMLHttpRequestTimeout = typeof XMLHttpRequestTimeout === 'number' ? XMLHttpRequestTimeout : 1000;
933
- if (typeof window.fetch === 'function') {
934
- var __fetch__1 = window.fetch;
935
- window.__fetch__ = __fetch__1;
936
- window.fetch = function (t) {
937
- var args = [];
938
- for (var _i = 1; _i < arguments.length; _i++) {
939
- args[_i - 1] = arguments[_i];
940
- }
941
- var begin = Date.now();
942
- //禁用数组的扩展运算符,否则portal的生产环境会报Uncaught TypeError: Object(...) is not a function,
943
- //编译后的__spreadArray函数有问题,而这个函数来自于tslib.具体报错原因不知.
944
- //其他平台使用数组的扩展运算符没有问题,比如前端监控平台的管理界面
945
- var params = [].concat(t).concat(args);
946
- return __fetch__1
947
- .apply(window, params)
948
- .then(function (res) {
949
- var response = res.clone();
950
- var headers = response.headers;
951
- if (headers && typeof headers.get === 'function') {
952
- var ct = headers.get('content-type');
953
- if (ct && !/(text)|(json)/.test(ct)) {
954
- return res;
955
- }
956
- }
957
- var loadTime = Date.now() - begin;
958
- response
959
- .text()
960
- .then(function (result) {
961
- var url = response.url, status = response.status, statusText = response.statusText, ok = response.ok;
962
- var request = {
963
- requestType: 'fetch',
964
- responseURL: url,
965
- status: status,
966
- loadTime: loadTime,
967
- statusText: statusText,
968
- reason: '',
969
- detail: ''
970
- };
971
- if (!ok || status >= 300) {
972
- request.reason = 'failed';
973
- request.detail = "request is failed, status: ".concat(status, ", statusText: ").concat(statusText);
974
- }
975
- else if (loadTime > XMLHttpRequestTimeout) {
976
- request.reason = 'slow';
977
- request.detail = "request is too slow, XMLHttpRequestTimeout: ".concat(XMLHttpRequestTimeout, ", result: ").concat(result === null || result === void 0 ? void 0 : result.slice(0, 500));
978
- }
979
- if (request.reason) {
980
- if (!tempUrlInfo[url]) {
981
- tempUrlInfo[url] = true;
982
- setTimeout(function () {
983
- delete tempUrlInfo[url];
984
- }, 10);
985
- setReportValue('error', null);
986
- report(__assign(__assign({}, getReport()), {
987
- page: location.href,
988
- originPage: location.href,
989
- type: 'request',
990
- req: request
991
- }));
992
- }
993
- }
994
- })["catch"](function (err) {
995
- // eslint-disable-next-line no-console
996
- console.log('hackFetch response.text() err', err);
997
- });
998
- return res;
999
- })["catch"](function (err) {
1000
- // eslint-disable-next-line no-console
1001
- console.log('hackFetch err', err);
1002
- });
1003
- };
1004
- }
698
+ /** 上报数据映射 */
699
+ var reportMap = {
700
+ /** SDK 版本 */
701
+ version: '_v',
702
+ /** 项目ID */
703
+ projectID: 'pid',
704
+ /** location.host */
705
+ host: 'host',
706
+ /** 用户uuid */
707
+ uuid: '_id',
708
+ /** 上报日志的时间戳 */
709
+ time: 'time',
710
+ /** 源页面 */
711
+ originPage: 'o',
712
+ /** 当前页面 */
713
+ page: 'p',
714
+ /** 自定义payload */
715
+ customPayload: 'cP',
716
+ /** 信息类型 */
717
+ type: 't',
718
+ /** viewport的宽高 */
719
+ viewport: 'vp',
720
+ /** 屏幕宽高 */
721
+ screen: 'sc',
722
+ /** 页面title */
723
+ pageTitle: 'pt',
724
+ /** 页面referrer */
725
+ referrer: 'ref',
726
+ /** 页面编码 */
727
+ charset: 'char',
728
+ /** language */
729
+ language: 'lan',
730
+ /** vendor, 浏览器厂商 */
731
+ navigatorVendor: 'nV',
732
+ /** 网络连接类型 */
733
+ connectionType: 'cT',
734
+ /** 浏览器名称 */
735
+ browserName: 'bN',
736
+ /** 浏览器版本 */
737
+ browserVersion: 'bV',
738
+ /** 浏览器引擎名称 */
739
+ engineName: 'enN',
740
+ /** 浏览器引擎版本 */
741
+ engineVersion: 'enV',
742
+ /** 操作系统名称 */
743
+ osName: 'osN',
744
+ /** 操作系统版本 */
745
+ osVersion: 'osV',
746
+ pv: {
747
+ /** 停留时间 */
748
+ stayTime: 'sT'
749
+ },
750
+ lag: {
751
+ /** 页面卡顿时间 */
752
+ lagTime: 'lT'
753
+ },
754
+ error: {
755
+ /** 子类型 */
756
+ subType: 'suT',
757
+ /** 错误行 */
758
+ line: 'line',
759
+ /** 错误列 */
760
+ column: 'col',
761
+ /** 错误信息 */
762
+ message: 'msg',
763
+ /** 错误文件 */
764
+ filename: 'fil',
765
+ /** 错误函数名 */
766
+ functionName: 'fn',
767
+ /** 错误栈信息 */
768
+ stack: '_st',
769
+ /** 错误帧 */
770
+ stackFrame: '_sf',
771
+ /** 视频录制数据 */
772
+ events: 'evt',
773
+ /** 页面截图 */
774
+ picture: 'pic'
775
+ },
776
+ ua: {
777
+ /** 子类型 */
778
+ subType: 'suT',
779
+ /** x轴坐标 */
780
+ x: 'x',
781
+ /** y轴坐标 */
782
+ y: 'y',
783
+ /** 元素路径 */
784
+ path: 'path'
785
+ },
786
+ /** 性能数据 */
787
+ perf: {
788
+ navigation: {
789
+ /**
790
+ *一个无符号短整型,表示是如何导航到这个页面的。可能的值如下:
791
+ TYPE_NAVIGATE (0)
792
+ 当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在url中直接输入地址,type值为0
793
+ TYPE_RELOAD (1)
794
+ 点击刷新页面按钮或者通过Location.reload()方法显示的页面,type值为1
795
+ The page was accessed by clicking the Reload button or via the Location.reload() method.
796
+ TYPE_BACK_FORWARD (2)
797
+ 页面通过历史记录和前进后退访问时。type值为2
798
+ TYPE_RESERVED (255)
799
+ 任何其他方式,type值为255
800
+ */
801
+ type: 't',
802
+ /** 无符号短整型,表示在到达这个页面之前重定向了多少次。 */
803
+ redirectCount: 'rc'
804
+ },
805
+ memory: {
806
+ /** 上下文内可用堆的最大体积,以字节计算。 */
807
+ jsHeapSizeLimit: 'hsl',
808
+ /** 已分配的堆体积,以字节计算。 */
809
+ totalJSHeapSize: 'ths',
810
+ /** 当前 JS 堆活跃段(segment)的体积,以字节计算。 */
811
+ usedJSHeapSize: 'uhs'
812
+ },
813
+ /** 返回性能测量开始时的时间的高精度时间戳。 */
814
+ timeOrigin: 'to',
815
+ timing: {
816
+ /** 是一个无符号long long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。 */
817
+ connectEnd: 'ce',
818
+ /** 是一个无符号long long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值 */
819
+ connectStart: 'cs',
820
+ /** 是一个无符号long long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange (en-US) 被触发时的Unix毫秒时间戳。 */
821
+ domComplete: 'dc',
822
+ /** 是一个无符号long long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。 */
823
+ domContentLoadedEventEnd: 'scLee',
824
+ /** 是一个无符号long long 型的毫秒数,返回当解析器发送DOMContentLoaded (en-US) 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。 */
825
+ domContentLoadedEventStart: 'dcLes',
826
+ /** 是一个无符号long long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、
827
+ * 相应的readystatechange (en-US)事件触发时)的Unix毫秒时间戳。 */
828
+ domInteractive: 'di',
829
+ /** 是一个无符号long long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange (en-US)事件触发时)的Unix毫秒时间戳。 */
830
+ domLoading: 'dl',
831
+ /** 是一个无符号long long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection)
832
+ * 或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。 */
833
+ domainLookupEnd: 'dle',
834
+ /** 是一个无符号long long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection)
835
+ * 或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。 */
836
+ domainLookupStart: 'dls',
837
+ /** 是一个无符号long long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。 */
838
+ fetchStart: 'fs',
839
+ /** 是一个无符号long long 型的毫秒数,返回当load (en-US)事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0. */
840
+ loadEventEnd: 'lee',
841
+ /** 是一个无符号long long 型的毫秒数,返回该文档下,load (en-US)事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。 */
842
+ loadEventStart: 'les',
843
+ /** 是一个无符号long long 型的毫秒数,表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同 */
844
+ navigationStart: 'ns',
845
+ /** 是一个无符号long long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0. */
846
+ redirectEnd: 're',
847
+ /** 是一个无符号long long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0. */
848
+ redirectStart: 'redS',
849
+ /** 是一个无符号long long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。 */
850
+ requestStart: 'reqS',
851
+ /** 是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。 */
852
+ responseEnd: 'resE',
853
+ /** 是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。 */
854
+ responseStart: 'resS',
855
+ /** 是一个无符号long long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。 */
856
+ secureConnectionStart: 'scs',
857
+ /** 是一个无符号long long 型的毫秒数,表征了unload (en-US)事件处理完成时的UNIX时间戳。如果没有上一个文档,
858
+ * or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0. */
859
+ unloadEventEnd: 'uee',
860
+ /** 是一个无符号long long 型的毫秒数,表征了unload (en-US)事件抛出时的UNIX时间戳。如果没有上一个文档,
861
+ * or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0. */
862
+ unloadEventStart: 'ees'
863
+ }
864
+ },
865
+ /** api请求相关 */
866
+ req: {
867
+ /** 请求类型,xhr或fetch */
868
+ requestType: 'rT',
869
+ /** 请求地址 */
870
+ responseURL: 'url',
871
+ /** 请求结果 */
872
+ status: 'stat',
873
+ /** 请求耗时 */
874
+ loadTime: 'lT',
875
+ /** 请求状态 */
876
+ statusText: 'sT',
877
+ /** 上报原因, slow或failed */
878
+ reason: 're',
879
+ /** 上报时带上的详细信息 */
880
+ detail: 'de',
881
+ /** 上报时带上的请求参数 */
882
+ requestData: 'rD',
883
+ /** 上报时带上的请求方法 */
884
+ method: 'rM'
885
+ },
886
+ vD: {
887
+ /** 埋点名称 */
888
+ trackName: 'tN',
889
+ /** 埋点参数 */
890
+ params: 'params'
891
+ },
892
+ manualReport: 'mR',
893
+ lifeCycleId: 'li',
894
+ sessionId: 'sI'
895
+ };
896
+ var xhrFunc = function (filename, userId, asyncUpdateId, file, res, callback, rest) {
897
+ var formData = new FormData();
898
+ for (var i in rest) {
899
+ if (Object.prototype.hasOwnProperty.call(rest, i)) {
900
+ formData.append(i, rest[i]);
901
+ }
902
+ }
903
+ formData.append('filename', filename);
904
+ formData.append('asyncUpdateId', asyncUpdateId); //用于上传到oss后异步更新到db的id
905
+ formData.append('userId', userId);
906
+ formData.append('files', file);
907
+ var xhr = new XMLHttpRequest();
908
+ xhr.addEventListener('error', function () { return res(); }, false);
909
+ xhr.open('POST', Config.reportUrl);
910
+ xhr.onreadystatechange = function () {
911
+ var _a;
912
+ if (xhr.readyState === 4) {
913
+ if (xhr.status === 200) {
914
+ try {
915
+ var response = xhr.responseText;
916
+ var resp = JSON.parse(response);
917
+ callback((_a = resp.result) === null || _a === void 0 ? void 0 : _a.response);
918
+ }
919
+ catch (_err) {
920
+ }
921
+ res();
922
+ }
923
+ else {
924
+ res();
925
+ }
926
+ }
927
+ };
928
+ xhr.send(formData);
1005
929
  };
930
+ var ERROR_MESSAGE_MAP = {};
931
+ function reportFunc(data) {
932
+ var _a, _b, _c;
933
+ var payload = convertToSchema(data, reportMap);
934
+ payload.time = new Date().getTime(); // 设置上报时间
935
+ var picturePromise = Promise.resolve();
936
+ var hasErrorEvent = false;
937
+ var filename = uuid.v4();
938
+ if (data.type === 'error') {
939
+ var message_1 = ((_a = data.error) === null || _a === void 0 ? void 0 : _a.message) || 'default';
940
+ if (((_b = data.error) === null || _b === void 0 ? void 0 : _b.subType) === 'resource') {
941
+ message_1 = 'resource';
942
+ }
943
+ if (ERROR_MESSAGE_MAP[message_1]) ;
944
+ else {
945
+ ERROR_MESSAGE_MAP[message_1] = true;
946
+ setTimeout(function () {
947
+ //节流效果,每隔3秒上报一次重复的错误录像和错误截图
948
+ delete ERROR_MESSAGE_MAP[message_1];
949
+ }, 3000);
950
+ var p_1 = payload.p, pid_1 = payload.pid, host_1 = payload.host, bN_1 = payload.bN, pt_1 = payload.pt;
951
+ if ((_c = data.error) === null || _c === void 0 ? void 0 : _c.events) {
952
+ hasErrorEvent = true;
953
+ var errorEvent_1 = data.error.events;
954
+ var delayTime = Math.ceil(Math.random() * 10000);
955
+ setTimeout(function () {
956
+ var blob = new Blob([errorEvent_1], { type: 'text/json' });
957
+ return new Promise(function (res) {
958
+ xhrFunc("".concat(filename, ".json"), data.uuid, filename, blob, res, function () { }, { p: p_1, pid: pid_1, host: host_1, bN: bN_1, pt: pt_1 });
959
+ });
960
+ }, delayTime < 3500 ? 3500 : delayTime);
961
+ }
962
+ picturePromise = getFullScreenShoot(filename)
963
+ .then(function (file) {
964
+ return new Promise(function (res) {
965
+ xhrFunc(file.name, data.uuid, filename, file, res, function (result) {
966
+ payload.error.pic = result;
967
+ }, { p: p_1, pid: pid_1, host: host_1, bN: bN_1, pt: pt_1 });
968
+ });
969
+ })["catch"](function (err) {
970
+ payload.error.picError = (err === null || err === void 0 ? void 0 : err.stack) || (err === null || err === void 0 ? void 0 : err.toString());
971
+ });
972
+ }
973
+ }
974
+ return picturePromise
975
+ .then(function () {
976
+ var _a;
977
+ if ((_a = payload.error) === null || _a === void 0 ? void 0 : _a.evt)
978
+ delete payload.error.evt;
979
+ if (window && window.navigator && typeof window.navigator.sendBeacon === 'function') {
980
+ var formData_1 = new FormData();
981
+ Object.keys(payload).forEach(function (key) {
982
+ var value = payload[key];
983
+ if (value !== null && value !== undefined) {
984
+ if (typeof value === 'object') {
985
+ value = JSON.stringify(value);
986
+ }
987
+ formData_1.append(key, value);
988
+ }
989
+ });
990
+ //eventErrorFilename,将错误录像的文件名附在错误上报的埋点里,便于将错误录像上报回调地址异步的更新到db
991
+ if (hasErrorEvent)
992
+ formData_1.append('eef', filename);
993
+ window.navigator.sendBeacon(Config.reportUrl, formData_1);
994
+ }
995
+ else {
996
+ if (hasErrorEvent)
997
+ payload.eef = filename;
998
+ new Image().src = "".concat(Config.reportUrl, "?").concat(serialize(payload));
999
+ }
1000
+ })["catch"](function (_err) {
1001
+ //
1002
+ });
1003
+ }
1004
+ // 上报
1005
+ function report(data) {
1006
+ return new Promise(function (res) {
1007
+ //在帧的空闲时间上报
1008
+ var dataCopy = JSON.parse(JSON.stringify(data));
1009
+ if (typeof window.requestIdleCallback === 'function') {
1010
+ window.requestIdleCallback(function () { return reportFunc(dataCopy).then(res); }, { timeout: 2000 });
1011
+ }
1012
+ else {
1013
+ setTimeout(function () { return reportFunc(dataCopy).then(res); }, 0);
1014
+ }
1015
+ });
1016
+ }
1017
+ /**
1018
+ * @description 将数据转换成最终提交的数据,主要目的是简化数据的key的长度,从而降低荷载大小
1019
+ * @param data 要转换的数据
1020
+ * @param map 要转换的数据的字段映射
1021
+ * @param redundancyData 冗余数据
1022
+ */
1023
+ function convertToSchema(data, map, redundancyData) {
1024
+ var reportData = {};
1025
+ if (redundancyData) {
1026
+ map = __assign(__assign({}, map), redundancyData);
1027
+ }
1028
+ for (var _i = 0, _a = Object.entries(map); _i < _a.length; _i++) {
1029
+ var result = _a[_i];
1030
+ var _b = result, key = _b[0], mapKey = _b[1];
1031
+ if (key === 'manualReport') {
1032
+ reportData[mapKey] = data[key];
1033
+ }
1034
+ else {
1035
+ //字段值为数组的场景,直接转换
1036
+ if (typeof data[key] === 'object' && !Array.isArray(data[key])) {
1037
+ if (data[key]) {
1038
+ if (redundancyData && Object.prototype.hasOwnProperty.call(redundancyData, key)) {
1039
+ reportData[key] = data[key];
1040
+ }
1041
+ else {
1042
+ reportData[key] = convertToSchema(data[key], map[key]);
1043
+ }
1044
+ }
1045
+ else {
1046
+ reportData[key] = data[key];
1047
+ }
1048
+ }
1049
+ else {
1050
+ // 排除undefined
1051
+ if (data[key] !== undefined) {
1052
+ reportData[mapKey] = data[key];
1053
+ }
1054
+ }
1055
+ }
1056
+ }
1057
+ return reportData;
1058
+ }
1006
1059
 
1007
- /**
1008
- * @description 点击事件触发后的操作
1009
- */
1010
- function handleClick(event) {
1011
- var target = event.target;
1012
- if (target.nodeName === 'INPUT' || target.nodeName === 'TEXTAREA') {
1013
- return;
1014
- }
1015
- var path = getElmPath(target);
1016
- if (path) {
1017
- setReportValue('type', 'ua');
1018
- setReportValue('ua', {
1019
- subType: 'ui.click',
1020
- x: event.x,
1021
- y: event.y,
1022
- path: path
1023
- });
1024
- report(getReport());
1025
- }
1026
- }
1027
- /**
1028
- * @description 点击触发失焦后的操作
1029
- */
1030
- function handleBlur(event) {
1031
- var target = event.target;
1032
- if (target.nodeName !== 'INPUT' && target.nodeName !== 'TEXTAREA') {
1033
- return;
1034
- }
1035
- var path = getElmPath(target);
1036
- if (path) {
1037
- setReportValue('type', 'ua');
1038
- setReportValue('ua', {
1039
- subType: 'ui.blur',
1040
- x: event.x,
1041
- y: event.y,
1042
- path: path,
1043
- value: target.value
1044
- });
1045
- report(getReport());
1046
- }
1047
- }
1048
- /**
1049
- * @description 滚动的操作
1050
- */
1051
- var timeout$1;
1052
- function handleScroll(_event) {
1053
- clearTimeout(timeout$1);
1054
- timeout$1 = setTimeout(function () {
1055
- setReportValue('type', 'ua');
1056
- setReportValue('ua', {
1057
- subType: 'ui.scroll'
1058
- });
1059
- report(getReport());
1060
- }, 1000);
1060
+ /** 路由栈数组,存储路由变化信息 */
1061
+ var routerStack = [];
1062
+ /** 路由触发的时间 */
1063
+ var time = 0;
1064
+ /**
1065
+ * @description 派发pushState, replaceState 的监听
1066
+ * @param type
1067
+ */
1068
+ function _history(type) {
1069
+ var origin = history[type];
1070
+ return function () {
1071
+ // @ts-ignore
1072
+ // eslint-disable-next-line prefer-rest-params
1073
+ var r = origin.apply(this, arguments);
1074
+ var e = new Event(type);
1075
+ // @ts-ignore
1076
+ // eslint-disable-next-line prefer-rest-params
1077
+ e.arguments = arguments;
1078
+ window.dispatchEvent(e);
1079
+ return r;
1080
+ };
1081
+ }
1082
+ /**
1083
+ * @description 处理history变化
1084
+ * @param e history事件
1085
+ */
1086
+ function handleHistoryChange(__e) {
1087
+ visualTrackFunc();
1088
+ setReport({ type: 'pv' });
1089
+ }
1090
+ /**
1091
+ * 设置信息并触发上报
1092
+ */
1093
+ function setReport(_a) {
1094
+ var type = _a.type, lagTime = _a.lagTime;
1095
+ var pageUrl = location.href;
1096
+ /**
1097
+ * 此判断的逻辑是因为spa且hash模式下,路由搜索参数更改也会触发handleHistoryChange事件
1098
+ * 所以路由栈中的信息只保存无搜索参数的地址
1099
+ */
1100
+ if (Config.hash && Config.spa) {
1101
+ var currentUrlSplit = location.href.split('#');
1102
+ var currentUrlPostfix = '';
1103
+ if (currentUrlSplit[1]) {
1104
+ var index = currentUrlSplit[1].indexOf('?');
1105
+ if (index > -1) {
1106
+ currentUrlPostfix = currentUrlSplit[1].substring(0, index);
1107
+ }
1108
+ else {
1109
+ currentUrlPostfix = currentUrlSplit[1];
1110
+ }
1111
+ }
1112
+ var currentUrlResult = "".concat(currentUrlSplit[0], "#").concat(currentUrlPostfix);
1113
+ pageUrl = currentUrlResult;
1114
+ }
1115
+ var _b = setRouteStack(pageUrl), originPage = _b.originPage, page = _b.page;
1116
+ // 愿页面等于当前页面,说明无路由变更。只有spa且hash模式下才会出现
1117
+ if (originPage === page && type === 'pv') {
1118
+ return;
1119
+ }
1120
+ var currentTime = new Date().getTime();
1121
+ // 页面停留时间
1122
+ var stayTime = currentTime - time;
1123
+ // 设置触发路由变化的时间
1124
+ time = currentTime;
1125
+ // 设置信息
1126
+ setReportValue('originPage', originPage);
1127
+ setReportValue('page', page);
1128
+ if (type === 'pv') {
1129
+ setReportValue('type', 'pv');
1130
+ setReportValue('pv', { stayTime: stayTime });
1131
+ }
1132
+ else if (type === 'lag') {
1133
+ setReportValue('type', 'lag');
1134
+ setReportValue('lag', { lagTime: lagTime });
1135
+ }
1136
+ // 上报数据
1137
+ report(getReport());
1138
+ }
1139
+ function setPVTime() {
1140
+ time = new Date().getTime();
1141
+ }
1142
+ /**
1143
+ * @description 添加路由栈信息
1144
+ * @param page 页面信息
1145
+ */
1146
+ function setRouteStack(page) {
1147
+ if (typeof page === 'string') {
1148
+ routerStack.push(page);
1149
+ }
1150
+ else {
1151
+ routerStack = routerStack.concat(page);
1152
+ }
1153
+ // 路由栈只需保留两个来作为源页面和当前页面
1154
+ routerStack = routerStack.slice(-2);
1155
+ return {
1156
+ originPage: routerStack[0],
1157
+ page: routerStack[1] || routerStack[0]
1158
+ };
1159
+ }
1160
+ function handlePageLag(lagTime) {
1161
+ // console.log('检测到页面卡顿', lagTime);
1162
+ setReport({ type: 'lag', lagTime: lagTime });
1061
1163
  }
1062
1164
 
1063
- var parser = new uaParserJs.UAParser();
1064
- /**
1065
- * @description 获取uuid
1066
- */
1067
- function getUid() {
1068
- var uid = localStorage.getItem(monitorTrackId) || '';
1069
- if (!uid) {
1070
- uid = uuid.v4();
1071
- localStorage.setItem(monitorTrackId, uid);
1072
- }
1073
- return uid;
1074
- }
1075
- /**
1076
- * @description 获取session id
1077
- */
1078
- function getSessionId() {
1079
- var sessionId = sessionStorage.getItem(monitorTrackSessionId);
1080
- if (sessionId) {
1081
- return sessionId;
1082
- }
1083
- else {
1084
- var id = uuid.v4();
1085
- sessionStorage.setItem(monitorTrackSessionId, id);
1086
- return id;
1087
- }
1088
- }
1089
- /**
1090
- * @description 获取浏览器信息
1091
- */
1092
- function getNavigator() {
1093
- var _a;
1094
- var uaResult = parser.getResult();
1095
- return {
1096
- language: navigator.language,
1097
- navigatorVendor: navigator.vendor,
1098
- connectionType: ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.connection) === null || _a === void 0 ? void 0 : _a.effectiveType) || '2g',
1099
- browserName: uaResult.browser.name || '',
1100
- browserVersion: uaResult.browser.version || '',
1101
- engineName: uaResult.engine.name || '',
1102
- engineVersion: uaResult.engine.version || '',
1103
- osName: uaResult.os.name || '',
1104
- osVersion: uaResult.os.version || ''
1105
- };
1106
- }
1107
- /**
1108
- * @description 获取viewport的宽高
1109
- */
1110
- function getViewport() {
1111
- var w = document.documentElement.clientWidth || document.body.clientWidth;
1112
- var h = document.documentElement.clientHeight || document.body.clientHeight;
1113
- return "".concat(w, " x ").concat(h);
1114
- }
1115
- /**
1116
- * @description 获取元素路径,最多保留5层
1117
- * @param e
1118
- */
1119
- var getElmPath = function (target) {
1120
- if (!target || target.nodeType !== 1) {
1121
- return '';
1122
- }
1123
- var ret = [];
1124
- // 层数,最多5层
1125
- var deepLength = 0;
1126
- // 元素
1127
- var elm = '';
1128
- if (typeof target.innerText === 'string') {
1129
- ret.push("(".concat(target.innerText.substr(0, 50), ")"));
1130
- }
1131
- for (var t = target || null; t && deepLength++ < 5 && !((elm = normalTarget(t)) === 'html');) {
1132
- // eslint-disable-next-line no-sequences
1133
- ret.push(elm), (t = t.parentNode);
1134
- }
1135
- return ret.reverse().join(' > ');
1136
- };
1137
- /**
1138
- * @description 处理html node
1139
- * @param e
1140
- */
1141
- var normalTarget = function (target) {
1142
- var t, n, r, a, i;
1143
- var o = [];
1144
- if (!target || !target.tagName) {
1145
- return '';
1146
- }
1147
- o.push(target.tagName.toLowerCase());
1148
- if (target.id) {
1149
- o.push('#'.concat(target.id));
1150
- }
1151
- if ((t = target.className) && Object.prototype.toString.call(t) === '[object String]') {
1152
- for (n = t.split(/\s+/), i = 0; i < n.length; i++) {
1153
- // className包含active的不加入路径
1154
- if (n[i].indexOf('active') < 0)
1155
- o.push('.'.concat(n[i]));
1156
- }
1157
- }
1158
- var s = ['type', 'name', 'title', 'alt'];
1159
- for (i = 0; i < s.length; i++) {
1160
- r = s[i];
1161
- if ((a = target.getAttribute(r))) {
1162
- o.push('['.concat(r, '="').concat(a, '"]'));
1163
- }
1164
- }
1165
- return o.join('');
1166
- };
1167
- /**
1168
- * @description 序列化对象
1169
- * @param obj
1170
- */
1171
- function serialize(obj) {
1172
- var str = [];
1173
- for (var p in obj) {
1174
- if (Object.prototype.hasOwnProperty.call(obj, p) && typeof obj[p] !== 'undefined') {
1175
- var value = typeof obj[p] === 'object' ? JSON.stringify(obj[p]) : obj[p];
1176
- str.push(encodeURIComponent(p) + '=' + encodeURIComponent(value));
1177
- }
1178
- }
1179
- return str.join('&');
1180
- }
1181
- /**
1182
- * @description 注册事件监听
1183
- * @param event 事件
1184
- * @param fn 回调方法
1185
- */
1186
- var on = function (event, fn) {
1187
- if (window.addEventListener) {
1188
- window.addEventListener(event, fn, true);
1189
- }
1190
- else {
1191
- window.attachEvent("on".concat(event), fn);
1192
- }
1193
- };
1194
- /**
1195
- * @description 移除事件监听
1196
- * @param event 事件
1197
- * @param fn 回调方法
1198
- */
1199
- var off = function (event, fn) {
1200
- if (window.removeEventListener) {
1201
- return window.removeEventListener(event, fn);
1202
- }
1203
- else {
1204
- return window.detachEvent("on".concat(event), fn);
1205
- }
1206
- };
1207
- var formatTrackElementXPath = function (xPath) {
1208
- var result = [];
1209
- var array = xPath.split('/');
1210
- array.forEach(function (item) {
1211
- if (!['svg', 'path', 'g', 'image', 'text', 'line', 'rect', 'polygon', 'circle', 'ellipse'].some(function (child) {
1212
- if (item.toLowerCase().split('[')[0] === child) {
1213
- result.push("/*[name()=\"".concat(child, "\"]"));
1214
- return true;
1215
- }
1216
- else {
1217
- return false;
1218
- }
1219
- })) {
1220
- result.push(item);
1221
- }
1222
- });
1223
- return result.join('/');
1224
- };
1225
- var formatElementXPath = function (info) {
1226
- var _a;
1227
- var elementXPathValue = '';
1228
- var elementXPath = info.elementXPath;
1229
- if (elementXPath) {
1230
- var xpathElement = document.evaluate(formatTrackElementXPath(elementXPath), document).iterateNext();
1231
- elementXPathValue = xpathElement ? (_a = (xpathElement.textContent || xpathElement.value)) === null || _a === void 0 ? void 0 : _a.trim() : '';
1232
- }
1233
- return {
1234
- elementXPath: elementXPath,
1235
- elementXPathValue: elementXPathValue
1236
- };
1237
- };
1238
- var existTrackListenEvent = {};
1239
- var elements = [];
1240
- var timeout;
1241
- var visualTrackFunc = function () {
1242
- if (Config.enableVisualTrack) {
1243
- if (timeout)
1244
- clearTimeout(timeout);
1245
- timeout = setTimeout(function () {
1246
- timeout = null;
1247
- var url = location.href.split('?')[0];
1248
- axios__default["default"]
1249
- .get("".concat(Config.reportUrl.replace('/s/r', ''), "/visual/get_data_list?url=").concat(encodeURIComponent(url)))
1250
- .then(function (res) {
1251
- var _a, _b;
1252
- var listData = (_b = (_a = res.data.result) === null || _a === void 0 ? void 0 : _a.result) === null || _b === void 0 ? void 0 : _b.dataList;
1253
- if (Array.isArray(listData)) {
1254
- try {
1255
- listData.forEach(function (item) {
1256
- if (item.trackType === 'mount') {
1257
- visualReportEvent(item);
1258
- }
1259
- else {
1260
- var element = document
1261
- .evaluate(formatTrackElementXPath(item.trackElementXPath), document)
1262
- .iterateNext();
1263
- var key = url + item.trackElementXPath;
1264
- if (element) {
1265
- if (!existTrackListenEvent[key]) {
1266
- existTrackListenEvent[key] = true;
1267
- elements.push(element);
1268
- //@ts-ignore
1269
- element.setAttribute('visual-track-data-attr', JSON.stringify(item));
1270
- element.addEventListener('click', clickEventFunc);
1271
- }
1272
- }
1273
- else {
1274
- var func_1 = function () {
1275
- var _a;
1276
- var _b = getNavigator(), browserName = _b.browserName, browserVersion = _b.browserVersion;
1277
- var payload = {
1278
- projectID: Config.projectID,
1279
- version: version,
1280
- url: url,
1281
- trackElementXPath: item.trackElementXPath,
1282
- trackNameInfo: item.trackNameInfo,
1283
- params: item.params,
1284
- customPayload: Config.customPayload,
1285
- type: 'visual_track_xpath_not_found',
1286
- browser: "".concat(browserName, " - ").concat(browserVersion)
1287
- };
1288
- if (typeof ((_a = window.navigator) === null || _a === void 0 ? void 0 : _a.sendBeacon) === 'function') {
1289
- var formData = new FormData();
1290
- for (var i in payload) {
1291
- if (Object.prototype.hasOwnProperty.call(payload, i)) {
1292
- var value = payload[i];
1293
- formData.append(i, value && typeof value === 'object' ? JSON.stringify(value) : value);
1294
- }
1295
- }
1296
- window.navigator.sendBeacon(Config.reportUrl, formData);
1297
- }
1298
- else {
1299
- new Image().src = "".concat(Config.reportUrl, "?").concat(serialize(payload));
1300
- }
1301
- };
1302
- if (typeof window.requestIdleCallback === 'function') {
1303
- window.requestIdleCallback(function () { return func_1(); }, { timeout: 2000 });
1304
- }
1305
- else {
1306
- setTimeout(function () { return func_1(); }, 0);
1307
- }
1308
- }
1309
- }
1310
- });
1311
- }
1312
- catch (err) {
1313
- // eslint-disable-next-line no-console
1314
- console.log('visual track evaluate err', err);
1315
- }
1316
- }
1317
- });
1318
- }, 1000);
1319
- }
1320
- };
1321
- var visualReportEvent = function (item) {
1322
- var _a;
1323
- var _b = formatElementXPath(item.trackNameInfo), elementXPath = _b.elementXPath, elementXPathValue = _b.elementXPathValue;
1324
- var visualTrackData = {
1325
- trackType: item.trackType,
1326
- trackName: elementXPath ? elementXPathValue : item.trackNameInfo.name,
1327
- params: (_a = item.params) === null || _a === void 0 ? void 0 : _a.map(function (child) {
1328
- var _a = formatElementXPath(child), elementXPath = _a.elementXPath, elementXPathValue = _a.elementXPathValue;
1329
- return {
1330
- name: child.name,
1331
- value: elementXPath ? elementXPathValue : child.value
1332
- };
1333
- })
1334
- };
1335
- setReportValue('type', 'visual');
1336
- setReportValue('vD', visualTrackData);
1337
- report(getReport());
1338
- setReportValue('vD', undefined);
1339
- };
1340
- var clickEventFunc = function (e) {
1341
- //@ts-ignore
1342
- var item = JSON.parse(e.target.getAttribute('visual-track-data-attr'));
1343
- visualReportEvent(item);
1344
- };
1345
- var initWindowObjectFunction = function () {
1346
- if (typeof window.manualReportTrackFunc === 'undefined') {
1347
- window.manualReportTrackFunc = function (data) {
1348
- if (Object.prototype.toString.call(data) !== '[object Object]') {
1349
- // eslint-disable-next-line no-console
1350
- console.warn('manualReportTrackFunc参数必须是一个对象!');
1351
- }
1352
- else {
1353
- setReportValue('type', 'manual');
1354
- setReportValue('manualReport', data);
1355
- report(getReport());
1356
- setReportValue('manualReport', undefined);
1357
- }
1358
- };
1359
- }
1360
- if (typeof window.getFullScreenShootFunc === 'undefined') {
1361
- //下载页面截图
1362
- window.getFullScreenShootFunc = function (filename) { return __awaiter(void 0, void 0, void 0, function () {
1363
- var file, url, tagA;
1364
- return __generator(this, function (_a) {
1365
- switch (_a.label) {
1366
- case 0: return [4 /*yield*/, getFullScreenShoot(filename)];
1367
- case 1:
1368
- file = _a.sent();
1369
- url = window.URL.createObjectURL(file);
1370
- tagA = document.createElement('a');
1371
- tagA.setAttribute('href', url);
1372
- tagA.setAttribute('download', file.name);
1373
- tagA.setAttribute('target', '_blank');
1374
- document.body.appendChild(tagA);
1375
- tagA.click();
1376
- document.body.removeChild(tagA);
1377
- return [2 /*return*/];
1378
- }
1379
- });
1380
- }); };
1381
- }
1382
- if (typeof window.getRRWebUserEventsCaptureFunc === 'undefined') {
1383
- window.getRRWebUserEventsCaptureFunc = function () {
1384
- setReportValue('type', 'error');
1385
- setReportValue('error', {
1386
- subType: 'manual',
1387
- message: 'manual trigger',
1388
- events: getUserEvents(true)
1389
- });
1390
- report(getReport());
1391
- };
1392
- }
1393
- };
1394
- var prewHref = '';
1395
- var handleLocationChange = function (e) {
1396
- var curHref = location.href;
1397
- // href中`?`后面的值改变,不触发后续动作
1398
- if (prewHref.split('?')[0] === curHref.split('?')[0]) {
1399
- return;
1400
- }
1401
- prewHref = curHref;
1402
- handleHistoryChange();
1403
- elements.forEach(function (element) {
1404
- element === null || element === void 0 ? void 0 : element.removeEventListener('click', clickEventFunc);
1405
- });
1406
- //清空可视化埋点之前的数据
1407
- existTrackListenEvent = {};
1408
- elements.length = 0;
1409
- visualTrackFunc();
1165
+ // 上报数据
1166
+ var Report = {
1167
+ uuid: '',
1168
+ projectID: '',
1169
+ host: '',
1170
+ originPage: '',
1171
+ page: '',
1172
+ time: 0,
1173
+ browserName: '',
1174
+ browserVersion: '',
1175
+ engineName: '',
1176
+ engineVersion: '',
1177
+ language: '',
1178
+ navigatorVendor: '',
1179
+ connectionType: '2g',
1180
+ osName: '',
1181
+ osVersion: '',
1182
+ type: 'init',
1183
+ viewport: '',
1184
+ screen: '',
1185
+ version: '',
1186
+ charset: '',
1187
+ pageTitle: '',
1188
+ referrer: '',
1189
+ pv: null,
1190
+ lag: null,
1191
+ ua: null,
1192
+ error: null,
1193
+ req: null,
1194
+ dpr: 1,
1195
+ perf: null,
1196
+ vD: undefined,
1197
+ manualReport: undefined,
1198
+ lifeCycleId: uuid.v4(),
1199
+ sessionId: getSessionId()
1200
+ };
1201
+ /**
1202
+ * @description 初始化上报数据并上报
1203
+ */
1204
+ function initReport() {
1205
+ if (document.readyState === 'complete') {
1206
+ initReportFunc();
1207
+ }
1208
+ else {
1209
+ // 注意:这里不要使用window.onload,因为一个项目window.onload只能用一次,如果这里用了就会影响宿主项目的功能
1210
+ window.addEventListener('load', initReportFunc);
1211
+ }
1212
+ }
1213
+ function initReportFunc() {
1214
+ setRouteStack([location.href, location.href]);
1215
+ setPVTime();
1216
+ Report = __assign(__assign({}, getReport()), {
1217
+ page: location.href,
1218
+ originPage: location.href,
1219
+ type: 'init',
1220
+ perf: performance,
1221
+ sessionId: getSessionId(),
1222
+ lifeCycleId: uuid.v4()
1223
+ });
1224
+ report(Report);
1225
+ }
1226
+ /**
1227
+ * @description 设置Report的值
1228
+ * @param key Report key
1229
+ * @param value Report 值
1230
+ */
1231
+ function setReportValue(key, value) {
1232
+ if (Object.prototype.hasOwnProperty.call(Report, key)) {
1233
+ if (['pv', 'ua', 'error', 'request', 'lag'].includes(key)) {
1234
+ Report = __assign(__assign({}, Report), {
1235
+ pv: null,
1236
+ lag: null,
1237
+ ua: null,
1238
+ error: null,
1239
+ req: null,
1240
+ manualReport: undefined,
1241
+ perf: null
1242
+ });
1243
+ }
1244
+ Report[key] = value;
1245
+ }
1246
+ }
1247
+ /**
1248
+ * @description 获取上报数据
1249
+ */
1250
+ function getReport() {
1251
+ var nav = getNavigator();
1252
+ var viewport = getViewport();
1253
+ var uuid = localStorage.getItem('username') || getUid();
1254
+ return __assign(__assign(__assign({}, Report), nav), {
1255
+ version: version,
1256
+ projectID: Config.projectID,
1257
+ host: location.host,
1258
+ uuid: uuid,
1259
+ viewport: viewport,
1260
+ screen: "".concat(screen.width, " x ").concat(screen.height),
1261
+ pageTitle: document.title,
1262
+ referrer: document.referrer,
1263
+ charset: document.charset,
1264
+ customPayload: Config.customPayload,
1265
+ dpr: window.devicePixelRatio
1266
+ });
1267
+ }
1268
+ function ajaxEventTrigger(event) {
1269
+ var ajaxEvent = new CustomEvent(event, {
1270
+ detail: this
1271
+ });
1272
+ window.dispatchEvent(ajaxEvent);
1273
+ }
1274
+ var OldXHR = window.XMLHttpRequest;
1275
+ function newXHR() {
1276
+ var realXHR = new OldXHR();
1277
+ realXHR.addEventListener('loadstart', function () {
1278
+ ajaxEventTrigger.call(this, 'ajaxLoadStart');
1279
+ }, false);
1280
+ realXHR.addEventListener('loadend', function () {
1281
+ ajaxEventTrigger.call(this, 'ajaxLoadEnd');
1282
+ }, false);
1283
+ // 此处的捕获的异常会连日志接口也一起捕获,如果日志上报接口异常了,就会导致死循环了。
1284
+ realXHR.onerror = function (e) {
1285
+ // eslint-disable-next-line no-console
1286
+ console.warn('realXHR.onerror, e', e);
1287
+ };
1288
+ return realXHR;
1289
+ }
1290
+ /**
1291
+ * 页面接口请求监控
1292
+ */
1293
+ var tempUrlInfo = {};
1294
+ var recordXMLHttpRequestLog = function (XMLHttpRequestTimeout) {
1295
+ XMLHttpRequestTimeout = typeof XMLHttpRequestTimeout === 'number' ? XMLHttpRequestTimeout : 1000;
1296
+ var timeRecordArray = [];
1297
+ //@ts-ignore
1298
+ window.__XMLHttpRequest__ = window.XMLHttpRequest;
1299
+ //@ts-ignore
1300
+ window.XMLHttpRequest = newXHR;
1301
+ window.addEventListener('ajaxLoadStart', function (e) {
1302
+ var tempObj = {
1303
+ timeStamp: new Date().getTime(),
1304
+ event: e
1305
+ };
1306
+ timeRecordArray.push(tempObj);
1307
+ });
1308
+ window.addEventListener('ajaxLoadEnd', function () {
1309
+ var timeRecordArrayCopy = [].concat(timeRecordArray);
1310
+ var _loop_1 = function (i) {
1311
+ if (timeRecordArrayCopy[i].event.detail && timeRecordArrayCopy[i].event.detail.status > 0) {
1312
+ var currentTime = new Date().getTime();
1313
+ var _a = timeRecordArrayCopy[i].event.detail, responseURL_1 = _a.responseURL, status_1 = _a.status, statusText = _a.statusText, timeStamp = _a.timeStamp;
1314
+ var previousTime = timeStamp || timeRecordArrayCopy[i].timeStamp;
1315
+ var loadTime = currentTime - previousTime;
1316
+ var request = {
1317
+ requestType: 'xhr',
1318
+ responseURL: responseURL_1,
1319
+ status: status_1,
1320
+ loadTime: loadTime,
1321
+ statusText: statusText,
1322
+ reason: '',
1323
+ detail: '',
1324
+ requestData: '',
1325
+ method: ''
1326
+ };
1327
+ if (loadTime && loadTime > XMLHttpRequestTimeout) {
1328
+ request.reason = 'slow';
1329
+ request.detail = "request is too slow, XMLHttpRequestTimeout: ".concat(loadTime);
1330
+ }
1331
+ else if (status_1 && status_1 >= 300 && statusText && statusText.toLowerCase() !== 'ok') {
1332
+ request.reason = 'failed';
1333
+ request.detail = "request is failed, status: ".concat(status_1, ", statusText: ").concat(statusText);
1334
+ }
1335
+ else ;
1336
+ if (request.reason) {
1337
+ if (!tempUrlInfo[responseURL_1]) {
1338
+ tempUrlInfo[responseURL_1] = true;
1339
+ setTimeout(function () {
1340
+ delete tempUrlInfo[responseURL_1];
1341
+ }, 10);
1342
+ setReportValue('error', null);
1343
+ report(__assign(__assign({}, getReport()), {
1344
+ page: location.href,
1345
+ originPage: location.href,
1346
+ type: 'request',
1347
+ req: request
1348
+ }));
1349
+ }
1350
+ }
1351
+ }
1352
+ // 当前请求成功后就在数组中移除掉
1353
+ timeRecordArray.splice(i, 1);
1354
+ };
1355
+ for (var i = 0; i < timeRecordArrayCopy.length; i++) {
1356
+ _loop_1(i);
1357
+ }
1358
+ });
1359
+ };
1360
+ var hackFetch = function (XMLHttpRequestTimeout) {
1361
+ XMLHttpRequestTimeout = typeof XMLHttpRequestTimeout === 'number' ? XMLHttpRequestTimeout : 1000;
1362
+ if (typeof window.fetch === 'function') {
1363
+ var __fetch__1 = window.fetch;
1364
+ window.__fetch__ = __fetch__1;
1365
+ //@ts-ignore
1366
+ window.fetch = function (t) {
1367
+ var args = [];
1368
+ for (var _i = 1; _i < arguments.length; _i++) {
1369
+ args[_i - 1] = arguments[_i];
1370
+ }
1371
+ var begin = Date.now();
1372
+ //禁用数组的扩展运算符,否则portal的生产环境会报Uncaught TypeError: Object(...) is not a function,
1373
+ //编译后的__spreadArray函数有问题,而这个函数来自于tslib.具体报错原因不知.
1374
+ //其他平台使用数组的扩展运算符没有问题,比如前端监控平台的管理界面
1375
+ var params = [].concat(t).concat(args);
1376
+ return __fetch__1
1377
+ .apply(window, params)
1378
+ .then(function (res) {
1379
+ var response = res.clone();
1380
+ var headers = response.headers;
1381
+ if (headers && typeof headers.get === 'function') {
1382
+ var ct = headers.get('content-type');
1383
+ if (ct && !/(text)|(json)/.test(ct)) {
1384
+ return res;
1385
+ }
1386
+ }
1387
+ var loadTime = Date.now() - begin;
1388
+ response
1389
+ .text()
1390
+ .then(function (result) {
1391
+ var url = response.url, status = response.status, statusText = response.statusText, ok = response.ok;
1392
+ var request = {
1393
+ requestType: 'fetch',
1394
+ responseURL: url,
1395
+ status: status,
1396
+ loadTime: loadTime,
1397
+ statusText: statusText,
1398
+ reason: '',
1399
+ detail: '',
1400
+ requestData: '',
1401
+ method: ''
1402
+ };
1403
+ if (!ok || status >= 300) {
1404
+ request.reason = 'failed';
1405
+ request.detail = "request is failed, status: ".concat(status, ", statusText: ").concat(statusText);
1406
+ }
1407
+ else if (loadTime > XMLHttpRequestTimeout) {
1408
+ request.reason = 'slow';
1409
+ request.detail = "request is too slow, XMLHttpRequestTimeout: ".concat(loadTime, ", result: ").concat(result === null || result === void 0 ? void 0 : result.slice(0, 500));
1410
+ }
1411
+ if (request.reason) {
1412
+ if (!tempUrlInfo[url]) {
1413
+ tempUrlInfo[url] = true;
1414
+ setTimeout(function () {
1415
+ delete tempUrlInfo[url];
1416
+ }, 10);
1417
+ setReportValue('error', null);
1418
+ report(__assign(__assign({}, getReport()), {
1419
+ page: location.href,
1420
+ originPage: location.href,
1421
+ type: 'request',
1422
+ req: request
1423
+ }));
1424
+ }
1425
+ }
1426
+ })["catch"](function (err) {
1427
+ // eslint-disable-next-line no-console
1428
+ console.log('hackFetch response.text() err', err);
1429
+ });
1430
+ return res;
1431
+ })["catch"](function (err) {
1432
+ // eslint-disable-next-line no-console
1433
+ console.log('hackFetch err', err);
1434
+ });
1435
+ };
1436
+ }
1410
1437
  };
1411
1438
 
1412
- var Track = /** @class */ (function () {
1413
- function Track() {
1414
- this.visualTrack = function () {
1415
- window.onload = function () {
1416
- visualTrackFunc();
1417
- };
1418
- };
1419
- }
1420
- Track.prototype.init = function (config) {
1421
- var _a;
1422
- // 是否开启日志收集
1423
- if (!config || !config.enable) {
1424
- return;
1425
- }
1426
- // 没有项目ID,则不监听任何事件
1427
- if (!config.projectID) {
1428
- // eslint-disable-next-line no-console
1429
- console.warn('缺少项目ID或token!');
1430
- return;
1431
- }
1432
- // 没有reportUrl
1433
- if (!config.reportUrl) {
1434
- // eslint-disable-next-line no-console
1435
- console.warn('缺少上报地址!');
1436
- return;
1437
- }
1438
- if (this.ignoreUrl(((_a = config === null || config === void 0 ? void 0 : config.ignore) === null || _a === void 0 ? void 0 : _a.urls) || [])) {
1439
- return;
1440
- }
1441
- setConfig(config);
1442
- initReport();
1443
- recordXMLHttpRequestLog(config.XMLHttpRequestTimeout);
1444
- hackFetch(config.XMLHttpRequestTimeout);
1445
- Config.spa && this.addListenRouterChange();
1446
- Config.enableBehavior && this.addListenUserActivity();
1447
- Config.enableError && this.addListenJSUncaught();
1448
- this.visualTrack();
1449
- this.addListenUnload();
1450
- initWindowObjectFunction();
1451
- };
1452
- /**
1453
- * @description 监听错误异常
1454
- */
1455
- Track.prototype.addListenJSUncaught = function () {
1456
- on('error', handleError);
1457
- on('unhandledrejection', handleError);
1458
- };
1459
- /**
1460
- * @description 监听行为
1461
- */
1462
- Track.prototype.addListenUserActivity = function () {
1463
- on('click', handleClick); // 非输入框点击
1464
- on('blur', handleBlur); // 输入框失焦
1465
- on('scroll', handleScroll); // 输入框失焦
1466
- };
1467
- /**
1468
- * @description 监听路由变化
1469
- */
1470
- Track.prototype.addListenRouterChange = function () {
1471
- on('popstate', handleLocationChange);
1472
- // on('hashchange', pv.handleHashChange); //触发hashchange的同时也会触发popstate
1473
- if (Config.spa && !Config.hash) {
1474
- history.pushState = _history('pushState');
1475
- history.replaceState = _history('replaceState');
1476
- on('pushState', handleLocationChange);
1477
- on('replaceState', handleLocationChange);
1478
- }
1479
- };
1480
- /**
1481
- * @description 页面将要关闭
1482
- */
1483
- Track.prototype.addListenUnload = function () {
1484
- var _this = this;
1485
- on('beforeunload', function () {
1486
- _this.destroy();
1487
- });
1488
- };
1489
- /**
1490
- * 忽略的url
1491
- * @param urls
1492
- */
1493
- Track.prototype.ignoreUrl = function (urls) {
1494
- var someUrl = urls.some(function (url) { return location.href.includes(url); });
1495
- return someUrl;
1496
- };
1497
- /**
1498
- * @description 销毁监听器
1499
- */
1500
- Track.prototype.destroy = function () {
1501
- if (Config.spa) {
1502
- // off('hashchange', pv.handleHashChange);
1503
- off('pushState', handleHistoryChange);
1504
- off('popstate', handleHistoryChange);
1505
- off('replaceState', handleHistoryChange);
1506
- }
1507
- if (Config.enableBehavior) {
1508
- off('click', handleClick);
1509
- off('blur', handleBlur);
1510
- off('scroll', handleScroll);
1511
- }
1512
- if (Config.enableError) {
1513
- off('error', handleError);
1514
- }
1515
- sessionStorage.removeItem(monitorTrackSessionId);
1516
- };
1517
- return Track;
1439
+ var Track = /** @class */ (function () {
1440
+ function Track() {
1441
+ var _this = this;
1442
+ this.lagTimer = null;
1443
+ this.observer = null;
1444
+ this.visualTrack = function () {
1445
+ window.onload = function () {
1446
+ visualTrackFunc();
1447
+ };
1448
+ };
1449
+ this.listenPageLag = function () {
1450
+ _this.observer = new PerformanceObserver(function (list) {
1451
+ list.getEntries().forEach(function (entry) {
1452
+ if (entry.duration > 400) {
1453
+ handlePageLag(entry.duration);
1454
+ }
1455
+ });
1456
+ });
1457
+ _this.observer.observe({ entryTypes: ['longtask'] });
1458
+ };
1459
+ }
1460
+ Track.prototype.init = function (config) {
1461
+ var _a;
1462
+ // 是否开启日志收集
1463
+ if (!config || !config.enable) {
1464
+ return;
1465
+ }
1466
+ // 没有项目ID,则不监听任何事件
1467
+ if (!config.projectID) {
1468
+ // eslint-disable-next-line no-console
1469
+ console.warn('缺少项目ID或projectID!');
1470
+ return;
1471
+ }
1472
+ // 没有reportUrl
1473
+ if (!config.reportUrl) {
1474
+ // eslint-disable-next-line no-console
1475
+ console.warn('缺少上报地址!');
1476
+ return;
1477
+ }
1478
+ if (this.ignoreUrl(((_a = config === null || config === void 0 ? void 0 : config.ignore) === null || _a === void 0 ? void 0 : _a.urls) || [])) {
1479
+ return;
1480
+ }
1481
+ setConfig(config);
1482
+ initReport();
1483
+ recordXMLHttpRequestLog(config.XMLHttpRequestTimeout);
1484
+ hackFetch(config.XMLHttpRequestTimeout);
1485
+ Config.spa && this.addListenRouterChange();
1486
+ Config.enableBehavior && this.addListenUserActivity();
1487
+ Config.enableError && this.addListenJSUncaught();
1488
+ this.visualTrack();
1489
+ this.listenPageLag();
1490
+ this.addListenUnload();
1491
+ initWindowObjectFunction();
1492
+ };
1493
+ /**
1494
+ * @description 监听错误异常
1495
+ */
1496
+ Track.prototype.addListenJSUncaught = function () {
1497
+ on('error', handleError);
1498
+ on('unhandledrejection', handleError);
1499
+ };
1500
+ /**
1501
+ * @description 监听行为
1502
+ */
1503
+ Track.prototype.addListenUserActivity = function () {
1504
+ on('click', handleClick); // 非输入框点击
1505
+ on('blur', handleBlur); // 输入框失焦
1506
+ on('scroll', handleScroll); // 输入框失焦
1507
+ };
1508
+ /**
1509
+ * @description 监听路由变化
1510
+ */
1511
+ Track.prototype.addListenRouterChange = function () {
1512
+ on('popstate', handleLocationChange);
1513
+ // on('hashchange', pv.handleHashChange); //触发hashchange的同时也会触发popstate
1514
+ if (Config.spa && !Config.hash) {
1515
+ history.pushState = _history('pushState');
1516
+ history.replaceState = _history('replaceState');
1517
+ on('pushState', handleLocationChange);
1518
+ on('replaceState', handleLocationChange);
1519
+ }
1520
+ };
1521
+ /**
1522
+ * @description 页面将要关闭
1523
+ */
1524
+ Track.prototype.addListenUnload = function () {
1525
+ var _this = this;
1526
+ on('beforeunload', function () {
1527
+ _this.destroy();
1528
+ });
1529
+ };
1530
+ /**
1531
+ * 忽略的url
1532
+ * @param urls
1533
+ */
1534
+ Track.prototype.ignoreUrl = function (urls) {
1535
+ var someUrl = urls.some(function (url) { return location.href.includes(url); });
1536
+ return someUrl;
1537
+ };
1538
+ /**
1539
+ * @description 销毁监听器
1540
+ */
1541
+ Track.prototype.destroy = function () {
1542
+ var _a;
1543
+ if (Config.spa) {
1544
+ // off('hashchange', pv.handleHashChange);
1545
+ off('pushState', handleHistoryChange);
1546
+ off('popstate', handleHistoryChange);
1547
+ off('replaceState', handleHistoryChange);
1548
+ }
1549
+ if (Config.enableBehavior) {
1550
+ off('click', handleClick);
1551
+ off('blur', handleBlur);
1552
+ off('scroll', handleScroll);
1553
+ }
1554
+ if (Config.enableError) {
1555
+ off('error', handleError);
1556
+ }
1557
+ this.lagTimer && clearInterval(this.lagTimer);
1558
+ (_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
1559
+ sessionStorage.removeItem(monitorTrackSessionId);
1560
+ };
1561
+ return Track;
1518
1562
  }());
1519
1563
 
1520
1564
  return Track;
1521
1565
 
1522
- })(axios, uuid, uaParserJs, rrweb, ErrorStackParser, html2canvas);
1566
+ })(uuid, ErrorStackParser, html2canvas, rrweb, axios, uaParserJs);