spy-client 2.0.7 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +119 -10
  2. package/dist/head/base.d.ts +1 -1
  3. package/dist/lib/huffman.d.ts +9 -0
  4. package/dist/lib/interface.d.ts +59 -1
  5. package/dist/lib/util.d.ts +22 -0
  6. package/dist/module/longtask.d.ts +1 -1
  7. package/dist/module/resource.d.ts +19 -9
  8. package/dist/module/tti.d.ts +1 -1
  9. package/dist/spy-client-basic.esm.js +233 -1167
  10. package/dist/spy-client-basic.iife.js +237 -1171
  11. package/dist/spy-client-basic.iife.min.js +1 -1
  12. package/dist/spy-client-basic.js +238 -1172
  13. package/dist/spy-client-basic.min.js +1 -1
  14. package/dist/spy-client-basic.mjs +82 -54
  15. package/dist/spy-client.d.ts +5 -4
  16. package/dist/spy-client.esm.js +1393 -5662
  17. package/dist/spy-client.iife.js +1401 -5670
  18. package/dist/spy-client.iife.min.js +1 -1
  19. package/dist/spy-client.js +1402 -5671
  20. package/dist/spy-client.min.js +1 -1
  21. package/dist/spy-client.mjs +704 -455
  22. package/dist/spy-head.js +330 -276
  23. package/dist/spy-head.min.js +1 -1
  24. package/dist/spy-local-cache.d.ts +18 -0
  25. package/dist/spy-local-cache.js +690 -0
  26. package/dist/spy-local-cache.min.js +1 -0
  27. package/example/index.html +8 -2
  28. package/example/local-cache.html +97 -0
  29. package/example/saveCache.js +41 -0
  30. package/karma.conf.js +111 -0
  31. package/package.json +21 -20
  32. package/src/head/error.ts +14 -15
  33. package/src/head/whitescreen.ts +8 -5
  34. package/src/lib/huffman.ts +326 -0
  35. package/src/lib/interface.ts +70 -1
  36. package/src/lib/util.ts +101 -0
  37. package/src/module/resource.ts +226 -129
  38. package/src/spy-client-basic.ts +8 -7
  39. package/src/spy-client.ts +15 -6
  40. package/src/spy-local-cache.ts +385 -0
  41. package/src/types/globals.d.ts +1 -1
package/dist/spy-head.js CHANGED
@@ -1,293 +1,347 @@
1
1
  (function (global, factory) {
2
2
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
3
  typeof define === 'function' && define.amd ? define(factory) :
4
- (global = global || self, global.__spyHead = factory());
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.__spyHead = factory());
5
5
  }(this, (function () { 'use strict';
6
6
 
7
- var spyHead = {
8
- conf: {},
9
- winerrors: [],
10
- errorDestroy: function errorDestroy() {},
11
- observerDestroy: function observerDestroy() {},
12
- entryMap: {},
13
- init: function init(conf) {
14
- this.conf = conf;
15
- },
16
- send: function send(obj, logServer) {
17
- obj.type = obj.type || 'except';
18
- var conf = this.conf;
19
- logServer = logServer || conf.logServer;
20
- var logUrl = "".concat(logServer, "?pid=").concat(conf.pid, "&lid=").concat(conf.lid, "&ts=").concat(Date.now()) + "&type=".concat(obj.type, "&group=").concat(obj.group, "&info=").concat(encodeURIComponent(JSON.stringify(obj.info)));
21
-
22
- if (obj.dim) {
23
- logUrl += '&dim=' + encodeURIComponent(JSON.stringify(obj.dim));
24
- }
25
-
26
- var img = new Image();
27
- img.src = logUrl;
7
+ /**
8
+ * @file utils
9
+ * @author kaivean
10
+ */
11
+ function getUrlInfoFromURL(url) {
12
+ if (URL) {
13
+ var obj = new URL(url);
14
+ if (obj.host !== undefined) {
15
+ return {
16
+ protocol: obj.protocol,
17
+ host: obj.host,
18
+ pathname: obj.pathname,
19
+ ext: '',
20
+ };
21
+ }
22
+ }
23
+ return;
24
+ }
25
+ function getUrlInfo(url) {
26
+ var info = getUrlInfoFromURL(url);
27
+ if (!info) {
28
+ var parser = document.createElement('a');
29
+ parser.href = url;
30
+ info = {
31
+ protocol: parser.protocol,
32
+ host: parser.host || location.host,
33
+ pathname: parser.pathname,
34
+ ext: '',
35
+ };
36
+ }
37
+ var split = info.pathname.split('.');
38
+ info.ext = split[split.length - 1];
39
+ return info;
40
+ }
41
+ function f(n) {
42
+ return +n.toFixed(1);
43
+ }
44
+ function getResTiming(t) {
45
+ return {
46
+ wait: f(t.domainLookupStart - (t.navigationStart || t.fetchStart || t.startTime)),
47
+ dns: f(t.domainLookupEnd - t.domainLookupStart),
48
+ connect: f(t.connectEnd - t.connectStart),
49
+ req: f(t.responseStart - t.requestStart),
50
+ res: f(t.responseEnd - t.responseStart),
51
+ };
52
+ }
53
+ function getxpath(el) {
54
+ if (!el) {
55
+ return { xpath: '' };
56
+ }
57
+ var xpath = [];
58
+ while (el && el.nodeType === 1 && el !== el.parentNode) {
59
+ var t = el.tagName.toLowerCase();
60
+ if (el.getAttribute('id')) {
61
+ t += '[#' + el.getAttribute('id') + ']';
62
+ }
63
+ else if (el.classList && el.classList.length) {
64
+ t += '[.' + el.classList[el.classList.length - 1] + ']';
65
+ }
66
+ xpath.push(t);
67
+ if (el === document.body) {
68
+ break;
69
+ }
70
+ el = el.parentNode; // 修复缺陷检查
71
+ }
72
+ return {
73
+ xpath: xpath.join('<'),
74
+ };
75
+ }
28
76
 
29
- img.onload = img.onerror = function () {
30
- img = null;
31
- };
32
- }
77
+ /**
78
+ * @file 资源、JS、白屏监控的基础
79
+ * @author kaivean
80
+ */
81
+ var spyHead = {
82
+ conf: {},
83
+ winerrors: [],
84
+ errorDestroy: function () { },
85
+ observerDestroy: function () { },
86
+ entryMap: {},
87
+ init: function (conf) {
88
+ this.conf = conf;
89
+ },
90
+ send: function (obj, logServer) {
91
+ obj.type = obj.type || 'except';
92
+ var conf = this.conf;
93
+ logServer = logServer || conf.logServer;
94
+ var logUrl = logServer + "?pid=" + conf.pid + "&lid=" + conf.lid + "&ts=" + Date.now()
95
+ + ("&type=" + obj.type + "&group=" + obj.group + "&info=" + encodeURIComponent(JSON.stringify(obj.info)));
96
+ if (obj.dim) {
97
+ logUrl += '&dim=' + encodeURIComponent(JSON.stringify(obj.dim));
98
+ }
99
+ var img = new Image();
100
+ img.src = logUrl;
101
+ img.onload = img.onerror = function () {
102
+ img = null;
103
+ };
104
+ },
33
105
  };
34
106
 
35
- function init(conf) {
36
- var resourceError = conf.resourceError || {};
37
- var jsError = conf.jsError || {};
38
- var isSendJserror = Math.random() < (jsError.sample ? jsError.sample : 0);
39
- var isSendResource = Math.random() < (resourceError.sample ? resourceError.sample : 0);
40
- var winerrors = spyHead.winerrors;
41
-
42
- function getxpath(el) {
43
- var xpath = [];
44
-
45
- while (el && el.nodeType === 1 && el !== el.parentNode) {
46
- xpath.push(el.tagName.toLowerCase());
47
-
48
- if (el === document.body) {
49
- break;
50
- }
51
-
52
- el = el.parentNode;
53
- }
54
-
55
- return {
56
- xpath: xpath.join('<')
57
- };
58
- }
59
-
60
- function spyListenError(event) {
61
- try {
62
- var el = event.target;
63
- var obj = {
64
- info: {},
65
- dim: {},
66
- group: ''
67
- };
68
- var info = obj.info;
69
- var srcElement = event.srcElement;
70
- var dataConnection = navigator.connection || {};
71
- info.downlink = dataConnection.downlink;
72
- info.effectiveType = dataConnection.effectiveType;
73
- info.rtt = dataConnection.rtt;
74
- info.deviceMemory = navigator.deviceMemory || 0;
75
- info.hardwareConcurrency = navigator.hardwareConcurrency || 0;
76
-
77
- if (srcElement === window) {
78
- obj.group = jsError.group;
79
- var error = event.error || {};
80
- info.msg = event.message;
81
- info.file = event.filename;
82
- info.ln = event.lineno;
83
- info.col = event.colno;
84
- info.stack = (error.stack || '').split('\n').slice(0, 3).join('\n');
85
-
86
- if (info.msg.indexOf('MODULE_TIMEOUT') !== -1) {
87
- var matches = info.msg.match(/^.*Hang:(.*); Miss:(.*)/);
88
-
89
- if (matches && matches[2]) {
90
- info.msg = 'MODULE_TIMEOUT for miss:' + matches[2];
91
- }
92
- }
93
-
94
- var historys = [];
95
-
96
- for (var index = 0; index < spyHead.winerrors.length; index++) {
97
- var item = spyHead.winerrors[index];
98
- var prefix = item.count > 1 ? "(".concat(item.count, ")") : '';
99
- historys.push(prefix + item.msg);
100
- }
101
-
102
- info.hisErrors = historys.join('----');
103
- var allow = true;
104
-
105
- if (jsError.handler) {
106
- allow = jsError.handler(obj);
107
- }
108
-
109
- if (allow !== false && isSendJserror) {
110
- spyHead.send(obj);
111
- }
112
- } else {
113
- obj.group = resourceError.group;
114
- obj.dim.type = srcElement.tagName.toLowerCase();
115
- var url = srcElement.src || srcElement.href;
116
- info.msg = url || 'unknown load eror';
117
-
118
- if (el && el.tagName === 'IMG') {
119
- info.xpath = getxpath(el).xpath;
120
- }
121
-
122
- var _allow = true;
123
-
124
- if (resourceError.handler) {
125
- _allow = resourceError.handler(obj);
126
- }
127
-
128
- if (_allow !== false && isSendResource) {
129
- spyHead.send(obj);
130
- }
131
- }
132
-
133
- if (winerrors.length > 0) {
134
- var lastInfo = winerrors[winerrors.length - 1];
135
-
136
- if (info.msg === lastInfo.msg) {
137
- lastInfo.count += lastInfo.count || 0;
138
- return;
139
- }
140
- }
141
-
142
- winerrors.push(info);
143
- } catch (e) {
144
- console.error(e);
145
- }
146
- }
147
-
148
- window.addEventListener('error', spyListenError, true);
149
-
150
- spyHead.errorDestroy = function () {
151
- window.removeEventListener('error', spyListenError, true);
152
- spyHead.winerrors = null;
153
- };
107
+ /**
108
+ * @file 资源和JS错误监控
109
+ * @author kaivean
110
+ */
111
+ function init$2(conf) {
112
+ var resourceError = conf.resourceError || {};
113
+ var jsError = conf.jsError || {};
114
+ var isSendJserror = Math.random() < (jsError.sample ? jsError.sample : 0);
115
+ var isSendResource = Math.random() < (resourceError.sample ? resourceError.sample : 0);
116
+ var winerrors = spyHead.winerrors;
117
+ var resourceErrorCount = 0;
118
+ function spyListenError(event) {
119
+ try {
120
+ var el = event.target;
121
+ var obj = { info: {}, dim: {}, group: '' };
122
+ var info = obj.info;
123
+ var srcElement = event.srcElement;
124
+ // 设备信息
125
+ var dataConnection = navigator.connection || {};
126
+ info.downlink = dataConnection.downlink; // 网站下载速度 M/s
127
+ info.effectiveType = dataConnection.effectiveType; // 网络类型
128
+ info.rtt = dataConnection.rtt; // 网络往返时间 ms
129
+ info.deviceMemory = navigator.deviceMemory || 0;
130
+ info.hardwareConcurrency = navigator.hardwareConcurrency || 0;
131
+ // JS错误
132
+ if (srcElement === window) {
133
+ obj.group = jsError.group;
134
+ // 异常信息
135
+ var error = event.error || {};
136
+ info.msg = event.message;
137
+ info.file = event.filename;
138
+ info.ln = event.lineno;
139
+ info.col = event.colno;
140
+ info.stack = (error.stack || '').split('\n').slice(0, 3).join('\n');
141
+ // 针对esl的MODULE_TIMEOUT处理
142
+ if (info.msg.indexOf('MODULE_TIMEOUT') !== -1) {
143
+ var matches = info.msg.match(/^.*Hang:(.*); Miss:(.*)/);
144
+ if (matches && matches[2]) {
145
+ info.msg = 'MODULE_TIMEOUT for miss:' + (matches[2]);
146
+ }
147
+ }
148
+ // 历史错误
149
+ var historys = [];
150
+ for (var index = 0; index < spyHead.winerrors.length; index++) {
151
+ var item = spyHead.winerrors[index];
152
+ var prefix = item.count > 1 ? "(" + item.count + ")" : '';
153
+ historys.push(prefix + item.msg);
154
+ }
155
+ info.hisErrors = historys.join('----');
156
+ var allow = true;
157
+ if (jsError.handler) {
158
+ allow = jsError.handler(obj);
159
+ }
160
+ if (allow !== false && isSendJserror) {
161
+ spyHead.send(obj);
162
+ }
163
+ }
164
+ // 资源错误
165
+ else {
166
+ obj.group = resourceError.group;
167
+ obj.dim.type = srcElement.tagName.toLowerCase();
168
+ var url = srcElement.src || srcElement.href;
169
+ info.msg = url || 'unknown load eror';
170
+ obj.dim.host = getUrlInfo(url).host;
171
+ if (el && el.tagName === 'IMG') {
172
+ info.xpath = getxpath(el).xpath;
173
+ }
174
+ if (resourceErrorCount) {
175
+ info.hisErrCount = resourceErrorCount;
176
+ }
177
+ var allow = true;
178
+ if (resourceError.handler) {
179
+ allow = resourceError.handler(obj);
180
+ }
181
+ if (allow !== false && isSendResource) {
182
+ spyHead.send(obj);
183
+ }
184
+ resourceErrorCount++;
185
+ }
186
+ // 有些错误一下出现很多次,都聚合都一个错误,加上次数
187
+ if (winerrors.length > 0) {
188
+ var lastInfo = winerrors[winerrors.length - 1];
189
+ if (info.msg === lastInfo.msg) {
190
+ lastInfo.count += (lastInfo.count || 0);
191
+ return;
192
+ }
193
+ }
194
+ winerrors.push(info);
195
+ }
196
+ catch (e) {
197
+ console.error(e);
198
+ }
199
+ }
200
+ window.addEventListener('error', spyListenError, true);
201
+ spyHead.errorDestroy = function () {
202
+ window.removeEventListener('error', spyListenError, true);
203
+ spyHead.winerrors = null;
204
+ };
154
205
  }
155
206
 
156
- function init$1() {
157
- if (window.PerformanceObserver) {
158
- var observer = new window.PerformanceObserver(function spyObserveLongtask(list) {
159
- var entryMap = spyHead.entryMap;
160
- var entries = list.getEntries();
161
-
162
- for (var i = 0; i < entries.length; i++) {
163
- var entry = entries[i];
164
-
165
- if (!entryMap[entry.entryType]) {
166
- entryMap[entry.entryType] = [];
167
- }
168
-
169
- entryMap[entry.entryType].push(entry);
170
- }
171
- });
172
-
173
- spyHead.observerDestroy = function () {
174
- observer.disconnect();
175
- };
176
-
177
- try {
178
- observer.observe({
179
- entryTypes: ['longtask', 'layout-shift', 'first-input', 'largest-contentful-paint']
180
- });
181
- } catch (e) {}
182
- }
207
+ /**
208
+ * @file PerformanceObserver指标采集
209
+ * @author kaivean
210
+ */
211
+ function init$1() {
212
+ // Longtask监控
213
+ if (window.PerformanceObserver) {
214
+ var observer_1 = new window.PerformanceObserver(function spyObserveLongtask(list) {
215
+ var entryMap = spyHead.entryMap;
216
+ var entries = list.getEntries();
217
+ for (var i = 0; i < entries.length; i++) {
218
+ var entry = entries[i];
219
+ if (!entryMap[entry.entryType]) {
220
+ entryMap[entry.entryType] = [];
221
+ }
222
+ entryMap[entry.entryType].push(entry);
223
+ }
224
+ });
225
+ spyHead.observerDestroy = function () {
226
+ observer_1.disconnect();
227
+ };
228
+ // 在ios下,没有一个类似的监控项是支持的,就抛错,chrome会console warn
229
+ try {
230
+ observer_1.observe({ entryTypes: [
231
+ 'longtask',
232
+ 'layout-shift',
233
+ 'first-input',
234
+ 'largest-contentful-paint',
235
+ ] });
236
+ }
237
+ catch (e) { }
238
+ }
183
239
  }
184
240
 
185
- function init$2(conf) {
186
- var whiteScreenError = conf.whiteScreenError || {};
187
- var handler = whiteScreenError.handler;
188
- var selector = whiteScreenError.selector;
189
- var subSelector = whiteScreenError.subSelector;
190
- var timeout = whiteScreenError.timeout || 6000;
191
- var isSend = Math.random() < (whiteScreenError.sample ? whiteScreenError.sample : 0);
192
-
193
- function getNetTime() {
194
- if (!window.performance) {
195
- return false;
196
- }
197
-
198
- var pf = window.performance.timing;
199
- var netStr = "&dns=".concat(pf.domainLookupEnd - pf.domainLookupStart) + "&tcp=".concat(pf.connectEnd - pf.connectStart) + "&requestTime=".concat(pf.responseStart - pf.requestStart) + "&resoneTime=".concat(pf.responseEnd - pf.responseStart);
200
- return netStr;
201
- }
202
-
203
- function getHisError() {
204
- if (!spyHead.winerrors) {
205
- return false;
206
- }
207
-
208
- var errors = spyHead.winerrors;
209
- var historys = [];
210
-
211
- for (var i = 0; i < errors.length; i++) {
212
- var stack = (errors[i].stack || '').split('\n')[0];
213
- historys.push("(".concat(i, ")").concat(stack || errors[i].msg));
214
- }
215
-
216
- return historys.join(';;');
217
- }
218
-
219
- function getDeviceInfo() {
220
- var ret = {};
221
- var dataConnection = navigator.connection || {};
222
- ret.downlink = dataConnection.downlink;
223
- ret.effectiveType = dataConnection.effectiveType;
224
- ret.rtt = dataConnection.rtt;
225
- ret.deviceMemory = navigator.deviceMemory || 0;
226
- ret.hardwareConcurrency = navigator.hardwareConcurrency || 0;
227
- return ret;
228
- }
229
-
230
- function isWhiteScreen() {
231
- var ele = document.querySelector(selector);
232
-
233
- if (!ele) {
234
- return true;
235
- }
236
-
237
- var sub = ele.querySelector(subSelector);
238
-
239
- if (!sub) {
240
- return true;
241
- }
242
-
243
- if (ele.clientHeight < window.innerHeight * 2 / 3) {
244
- return true;
245
- }
246
-
247
- return false;
248
- }
249
-
250
- if (isSend) {
251
- setTimeout(function () {
252
- var obj = {
253
- group: whiteScreenError.group,
254
- info: {
255
- msg: '',
256
- netTime: getNetTime(),
257
- hisErrors: getHisError(),
258
- deviceInfo: getDeviceInfo()
259
- }
260
- };
261
-
262
- if (isWhiteScreen()) {
263
- obj.info.msg = 'WhiteScren Error';
264
- var allow = true;
265
-
266
- if (handler) {
267
- allow = handler(obj);
268
- }
269
-
270
- if (allow !== false && obj.info.msg) {
271
- spyHead && spyHead.send(obj);
272
- }
273
- }
274
- }, timeout);
275
- }
241
+ /**
242
+ * @file 白屏监控
243
+ * @author kaivean
244
+ */
245
+ function init(conf) {
246
+ var whiteScreenError = conf.whiteScreenError || {};
247
+ var handler = whiteScreenError.handler;
248
+ var selector = whiteScreenError.selector;
249
+ var subSelector = whiteScreenError.subSelector;
250
+ var timeout = whiteScreenError.timeout || 6000;
251
+ var isSend = Math.random() < (whiteScreenError.sample ? whiteScreenError.sample : 0);
252
+ // 补充白屏信息:期间的网络时间
253
+ function getNetTime() {
254
+ if (!window.performance) {
255
+ return false;
256
+ }
257
+ var pf = getResTiming(window.performance.timing);
258
+ var netStr = "&wait=" + pf.wait
259
+ + ("&dns=" + pf.dns)
260
+ + ("&connect=" + pf.connect)
261
+ + ("&requestTime=" + pf.req)
262
+ + ("&resoneTime=" + pf.res);
263
+ return netStr;
264
+ }
265
+ // 补充白屏信息:期间发生的JS Error 和 资源 Error
266
+ function getHisError() {
267
+ if (!(spyHead.winerrors)) {
268
+ return false;
269
+ }
270
+ var errors = spyHead.winerrors;
271
+ var historys = [];
272
+ for (var i = 0; i < errors.length; i++) {
273
+ var stack = (errors[i].stack || '').split('\n')[0];
274
+ historys.push("(" + i + ")" + (stack || errors[i].msg));
275
+ }
276
+ return historys.join(';;');
277
+ }
278
+ // 补充白屏信息: 设备信息
279
+ function getDeviceInfo() {
280
+ var ret = {};
281
+ // 设备信息
282
+ var dataConnection = navigator.connection || {};
283
+ ret.downlink = dataConnection.downlink; // 网站下载速度 M/s
284
+ ret.effectiveType = dataConnection.effectiveType; // 网络类型
285
+ ret.rtt = dataConnection.rtt; // 网络往返时间 ms
286
+ ret.deviceMemory = navigator.deviceMemory || 0;
287
+ ret.hardwareConcurrency = navigator.hardwareConcurrency || 0;
288
+ return ret;
289
+ }
290
+ function isWhiteScreen() {
291
+ var ele = document.querySelector(selector);
292
+ if (!ele) {
293
+ return true;
294
+ }
295
+ var sub = ele.querySelector(subSelector);
296
+ if (!sub) {
297
+ return true;
298
+ }
299
+ if (ele.clientHeight < (window.innerHeight * 2 / 3)) {
300
+ return true;
301
+ }
302
+ return false;
303
+ }
304
+ if (isSend) {
305
+ setTimeout(function () {
306
+ var obj = {
307
+ group: whiteScreenError.group,
308
+ info: {
309
+ msg: '',
310
+ netTime: getNetTime(),
311
+ hisErrors: getHisError(),
312
+ deviceInfo: getDeviceInfo(),
313
+ },
314
+ };
315
+ if (isWhiteScreen()) {
316
+ obj.info.msg = 'WhiteScren Error';
317
+ var allow = true;
318
+ if (handler) {
319
+ allow = handler(obj);
320
+ }
321
+ if (allow !== false && obj.info.msg) {
322
+ spyHead && spyHead.send(obj);
323
+ }
324
+ }
325
+ }, timeout);
326
+ }
276
327
  }
277
328
 
278
- spyHead.init = function (conf) {
279
- if (!conf.logServer) {
280
- conf.logServer = 'https://sp1.baidu.com/5b1ZeDe5KgQFm2e88IuM_a/mwb2.gif';
281
- }
282
-
283
- this.conf = conf;
284
- init(conf);
285
- init$1();
286
- init$2(conf);
287
- };
288
-
289
- if (window.__spyclientConf) {
290
- spyHead.init(window.__spyclientConf);
329
+ /**
330
+ * @file SpyClient
331
+ * @author kaivean
332
+ */
333
+ spyHead.init = function (conf) {
334
+ if (!conf.logServer) {
335
+ conf.logServer = 'https://sp1.baidu.com/5b1ZeDe5KgQFm2e88IuM_a/mwb2.gif';
336
+ }
337
+ this.conf = conf;
338
+ init$2(conf);
339
+ init$1();
340
+ init(conf);
341
+ };
342
+ // 兼容全局变量定义的初始化方式
343
+ if (window.__spyclientConf) {
344
+ spyHead.init(window.__spyclientConf);
291
345
  }
292
346
 
293
347
  return spyHead;