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
@@ -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 = Object.assign(
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(Object.assign({
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(Object.assign({
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(Object.assign({
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(Object.assign({
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 = Object.assign({}, option);
278
- newOpt.info = Object.assign({}, option.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
- listenBigImg(cb: ResourceErrorCB, maxSize?: number) {
111
- this.invoke('listenBigImg', cb as any, maxSize);
119
+ listenHttpResource(cb: ResourceErrorCB, option?: HttpResOption) {
120
+ this.invoke('listenHttpResource', cb as any, option);
112
121
  }
113
122
 
114
- listenHttpResource(cb: ResourceErrorCB) {
115
- this.invoke('listenHttpResource', cb as any);
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);