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.
- package/README.md +119 -10
- package/dist/head/base.d.ts +1 -1
- package/dist/lib/huffman.d.ts +9 -0
- package/dist/lib/interface.d.ts +59 -1
- package/dist/lib/util.d.ts +22 -0
- package/dist/module/longtask.d.ts +1 -1
- package/dist/module/resource.d.ts +19 -9
- package/dist/module/tti.d.ts +1 -1
- package/dist/spy-client-basic.esm.js +233 -1167
- package/dist/spy-client-basic.iife.js +237 -1171
- package/dist/spy-client-basic.iife.min.js +1 -1
- package/dist/spy-client-basic.js +238 -1172
- package/dist/spy-client-basic.min.js +1 -1
- package/dist/spy-client-basic.mjs +82 -54
- package/dist/spy-client.d.ts +5 -4
- package/dist/spy-client.esm.js +1393 -5662
- package/dist/spy-client.iife.js +1401 -5670
- package/dist/spy-client.iife.min.js +1 -1
- package/dist/spy-client.js +1402 -5671
- package/dist/spy-client.min.js +1 -1
- package/dist/spy-client.mjs +704 -455
- package/dist/spy-head.js +330 -276
- package/dist/spy-head.min.js +1 -1
- package/dist/spy-local-cache.d.ts +18 -0
- package/dist/spy-local-cache.js +690 -0
- package/dist/spy-local-cache.min.js +1 -0
- package/example/index.html +8 -2
- package/example/local-cache.html +97 -0
- package/example/saveCache.js +41 -0
- package/karma.conf.js +111 -0
- package/package.json +21 -20
- package/src/head/error.ts +14 -15
- package/src/head/whitescreen.ts +8 -5
- package/src/lib/huffman.ts +326 -0
- package/src/lib/interface.ts +70 -1
- package/src/lib/util.ts +101 -0
- package/src/module/resource.ts +226 -129
- package/src/spy-client-basic.ts +8 -7
- package/src/spy-client.ts +15 -6
- package/src/spy-local-cache.ts +385 -0
- package/src/types/globals.d.ts +1 -1
package/src/spy-client-basic.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import {SpyClientOption} from './lib/interface';
|
|
7
|
+
import {assign} from './lib/util';
|
|
7
8
|
|
|
8
9
|
interface Option {
|
|
9
10
|
/**
|
|
@@ -131,7 +132,7 @@ export default class SpyClient {
|
|
|
131
132
|
return;
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
query =
|
|
135
|
+
query = assign(
|
|
135
136
|
{
|
|
136
137
|
pid: this.option.pid,
|
|
137
138
|
lid: this.option.lid,
|
|
@@ -233,7 +234,7 @@ export default class SpyClient {
|
|
|
233
234
|
* @param option 配置
|
|
234
235
|
*/
|
|
235
236
|
sendPerf(option: Option) {
|
|
236
|
-
this.send(
|
|
237
|
+
this.send(assign({
|
|
237
238
|
type: 'perf',
|
|
238
239
|
}, option));
|
|
239
240
|
}
|
|
@@ -243,7 +244,7 @@ export default class SpyClient {
|
|
|
243
244
|
* @param option 错误配置项
|
|
244
245
|
*/
|
|
245
246
|
sendExcept(option: ErrorOption) {
|
|
246
|
-
this.send(
|
|
247
|
+
this.send(assign({
|
|
247
248
|
type: 'except',
|
|
248
249
|
}, option));
|
|
249
250
|
}
|
|
@@ -253,7 +254,7 @@ export default class SpyClient {
|
|
|
253
254
|
* @param option 配置
|
|
254
255
|
*/
|
|
255
256
|
sendDist(option: Option) {
|
|
256
|
-
this.send(
|
|
257
|
+
this.send(assign({
|
|
257
258
|
type: 'dist',
|
|
258
259
|
}, option));
|
|
259
260
|
}
|
|
@@ -263,7 +264,7 @@ export default class SpyClient {
|
|
|
263
264
|
* @param option 配置
|
|
264
265
|
*/
|
|
265
266
|
sendCount(option: Option) {
|
|
266
|
-
this.send(
|
|
267
|
+
this.send(assign({
|
|
267
268
|
type: 'count',
|
|
268
269
|
}, option));
|
|
269
270
|
}
|
|
@@ -274,8 +275,8 @@ export default class SpyClient {
|
|
|
274
275
|
* @param option 错误配置项
|
|
275
276
|
*/
|
|
276
277
|
sendExceptForError(e: Error, option: ErrorOption) {
|
|
277
|
-
const newOpt: ErrorOption =
|
|
278
|
-
newOpt.info =
|
|
278
|
+
const newOpt: ErrorOption = assign({}, option);
|
|
279
|
+
newOpt.info = assign({}, option.info || {}, {
|
|
279
280
|
msg: e.message,
|
|
280
281
|
stack: e.stack,
|
|
281
282
|
});
|
package/src/spy-client.ts
CHANGED
|
@@ -23,6 +23,11 @@ import {
|
|
|
23
23
|
TimingCB,
|
|
24
24
|
TTICB,
|
|
25
25
|
TTIOption,
|
|
26
|
+
|
|
27
|
+
ResOption,
|
|
28
|
+
BigImgOption,
|
|
29
|
+
HttpResOption,
|
|
30
|
+
SlowOption
|
|
26
31
|
} from './lib/interface';
|
|
27
32
|
|
|
28
33
|
import FID from './module/fid';
|
|
@@ -103,16 +108,20 @@ export default class SpyClient extends SpyClientBasic {
|
|
|
103
108
|
return this.invoke('getNavigatorInfo');
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
listenResource(cb: ResourceCB) {
|
|
107
|
-
this.invoke('listenResource', cb as any);
|
|
111
|
+
listenResource(cb: ResourceCB, option?: ResOption) {
|
|
112
|
+
this.invoke('listenResource', cb as any, option);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
listenBigImg(cb: ResourceErrorCB, option?: BigImgOption) {
|
|
116
|
+
this.invoke('listenBigImg', cb as any, option);
|
|
108
117
|
}
|
|
109
118
|
|
|
110
|
-
|
|
111
|
-
this.invoke('
|
|
119
|
+
listenHttpResource(cb: ResourceErrorCB, option?: HttpResOption) {
|
|
120
|
+
this.invoke('listenHttpResource', cb as any, option);
|
|
112
121
|
}
|
|
113
122
|
|
|
114
|
-
|
|
115
|
-
this.invoke('
|
|
123
|
+
listenSlowResource(cb: ResourceErrorCB, option?: SlowOption) {
|
|
124
|
+
this.invoke('listenSlowResource', cb as any, option);
|
|
116
125
|
}
|
|
117
126
|
|
|
118
127
|
listenTiming(cb: TimingCB) {
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import {huffmanEncode, huffmanDecode} from './lib/huffman';
|
|
2
|
+
|
|
3
|
+
interface Option {
|
|
4
|
+
defaultTrigger: boolean;
|
|
5
|
+
compress: 'huffman' | 'lzw' | 'no';
|
|
6
|
+
key: string;
|
|
7
|
+
maxRecordLen: number;
|
|
8
|
+
interval: number;
|
|
9
|
+
onFlush: (res: any) => void;
|
|
10
|
+
storage: 'indexedDB' | 'localstorage' | 'empty';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class Storage {
|
|
14
|
+
static isSupport: () => boolean;
|
|
15
|
+
|
|
16
|
+
set(key: string, value: any) {
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get(key: string, cb: any) {
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
rm(key: string) {
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class LS extends Storage {
|
|
30
|
+
static isSupport() {
|
|
31
|
+
return !!window.localStorage;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
set(key: string, value: any) {
|
|
35
|
+
try {
|
|
36
|
+
localStorage.setItem(key, value);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
console.error(e);
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get(key: string, cb: any) {
|
|
45
|
+
let res = null;
|
|
46
|
+
try {
|
|
47
|
+
res = localStorage.getItem(key);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
console.error(e);
|
|
51
|
+
}
|
|
52
|
+
if (cb) {
|
|
53
|
+
cb(res);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
rm(key: string) {
|
|
59
|
+
try {
|
|
60
|
+
localStorage.removeItem(key);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
console.error(e);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class IndexedDB extends Storage {
|
|
71
|
+
static isSupport() {
|
|
72
|
+
return !!window.indexedDB;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
set(key: string, value: any) {
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get(key: string, cb: any) {
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function assign(...args: any[]) {
|
|
85
|
+
const __assign = Object.assign || function __assign(t: any) {
|
|
86
|
+
for (let s, i = 1, n = arguments.length; i < n; i++) {
|
|
87
|
+
// eslint-disable-next-line prefer-rest-params
|
|
88
|
+
s = arguments[i];
|
|
89
|
+
for (const p in s) {
|
|
90
|
+
if (Object.prototype.hasOwnProperty.call(s, p)) {
|
|
91
|
+
t[p] = s[p];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return t;
|
|
96
|
+
};
|
|
97
|
+
return __assign.apply(this, args);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function utf8Encode(text: string) {
|
|
101
|
+
let result = '';
|
|
102
|
+
for (let n = 0; n < text.length; n++) {
|
|
103
|
+
const c = text.charCodeAt(n);
|
|
104
|
+
if (c < 128) {
|
|
105
|
+
result += String.fromCharCode(c);
|
|
106
|
+
}
|
|
107
|
+
else if (c > 127 && c < 2048) {
|
|
108
|
+
result += String.fromCharCode((c >> 6) | 192);
|
|
109
|
+
result += String.fromCharCode((c & 63) | 128);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
result += String.fromCharCode((c >> 12) | 224);
|
|
113
|
+
result += String.fromCharCode(((c >> 6) & 63) | 128);
|
|
114
|
+
result += String.fromCharCode((c & 63) | 128);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
// return window.btoa(result);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function utf8Decode(text: string) {
|
|
122
|
+
// text = window.atob(text);
|
|
123
|
+
let result = '';
|
|
124
|
+
let i = 0;
|
|
125
|
+
let c1 = 0;
|
|
126
|
+
let c2 = 0;
|
|
127
|
+
let c3 = 0;
|
|
128
|
+
while (i < text.length) {
|
|
129
|
+
c1 = text.charCodeAt(i);
|
|
130
|
+
if (c1 < 128) {
|
|
131
|
+
result += String.fromCharCode(c1);
|
|
132
|
+
i++;
|
|
133
|
+
}
|
|
134
|
+
else if (c1 > 191 && c1 < 224) {
|
|
135
|
+
c2 = text.charCodeAt(i + 1);
|
|
136
|
+
result += String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
|
|
137
|
+
i += 2;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
c2 = text.charCodeAt(i + 1);
|
|
141
|
+
c3 = text.charCodeAt(i + 2);
|
|
142
|
+
result += String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
|
143
|
+
i += 3;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
function lzw(text: string, deCompress = false) {
|
|
151
|
+
if (!text) {
|
|
152
|
+
return '';
|
|
153
|
+
};
|
|
154
|
+
if (!deCompress) {
|
|
155
|
+
text = utf8Encode(text);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let dict: any = {};
|
|
159
|
+
let out: any[] = [];
|
|
160
|
+
let prefix = text.charAt(0);
|
|
161
|
+
let curChar = prefix;
|
|
162
|
+
let oldPrefix = curChar;
|
|
163
|
+
let idx = 256;
|
|
164
|
+
let i;
|
|
165
|
+
let c;
|
|
166
|
+
let d;
|
|
167
|
+
let g = function () {
|
|
168
|
+
out.push(prefix.length > 1 ? String.fromCharCode(dict[prefix]) : prefix);
|
|
169
|
+
};
|
|
170
|
+
if (deCompress) {
|
|
171
|
+
out.push(prefix);
|
|
172
|
+
}
|
|
173
|
+
for (i = 1, c, d; i < text.length; i++) {
|
|
174
|
+
c = text.charAt(i);
|
|
175
|
+
if (deCompress) {
|
|
176
|
+
d = text.charCodeAt(i);
|
|
177
|
+
prefix = d < 256 ? c : dict[d] || (prefix + curChar);
|
|
178
|
+
out.push(prefix);
|
|
179
|
+
curChar = prefix.charAt(0);
|
|
180
|
+
dict[idx++] = oldPrefix + curChar;
|
|
181
|
+
oldPrefix = prefix;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
if (dict.hasOwnProperty(prefix + c)) {
|
|
185
|
+
prefix += c;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
g();
|
|
189
|
+
dict[prefix + c] = idx++;
|
|
190
|
+
prefix = c;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!deCompress) {
|
|
195
|
+
g();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let ret = out.join('');
|
|
199
|
+
if (deCompress) {
|
|
200
|
+
ret = utf8Decode(ret);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return ret;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
export default class SpyLocalCache {
|
|
208
|
+
private readonly option: Option;
|
|
209
|
+
private storage: Storage;
|
|
210
|
+
private timer: ReturnType<typeof setTimeout>;
|
|
211
|
+
private tmpList: string[] = [];
|
|
212
|
+
constructor(option: any = {}) {
|
|
213
|
+
this.option = assign({
|
|
214
|
+
defaultTrigger: true,
|
|
215
|
+
compress: 'lzw', // huffman lzw no
|
|
216
|
+
key: 'SpyLocalCache',
|
|
217
|
+
interval: 500,
|
|
218
|
+
maxRecordLen: 30,
|
|
219
|
+
onFlush: () => {},
|
|
220
|
+
storage: IndexedDB.isSupport()
|
|
221
|
+
? 'indexedDB'
|
|
222
|
+
: LS.isSupport()
|
|
223
|
+
? 'localstorage'
|
|
224
|
+
: 'empty',
|
|
225
|
+
}, option);
|
|
226
|
+
|
|
227
|
+
this.load = this.load.bind(this);
|
|
228
|
+
|
|
229
|
+
this.init();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
init() {
|
|
233
|
+
if (this.option.storage === 'indexedDB') {
|
|
234
|
+
this.storage = new IndexedDB();
|
|
235
|
+
}
|
|
236
|
+
else if (this.option.storage === 'localstorage') {
|
|
237
|
+
this.storage = new LS();
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
this.storage = new Storage();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (document.readyState === 'complete') {
|
|
244
|
+
this.load();
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
window.addEventListener('load', this.load);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
load() {
|
|
252
|
+
if (location.search.indexOf('_FlushLogLocalCache=1') > -1 && this.option.defaultTrigger) {
|
|
253
|
+
this.flushLog();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
addLog(info: any) {
|
|
258
|
+
info = JSON.stringify(info);
|
|
259
|
+
|
|
260
|
+
this.tmpList.push(info as string);
|
|
261
|
+
|
|
262
|
+
// 控制写日志频率
|
|
263
|
+
if (this.timer) {
|
|
264
|
+
clearTimeout(this.timer);
|
|
265
|
+
}
|
|
266
|
+
this.timer = setTimeout(() => {
|
|
267
|
+
this.save();
|
|
268
|
+
}, this.option.interval);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
getData(cb: any) {
|
|
272
|
+
try {
|
|
273
|
+
this.storage.get(this.option.key, (encodeStr: string) => {
|
|
274
|
+
if (encodeStr) {
|
|
275
|
+
this.storage.get(this.option.key + 'Codes', (codes: any) => {
|
|
276
|
+
let res: any[] = [];
|
|
277
|
+
try {
|
|
278
|
+
if (codes) {
|
|
279
|
+
codes = JSON.parse(codes);
|
|
280
|
+
}
|
|
281
|
+
const str = this.unzip(encodeStr, codes);
|
|
282
|
+
res = str.split('\n');
|
|
283
|
+
}
|
|
284
|
+
catch (e) {
|
|
285
|
+
console.error(e);
|
|
286
|
+
}
|
|
287
|
+
cb(res);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
cb([]);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
}
|
|
296
|
+
catch (e) {
|
|
297
|
+
console.error(e);
|
|
298
|
+
cb([]);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
save() {
|
|
303
|
+
this.getData((list: any[]) => {
|
|
304
|
+
// 只保留最近的maxRecordLen条日志
|
|
305
|
+
const reserveLen = this.option.maxRecordLen - 1;
|
|
306
|
+
const originList = list.length > reserveLen ? list.slice(list.length - reserveLen, list.length) : list;
|
|
307
|
+
let content = originList.concat(this.tmpList).join('\n');
|
|
308
|
+
let data = this.zip(content);
|
|
309
|
+
let codesStr = '';
|
|
310
|
+
if (data.codes) {
|
|
311
|
+
codesStr = JSON.stringify(data.codes);
|
|
312
|
+
this.storage.set(this.option.key + 'Codes', codesStr);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
this.storage.rm(this.option.key + 'Codes');
|
|
316
|
+
}
|
|
317
|
+
this.storage.set(this.option.key, data.result);
|
|
318
|
+
|
|
319
|
+
this.tmpList = [];
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
flushLog() {
|
|
324
|
+
this.getData((list: any[]) => {
|
|
325
|
+
|
|
326
|
+
// 先解析来自存储的数据,若失败,说明格式有问题,清空存储
|
|
327
|
+
try {
|
|
328
|
+
for (let index = 0; index < list.length; index++) {
|
|
329
|
+
list[index] = JSON.parse(list[index]);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
list = [];
|
|
334
|
+
this.storage.rm(this.option.key);
|
|
335
|
+
console.error(e);
|
|
336
|
+
}
|
|
337
|
+
// 未落盘的数据也加上
|
|
338
|
+
for (let index = 0; index < this.tmpList.length; index++) {
|
|
339
|
+
list.push(JSON.parse(this.tmpList[index]));
|
|
340
|
+
}
|
|
341
|
+
this.option.onFlush && this.option.onFlush(list);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
zip(str: string) {
|
|
346
|
+
if (this.option.compress === 'lzw') {
|
|
347
|
+
return {
|
|
348
|
+
codes: null,
|
|
349
|
+
result: lzw(str),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
else if (this.option.compress === 'huffman') {
|
|
353
|
+
return huffmanEncode(str);
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
codes: null,
|
|
357
|
+
result: str,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
unzip(str: string, codes?: any) {
|
|
362
|
+
if (this.option.compress === 'lzw') {
|
|
363
|
+
return lzw(str, true);
|
|
364
|
+
}
|
|
365
|
+
else if (this.option.compress === 'huffman') {
|
|
366
|
+
return huffmanDecode(codes, str);
|
|
367
|
+
}
|
|
368
|
+
return str;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// For test
|
|
373
|
+
// let content = JSON.stringify({
|
|
374
|
+
// type: 3,
|
|
375
|
+
// fm: 'disp',
|
|
376
|
+
// data: [{"base":{"size":{"doc":{"w":360,"h":4875},"wind":{"w":360,"h":640},"scr":{"w":360,"h":640}},"vsb":"visible","num":16},"t":1629773746698,"path":"/s"}],
|
|
377
|
+
// qid: 10991431029479106376,
|
|
378
|
+
// did: '8dd09c47c7bc90c9fd7274f0ad2c581e',
|
|
379
|
+
// q: '刘德华',
|
|
380
|
+
// t: 1629773746698,
|
|
381
|
+
// });
|
|
382
|
+
// const compressText = lzw(content);
|
|
383
|
+
// const deCompressText = lzw(compressText, true);
|
|
384
|
+
// console.log('compressText', compressText, compressText.length);
|
|
385
|
+
// console.log('deCompressText', deCompressText, deCompressText.length);
|