@gctrack/uni 0.1.0
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/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/index.cjs +131 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yinuo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @gctrack/uni
|
|
2
|
+
|
|
3
|
+
小程序多端适配 —— 基于 uni-app 标准 API 的 `PlatformAdapter`,覆盖**微信 / 支付宝 / 京东 / 小红书 / 百度 / H5**。提供 `uni.request` 上报封装、运行时平台探测与一键错误捕获。
|
|
4
|
+
|
|
5
|
+
> **当前能力范围**:平台适配器 + `uni.request` 上报 + 平台探测 + 错误捕获(`Vue.errorHandler` / `uni.onUnhandledRejection`)。
|
|
6
|
+
> PV / 点击 / 性能 / 白屏等采集待后续完善阶段补充。
|
|
7
|
+
|
|
8
|
+
## 安装
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i @gctrack/uni @gctrack/core
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 用法
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createTracker } from '@gctrack/core'
|
|
18
|
+
import { createUniSendFn, detectPlatform, installErrorCapture } from '@gctrack/uni'
|
|
19
|
+
|
|
20
|
+
const reportUrl = 'https://your-collector/collect'
|
|
21
|
+
const tracker = createTracker({
|
|
22
|
+
reportUrl,
|
|
23
|
+
appKey: 'your-app-key',
|
|
24
|
+
platform: detectPlatform(), // 自动识别 wx_mp/alipay_mp/...
|
|
25
|
+
sendFn: createUniSendFn(reportUrl, 'your-app-key'), // 小程序无 fetch/sendBeacon,走 uni.request
|
|
26
|
+
signMode: 'none', // 小程序默认不内置 secret
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
错误捕获(在入口文件,`Vue2` 环境):
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import Vue from 'vue'
|
|
34
|
+
installErrorCapture(Vue, tracker)
|
|
35
|
+
// App.onError 需在 App.vue 里手动 tracker.track(见平台 design 文档)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## License
|
|
39
|
+
|
|
40
|
+
MIT (c) yinuo
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _gctrack_protocol = require("@gctrack/protocol");
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
/** uni 适配实现 —— 全端用 uni.* 标准 API,不写 #ifdef */
|
|
5
|
+
const uniAdapter = {
|
|
6
|
+
getStorage: (k) => {
|
|
7
|
+
try {
|
|
8
|
+
return uni.getStorageSync(k);
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
setStorage: (k, v) => {
|
|
14
|
+
try {
|
|
15
|
+
uni.setStorageSync(k, v);
|
|
16
|
+
} catch {}
|
|
17
|
+
},
|
|
18
|
+
removeStorage: (k) => {
|
|
19
|
+
try {
|
|
20
|
+
uni.removeStorageSync(k);
|
|
21
|
+
} catch {}
|
|
22
|
+
},
|
|
23
|
+
request: (opts) => new Promise((resolve, reject) => {
|
|
24
|
+
uni.request({
|
|
25
|
+
url: opts.url,
|
|
26
|
+
method: opts.method || "POST",
|
|
27
|
+
data: opts.data,
|
|
28
|
+
header: opts.header || {},
|
|
29
|
+
success: (res) => resolve({
|
|
30
|
+
statusCode: res.statusCode,
|
|
31
|
+
data: res.data
|
|
32
|
+
}),
|
|
33
|
+
fail: (err) => reject(err)
|
|
34
|
+
});
|
|
35
|
+
}),
|
|
36
|
+
getSystemInfo: () => {
|
|
37
|
+
try {
|
|
38
|
+
return uni.getSystemInfoSync();
|
|
39
|
+
} catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
getPageInfo: () => {
|
|
44
|
+
try {
|
|
45
|
+
const pages = getCurrentPages();
|
|
46
|
+
const top = pages[pages.length - 1];
|
|
47
|
+
const route = top?.route || "";
|
|
48
|
+
const options = top?.options || {};
|
|
49
|
+
const query = Object.keys(options).map((k) => `${k}=${options[k]}`).join("&");
|
|
50
|
+
return {
|
|
51
|
+
pathname: "/" + route,
|
|
52
|
+
url: "/" + route + (query ? "?" + query : ""),
|
|
53
|
+
title: ""
|
|
54
|
+
};
|
|
55
|
+
} catch {
|
|
56
|
+
return {
|
|
57
|
+
pathname: "",
|
|
58
|
+
url: "",
|
|
59
|
+
title: ""
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
onUnload: (cb) => {
|
|
64
|
+
try {
|
|
65
|
+
uni.onAppHide && uni.onAppHide(cb);
|
|
66
|
+
} catch {}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* 构造基于 uni.request 的 sendFn,供 createTracker({ sendFn }) 使用。
|
|
71
|
+
* 小程序无 sendBeacon/fetch,统一走 uni.request;header 值强制 String()(XHS 要求)。
|
|
72
|
+
*/
|
|
73
|
+
function createUniSendFn(reportUrl, appKey) {
|
|
74
|
+
return (events) => uniAdapter.request({
|
|
75
|
+
url: reportUrl,
|
|
76
|
+
method: "POST",
|
|
77
|
+
data: events,
|
|
78
|
+
header: {
|
|
79
|
+
"content-type": "application/json",
|
|
80
|
+
"X-App-Key": appKey ? String(appKey) : ""
|
|
81
|
+
}
|
|
82
|
+
}).then(() => void 0);
|
|
83
|
+
}
|
|
84
|
+
/** 根据 uni 运行环境推断 Platform 标识(写入 context.platform) */
|
|
85
|
+
function detectPlatform() {
|
|
86
|
+
try {
|
|
87
|
+
const info = uni.getSystemInfoSync();
|
|
88
|
+
const p = info.uniPlatform || info.platform;
|
|
89
|
+
if (typeof p === "string") {
|
|
90
|
+
if (p.includes("mp-weixin") || p.includes("weixin")) return _gctrack_protocol.Platform.WeixinMp;
|
|
91
|
+
if (p.includes("mp-alipay") || p.includes("alipay")) return _gctrack_protocol.Platform.AlipayMp;
|
|
92
|
+
if (p.includes("mp-jd") || p.includes("jd")) return _gctrack_protocol.Platform.JdMp;
|
|
93
|
+
if (p.includes("mp-xhs") || p.includes("xhs")) return _gctrack_protocol.Platform.XhsMp;
|
|
94
|
+
if (p.includes("mp-baidu") || p.includes("baidu")) return _gctrack_protocol.Platform.BaiduMp;
|
|
95
|
+
if (p.includes("h5")) return _gctrack_protocol.Platform.H5;
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
return _gctrack_protocol.Platform.WeixinMp;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 错误捕获一键挂载:
|
|
102
|
+
* - Vue.config.errorHandler → vue_error
|
|
103
|
+
* - uni.onUnhandledRejection → promise_error
|
|
104
|
+
* App.onError 需在 App.vue 里手动调用 tracker.track(见 design.md 13.2)。
|
|
105
|
+
*/
|
|
106
|
+
function installErrorCapture(Vue, tracker) {
|
|
107
|
+
if (Vue?.config) Vue.config.errorHandler = (err, vm, info) => {
|
|
108
|
+
tracker.track(_gctrack_protocol.EventType.VueError, {
|
|
109
|
+
message: err?.message || String(err),
|
|
110
|
+
stack: err?.stack,
|
|
111
|
+
info,
|
|
112
|
+
componentName: vm?.$options?.name || vm?.$options?._componentTag
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
try {
|
|
116
|
+
uni.onUnhandledRejection && uni.onUnhandledRejection((res) => {
|
|
117
|
+
const reason = res?.reason;
|
|
118
|
+
tracker.track(_gctrack_protocol.EventType.PromiseError, {
|
|
119
|
+
message: reason?.message || String(reason),
|
|
120
|
+
stack: reason?.stack
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
} catch {}
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
exports.createUniSendFn = createUniSendFn;
|
|
127
|
+
exports.detectPlatform = detectPlatform;
|
|
128
|
+
exports.installErrorCapture = installErrorCapture;
|
|
129
|
+
exports.uniAdapter = uniAdapter;
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["Platform","EventType"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @gctrack/uni —— 小程序多端适配\n *\n * 小程序无 window/document/navigator/sendBeacon/fetch/PerformanceObserver,\n * 本包提供 PlatformAdapter(存储/网络/生命周期/设备/页面)的 uni 实现,\n * 以及错误捕获(App.onError / unhandledRejection / Vue.errorHandler)一键挂载。\n *\n * 详见 design.md 5.5。完整实现(PV、点击、白屏耗时等)在「@gctrack/uni 完善」阶段补充。\n */\nimport type { Tracker } from '@gctrack/core'\nimport { EventType, Platform } from '@gctrack/protocol'\n\n// 小程序运行时注入的全局(各端一致提供 uni / getCurrentPages)\ndeclare const uni: any\ndeclare function getCurrentPages(): any[]\n\n/** 平台适配器接口 —— core 通过它解耦浏览器 API,详见 design.md 5.5 */\nexport interface PlatformAdapter {\n getStorage(key: string): any\n setStorage(key: string, value: any): void\n removeStorage(key: string): void\n request(opts: {\n url: string\n method?: string\n data?: any\n header?: Record<string, string>\n }): Promise<{ statusCode: number; data: any }>\n getSystemInfo(): Record<string, any>\n getPageInfo(): { pathname: string; url: string; title: string }\n onUnload(cb: () => void): void\n}\n\n/** uni 适配实现 —— 全端用 uni.* 标准 API,不写 #ifdef */\nexport const uniAdapter: PlatformAdapter = {\n getStorage: (k) => {\n try {\n return uni.getStorageSync(k)\n } catch {\n return null\n }\n },\n setStorage: (k, v) => {\n try {\n uni.setStorageSync(k, v)\n } catch {\n /* ignore */\n }\n },\n removeStorage: (k) => {\n try {\n uni.removeStorageSync(k)\n } catch {\n /* ignore */\n }\n },\n request: (opts) =>\n new Promise((resolve, reject) => {\n uni.request({\n url: opts.url,\n method: (opts.method || 'POST') as any,\n data: opts.data,\n header: opts.header || {},\n success: (res: any) => resolve({ statusCode: res.statusCode, data: res.data }),\n fail: (err: any) => reject(err),\n })\n }),\n getSystemInfo: () => {\n try {\n return uni.getSystemInfoSync()\n } catch {\n return {}\n }\n },\n getPageInfo: () => {\n try {\n const pages = getCurrentPages()\n const top = pages[pages.length - 1]\n const route: string = top?.route || ''\n const options = top?.options || {}\n const query = Object.keys(options)\n .map((k) => `${k}=${options[k]}`)\n .join('&')\n return {\n pathname: '/' + route,\n url: '/' + route + (query ? '?' + query : ''),\n title: '',\n }\n } catch {\n return { pathname: '', url: '', title: '' }\n }\n },\n onUnload: (cb) => {\n // 兜底:App.onHide / Page.onHide 由 installAppLifecycle 接管,这里注册 uni 级监听\n try {\n uni.onAppHide && uni.onAppHide(cb)\n } catch {\n /* ignore */\n }\n },\n}\n\n/**\n * 构造基于 uni.request 的 sendFn,供 createTracker({ sendFn }) 使用。\n * 小程序无 sendBeacon/fetch,统一走 uni.request;header 值强制 String()(XHS 要求)。\n */\nexport function createUniSendFn(reportUrl: string, appKey?: string) {\n return (events: any[]): Promise<void> =>\n uniAdapter\n .request({\n url: reportUrl,\n method: 'POST',\n data: events,\n header: {\n 'content-type': 'application/json',\n 'X-App-Key': appKey ? String(appKey) : '',\n },\n })\n .then(() => undefined)\n}\n\n/** 根据 uni 运行环境推断 Platform 标识(写入 context.platform) */\nexport function detectPlatform(): string {\n try {\n const info = uni.getSystemInfoSync()\n // uni-app 注入 UNI_PLATFORM / uniPlatform\n const p = info.uniPlatform || info.platform\n if (typeof p === 'string') {\n if (p.includes('mp-weixin') || p.includes('weixin')) return Platform.WeixinMp\n if (p.includes('mp-alipay') || p.includes('alipay')) return Platform.AlipayMp\n if (p.includes('mp-jd') || p.includes('jd')) return Platform.JdMp\n if (p.includes('mp-xhs') || p.includes('xhs')) return Platform.XhsMp\n if (p.includes('mp-baidu') || p.includes('baidu')) return Platform.BaiduMp\n if (p.includes('h5')) return Platform.H5\n }\n } catch {\n /* ignore */\n }\n return Platform.WeixinMp\n}\n\n/**\n * 错误捕获一键挂载:\n * - Vue.config.errorHandler → vue_error\n * - uni.onUnhandledRejection → promise_error\n * App.onError 需在 App.vue 里手动调用 tracker.track(见 design.md 13.2)。\n */\nexport function installErrorCapture(Vue: any, tracker: Tracker): void {\n if (Vue?.config) {\n Vue.config.errorHandler = (err: any, vm: any, info: string) => {\n tracker.track(EventType.VueError, {\n message: err?.message || String(err),\n stack: err?.stack,\n info,\n componentName: vm?.$options?.name || vm?.$options?._componentTag,\n })\n }\n }\n try {\n uni.onUnhandledRejection &&\n uni.onUnhandledRejection((res: any) => {\n const reason = res?.reason\n tracker.track(EventType.PromiseError, {\n message: reason?.message || String(reason),\n stack: reason?.stack,\n })\n })\n } catch {\n /* ignore */\n }\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Tracker } from "@gctrack/core";
|
|
2
|
+
export interface PlatformAdapter {
|
|
3
|
+
getStorage(key: string): any;
|
|
4
|
+
setStorage(key: string, value: any): void;
|
|
5
|
+
removeStorage(key: string): void;
|
|
6
|
+
request(opts: {
|
|
7
|
+
url: string;
|
|
8
|
+
method?: string;
|
|
9
|
+
data?: any;
|
|
10
|
+
header?: Record<string, string>;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
statusCode: number;
|
|
13
|
+
data: any;
|
|
14
|
+
}>;
|
|
15
|
+
getSystemInfo(): Record<string, any>;
|
|
16
|
+
getPageInfo(): {
|
|
17
|
+
pathname: string;
|
|
18
|
+
url: string;
|
|
19
|
+
title: string;
|
|
20
|
+
};
|
|
21
|
+
onUnload(cb: () => void): void;
|
|
22
|
+
}
|
|
23
|
+
export declare const uniAdapter: PlatformAdapter;
|
|
24
|
+
export declare function createUniSendFn(reportUrl: string, appKey?: string): (events: any[]) => Promise<void>;
|
|
25
|
+
export declare function detectPlatform(): string;
|
|
26
|
+
export declare function installErrorCapture(Vue: any, tracker: Tracker): void;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Tracker } from "@gctrack/core";
|
|
2
|
+
export interface PlatformAdapter {
|
|
3
|
+
getStorage(key: string): any;
|
|
4
|
+
setStorage(key: string, value: any): void;
|
|
5
|
+
removeStorage(key: string): void;
|
|
6
|
+
request(opts: {
|
|
7
|
+
url: string;
|
|
8
|
+
method?: string;
|
|
9
|
+
data?: any;
|
|
10
|
+
header?: Record<string, string>;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
statusCode: number;
|
|
13
|
+
data: any;
|
|
14
|
+
}>;
|
|
15
|
+
getSystemInfo(): Record<string, any>;
|
|
16
|
+
getPageInfo(): {
|
|
17
|
+
pathname: string;
|
|
18
|
+
url: string;
|
|
19
|
+
title: string;
|
|
20
|
+
};
|
|
21
|
+
onUnload(cb: () => void): void;
|
|
22
|
+
}
|
|
23
|
+
export declare const uniAdapter: PlatformAdapter;
|
|
24
|
+
export declare function createUniSendFn(reportUrl: string, appKey?: string): (events: any[]) => Promise<void>;
|
|
25
|
+
export declare function detectPlatform(): string;
|
|
26
|
+
export declare function installErrorCapture(Vue: any, tracker: Tracker): void;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { EventType, Platform } from "@gctrack/protocol";
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
/** uni 适配实现 —— 全端用 uni.* 标准 API,不写 #ifdef */
|
|
5
|
+
const uniAdapter = {
|
|
6
|
+
getStorage: (k) => {
|
|
7
|
+
try {
|
|
8
|
+
return uni.getStorageSync(k);
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
setStorage: (k, v) => {
|
|
14
|
+
try {
|
|
15
|
+
uni.setStorageSync(k, v);
|
|
16
|
+
} catch {}
|
|
17
|
+
},
|
|
18
|
+
removeStorage: (k) => {
|
|
19
|
+
try {
|
|
20
|
+
uni.removeStorageSync(k);
|
|
21
|
+
} catch {}
|
|
22
|
+
},
|
|
23
|
+
request: (opts) => new Promise((resolve, reject) => {
|
|
24
|
+
uni.request({
|
|
25
|
+
url: opts.url,
|
|
26
|
+
method: opts.method || "POST",
|
|
27
|
+
data: opts.data,
|
|
28
|
+
header: opts.header || {},
|
|
29
|
+
success: (res) => resolve({
|
|
30
|
+
statusCode: res.statusCode,
|
|
31
|
+
data: res.data
|
|
32
|
+
}),
|
|
33
|
+
fail: (err) => reject(err)
|
|
34
|
+
});
|
|
35
|
+
}),
|
|
36
|
+
getSystemInfo: () => {
|
|
37
|
+
try {
|
|
38
|
+
return uni.getSystemInfoSync();
|
|
39
|
+
} catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
getPageInfo: () => {
|
|
44
|
+
try {
|
|
45
|
+
const pages = getCurrentPages();
|
|
46
|
+
const top = pages[pages.length - 1];
|
|
47
|
+
const route = top?.route || "";
|
|
48
|
+
const options = top?.options || {};
|
|
49
|
+
const query = Object.keys(options).map((k) => `${k}=${options[k]}`).join("&");
|
|
50
|
+
return {
|
|
51
|
+
pathname: "/" + route,
|
|
52
|
+
url: "/" + route + (query ? "?" + query : ""),
|
|
53
|
+
title: ""
|
|
54
|
+
};
|
|
55
|
+
} catch {
|
|
56
|
+
return {
|
|
57
|
+
pathname: "",
|
|
58
|
+
url: "",
|
|
59
|
+
title: ""
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
onUnload: (cb) => {
|
|
64
|
+
try {
|
|
65
|
+
uni.onAppHide && uni.onAppHide(cb);
|
|
66
|
+
} catch {}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* 构造基于 uni.request 的 sendFn,供 createTracker({ sendFn }) 使用。
|
|
71
|
+
* 小程序无 sendBeacon/fetch,统一走 uni.request;header 值强制 String()(XHS 要求)。
|
|
72
|
+
*/
|
|
73
|
+
function createUniSendFn(reportUrl, appKey) {
|
|
74
|
+
return (events) => uniAdapter.request({
|
|
75
|
+
url: reportUrl,
|
|
76
|
+
method: "POST",
|
|
77
|
+
data: events,
|
|
78
|
+
header: {
|
|
79
|
+
"content-type": "application/json",
|
|
80
|
+
"X-App-Key": appKey ? String(appKey) : ""
|
|
81
|
+
}
|
|
82
|
+
}).then(() => void 0);
|
|
83
|
+
}
|
|
84
|
+
/** 根据 uni 运行环境推断 Platform 标识(写入 context.platform) */
|
|
85
|
+
function detectPlatform() {
|
|
86
|
+
try {
|
|
87
|
+
const info = uni.getSystemInfoSync();
|
|
88
|
+
const p = info.uniPlatform || info.platform;
|
|
89
|
+
if (typeof p === "string") {
|
|
90
|
+
if (p.includes("mp-weixin") || p.includes("weixin")) return Platform.WeixinMp;
|
|
91
|
+
if (p.includes("mp-alipay") || p.includes("alipay")) return Platform.AlipayMp;
|
|
92
|
+
if (p.includes("mp-jd") || p.includes("jd")) return Platform.JdMp;
|
|
93
|
+
if (p.includes("mp-xhs") || p.includes("xhs")) return Platform.XhsMp;
|
|
94
|
+
if (p.includes("mp-baidu") || p.includes("baidu")) return Platform.BaiduMp;
|
|
95
|
+
if (p.includes("h5")) return Platform.H5;
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
return Platform.WeixinMp;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 错误捕获一键挂载:
|
|
102
|
+
* - Vue.config.errorHandler → vue_error
|
|
103
|
+
* - uni.onUnhandledRejection → promise_error
|
|
104
|
+
* App.onError 需在 App.vue 里手动调用 tracker.track(见 design.md 13.2)。
|
|
105
|
+
*/
|
|
106
|
+
function installErrorCapture(Vue, tracker) {
|
|
107
|
+
if (Vue?.config) Vue.config.errorHandler = (err, vm, info) => {
|
|
108
|
+
tracker.track(EventType.VueError, {
|
|
109
|
+
message: err?.message || String(err),
|
|
110
|
+
stack: err?.stack,
|
|
111
|
+
info,
|
|
112
|
+
componentName: vm?.$options?.name || vm?.$options?._componentTag
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
try {
|
|
116
|
+
uni.onUnhandledRejection && uni.onUnhandledRejection((res) => {
|
|
117
|
+
const reason = res?.reason;
|
|
118
|
+
tracker.track(EventType.PromiseError, {
|
|
119
|
+
message: reason?.message || String(reason),
|
|
120
|
+
stack: reason?.stack
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
} catch {}
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
export { createUniSendFn, detectPlatform, installErrorCapture, uniAdapter };
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @gctrack/uni —— 小程序多端适配\n *\n * 小程序无 window/document/navigator/sendBeacon/fetch/PerformanceObserver,\n * 本包提供 PlatformAdapter(存储/网络/生命周期/设备/页面)的 uni 实现,\n * 以及错误捕获(App.onError / unhandledRejection / Vue.errorHandler)一键挂载。\n *\n * 详见 design.md 5.5。完整实现(PV、点击、白屏耗时等)在「@gctrack/uni 完善」阶段补充。\n */\nimport type { Tracker } from '@gctrack/core'\nimport { EventType, Platform } from '@gctrack/protocol'\n\n// 小程序运行时注入的全局(各端一致提供 uni / getCurrentPages)\ndeclare const uni: any\ndeclare function getCurrentPages(): any[]\n\n/** 平台适配器接口 —— core 通过它解耦浏览器 API,详见 design.md 5.5 */\nexport interface PlatformAdapter {\n getStorage(key: string): any\n setStorage(key: string, value: any): void\n removeStorage(key: string): void\n request(opts: {\n url: string\n method?: string\n data?: any\n header?: Record<string, string>\n }): Promise<{ statusCode: number; data: any }>\n getSystemInfo(): Record<string, any>\n getPageInfo(): { pathname: string; url: string; title: string }\n onUnload(cb: () => void): void\n}\n\n/** uni 适配实现 —— 全端用 uni.* 标准 API,不写 #ifdef */\nexport const uniAdapter: PlatformAdapter = {\n getStorage: (k) => {\n try {\n return uni.getStorageSync(k)\n } catch {\n return null\n }\n },\n setStorage: (k, v) => {\n try {\n uni.setStorageSync(k, v)\n } catch {\n /* ignore */\n }\n },\n removeStorage: (k) => {\n try {\n uni.removeStorageSync(k)\n } catch {\n /* ignore */\n }\n },\n request: (opts) =>\n new Promise((resolve, reject) => {\n uni.request({\n url: opts.url,\n method: (opts.method || 'POST') as any,\n data: opts.data,\n header: opts.header || {},\n success: (res: any) => resolve({ statusCode: res.statusCode, data: res.data }),\n fail: (err: any) => reject(err),\n })\n }),\n getSystemInfo: () => {\n try {\n return uni.getSystemInfoSync()\n } catch {\n return {}\n }\n },\n getPageInfo: () => {\n try {\n const pages = getCurrentPages()\n const top = pages[pages.length - 1]\n const route: string = top?.route || ''\n const options = top?.options || {}\n const query = Object.keys(options)\n .map((k) => `${k}=${options[k]}`)\n .join('&')\n return {\n pathname: '/' + route,\n url: '/' + route + (query ? '?' + query : ''),\n title: '',\n }\n } catch {\n return { pathname: '', url: '', title: '' }\n }\n },\n onUnload: (cb) => {\n // 兜底:App.onHide / Page.onHide 由 installAppLifecycle 接管,这里注册 uni 级监听\n try {\n uni.onAppHide && uni.onAppHide(cb)\n } catch {\n /* ignore */\n }\n },\n}\n\n/**\n * 构造基于 uni.request 的 sendFn,供 createTracker({ sendFn }) 使用。\n * 小程序无 sendBeacon/fetch,统一走 uni.request;header 值强制 String()(XHS 要求)。\n */\nexport function createUniSendFn(reportUrl: string, appKey?: string) {\n return (events: any[]): Promise<void> =>\n uniAdapter\n .request({\n url: reportUrl,\n method: 'POST',\n data: events,\n header: {\n 'content-type': 'application/json',\n 'X-App-Key': appKey ? String(appKey) : '',\n },\n })\n .then(() => undefined)\n}\n\n/** 根据 uni 运行环境推断 Platform 标识(写入 context.platform) */\nexport function detectPlatform(): string {\n try {\n const info = uni.getSystemInfoSync()\n // uni-app 注入 UNI_PLATFORM / uniPlatform\n const p = info.uniPlatform || info.platform\n if (typeof p === 'string') {\n if (p.includes('mp-weixin') || p.includes('weixin')) return Platform.WeixinMp\n if (p.includes('mp-alipay') || p.includes('alipay')) return Platform.AlipayMp\n if (p.includes('mp-jd') || p.includes('jd')) return Platform.JdMp\n if (p.includes('mp-xhs') || p.includes('xhs')) return Platform.XhsMp\n if (p.includes('mp-baidu') || p.includes('baidu')) return Platform.BaiduMp\n if (p.includes('h5')) return Platform.H5\n }\n } catch {\n /* ignore */\n }\n return Platform.WeixinMp\n}\n\n/**\n * 错误捕获一键挂载:\n * - Vue.config.errorHandler → vue_error\n * - uni.onUnhandledRejection → promise_error\n * App.onError 需在 App.vue 里手动调用 tracker.track(见 design.md 13.2)。\n */\nexport function installErrorCapture(Vue: any, tracker: Tracker): void {\n if (Vue?.config) {\n Vue.config.errorHandler = (err: any, vm: any, info: string) => {\n tracker.track(EventType.VueError, {\n message: err?.message || String(err),\n stack: err?.stack,\n info,\n componentName: vm?.$options?.name || vm?.$options?._componentTag,\n })\n }\n }\n try {\n uni.onUnhandledRejection &&\n uni.onUnhandledRejection((res: any) => {\n const reason = res?.reason\n tracker.track(EventType.PromiseError, {\n message: reason?.message || String(reason),\n stack: reason?.stack,\n })\n })\n } catch {\n /* ignore */\n }\n}\n"],"mappings":";;;;AAiCA,MAAa,aAA8B;CACzC,aAAa,MAAM;EACjB,IAAI;GACF,OAAO,IAAI,eAAe,EAAE;UACtB;GACN,OAAO;;;CAGX,aAAa,GAAG,MAAM;EACpB,IAAI;GACF,IAAI,eAAe,GAAG,EAAE;UAClB;;CAIV,gBAAgB,MAAM;EACpB,IAAI;GACF,IAAI,kBAAkB,EAAE;UAClB;;CAIV,UAAU,SACR,IAAI,SAAS,SAAS,WAAW;EAC/B,IAAI,QAAQ;GACV,KAAK,KAAK;GACV,QAAS,KAAK,UAAU;GACxB,MAAM,KAAK;GACX,QAAQ,KAAK,UAAU,EAAE;GACzB,UAAU,QAAa,QAAQ;IAAE,YAAY,IAAI;IAAY,MAAM,IAAI;IAAM,CAAC;GAC9E,OAAO,QAAa,OAAO,IAAI;GAChC,CAAC;GACF;CACJ,qBAAqB;EACnB,IAAI;GACF,OAAO,IAAI,mBAAmB;UACxB;GACN,OAAO,EAAE;;;CAGb,mBAAmB;EACjB,IAAI;GACF,MAAM,QAAQ,iBAAiB;GAC/B,MAAM,MAAM,MAAM,MAAM,SAAS;GACjC,MAAM,QAAgB,KAAK,SAAS;GACpC,MAAM,UAAU,KAAK,WAAW,EAAE;GAClC,MAAM,QAAQ,OAAO,KAAK,QAAQ,CAC/B,KAAK,MAAM,GAAG,EAAE,GAAG,QAAQ,KAAK,CAChC,KAAK,IAAI;GACZ,OAAO;IACL,UAAU,MAAM;IAChB,KAAK,MAAM,SAAS,QAAQ,MAAM,QAAQ;IAC1C,OAAO;IACR;UACK;GACN,OAAO;IAAE,UAAU;IAAI,KAAK;IAAI,OAAO;IAAI;;;CAG/C,WAAW,OAAO;EAEhB,IAAI;GACF,IAAI,aAAa,IAAI,UAAU,GAAG;UAC5B;;CAIX;;;;;AAMD,SAAgB,gBAAgB,WAAmB,QAAiB;CAClE,QAAQ,WACN,WACG,QAAQ;EACP,KAAK;EACL,QAAQ;EACR,MAAM;EACN,QAAQ;GACN,gBAAgB;GAChB,aAAa,SAAS,OAAO,OAAO,GAAG;GACxC;EACF,CAAC,CACD,WAAW,KAAA,EAAU;;;AAI5B,SAAgB,iBAAyB;CACvC,IAAI;EACF,MAAM,OAAO,IAAI,mBAAmB;EAEpC,MAAM,IAAI,KAAK,eAAe,KAAK;EACnC,IAAI,OAAO,MAAM,UAAU;GACzB,IAAI,EAAE,SAAS,YAAY,IAAI,EAAE,SAAS,SAAS,EAAE,OAAO,SAAS;GACrE,IAAI,EAAE,SAAS,YAAY,IAAI,EAAE,SAAS,SAAS,EAAE,OAAO,SAAS;GACrE,IAAI,EAAE,SAAS,QAAQ,IAAI,EAAE,SAAS,KAAK,EAAE,OAAO,SAAS;GAC7D,IAAI,EAAE,SAAS,SAAS,IAAI,EAAE,SAAS,MAAM,EAAE,OAAO,SAAS;GAC/D,IAAI,EAAE,SAAS,WAAW,IAAI,EAAE,SAAS,QAAQ,EAAE,OAAO,SAAS;GACnE,IAAI,EAAE,SAAS,KAAK,EAAE,OAAO,SAAS;;SAElC;CAGR,OAAO,SAAS;;;;;;;;AASlB,SAAgB,oBAAoB,KAAU,SAAwB;CACpE,IAAI,KAAK,QACP,IAAI,OAAO,gBAAgB,KAAU,IAAS,SAAiB;EAC7D,QAAQ,MAAM,UAAU,UAAU;GAChC,SAAS,KAAK,WAAW,OAAO,IAAI;GACpC,OAAO,KAAK;GACZ;GACA,eAAe,IAAI,UAAU,QAAQ,IAAI,UAAU;GACpD,CAAC;;CAGN,IAAI;EACF,IAAI,wBACF,IAAI,sBAAsB,QAAa;GACrC,MAAM,SAAS,KAAK;GACpB,QAAQ,MAAM,UAAU,cAAc;IACpC,SAAS,QAAQ,WAAW,OAAO,OAAO;IAC1C,OAAO,QAAQ;IAChB,CAAC;IACF;SACE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gctrack/uni",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "GCTrack 小程序多端适配 —— uni-app 微信/支付宝/京东/小红书/H5",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"monitor",
|
|
21
|
+
"tracking",
|
|
22
|
+
"uni-app",
|
|
23
|
+
"miniprogram",
|
|
24
|
+
"wechat",
|
|
25
|
+
"alipay",
|
|
26
|
+
"jd",
|
|
27
|
+
"xhs"
|
|
28
|
+
],
|
|
29
|
+
"author": "yinuo",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@gctrack/protocol": "0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@gctrack/core": "0.1.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsdown",
|
|
42
|
+
"dev": "tsdown --watch"
|
|
43
|
+
}
|
|
44
|
+
}
|