@qiu_jun/exception 0.0.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 ADDED
@@ -0,0 +1,131 @@
1
+ # mc_exception-sdk
2
+
3
+ 异常分析平台上报 SDK(零依赖,支持按平台按需引入)。对应服务端模块:`packages/main-server/src/modules/exception/`。
4
+
5
+ ## 按需引入(子路径导出)
6
+
7
+ 包提供子路径导出,不同端只引自己平台的入口,互不携带:
8
+
9
+ ```ts
10
+ // 也可从主入口引全部(无副作用,多带一点代码而已)
11
+ import { initH5Exception } from 'mc_exception-sdk'
12
+
13
+ // H5 项目
14
+ import { initH5Exception } from 'mc_exception-sdk/h5'
15
+
16
+ // 自定义平台(只拿通用上报器)
17
+ import { createReporter } from 'mc_exception-sdk/reporter'
18
+
19
+ // 小程序项目
20
+ import { initWeappException } from 'mc_exception-sdk/weapp'
21
+ ```
22
+
23
+ 主入口 `mc_exception-sdk` 仍是 CJS 全量导出(无法 tree-shake),但对体积敏感的端建议用子路径。复制 `src` 源码方式接入的不受影响——只拷需要的文件即可。
24
+
25
+ ## 小程序接入(3 步)
26
+
27
+ ### 1. 后台创建接入项目
28
+
29
+ 管理后台「异常监控 → 接入项目」新建项目,拿到 `projectKey`。
30
+
31
+ ### 2. 安装
32
+
33
+ ```bash
34
+ pnpm add mc_exception-sdk --filter your-weapp-package
35
+ ```
36
+
37
+ (或把 `src` 直接复制进小程序项目 `utils/` 目录。)
38
+
39
+ ### 3. app.ts 包装 App()
40
+
41
+ ```ts
42
+ import { initWeappException } from 'mc_exception-sdk'
43
+
44
+ App(initWeappException({
45
+ endpoint: 'https://your-host/api/exception/report', // GLOBAL_PREFIX 默认 /api
46
+ projectKey: 'your-project-key',
47
+ release: '1.0.0',
48
+ environment: 'production',
49
+ sampleRate: 100, // 本地采样率(可选,默认 100)
50
+ captureRequest: true, // 拦截 wx.request 接口异常(可选,默认关闭)
51
+ }, {
52
+ onLaunch() {},
53
+ }))
54
+ ```
55
+
56
+ 自动捕获:`App.onError`(JS 错误)、`App.onUnhandledRejection`(未处理的 Promise 拒绝)、`captureRequest: true` 时连 `wx.request` 的接口异常一起拦(`statusCode >= requestErrorThreshold`(默认 500) 或请求 `fail`(超时/断网))。
57
+
58
+ 手动上报:
59
+
60
+ ```ts
61
+ const reporter = (getApp() as any).__excReporter
62
+ reporter?.capture({ type: 'custom', level: 'WARN', message: '支付回调超时' })
63
+ ```
64
+
65
+ ## H5 接入(二期新增)
66
+
67
+ ```ts
68
+ // main.ts 入口最顶部
69
+ import { initH5Exception } from 'mc_exception-sdk'
70
+
71
+ const reporter = initH5Exception({
72
+ endpoint: 'https://your-host/api/exception/report',
73
+ projectKey: 'your-project-key',
74
+ release: '1.0.0',
75
+ environment: 'production',
76
+ captureRequest: true, // 拦截 fetch/XHR 接口异常(可选,默认关闭)
77
+ // captureResource: false, // 资源加载错误默认捕获,可关
78
+ })
79
+ ```
80
+
81
+ 自动捕获:
82
+
83
+ | 捕获项 | 事件 type | 说明 |
84
+ |---|---|--- |
85
+ | `window.onerror` | `error` | Error 取 `name+message+stack`;无堆栈时拼 `msg (src:line:col)` |
86
+ | `unhandledrejection` | `unhandledrejection` | Promise 未捕获拒绝 |
87
+ | 资源加载错误 | `resource` | img/script/css 等,`Resource failed: <url>`,capture 阶段监听 |
88
+ | `fetch` / `XMLHttpRequest` | `request` | `captureRequest: true` 时,`Request failed [500]: GET /api/...`,与小程序端同格式同指纹 |
89
+
90
+ - 传输层 `makeH5Transport`:fetch + `keepalive: true`(页面卸载也能发出),自定义 header 带 `X-Project-Key`
91
+ - SDK 自身上报请求(url === endpoint)不捕获,防递归;4xx 属业务语义默认不上报
92
+ - 全部安装钩子幂等(`__excH5Patched` / `__excFetchPatched` / `__excXhrPatched`)
93
+ - 手动上报:`reporter.capture({ type: 'custom', message: '...' })`
94
+
95
+ ## 接口异常监控(`captureRequest: true`)
96
+
97
+ 开启后 SDK 会包装 `wx.request`,捕获两类接口异常并上报 `type: 'request'` 事件:
98
+
99
+ - `statusCode >= requestErrorThreshold`(默认 500)
100
+ - 请求 `fail`(超时、断网等)
101
+
102
+ ```
103
+ message: "Request failed [500]: GET /api/user?code=1"
104
+ message: "Request failed [fail request:timeout]: POST /api/order"
105
+ page: 当前页面路由 device: { durationMs, errMsg? }
106
+ ```
107
+
108
+ - 指纹按「状态码/失败原因 + 方法 + 路径」归并(query 会被服务端归一化去掉)——同一个接口挂了聚成一个 issue,直接回答「哪个接口在炸」
109
+ - **防递归**:SDK 自身的上报请求(`url === endpoint`)不捕获
110
+ - 4xx(401 登录过期等业务语义)默认不上报;需要时业务侧手动 `reporter.capture({ type: 'request', message: '...' })`
111
+ - 需要重复包装的场景(如多个 SDK)已做幂等保护,`wx.request` 只会被包一层
112
+
113
+ ## 行为说明
114
+
115
+ - **攒批**:缓冲满 10 条或每 5s 定时上报一批(`maxBatch` / `flushInterval` 可调),一次请求最多 50 条(服务端限制)
116
+ - **采样**:`sampleRate` 本地丢弃 + 服务端项目级采样,两层叠加
117
+ - **重试**:网络失败放回缓冲重试(上限 100 条,超出丢最旧);`403`(key 无效/项目停用)直接放弃
118
+ - **后台域名校验**:上报域名需加入小程序 request 合法域名,或开发者工具勾选「不校验合法域名」
119
+
120
+ ## API
121
+
122
+ | 导出 | 说明 |
123
+ |---|---|
124
+ | `initWeappException(options, appOptions)` | 包装小程序 App() 入参,挂全局错误捕获与 `__excReporter` |
125
+ | `initH5Exception(options)` | 安装 H5 全局采集(window 错误/unhandledrejection/资源/可选 fetch+XHR),返回 reporter |
126
+ | `createReporter(options, transport)` | 通用上报器(自定义平台提供 transport 即可复用) |
127
+ | `makeWeappTransport(endpoint)` | 微信小程序 wx.request transport |
128
+ | `makeH5Transport(endpoint)` | H5 fetch keepalive transport |
129
+ | `ExcReporter` / `SDK_VERSION` | 上报器类与 SDK 版本 |
130
+
131
+ 二期计划:H5/Web 端 transport(window.onerror / unhandledrejection)、sourcemap 上传配合。
package/dist/h5.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { ExcSdkOptions, ExcTransport } from './types';
2
+ import { ExcReporter } from './reporter';
3
+ export declare function makeH5Transport(endpoint: string): ExcTransport;
4
+ export declare function initH5Exception(options: ExcSdkOptions): ExcReporter;
package/dist/h5.js ADDED
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeH5Transport = makeH5Transport;
4
+ exports.initH5Exception = initH5Exception;
5
+ const reporter_1 = require("./reporter");
6
+ function makeH5Transport(endpoint) {
7
+ return async (payload, projectKey) => {
8
+ const res = await fetch(endpoint, {
9
+ method: 'POST',
10
+ headers: { 'content-type': 'application/json', 'X-Project-Key': projectKey },
11
+ body: JSON.stringify(payload),
12
+ keepalive: true,
13
+ });
14
+ if (!res.ok)
15
+ throw Object.assign(new Error(`exception-sdk: 上报失败 ${res.status}`), { statusCode: res.status });
16
+ };
17
+ }
18
+ function currentPageUrl() {
19
+ try {
20
+ const loc = globalThis.location;
21
+ return loc ? `${loc.pathname}${loc.search}` : undefined;
22
+ }
23
+ catch (_a) {
24
+ return undefined;
25
+ }
26
+ }
27
+ function patchGlobalCapture(reporter, options) {
28
+ const w = globalThis;
29
+ if (w.__excH5Patched)
30
+ return;
31
+ w.__excH5Patched = true;
32
+ w.onerror = (msg, src, lineno, colno, error) => {
33
+ if (error instanceof Error) {
34
+ reporter.captureError(error, 'error', { page: currentPageUrl(), sdkVersion: reporter_1.SDK_VERSION });
35
+ }
36
+ else {
37
+ const at = src ? ` (${src}:${lineno}:${colno})` : '';
38
+ reporter.captureError(`${msg}${at}`, 'error', { page: currentPageUrl(), sdkVersion: reporter_1.SDK_VERSION });
39
+ }
40
+ };
41
+ w.addEventListener('unhandledrejection', (e) => {
42
+ const reason = e === null || e === void 0 ? void 0 : e.reason;
43
+ reporter.captureError(reason !== null && reason !== void 0 ? reason : 'unhandled promise rejection', 'unhandledrejection', { page: currentPageUrl(), sdkVersion: reporter_1.SDK_VERSION });
44
+ });
45
+ if (options.captureResource !== false) {
46
+ w.addEventListener('error', (event) => {
47
+ const target = event === null || event === void 0 ? void 0 : event.target;
48
+ const url = (target === null || target === void 0 ? void 0 : target.src) || (target === null || target === void 0 ? void 0 : target.href);
49
+ if (!target || target === w || !url)
50
+ return;
51
+ reporter.captureError(`Resource failed: ${url}`, 'resource', {
52
+ page: currentPageUrl(),
53
+ device: { tagName: target.tagName },
54
+ sdkVersion: reporter_1.SDK_VERSION,
55
+ });
56
+ }, true);
57
+ }
58
+ }
59
+ function patchFetch(reporter, endpoint, threshold) {
60
+ const w = globalThis;
61
+ if (!w.fetch || w.__excFetchPatched)
62
+ return;
63
+ const original = w.fetch.bind(w);
64
+ w.fetch = async (input, init) => {
65
+ var _a, _b, _c;
66
+ const url = typeof input === 'string' ? input : (_a = input === null || input === void 0 ? void 0 : input.url) !== null && _a !== void 0 ? _a : '';
67
+ const method = String((_c = (_b = init === null || init === void 0 ? void 0 : init.method) !== null && _b !== void 0 ? _b : input === null || input === void 0 ? void 0 : input.method) !== null && _c !== void 0 ? _c : 'GET').toUpperCase();
68
+ const start = Date.now();
69
+ try {
70
+ const res = await original(input, init);
71
+ if (res.status >= threshold && url !== endpoint) {
72
+ reporter.captureError(`Request failed [${res.status}]: ${method} ${url}`, 'request', {
73
+ page: currentPageUrl(),
74
+ device: { durationMs: Date.now() - start },
75
+ });
76
+ }
77
+ return res;
78
+ }
79
+ catch (err) {
80
+ if (url !== endpoint) {
81
+ reporter.captureError(`Request failed [network]: ${method} ${url}`, 'request', {
82
+ page: currentPageUrl(),
83
+ device: { durationMs: Date.now() - start },
84
+ });
85
+ }
86
+ throw err;
87
+ }
88
+ };
89
+ w.__excFetchPatched = true;
90
+ }
91
+ function patchXhr(reporter, endpoint, threshold) {
92
+ var _a;
93
+ const w = globalThis;
94
+ const proto = (_a = w.XMLHttpRequest) === null || _a === void 0 ? void 0 : _a.prototype;
95
+ if (!proto || w.__excXhrPatched)
96
+ return;
97
+ const originalOpen = proto.open;
98
+ proto.open = function (method, url, ...rest) {
99
+ this.__excMeta = { method: String(method).toUpperCase(), url: String(url) };
100
+ return originalOpen.apply(this, [method, url, ...rest]);
101
+ };
102
+ const originalSend = proto.send;
103
+ proto.send = function (...args) {
104
+ this.addEventListener('loadend', () => {
105
+ const meta = this.__excMeta;
106
+ if (!meta || meta.url === endpoint)
107
+ return;
108
+ if (this.status >= threshold)
109
+ reporter.captureError(`Request failed [${this.status}]: ${meta.method} ${meta.url}`, 'request', { page: currentPageUrl() });
110
+ else if (this.status === 0)
111
+ reporter.captureError(`Request failed [network]: ${meta.method} ${meta.url}`, 'request', { page: currentPageUrl() });
112
+ });
113
+ return originalSend.apply(this, args);
114
+ };
115
+ w.__excXhrPatched = true;
116
+ }
117
+ function initH5Exception(options) {
118
+ var _a;
119
+ const reporter = (0, reporter_1.createReporter)(options, makeH5Transport(options.endpoint));
120
+ patchGlobalCapture(reporter, options);
121
+ if (options.captureRequest) {
122
+ const threshold = (_a = options.requestErrorThreshold) !== null && _a !== void 0 ? _a : 500;
123
+ patchFetch(reporter, options.endpoint, threshold);
124
+ patchXhr(reporter, options.endpoint, threshold);
125
+ }
126
+ return reporter;
127
+ }
@@ -0,0 +1,4 @@
1
+ export { initH5Exception, makeH5Transport } from './h5';
2
+ export { createReporter, ExcReporter, SDK_VERSION } from './reporter';
3
+ export type { ExcEventPayload, ExcSdkOptions, ExcTransport } from './types';
4
+ export { initWeappException, makeWeappTransport } from './weapp';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeWeappTransport = exports.initWeappException = exports.SDK_VERSION = exports.ExcReporter = exports.createReporter = exports.makeH5Transport = exports.initH5Exception = void 0;
4
+ var h5_1 = require("./h5");
5
+ Object.defineProperty(exports, "initH5Exception", { enumerable: true, get: function () { return h5_1.initH5Exception; } });
6
+ Object.defineProperty(exports, "makeH5Transport", { enumerable: true, get: function () { return h5_1.makeH5Transport; } });
7
+ var reporter_1 = require("./reporter");
8
+ Object.defineProperty(exports, "createReporter", { enumerable: true, get: function () { return reporter_1.createReporter; } });
9
+ Object.defineProperty(exports, "ExcReporter", { enumerable: true, get: function () { return reporter_1.ExcReporter; } });
10
+ Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return reporter_1.SDK_VERSION; } });
11
+ var weapp_1 = require("./weapp");
12
+ Object.defineProperty(exports, "initWeappException", { enumerable: true, get: function () { return weapp_1.initWeappException; } });
13
+ Object.defineProperty(exports, "makeWeappTransport", { enumerable: true, get: function () { return weapp_1.makeWeappTransport; } });
@@ -0,0 +1,17 @@
1
+ import type { ExcEventPayload, ExcSdkOptions, ExcTransport } from './types';
2
+ export declare const SDK_VERSION = "0.0.1";
3
+ export declare class ExcReporter {
4
+ private readonly options;
5
+ private readonly transport;
6
+ private buffer;
7
+ private timer;
8
+ private sending;
9
+ constructor(options: ExcSdkOptions, transport: ExcTransport);
10
+ capture(event: ExcEventPayload): void;
11
+ captureError(message: string | Error, type?: ExcEventPayload['type'], extra?: Partial<ExcEventPayload>): void;
12
+ flush(): Promise<void>;
13
+ destroy(): void;
14
+ private ensureTimer;
15
+ private clearTimer;
16
+ }
17
+ export declare function createReporter(options: ExcSdkOptions, transport: ExcTransport): ExcReporter;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExcReporter = exports.SDK_VERSION = void 0;
4
+ exports.createReporter = createReporter;
5
+ exports.SDK_VERSION = '0.0.1';
6
+ const DEFAULT_MAX_BATCH = 10;
7
+ const DEFAULT_FLUSH_INTERVAL = 5000;
8
+ const MAX_BUFFER = 100;
9
+ class ExcReporter {
10
+ constructor(options, transport) {
11
+ this.options = options;
12
+ this.transport = transport;
13
+ this.buffer = [];
14
+ this.timer = null;
15
+ this.sending = false;
16
+ }
17
+ capture(event) {
18
+ var _a, _b, _c, _d;
19
+ const sampleRate = (_a = this.options.sampleRate) !== null && _a !== void 0 ? _a : 100;
20
+ if (sampleRate < 100 && Math.random() * 100 >= sampleRate)
21
+ return;
22
+ this.buffer.push({
23
+ ...event,
24
+ level: (_b = event.level) !== null && _b !== void 0 ? _b : 'ERROR',
25
+ occurredAt: (_c = event.occurredAt) !== null && _c !== void 0 ? _c : new Date().toISOString(),
26
+ });
27
+ if (this.buffer.length > MAX_BUFFER)
28
+ this.buffer = this.buffer.slice(-MAX_BUFFER);
29
+ if (this.buffer.length >= ((_d = this.options.maxBatch) !== null && _d !== void 0 ? _d : DEFAULT_MAX_BATCH))
30
+ void this.flush();
31
+ else
32
+ this.ensureTimer();
33
+ }
34
+ captureError(message, type = 'error', extra) {
35
+ var _a;
36
+ const err = message instanceof Error ? message : null;
37
+ const text = err ? `${err.name}: ${err.message}` : String(message);
38
+ this.capture({
39
+ type,
40
+ message: text.slice(0, 2000),
41
+ stack: (_a = err === null || err === void 0 ? void 0 : err.stack) === null || _a === void 0 ? void 0 : _a.slice(0, 16000),
42
+ ...extra,
43
+ });
44
+ }
45
+ async flush() {
46
+ if (this.sending || this.buffer.length === 0)
47
+ return;
48
+ this.clearTimer();
49
+ this.sending = true;
50
+ const events = this.buffer;
51
+ this.buffer = [];
52
+ try {
53
+ await this.transport({ events }, this.options.projectKey);
54
+ }
55
+ catch (err) {
56
+ if ((err === null || err === void 0 ? void 0 : err.statusCode) !== 403) {
57
+ this.buffer = [...events, ...this.buffer].slice(-MAX_BUFFER);
58
+ this.ensureTimer();
59
+ }
60
+ }
61
+ finally {
62
+ this.sending = false;
63
+ }
64
+ }
65
+ destroy() {
66
+ this.clearTimer();
67
+ void this.flush();
68
+ }
69
+ ensureTimer() {
70
+ var _a;
71
+ if (this.timer)
72
+ return;
73
+ this.timer = setTimeout(() => {
74
+ this.timer = null;
75
+ void this.flush();
76
+ }, (_a = this.options.flushInterval) !== null && _a !== void 0 ? _a : DEFAULT_FLUSH_INTERVAL);
77
+ }
78
+ clearTimer() {
79
+ if (this.timer) {
80
+ clearTimeout(this.timer);
81
+ this.timer = null;
82
+ }
83
+ }
84
+ }
85
+ exports.ExcReporter = ExcReporter;
86
+ function createReporter(options, transport) {
87
+ return new ExcReporter(options, transport);
88
+ }
@@ -0,0 +1,26 @@
1
+ export interface ExcSdkOptions {
2
+ endpoint: string;
3
+ projectKey: string;
4
+ release?: string;
5
+ environment?: string;
6
+ sampleRate?: number;
7
+ captureRequest?: boolean;
8
+ captureResource?: boolean;
9
+ requestErrorThreshold?: number;
10
+ maxBatch?: number;
11
+ flushInterval?: number;
12
+ }
13
+ export interface ExcEventPayload {
14
+ type: 'error' | 'unhandledrejection' | 'resource' | 'request' | 'custom';
15
+ level?: 'FATAL' | 'ERROR' | 'WARN' | 'INFO';
16
+ message: string;
17
+ stack?: string;
18
+ page?: string;
19
+ userId?: string;
20
+ sdkVersion?: string;
21
+ occurredAt?: string;
22
+ device?: Record<string, any>;
23
+ }
24
+ export type ExcTransport = (payload: {
25
+ events: ExcEventPayload[];
26
+ }, projectKey: string) => Promise<void>;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ import type { ExcSdkOptions, ExcTransport } from './types';
2
+ export declare function makeWeappTransport(endpoint: string): ExcTransport;
3
+ export declare function initWeappException<T extends Record<string, any>>(options: ExcSdkOptions, appOptions: T): T;
package/dist/weapp.js ADDED
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeWeappTransport = makeWeappTransport;
4
+ exports.initWeappException = initWeappException;
5
+ const reporter_1 = require("./reporter");
6
+ function makeWeappTransport(endpoint) {
7
+ return (payload, projectKey) => new Promise((resolve, reject) => {
8
+ const wxApi = globalThis.wx;
9
+ if (!(wxApi === null || wxApi === void 0 ? void 0 : wxApi.request)) {
10
+ reject(new Error('exception-sdk: wx.request 不可用'));
11
+ return;
12
+ }
13
+ wxApi.request({
14
+ url: endpoint,
15
+ method: 'POST',
16
+ header: { 'content-type': 'application/json', 'X-Project-Key': projectKey },
17
+ data: payload,
18
+ success: (res) => {
19
+ if (res.statusCode >= 200 && res.statusCode < 300)
20
+ resolve();
21
+ else
22
+ reject(Object.assign(new Error(`exception-sdk: 上报失败 ${res.statusCode}`), { statusCode: res.statusCode }));
23
+ },
24
+ fail: (err) => { var _a; return reject(new Error(`exception-sdk: 上报网络错误 ${(_a = err === null || err === void 0 ? void 0 : err.errMsg) !== null && _a !== void 0 ? _a : ''}`)); },
25
+ });
26
+ });
27
+ }
28
+ function getCurrentPageRoute() {
29
+ var _a, _b, _c;
30
+ try {
31
+ const pages = (_c = (_b = (_a = globalThis).getCurrentPages) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : [];
32
+ const current = pages[pages.length - 1];
33
+ if (!(current === null || current === void 0 ? void 0 : current.route))
34
+ return undefined;
35
+ const query = current.options
36
+ ? Object.entries(current.options).map(([k, v]) => `${k}=${v}`).join('&')
37
+ : '';
38
+ return query ? `${current.route}?${query}` : current.route;
39
+ }
40
+ catch (_d) {
41
+ return undefined;
42
+ }
43
+ }
44
+ function patchWxRequest(reporter, endpoint, threshold) {
45
+ const wxApi = globalThis.wx;
46
+ if (!(wxApi === null || wxApi === void 0 ? void 0 : wxApi.request) || wxApi.__excRequestPatched)
47
+ return;
48
+ const original = wxApi.request.bind(wxApi);
49
+ wxApi.request = (options) => {
50
+ var _a, _b, _c, _d;
51
+ const url = (_a = options === null || options === void 0 ? void 0 : options.url) !== null && _a !== void 0 ? _a : '';
52
+ const method = String((_b = options === null || options === void 0 ? void 0 : options.method) !== null && _b !== void 0 ? _b : 'GET').toUpperCase();
53
+ const start = Date.now();
54
+ const originalSuccess = (_c = options === null || options === void 0 ? void 0 : options.success) === null || _c === void 0 ? void 0 : _c.bind(options);
55
+ const originalFail = (_d = options === null || options === void 0 ? void 0 : options.fail) === null || _d === void 0 ? void 0 : _d.bind(options);
56
+ options.success = (res) => {
57
+ if ((res === null || res === void 0 ? void 0 : res.statusCode) >= threshold && url !== endpoint) {
58
+ reporter.captureError(`Request failed [${res.statusCode}]: ${method} ${url}`, 'request', {
59
+ page: getCurrentPageRoute(),
60
+ device: { durationMs: Date.now() - start },
61
+ });
62
+ }
63
+ originalSuccess === null || originalSuccess === void 0 ? void 0 : originalSuccess(res);
64
+ };
65
+ options.fail = (err) => {
66
+ var _a;
67
+ if (url !== endpoint) {
68
+ reporter.captureError(`Request failed [fail ${(_a = err === null || err === void 0 ? void 0 : err.errMsg) !== null && _a !== void 0 ? _a : 'unknown'}]: ${method} ${url}`, 'request', {
69
+ page: getCurrentPageRoute(),
70
+ device: { durationMs: Date.now() - start, errMsg: err === null || err === void 0 ? void 0 : err.errMsg },
71
+ });
72
+ }
73
+ originalFail === null || originalFail === void 0 ? void 0 : originalFail(err);
74
+ };
75
+ return original(options);
76
+ };
77
+ wxApi.__excRequestPatched = true;
78
+ }
79
+ function initWeappException(options, appOptions) {
80
+ var _a;
81
+ const reporter = (0, reporter_1.createReporter)(options, makeWeappTransport(options.endpoint));
82
+ if (options.captureRequest)
83
+ patchWxRequest(reporter, options.endpoint, (_a = options.requestErrorThreshold) !== null && _a !== void 0 ? _a : 500);
84
+ const wrapped = { ...appOptions };
85
+ const originalOnError = appOptions.onError;
86
+ wrapped.onError = (msg) => {
87
+ reporter.captureError(msg, 'error', { sdkVersion: reporter_1.SDK_VERSION });
88
+ originalOnError === null || originalOnError === void 0 ? void 0 : originalOnError.call(wrapped, msg);
89
+ };
90
+ const originalOnUnhandledRejection = appOptions.onUnhandledRejection;
91
+ wrapped.onUnhandledRejection = (res) => {
92
+ var _a;
93
+ reporter.captureError((_a = res === null || res === void 0 ? void 0 : res.reason) !== null && _a !== void 0 ? _a : 'unhandled promise rejection', 'unhandledrejection', { sdkVersion: reporter_1.SDK_VERSION });
94
+ originalOnUnhandledRejection === null || originalOnUnhandledRejection === void 0 ? void 0 : originalOnUnhandledRejection.call(wrapped, res);
95
+ };
96
+ wrapped.__excReporter = reporter;
97
+ return wrapped;
98
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@qiu_jun/exception",
3
+ "version": "0.0.1",
4
+ "description": "异常分析平台小程序上报 SDK(零依赖)",
5
+ "sideEffects": false,
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ },
11
+ "./h5": {
12
+ "types": "./dist/h5.d.ts",
13
+ "default": "./dist/h5.js"
14
+ },
15
+ "./weapp": {
16
+ "types": "./dist/weapp.d.ts",
17
+ "default": "./dist/weapp.js"
18
+ },
19
+ "./reporter": {
20
+ "types": "./dist/reporter.d.ts",
21
+ "default": "./dist/reporter.js"
22
+ }
23
+ },
24
+ "main": "dist/index.js",
25
+ "types": "dist/index.d.ts",
26
+ "files": [
27
+ "dist/**/*"
28
+ ],
29
+ "scripts": {
30
+ "build": "run-s clean compile",
31
+ "clean": "rimraf dist",
32
+ "compile": "tsc -p tsconfig.json"
33
+ },
34
+ "devDependencies": {
35
+ "npm-run-all": "^4.1.5",
36
+ "rimraf": "^6.0.1",
37
+ "typescript": "~5.8.3"
38
+ }
39
+ }