@qiu_jun/monitors 0.0.6 → 0.0.8

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,118 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: !0
5
+ }), exports.ReplayController = void 0, exports.deriveReplayEndpoint = deriveReplayEndpoint;
6
+
7
+ const uuid_1 = require("../utils/uuid"), upload_1 = require("./upload"), MAX_SEGMENTS = 2, DEFAULTS = {
8
+ bufferSeconds: 30,
9
+ maxUploadsPerSession: 10,
10
+ minUploadIntervalMs: 3e4,
11
+ maxReplayBytes: 2097152
12
+ };
13
+
14
+ function deriveReplayEndpoint(e) {
15
+ const t = e.replace(/\/+$/, "");
16
+ return t.endsWith("/report") ? `${t.slice(0, -7)}/replay` : void 0;
17
+ }
18
+
19
+ class ReplayController {
20
+ constructor(e) {
21
+ Object.defineProperty(this, "opts", {
22
+ enumerable: !0,
23
+ configurable: !0,
24
+ writable: !0,
25
+ value: e
26
+ }), Object.defineProperty(this, "segments", {
27
+ enumerable: !0,
28
+ configurable: !0,
29
+ writable: !0,
30
+ value: []
31
+ }), Object.defineProperty(this, "stopFn", {
32
+ enumerable: !0,
33
+ configurable: !0,
34
+ writable: !0,
35
+ value: null
36
+ }), Object.defineProperty(this, "starting", {
37
+ enumerable: !0,
38
+ configurable: !0,
39
+ writable: !0,
40
+ value: !1
41
+ }), Object.defineProperty(this, "lastUploadAt", {
42
+ enumerable: !0,
43
+ configurable: !0,
44
+ writable: !0,
45
+ value: 0
46
+ }), Object.defineProperty(this, "uploadCount", {
47
+ enumerable: !0,
48
+ configurable: !0,
49
+ writable: !0,
50
+ value: 0
51
+ });
52
+ }
53
+ async start() {
54
+ var e, t, s;
55
+ if (!this.stopFn && !this.starting) {
56
+ this.starting = !0;
57
+ try {
58
+ const {record: r} = await (import("rrweb"));
59
+ this.stopFn = null !== (s = r({
60
+ emit: (e, t) => {
61
+ for ((t || 0 === this.segments.length) && this.segments.push([]), this.segments[this.segments.length - 1].push(e); this.segments.length > 2; ) this.segments.shift();
62
+ },
63
+ checkoutEveryNms: Math.max(500 * (null !== (e = this.opts.bufferSeconds) && void 0 !== e ? e : DEFAULTS.bufferSeconds), 1e4),
64
+ maskAllInputs: !0,
65
+ maskTextClass: null !== (t = this.opts.maskTextClass) && void 0 !== t ? t : "rr-mask",
66
+ blockClass: "rr-block",
67
+ sampling: {
68
+ mousemove: 50,
69
+ scroll: 150,
70
+ input: "last"
71
+ }
72
+ })) && void 0 !== s ? s : null;
73
+ } catch (e) {}
74
+ }
75
+ }
76
+ captureIfTriggered(e) {
77
+ var t, s, r;
78
+ if (!this.isTrigger(e)) return;
79
+ const l = Date.now();
80
+ if (l - this.lastUploadAt < (null !== (t = this.opts.minUploadIntervalMs) && void 0 !== t ? t : DEFAULTS.minUploadIntervalMs)) return;
81
+ if (this.uploadCount >= (null !== (s = this.opts.maxUploadsPerSession) && void 0 !== s ? s : DEFAULTS.maxUploadsPerSession)) return;
82
+ const n = [].concat(...this.segments);
83
+ if (0 === n.length) return;
84
+ const a = (0, uuid_1.randomId)();
85
+ this.lastUploadAt = l, this.uploadCount++;
86
+ const o = null !== (r = this.opts.maxReplayBytes) && void 0 !== r ? r : DEFAULTS.maxReplayBytes, i = this.segments.length > 1 && JSON.stringify(n).length > o ? [ ...this.segments[this.segments.length - 1] ] : n;
87
+ return this.compressAndUpload(a, i), a;
88
+ }
89
+ stop() {
90
+ var e;
91
+ null === (e = this.stopFn) || void 0 === e || e.call(this), this.stopFn = null;
92
+ }
93
+ isTrigger(e) {
94
+ return "error" === e.type || !!this.opts.captureRequest && ("xhr" === e.type || "fetch" === e.type || "batchXHR" === e.type);
95
+ }
96
+ async compressAndUpload(e, t) {
97
+ try {
98
+ const s = JSON.stringify(t), {deflate: r} = await (import("pako"));
99
+ await (0, upload_1.uploadReplay)(this.opts.replayEndpoint, this.opts.projectKey, {
100
+ replayId: e,
101
+ format: "rrweb@2",
102
+ codec: "deflate-base64",
103
+ size: s.length,
104
+ durationMs: t.length > 1 ? t[t.length - 1].timestamp - t[0].timestamp : 0,
105
+ occurredAt: (new Date).toISOString(),
106
+ data: toBase64(r(s))
107
+ });
108
+ } catch (e) {}
109
+ }
110
+ }
111
+
112
+ function toBase64(e) {
113
+ let t = "";
114
+ for (let s = 0; s < e.length; s += 32768) t += String.fromCharCode(...e.subarray(s, s + 32768));
115
+ return btoa(t);
116
+ }
117
+
118
+ exports.ReplayController = ReplayController;
@@ -0,0 +1,17 @@
1
+ /** 回放上传 payload(契约见 backend-integration.md「回放接口」) */
2
+ export interface ReplayUploadPayload {
3
+ replayId: string;
4
+ /** 事件流格式+版本,播放器按此选择解析方式 */
5
+ format: 'rrweb@2';
6
+ /** data 编码方式 */
7
+ codec: 'deflate-base64';
8
+ /** 解压前 JSON 字节数 */
9
+ size: number;
10
+ /** 回放覆盖时长(首末事件时间差 ms) */
11
+ durationMs: number;
12
+ occurredAt: string;
13
+ /** pako deflate + base64 后的 rrweb 事件流 */
14
+ data: string;
15
+ }
16
+ /** 回放独立上传:与事件上报共用鉴权头;失败由调用方静默处理(回放是可丢数据) */
17
+ export declare function uploadReplay(replayEndpoint: string, projectKey: string, payload: ReplayUploadPayload): Promise<void>;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uploadReplay = uploadReplay;
4
+ /** 回放独立上传:与事件上报共用鉴权头;失败由调用方静默处理(回放是可丢数据) */
5
+ async function uploadReplay(replayEndpoint, projectKey, payload) {
6
+ const res = await fetch(replayEndpoint, {
7
+ method: 'POST',
8
+ headers: { 'content-type': 'application/json', 'X-Project-Key': projectKey },
9
+ body: JSON.stringify(payload),
10
+ });
11
+ if (!res.ok)
12
+ throw Object.assign(new Error(`monitor: 回放上传失败 ${res.status}`), { statusCode: res.status });
13
+ }
@@ -4,4 +4,5 @@ export { default as getSelector } from './getSelector';
4
4
  export { default as checkWhiteScreen } from './checkWhiteScreen';
5
5
  export { default as parseQueryString } from './parseQueryString';
6
6
  export { default as formatStack } from './formatStack';
7
+ export { randomId } from './uuid';
7
8
  export { toExcEvents, isExceptionReportUrl, mapRequestLog } from './toExcEvent';
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.mapRequestLog = exports.isExceptionReportUrl = exports.toExcEvents = exports.formatStack = exports.parseQueryString = exports.checkWhiteScreen = exports.getSelector = exports.getLogBaseData = exports.getLastEvent = void 0;
6
+ exports.mapRequestLog = exports.isExceptionReportUrl = exports.toExcEvents = exports.randomId = exports.formatStack = exports.parseQueryString = exports.checkWhiteScreen = exports.getSelector = exports.getLogBaseData = exports.getLastEvent = void 0;
7
7
  var getLastEvent_1 = require("./getLastEvent");
8
8
  Object.defineProperty(exports, "getLastEvent", { enumerable: true, get: function () { return __importDefault(getLastEvent_1).default; } });
9
9
  var getLogBaseData_1 = require("./getLogBaseData");
@@ -16,6 +16,8 @@ var parseQueryString_1 = require("./parseQueryString");
16
16
  Object.defineProperty(exports, "parseQueryString", { enumerable: true, get: function () { return __importDefault(parseQueryString_1).default; } });
17
17
  var formatStack_1 = require("./formatStack");
18
18
  Object.defineProperty(exports, "formatStack", { enumerable: true, get: function () { return __importDefault(formatStack_1).default; } });
19
+ var uuid_1 = require("./uuid");
20
+ Object.defineProperty(exports, "randomId", { enumerable: true, get: function () { return uuid_1.randomId; } });
19
21
  var toExcEvent_1 = require("./toExcEvent");
20
22
  Object.defineProperty(exports, "toExcEvents", { enumerable: true, get: function () { return toExcEvent_1.toExcEvents; } });
21
23
  Object.defineProperty(exports, "isExceptionReportUrl", { enumerable: true, get: function () { return toExcEvent_1.isExceptionReportUrl; } });
@@ -32,7 +32,6 @@ function contextFields(base, opts) {
32
32
  };
33
33
  }
34
34
  function mapErrorLog(log, base, opts) {
35
- var _a;
36
35
  const ctx = contextFields(base, opts);
37
36
  if (log.errorType === 'promiseError') {
38
37
  return {
@@ -44,16 +43,6 @@ function mapErrorLog(log, base, opts) {
44
43
  device: { ...ctx.device, selector: log.selector, filename: log.filename, position: log.position, errorType: log.errorType },
45
44
  };
46
45
  }
47
- if (log.errorType === 'loadResourceError') {
48
- const url = (_a = log.filename) !== null && _a !== void 0 ? _a : log.message;
49
- return {
50
- type: 'resource',
51
- level: 'ERROR',
52
- message: url ? `Resource failed: ${url}` : log.message,
53
- ...ctx,
54
- device: { ...ctx.device, tagName: log.tagName, selector: log.selector, errorType: log.errorType },
55
- };
56
- }
57
46
  return {
58
47
  type: 'error',
59
48
  level: 'ERROR',
@@ -0,0 +1,2 @@
1
+ /** 生成关联用随机 id:优先 crypto.randomUUID,非安全上下文(http)降级为 getRandomValues 拼十六进制 */
2
+ export declare function randomId(): string;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.randomId = randomId;
4
+ /** 生成关联用随机 id:优先 crypto.randomUUID,非安全上下文(http)降级为 getRandomValues 拼十六进制 */
5
+ function randomId() {
6
+ const cryptoApi = globalThis.crypto;
7
+ if (typeof (cryptoApi === null || cryptoApi === void 0 ? void 0 : cryptoApi.randomUUID) === 'function')
8
+ return cryptoApi.randomUUID();
9
+ // randomUUID 仅安全上下文(HTTPS)可用;getRandomValues 在 http 下依然可用
10
+ if (typeof (cryptoApi === null || cryptoApi === void 0 ? void 0 : cryptoApi.getRandomValues) === 'function') {
11
+ const bytes = new Uint8Array(16);
12
+ cryptoApi.getRandomValues(bytes);
13
+ return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
14
+ }
15
+ // 极老环境兜底:仅作关联键用途,非安全场景
16
+ return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join('');
17
+ }
package/package.json CHANGED
@@ -1,62 +1,65 @@
1
- {
2
- "name": "@qiu_jun/monitors",
3
- "version": "0.0.6",
4
- "description": "前端监控,用于捕获,分析,和报告网站或应用程序中的异常,错误和性能问题的方法",
5
- "sideEffects": false,
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js",
12
- "require": "./dist/monitor.umd.js",
13
- "default": "./dist/index.js"
14
- },
15
- "./h5": {
16
- "types": "./dist/h5.d.ts",
17
- "default": "./dist/h5.js"
18
- },
19
- "./miniprogram": {
20
- "types": "./dist/miniprogram.d.ts",
21
- "default": "./dist/miniprogram.js"
22
- },
23
- "./reporter": {
24
- "types": "./dist/reporter.d.ts",
25
- "default": "./dist/reporter.js"
26
- }
27
- },
28
- "files": [
29
- "dist"
30
- ],
31
- "scripts": {
32
- "preinstall": "npx only-allow pnpm",
33
- "build": "pnpm run clean && pnpm run compile && pnpm run bundle",
34
- "prepublishOnly": "pnpm run build",
35
- "clean": "rimraf dist",
36
- "compile": "tsc -p tsconfig.build.json",
37
- "bundle": "vite build"
38
- },
39
- "publishConfig": {
40
- "access": "public",
41
- "registry": "https://registry.npmjs.org/"
42
- },
43
- "repository": {
44
- "type": "git",
45
- "url": "git+https://github.com/Qiu-Jun/monitor.git"
46
- },
47
- "author": "June",
48
- "license": "ISC",
49
- "bugs": {
50
- "url": "https://github.com/Qiu-Jun/monitor/issues"
51
- },
52
- "homepage": "https://github.com/Qiu-Jun/monitor#readme",
53
- "devDependencies": {
54
- "rimraf": "^6.0.1",
55
- "terser": "^5.43.1",
56
- "typescript": "~6.0.3",
57
- "vite": "^6.2.0"
58
- },
59
- "dependencies": {
60
- "ua-parser-js": "^2.0.4"
61
- }
62
- }
1
+ {
2
+ "name": "@qiu_jun/monitors",
3
+ "version": "0.0.8",
4
+ "description": "前端监控,用于捕获,分析,和报告网站或应用程序中的异常,错误和性能问题的方法",
5
+ "sideEffects": false,
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/monitor.umd.js",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./h5": {
16
+ "types": "./dist/h5.d.ts",
17
+ "default": "./dist/h5.js"
18
+ },
19
+ "./miniprogram": {
20
+ "types": "./dist/miniprogram.d.ts",
21
+ "default": "./dist/miniprogram.js"
22
+ },
23
+ "./reporter": {
24
+ "types": "./dist/reporter.d.ts",
25
+ "default": "./dist/reporter.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "scripts": {
32
+ "preinstall": "npx only-allow pnpm",
33
+ "build": "pnpm run clean && pnpm run compile && pnpm run bundle",
34
+ "prepublishOnly": "pnpm run build",
35
+ "clean": "rimraf dist",
36
+ "compile": "tsc -p tsconfig.build.json && node scripts/strip-console.mjs",
37
+ "bundle": "vite build"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "registry": "https://registry.npmjs.org/"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/Qiu-Jun/monitor.git"
46
+ },
47
+ "author": "June",
48
+ "license": "ISC",
49
+ "bugs": {
50
+ "url": "https://github.com/Qiu-Jun/monitor/issues"
51
+ },
52
+ "homepage": "https://github.com/Qiu-Jun/monitor#readme",
53
+ "devDependencies": {
54
+ "@types/pako": "^3.0.0",
55
+ "rimraf": "^6.0.1",
56
+ "terser": "^5.43.1",
57
+ "typescript": "~6.0.3",
58
+ "vite": "^6.2.0"
59
+ },
60
+ "dependencies": {
61
+ "pako": "^3.0.2",
62
+ "rrweb": "^2.1.7",
63
+ "ua-parser-js": "^2.0.4"
64
+ }
65
+ }