mon-reactive-h5 1.0.6

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.
@@ -0,0 +1,913 @@
1
+ "use strict";
2
+
3
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4
+ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i.return) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
5
+ function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
6
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
7
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
8
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
9
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
10
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
11
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
12
+ function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
13
+ function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
14
+ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
15
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
16
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
17
+ var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
18
+ function adopt(value) {
19
+ return value instanceof P ? value : new P(function (resolve) {
20
+ resolve(value);
21
+ });
22
+ }
23
+ return new (P || (P = Promise))(function (resolve, reject) {
24
+ function fulfilled(value) {
25
+ try {
26
+ step(generator.next(value));
27
+ } catch (e) {
28
+ reject(e);
29
+ }
30
+ }
31
+ function rejected(value) {
32
+ try {
33
+ step(generator["throw"](value));
34
+ } catch (e) {
35
+ reject(e);
36
+ }
37
+ }
38
+ function step(result) {
39
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
40
+ }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", {
45
+ value: true
46
+ });
47
+ exports.MonitorSDK = void 0;
48
+ var BatchReporter_1 = require("./BatchReporter");
49
+ var reporters_1 = require("./reporters");
50
+ /**
51
+ * 前端监控SDK核心类
52
+ */
53
+ var MonitorSDK = /*#__PURE__*/function () {
54
+ /**
55
+ * 构造函数
56
+ * @param config SDK配置
57
+ */
58
+ function MonitorSDK(config) {
59
+ _classCallCheck(this, MonitorSDK);
60
+ // 记录上一页URL,用于单页应用路由变化时的referrer
61
+ this.lastPageUrl = document.referrer || '';
62
+ this.appId = config.appId || '';
63
+ this.reportUrl = config.reportUrl || '';
64
+ // 确保sampleRates是Record<string, number>类型
65
+ if (config.sampleRates) {
66
+ this.sampleRates = Object.entries(config.sampleRates).reduce(function (acc, _ref) {
67
+ var _ref2 = _slicedToArray(_ref, 2),
68
+ key = _ref2[0],
69
+ value = _ref2[1];
70
+ acc[key] = value !== undefined ? value : 1;
71
+ return acc;
72
+ }, {});
73
+ } else {
74
+ this.sampleRates = {
75
+ pageview: 1,
76
+ jserror: 1,
77
+ api: 1,
78
+ resource: 1
79
+ };
80
+ }
81
+ this.batchReporter = new BatchReporter_1.BatchReporter({
82
+ url: this.reportUrl,
83
+ batchSize: config.batchSize || 10,
84
+ interval: config.interval || 5000
85
+ });
86
+ this.sessionId = this.generateSessionId();
87
+ // 优先使用配置中的userId,如果没有则尝试从token中提取
88
+ this.userId = config.userId || this.getUserIdFromToken();
89
+ // 获取版本号,PC端从process.env.VUE_APP_VERSION获取,小程序和APP使用各自的版本号
90
+ this.version = this.getAppVersion(config);
91
+ this.init();
92
+ }
93
+ /**
94
+ * 获取应用版本号
95
+ */
96
+ return _createClass(MonitorSDK, [{
97
+ key: "getAppVersion",
98
+ value: function getAppVersion(config) {
99
+ // 优先使用配置传入的版本号
100
+ if (config.version) {
101
+ return config.version;
102
+ }
103
+ // PC端尝试从环境变量获取
104
+ if (typeof process !== 'undefined' && process.env && process.env.VUE_APP_VERSION) {
105
+ return process.env.VUE_APP_VERSION;
106
+ }
107
+ // 小程序环境检测
108
+ if (typeof wx !== 'undefined' && wx.getSystemInfoSync) {
109
+ try {
110
+ var systemInfo = wx.getSystemInfoSync();
111
+ return systemInfo.AppVersion || systemInfo.version || '1.0.0';
112
+ } catch (e) {
113
+ // 忽略错误
114
+ }
115
+ }
116
+ // APP环境检测
117
+ if (typeof window !== 'undefined' && window.navigator && window.navigator.userAgent) {
118
+ // 根据实际APP的userAgent格式调整
119
+ var ua = window.navigator.userAgent;
120
+ // 这里可以根据具体APP的UA格式进行解析
121
+ }
122
+ return '1.0.0'; // 默认版本号
123
+ }
124
+ /**
125
+ * 生成会话ID
126
+ */
127
+ }, {
128
+ key: "generateSessionId",
129
+ value: function generateSessionId() {
130
+ return "".concat(Date.now(), "_").concat(Math.random().toString(36).substring(2, 9));
131
+ }
132
+ /**
133
+ * 从token中提取userId
134
+ * 小程序环境:直接从本地缓存中取"token"
135
+ * 网页环境:先尝试从sessionStorage中取"-token",再尝试从cookie里取"Admin-Token"
136
+ * 取出token后截取最后12位作为userid
137
+ */
138
+ }, {
139
+ key: "getUserIdFromToken",
140
+ value: function getUserIdFromToken() {
141
+ var token = null;
142
+ // 1. 先检查是否为小程序环境
143
+ try {
144
+ if (typeof wx !== 'undefined' && wx.getStorageSync) {
145
+ // 小程序环境,直接从本地缓存中获取token
146
+ var wxToken = wx.getStorageSync('token');
147
+ if (wxToken) {
148
+ token = wxToken;
149
+ }
150
+ }
151
+ } catch (error) {
152
+ console.warn('Failed to get token from wx storage:', error);
153
+ }
154
+ // 2. 如果不是小程序环境或未获取到token,尝试从sessionStorage中获取token
155
+ if (!token) {
156
+ try {
157
+ if (typeof window !== 'undefined' && window.sessionStorage) {
158
+ token = sessionStorage.getItem('-token');
159
+ }
160
+ } catch (error) {
161
+ console.warn('Failed to get token from sessionStorage:', error);
162
+ }
163
+ }
164
+ // 3. 如果sessionStorage中没有token,尝试从cookie中获取
165
+ if (!token) {
166
+ try {
167
+ if (typeof document !== 'undefined') {
168
+ var cookieMatch = document.cookie.match(/Admin-Token=([^;]+)/);
169
+ if (cookieMatch && cookieMatch[1]) {
170
+ token = cookieMatch[1];
171
+ }
172
+ }
173
+ } catch (error) {
174
+ console.warn('Failed to get token from cookie:', error);
175
+ }
176
+ }
177
+ // 4. 从token中提取userId(截取最后12位)
178
+ if (token && token.length >= 12) {
179
+ return token.slice(-12);
180
+ }
181
+ return undefined;
182
+ }
183
+ /**
184
+ * 初始化SDK
185
+ */
186
+ }, {
187
+ key: "init",
188
+ value: function init() {
189
+ var _this = this;
190
+ // 初始化各类监控
191
+ this.initPageViewMonitor();
192
+ this.initErrorMonitor();
193
+ this.initApiMonitor();
194
+ this.initResourceMonitor();
195
+ // 页面卸载时清理
196
+ window.addEventListener('unload', function () {
197
+ _this.cleanup();
198
+ });
199
+ }
200
+ /**
201
+ * 清理资源
202
+ */
203
+ }, {
204
+ key: "cleanup",
205
+ value: function cleanup() {
206
+ // 恢复原始方法
207
+ if (this.originalFetch) {
208
+ window.fetch = this.originalFetch;
209
+ }
210
+ if (this.originalXHRSend) {
211
+ XMLHttpRequest.prototype.send = this.originalXHRSend;
212
+ }
213
+ // 销毁批量上报器
214
+ if (this.batchReporter) {
215
+ this.batchReporter.destroy();
216
+ }
217
+ }
218
+ /**
219
+ * 恢复原始方法
220
+ */
221
+ }, {
222
+ key: "restoreOriginalMethods",
223
+ value: function restoreOriginalMethods() {
224
+ if (this.originalFetch) {
225
+ window.fetch = this.originalFetch;
226
+ }
227
+ if (this.originalXHRSend) {
228
+ XMLHttpRequest.prototype.send = this.originalXHRSend;
229
+ }
230
+ }
231
+ /**
232
+ * 页面浏览监控
233
+ */
234
+ }, {
235
+ key: "initPageViewMonitor",
236
+ value: function initPageViewMonitor() {
237
+ var _this2 = this;
238
+ var startTime = Date.now();
239
+ var pageInfo = {
240
+ pageUrl: window.location.href,
241
+ pageTitle: document.title,
242
+ referrer: document.referrer,
243
+ timestamp: startTime
244
+ };
245
+ // 更新lastPageUrl为当前页面URL
246
+ this.lastPageUrl = window.location.href;
247
+ // 页面加载完成上报
248
+ window.addEventListener('load', function () {
249
+ var performanceData = _this2.getPerformanceData();
250
+ _this2.reportPageView(Object.assign(Object.assign({}, pageInfo), {
251
+ duration: Date.now() - startTime,
252
+ isFirstVisit: _this2.isFirstVisit(),
253
+ performance: performanceData
254
+ }));
255
+ });
256
+ // 单页应用路由变化监控
257
+ if (window.history.pushState) {
258
+ var originalPushState = window.history.pushState;
259
+ var originalReplaceState = window.history.replaceState;
260
+ var handleHistoryChange = function handleHistoryChange() {
261
+ // 获取当前URL
262
+ var currentUrl = window.location.href;
263
+ // 只有当URL真正变化时才上报,避免replaceState导致的重复上报
264
+ if (currentUrl !== _this2.lastPageUrl) {
265
+ _this2.reportPageView({
266
+ pageUrl: currentUrl,
267
+ pageTitle: document.title,
268
+ referrer: _this2.lastPageUrl,
269
+ // 使用上一页URL作为referrer
270
+ timestamp: Date.now(),
271
+ duration: 0,
272
+ isFirstVisit: false,
273
+ performance: _this2.getPerformanceData()
274
+ });
275
+ // 更新上一页URL为当前URL
276
+ _this2.lastPageUrl = currentUrl;
277
+ }
278
+ };
279
+ window.history.pushState = function () {
280
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
281
+ args[_key] = arguments[_key];
282
+ }
283
+ originalPushState.apply(this, args);
284
+ handleHistoryChange();
285
+ };
286
+ window.history.replaceState = function () {
287
+ // 保存调用前的URL
288
+ var prevUrl = window.location.href;
289
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
290
+ args[_key2] = arguments[_key2];
291
+ }
292
+ originalReplaceState.apply(this, args);
293
+ handleHistoryChange();
294
+ };
295
+ // 监听popstate事件
296
+ window.addEventListener('popstate', handleHistoryChange);
297
+ }
298
+ }
299
+ /**
300
+ * 错误监控
301
+ */
302
+ }, {
303
+ key: "initErrorMonitor",
304
+ value: function initErrorMonitor() {
305
+ var _this3 = this;
306
+ // JS错误
307
+ window.addEventListener('error', function (event) {
308
+ _this3.reportJSError({
309
+ message: event.message,
310
+ errorType: event.error ? event.error.name : 'Error',
311
+ file: event.filename,
312
+ line: event.lineno || 0,
313
+ column: event.colno || 0,
314
+ stack: event.error ? event.error.stack || '' : '',
315
+ url: window.location.href,
316
+ timestamp: Date.now()
317
+ });
318
+ });
319
+ // Promise错误
320
+ window.addEventListener('unhandledrejection', function (event) {
321
+ _this3.reportJSError({
322
+ message: event.reason ? String(event.reason) : 'Promise rejection',
323
+ errorType: 'PromiseRejection',
324
+ file: '',
325
+ line: 0,
326
+ column: 0,
327
+ stack: event.reason ? event.reason.stack || '' : '',
328
+ url: window.location.href,
329
+ timestamp: Date.now()
330
+ });
331
+ });
332
+ }
333
+ /**
334
+ * API监控
335
+ */
336
+ }, {
337
+ key: "initApiMonitor",
338
+ value: function initApiMonitor() {
339
+ var _this4 = this;
340
+ // 生成请求ID
341
+ var generateRequestId = function generateRequestId() {
342
+ return "".concat(Date.now(), "_").concat(Math.random().toString(36).substring(2, 11));
343
+ };
344
+ // 解析URL,分离基础URL和完整URL
345
+ var parseUrl = function parseUrl(urlStr) {
346
+ try {
347
+ var urlObj = new URL(urlStr);
348
+ // 基础URL(不包含查询参数和hash)
349
+ var baseUrl = "".concat(urlObj.origin).concat(urlObj.pathname);
350
+ // 完整URL(包含查询参数和hash)
351
+ var fullUrl = urlObj.href;
352
+ return {
353
+ baseUrl: baseUrl,
354
+ fullUrl: fullUrl
355
+ };
356
+ } catch (_a) {
357
+ // 如果URL解析失败,则使用原始URL
358
+ return {
359
+ baseUrl: urlStr,
360
+ fullUrl: urlStr
361
+ };
362
+ }
363
+ };
364
+ // 重写fetch
365
+ this.originalFetch = window.fetch;
366
+ window.fetch = function () {
367
+ for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
368
+ args[_key3] = arguments[_key3];
369
+ }
370
+ return __awaiter(_this4, void 0, void 0, /*#__PURE__*/_regenerator().m(function _callee() {
371
+ var _a, url, options, urlStr, isSdkInternalRequest, knownReportPaths, isKnownReportPath, isConfiguredReportUrl, isBatchReportPath, startTime, method, requestId, _parseUrl, baseUrl, fullUrl, response, reportData, _reportData, _t;
372
+ return _regenerator().w(function (_context) {
373
+ while (1) switch (_context.p = _context.n) {
374
+ case 0:
375
+ url = args[0], options = args[1];
376
+ urlStr = typeof url === 'string' ? url : url.toString(); // 排除SDK自身的上报请求,避免死循环
377
+ // 1. 检查是否有特殊标记表示这是SDK内部请求
378
+ isSdkInternalRequest = ((_a = options === null || options === void 0 ? void 0 : options.headers) === null || _a === void 0 ? void 0 : _a['x-sdk-internal']) === 'true'; // 2. 检查是否是已知的SDK上报路径,包括批量上报相关路径
379
+ knownReportPaths = ['/v1/report/collect', '/v1/report/collectImg', '/api/report/collect'];
380
+ isKnownReportPath = knownReportPaths.some(function (path) {
381
+ return urlStr.includes(path);
382
+ }); // 3. 检查是否是配置的上报URL
383
+ isConfiguredReportUrl = this.reportUrl && urlStr.includes(this.reportUrl); // 4. 对于没有头信息的请求(如sendBeacon和img上报),增加更严格的路径匹配
384
+ isBatchReportPath = urlStr.includes('/v1/report/collect') || urlStr.includes('/v1/report/collectImg'); // 如果是SDK自身的上报请求,则直接使用原始fetch
385
+ if (!(isSdkInternalRequest || isKnownReportPath || isConfiguredReportUrl || isBatchReportPath)) {
386
+ _context.n = 1;
387
+ break;
388
+ }
389
+ return _context.a(2, this.originalFetch.apply(this, args));
390
+ case 1:
391
+ startTime = Date.now();
392
+ method = (options === null || options === void 0 ? void 0 : options.method) || 'GET';
393
+ requestId = generateRequestId();
394
+ _parseUrl = parseUrl(urlStr), baseUrl = _parseUrl.baseUrl, fullUrl = _parseUrl.fullUrl;
395
+ _context.p = 2;
396
+ _context.n = 3;
397
+ return this.originalFetch.apply(this, args);
398
+ case 3:
399
+ response = _context.v;
400
+ reportData = {
401
+ requestId: requestId,
402
+ url: baseUrl,
403
+ fullUrl: fullUrl,
404
+ method: method,
405
+ status: response.status,
406
+ isError: response.status >= 400,
407
+ responseTime: Date.now() - startTime,
408
+ timestamp: startTime,
409
+ pageUrl: window.location.href
410
+ }; // 上报API数据
411
+ this.reportApi(reportData);
412
+ return _context.a(2, response);
413
+ case 4:
414
+ _context.p = 4;
415
+ _t = _context.v;
416
+ _reportData = {
417
+ requestId: requestId,
418
+ url: baseUrl,
419
+ fullUrl: fullUrl,
420
+ method: method,
421
+ status: 0,
422
+ isError: true,
423
+ errorType: _t.name || 'FetchError',
424
+ responseTime: Date.now() - startTime,
425
+ timestamp: startTime,
426
+ pageUrl: window.location.href
427
+ }; // 上报错误API
428
+ this.reportApi(_reportData);
429
+ throw _t;
430
+ case 5:
431
+ return _context.a(2);
432
+ }
433
+ }, _callee, this, [[2, 4]]);
434
+ }));
435
+ };
436
+ // 重写XMLHttpRequest
437
+ var originalXHROpen = XMLHttpRequest.prototype.open;
438
+ var originalSend = XMLHttpRequest.prototype.send;
439
+ this.originalXHRSend = originalSend;
440
+ var xhrMap = new WeakMap();
441
+ var that = this;
442
+ // 覆盖XMLHttpRequest.open方法
443
+ var originalOpen = originalXHROpen;
444
+ XMLHttpRequest.prototype.open = function (method, url, async, username, password) {
445
+ var urlStr = typeof url === 'string' ? url : url.toString();
446
+ // 排除SDK自身的上报请求,避免死循环
447
+ // 1. 检查是否有特殊标记表示这是SDK内部请求(通过自定义属性)
448
+ if (this._isSdkInternalRequest) {
449
+ return originalOpen.call(this, method, url, async === undefined ? true : async, username, password);
450
+ }
451
+ // 2. 检查是否是已知的SDK上报路径,包括批量上报相关路径
452
+ var knownReportPaths = ['/v1/report/collect', '/v1/report/collectImg', '/api/report/collect'];
453
+ var isKnownReportPath = knownReportPaths.some(function (path) {
454
+ return urlStr.includes(path);
455
+ });
456
+ // 3. 检查是否是配置的上报URL
457
+ var isConfiguredReportUrl = that.reportUrl && urlStr.includes(that.reportUrl);
458
+ // 4. 对于没有头信息的请求(如sendBeacon和img上报),增加更严格的路径匹配
459
+ var isBatchReportPath = urlStr.includes('/v1/report/collect') || urlStr.includes('/v1/report/collectImg');
460
+ // 如果是SDK自身的上报请求,则直接使用原始open方法
461
+ if (isKnownReportPath || isConfiguredReportUrl || isBatchReportPath) {
462
+ return originalOpen.call(this, method, url, async === undefined ? true : async, username, password);
463
+ }
464
+ var _parseUrl2 = parseUrl(urlStr),
465
+ baseUrl = _parseUrl2.baseUrl,
466
+ fullUrl = _parseUrl2.fullUrl;
467
+ var requestId = generateRequestId();
468
+ xhrMap.set(this, {
469
+ startTime: Date.now(),
470
+ url: baseUrl,
471
+ fullUrl: fullUrl,
472
+ method: method || 'GET',
473
+ requestId: requestId
474
+ });
475
+ return originalOpen.call(this, method, url, async === undefined ? true : async, username, password);
476
+ };
477
+ XMLHttpRequest.prototype.send = function () {
478
+ var _this5 = this;
479
+ var xhrInfo = xhrMap.get(this);
480
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
481
+ args[_key4] = arguments[_key4];
482
+ }
483
+ if (!xhrInfo) {
484
+ // @ts-ignore
485
+ return originalSend.apply(this, args);
486
+ }
487
+ this.addEventListener('loadend', function () {
488
+ var endTime = Date.now();
489
+ var responseTime = endTime - xhrInfo.startTime;
490
+ var status = _this5.status;
491
+ var reportData = {
492
+ requestId: xhrInfo.requestId,
493
+ url: xhrInfo.url,
494
+ fullUrl: xhrInfo.fullUrl,
495
+ method: xhrInfo.method,
496
+ status: status,
497
+ isError: status >= 400 || _this5.status === 0,
498
+ responseTime: responseTime,
499
+ timestamp: xhrInfo.startTime,
500
+ pageUrl: window.location.href,
501
+ errorType: status === 0 ? 'NetworkError' : undefined
502
+ };
503
+ // 上报API数据
504
+ that.reportApi(reportData);
505
+ });
506
+ return originalSend.apply(this, args);
507
+ };
508
+ }
509
+ /**
510
+ * 资源监控
511
+ */
512
+ }, {
513
+ key: "initResourceMonitor",
514
+ value: function initResourceMonitor() {
515
+ var _this6 = this;
516
+ // 通过PerformanceObserver监控资源加载
517
+ if ('PerformanceObserver' in window) {
518
+ try {
519
+ var observer = new PerformanceObserver(function (list) {
520
+ list.getEntries().forEach(function (entry) {
521
+ if (entry.entryType === 'resource') {
522
+ var resourceEntry = entry;
523
+ // 检查资源是否加载失败
524
+ if (resourceEntry.responseStatus >= 400) {
525
+ _this6.reportResource({
526
+ url: resourceEntry.name,
527
+ type: _this6.getResourceType(resourceEntry.name),
528
+ status: resourceEntry.responseStatus,
529
+ timestamp: resourceEntry.startTime,
530
+ pageUrl: window.location.href,
531
+ duration: resourceEntry.duration,
532
+ size: resourceEntry.transferSize || 0,
533
+ initiator: resourceEntry.initiatorType
534
+ });
535
+ }
536
+ }
537
+ });
538
+ });
539
+ observer.observe({
540
+ entryTypes: ['resource'],
541
+ buffered: true
542
+ });
543
+ } catch (error) {
544
+ console.error('PerformanceObserver初始化失败:', error);
545
+ }
546
+ }
547
+ // 监听资源错误事件
548
+ window.addEventListener('error', function (event) {
549
+ if (event.target && (event.target instanceof HTMLScriptElement || event.target instanceof HTMLLinkElement || event.target instanceof HTMLImageElement || event.target instanceof HTMLAudioElement || event.target instanceof HTMLVideoElement)) {
550
+ var target = event.target;
551
+ _this6.reportResource({
552
+ url: target.src || target.href || '',
553
+ type: _this6.getResourceType(target.src || target.href || ''),
554
+ status: 0,
555
+ errorType: 'loaderror',
556
+ timestamp: Date.now(),
557
+ pageUrl: window.location.href,
558
+ initiator: target.tagName.toLowerCase(),
559
+ duration: 0,
560
+ size: 0
561
+ });
562
+ }
563
+ }, true);
564
+ }
565
+ /**
566
+ * 获取资源类型
567
+ */
568
+ }, {
569
+ key: "getResourceType",
570
+ value: function getResourceType(url) {
571
+ var _a;
572
+ var ext = (_a = url.split('?')[0].split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
573
+ if (!ext) return 'other';
574
+ if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico'].includes(ext)) return 'image';
575
+ if (['js'].includes(ext)) return 'js';
576
+ if (['css'].includes(ext)) return 'css';
577
+ if (['woff', 'woff2', 'ttf', 'eot', 'otf'].includes(ext)) return 'font';
578
+ if (['mp4', 'webm', 'ogg'].includes(ext)) return 'video';
579
+ if (['mp3', 'wav', 'ogg'].includes(ext)) return 'audio';
580
+ return 'other';
581
+ }
582
+ /**
583
+ * 获取性能数据
584
+ */
585
+ }, {
586
+ key: "getPerformanceData",
587
+ value: function getPerformanceData() {
588
+ var timing = performance.timing;
589
+ return {
590
+ navigationStart: timing.navigationStart,
591
+ domContentLoadedEventEnd: timing.domContentLoadedEventEnd,
592
+ loadEventEnd: timing.loadEventEnd
593
+ };
594
+ }
595
+ /**
596
+ * 判断是否首次访问
597
+ */
598
+ }, {
599
+ key: "isFirstVisit",
600
+ value: function isFirstVisit() {
601
+ var key = "first_visit_".concat(this.appId);
602
+ if (!localStorage.getItem(key)) {
603
+ localStorage.setItem(key, 'true');
604
+ return true;
605
+ }
606
+ return false;
607
+ }
608
+ /**
609
+ * 上报页面浏览
610
+ */
611
+ }, {
612
+ key: "reportPageView",
613
+ value: function reportPageView(data) {
614
+ var payload = {
615
+ type: 'pageview',
616
+ data: Object.assign({
617
+ appId: this.appId,
618
+ version: this.version,
619
+ sessionId: this.sessionId,
620
+ userId: this.userId,
621
+ deviceInfo: this.getDeviceInfo(),
622
+ networkInfo: this.getNetworkInfo()
623
+ }, data)
624
+ };
625
+ this.doReport(payload, 'pageview');
626
+ }
627
+ /**
628
+ * 上报JS错误
629
+ */
630
+ }, {
631
+ key: "reportJSError",
632
+ value: function reportJSError(data) {
633
+ var payload = {
634
+ type: 'jserror',
635
+ data: Object.assign({
636
+ appId: this.appId,
637
+ version: this.version,
638
+ sessionId: this.sessionId,
639
+ userId: this.userId,
640
+ deviceInfo: this.getDeviceInfo()
641
+ }, data)
642
+ };
643
+ this.doReport(payload, 'jserror');
644
+ }
645
+ /**
646
+ * 上报API数据
647
+ */
648
+ }, {
649
+ key: "reportApi",
650
+ value: function reportApi(data) {
651
+ var payload = {
652
+ type: 'api',
653
+ data: Object.assign({
654
+ appId: this.appId,
655
+ version: this.version,
656
+ sessionId: this.sessionId,
657
+ userId: this.userId,
658
+ networkInfo: this.getNetworkInfo()
659
+ }, data)
660
+ };
661
+ this.doReport(payload, 'api');
662
+ }
663
+ /**
664
+ * 上报资源错误
665
+ */
666
+ }, {
667
+ key: "reportResource",
668
+ value: function reportResource(data) {
669
+ var payload = {
670
+ type: 'resource',
671
+ data: Object.assign({
672
+ appId: this.appId,
673
+ version: this.version,
674
+ sessionId: this.sessionId,
675
+ userId: this.userId
676
+ }, data)
677
+ };
678
+ this.doReport(payload, 'resource');
679
+ }
680
+ /**
681
+ * 统一上报处理
682
+ */
683
+ }, {
684
+ key: "doReport",
685
+ value: function doReport(payload, type) {
686
+ var _this7 = this;
687
+ var _a;
688
+ // 采样处理
689
+ var sampleRate = (_a = this.sampleRates[type]) !== null && _a !== void 0 ? _a : 1;
690
+ if (Math.random() > sampleRate) return;
691
+ // 数据脱敏
692
+ var sanitizedPayload = Object.assign(Object.assign({}, payload), {
693
+ data: this.sanitizeData(Object.assign(Object.assign({}, payload.data), {
694
+ version: this.version
695
+ }))
696
+ });
697
+ // 根据数据类型选择上报方式
698
+ if (type === 'pageview' && typeof navigator.sendBeacon === 'function') {
699
+ // 页面浏览使用sendBeacon
700
+ (0, reporters_1.reportByBeacon)(sanitizedPayload, this.reportUrl);
701
+ } else if (type === 'jserror' || type === 'resource') {
702
+ // 错误数据立即上报
703
+ (0, reporters_1.reportByApi)(sanitizedPayload, this.reportUrl).catch(function () {
704
+ // 降级到img上报
705
+ (0, reporters_1.reportByImg)(sanitizedPayload, _this7.reportUrl);
706
+ });
707
+ } else {
708
+ // 其他数据批量上报
709
+ this.batchReporter.report(sanitizedPayload);
710
+ }
711
+ }
712
+ /**
713
+ * 获取设备信息
714
+ */
715
+ }, {
716
+ key: "getDeviceInfo",
717
+ value: function getDeviceInfo() {
718
+ var ua = navigator.userAgent;
719
+ return {
720
+ browser: this.getBrowser(ua),
721
+ browserVersion: this.getBrowserVersion(ua),
722
+ os: this.getOS(ua),
723
+ osVersion: this.getOSVersion(ua),
724
+ deviceType: this.getDeviceType(),
725
+ screenWidth: window.screen.width,
726
+ screenHeight: window.screen.height,
727
+ language: navigator.language || ''
728
+ };
729
+ }
730
+ /**
731
+ * 获取网络信息
732
+ */
733
+ }, {
734
+ key: "getNetworkInfo",
735
+ value: function getNetworkInfo() {
736
+ // 安全地获取网络信息
737
+ var connection = navigator.connection || null;
738
+ if (connection) {
739
+ return {
740
+ type: connection.effectiveType || 'unknown',
741
+ effectiveType: connection.effectiveType || 'unknown'
742
+ };
743
+ }
744
+ return {
745
+ type: 'unknown',
746
+ effectiveType: 'unknown'
747
+ };
748
+ }
749
+ /**
750
+ * 获取浏览器信息
751
+ */
752
+ }, {
753
+ key: "getBrowser",
754
+ value: function getBrowser(ua) {
755
+ if (ua.includes('Chrome')) return 'Chrome';
756
+ if (ua.includes('Safari') && !ua.includes('Chrome')) return 'Safari';
757
+ if (ua.includes('Firefox')) return 'Firefox';
758
+ if (ua.includes('Edge')) return 'Edge';
759
+ if (ua.includes('Trident') || ua.includes('MSIE')) return 'IE';
760
+ return 'Unknown';
761
+ }
762
+ /**
763
+ * 获取浏览器版本
764
+ */
765
+ }, {
766
+ key: "getBrowserVersion",
767
+ value: function getBrowserVersion(ua) {
768
+ // 简化版本,实际项目中可能需要更复杂的正则匹配
769
+ var versionRegex = /(Chrome|Safari|Firefox|Edge|MSIE)\/?\s*(\d+\.\d*)/i;
770
+ var match = ua.match(versionRegex);
771
+ return match ? match[2] : 'Unknown';
772
+ }
773
+ /**
774
+ * 获取操作系统
775
+ */
776
+ }, {
777
+ key: "getOS",
778
+ value: function getOS(ua) {
779
+ if (ua.includes('Windows')) return 'Windows';
780
+ if (ua.includes('Mac OS')) return 'MacOS';
781
+ if (ua.includes('iOS')) return 'iOS';
782
+ if (ua.includes('Android')) return 'Android';
783
+ if (ua.includes('Linux')) return 'Linux';
784
+ return 'Unknown';
785
+ }
786
+ /**
787
+ * 获取操作系统版本
788
+ */
789
+ }, {
790
+ key: "getOSVersion",
791
+ value: function getOSVersion(ua) {
792
+ // 简化版本
793
+ if (ua.includes('Windows')) {
794
+ if (ua.includes('Windows NT 10.0')) return '10';
795
+ if (ua.includes('Windows NT 6.3')) return '8.1';
796
+ if (ua.includes('Windows NT 6.2')) return '8';
797
+ if (ua.includes('Windows NT 6.1')) return '7';
798
+ return 'Unknown';
799
+ }
800
+ return 'Unknown';
801
+ }
802
+ /**
803
+ * 获取设备类型
804
+ */
805
+ }, {
806
+ key: "getDeviceType",
807
+ value: function getDeviceType() {
808
+ var ua = navigator.userAgent;
809
+ if (/mobile|android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(ua)) {
810
+ return 'mobile';
811
+ }
812
+ return 'desktop';
813
+ }
814
+ /**
815
+ * 数据脱敏
816
+ */
817
+ }, {
818
+ key: "sanitizeData",
819
+ value: function sanitizeData(data) {
820
+ // 深拷贝数据,避免修改原始数据
821
+ var sanitized = JSON.parse(JSON.stringify(data));
822
+ // 脱敏处理函数
823
+ var _sanitize = function sanitize(obj) {
824
+ if (!obj || _typeof(obj) !== 'object') return obj;
825
+ // 遍历对象属性
826
+ Object.keys(obj).forEach(function (key) {
827
+ // 对常见敏感字段进行脱敏
828
+ if (['password', 'token', 'auth', 'credential', 'key'].some(function (sensitive) {
829
+ return key.toLowerCase().includes(sensitive);
830
+ })) {
831
+ obj[key] = '***';
832
+ }
833
+ // 邮箱脱敏
834
+ else if (key.toLowerCase().includes('email') && typeof obj[key] === 'string') {
835
+ obj[key] = obj[key].replace(/(.{2})(.*)(@.*)/, '$1***$3');
836
+ }
837
+ // 手机号脱敏
838
+ else if (key.toLowerCase().includes('phone') && typeof obj[key] === 'string') {
839
+ obj[key] = obj[key].replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
840
+ }
841
+ // 递归处理嵌套对象
842
+ else if (_typeof(obj[key]) === 'object') {
843
+ _sanitize(obj[key]);
844
+ }
845
+ });
846
+ };
847
+ _sanitize(sanitized);
848
+ return sanitized;
849
+ }
850
+ /**
851
+ * 上报自定义事件
852
+ */
853
+ }, {
854
+ key: "reportCustomEvent",
855
+ value: function reportCustomEvent(eventType, eventData) {
856
+ var _a;
857
+ // 根据采样率决定是否上报
858
+ var sampleRate = (_a = this.sampleRates['custom']) !== null && _a !== void 0 ? _a : 1;
859
+ if (Math.random() > sampleRate) {
860
+ return;
861
+ }
862
+ var reportData = {
863
+ type: 'custom',
864
+ data: {
865
+ appId: this.appId,
866
+ version: this.version,
867
+ sessionId: this.sessionId,
868
+ userId: this.userId,
869
+ eventName: eventType,
870
+ eventData: this.sanitizeData(eventData),
871
+ timestamp: Date.now(),
872
+ pageUrl: window.location.href
873
+ }
874
+ };
875
+ // 使用批量上报
876
+ this.batchReporter.report(reportData);
877
+ }
878
+ /**
879
+ * 立即上报缓冲区数据
880
+ */
881
+ }, {
882
+ key: "flush",
883
+ value: function flush() {
884
+ return __awaiter(this, void 0, void 0, /*#__PURE__*/_regenerator().m(function _callee2() {
885
+ return _regenerator().w(function (_context2) {
886
+ while (1) switch (_context2.n) {
887
+ case 0:
888
+ if (!this.batchReporter) {
889
+ _context2.n = 1;
890
+ break;
891
+ }
892
+ _context2.n = 1;
893
+ return this.batchReporter.flush();
894
+ case 1:
895
+ return _context2.a(2);
896
+ }
897
+ }, _callee2, this);
898
+ }));
899
+ }
900
+ /**
901
+ * 销毁SDK实例
902
+ */
903
+ }, {
904
+ key: "destroy",
905
+ value: function destroy() {
906
+ this.cleanup();
907
+ this.batchReporter.destroy();
908
+ }
909
+ }]);
910
+ }();
911
+ exports.MonitorSDK = MonitorSDK;
912
+ exports.default = MonitorSDK;
913
+ //# sourceMappingURL=MonitorSDK.js.map