@fz-firefly/vue-plugin 0.0.0-stage → 0.2.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/README.md +12 -2
- package/dist/index.cjs +239 -0
- package/dist/index.d.cts +98 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +236 -0
- package/package.json +43 -3
package/README.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fz-firefly/vue-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Firefly 的 Vue 2 / Vue 3 错误处理与路由面包屑插件。
|
|
4
|
+
|
|
5
|
+
这是 `@fz-firefly/vue` 和 `@fz-firefly/uniapp` 的内部依赖,通常不需要直接安装或使用。
|
|
6
|
+
|
|
7
|
+
如果你已经自己创建了 Firefly 客户端,想手动安装 Vue 插件,可以使用:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { fireflyVue, fireflyVue2 } from '@fz-firefly/vue-plugin'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
普通 Vue 项目建议直接使用 `@fz-firefly/vue`,uni-app 项目使用 `@fz-firefly/uniapp`。
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/typeof.js
|
|
3
|
+
function _typeof(o) {
|
|
4
|
+
"@babel/helpers - typeof";
|
|
5
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
6
|
+
return typeof o;
|
|
7
|
+
} : function(o) {
|
|
8
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
9
|
+
}, _typeof(o);
|
|
10
|
+
}
|
|
11
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPrimitive.js
|
|
12
|
+
function toPrimitive(t, r) {
|
|
13
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
14
|
+
var e = t[Symbol.toPrimitive];
|
|
15
|
+
if (void 0 !== e) {
|
|
16
|
+
var i = e.call(t, r || "default");
|
|
17
|
+
if ("object" != _typeof(i)) return i;
|
|
18
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
19
|
+
}
|
|
20
|
+
return ("string" === r ? String : Number)(t);
|
|
21
|
+
}
|
|
22
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPropertyKey.js
|
|
23
|
+
function toPropertyKey(t) {
|
|
24
|
+
var i = toPrimitive(t, "string");
|
|
25
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
26
|
+
}
|
|
27
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/defineProperty.js
|
|
28
|
+
function _defineProperty(e, r, t) {
|
|
29
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
30
|
+
value: t,
|
|
31
|
+
enumerable: !0,
|
|
32
|
+
configurable: !0,
|
|
33
|
+
writable: !0
|
|
34
|
+
}) : e[r] = t, e;
|
|
35
|
+
}
|
|
36
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/objectSpread2.js
|
|
37
|
+
function ownKeys(e, r) {
|
|
38
|
+
var t = Object.keys(e);
|
|
39
|
+
if (Object.getOwnPropertySymbols) {
|
|
40
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
41
|
+
r && (o = o.filter(function(r) {
|
|
42
|
+
return Object.getOwnPropertyDescriptor(e, r).enumerable;
|
|
43
|
+
})), t.push.apply(t, o);
|
|
44
|
+
}
|
|
45
|
+
return t;
|
|
46
|
+
}
|
|
47
|
+
function _objectSpread2(e) {
|
|
48
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
49
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
50
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
|
|
51
|
+
_defineProperty(e, r, t[r]);
|
|
52
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
|
|
53
|
+
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return e;
|
|
57
|
+
}
|
|
58
|
+
// src/router.ts
|
|
59
|
+
/** 安全还原 URL 百分号编码;格式不合法时保留原值,避免 SDK 抛错。 */
|
|
60
|
+
function decodePath(value) {
|
|
61
|
+
try {
|
|
62
|
+
return decodeURIComponent(value);
|
|
63
|
+
} catch (_unused) {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** 路由对象转成可读路径,拿不到时回退为空串。 */
|
|
68
|
+
function pathOf(route) {
|
|
69
|
+
var _ref, _route$fullPath;
|
|
70
|
+
return decodePath((_ref = (_route$fullPath = route === null || route === void 0 ? void 0 : route.fullPath) !== null && _route$fullPath !== void 0 ? _route$fullPath : route === null || route === void 0 ? void 0 : route.path) !== null && _ref !== void 0 ? _ref : "");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 路由参数只保留可序列化的原始值。
|
|
74
|
+
*
|
|
75
|
+
* query 里可能出现 null、函数或循环引用,直接上报会让信封序列化失败。
|
|
76
|
+
*/
|
|
77
|
+
function plainValues(value) {
|
|
78
|
+
if (!value || typeof value !== "object") return void 0;
|
|
79
|
+
const result = {};
|
|
80
|
+
for (const [key, item] of Object.entries(value).slice(0, 20)) if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") result[key] = item;
|
|
81
|
+
else if (Array.isArray(item)) {
|
|
82
|
+
const list = item.filter((entry) => typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean");
|
|
83
|
+
if (list.length > 0) result[key] = list;
|
|
84
|
+
}
|
|
85
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 挂载路由相关钩子。
|
|
89
|
+
*
|
|
90
|
+
* - onError:路由懒加载、导航守卫里的异常;
|
|
91
|
+
* - afterEach:导航面包屑,并在导航失败对象是 Error 时补报一条。
|
|
92
|
+
*
|
|
93
|
+
* 所有注册都做容错,SDK 不能因为路由实现差异阻断业务启动。
|
|
94
|
+
*/
|
|
95
|
+
function installRouterHooks(router, client) {
|
|
96
|
+
try {
|
|
97
|
+
var _router$onError;
|
|
98
|
+
(_router$onError = router.onError) === null || _router$onError === void 0 || _router$onError.call(router, (error, to, from) => {
|
|
99
|
+
try {
|
|
100
|
+
client.captureException(error, {
|
|
101
|
+
tags: { source: "router" },
|
|
102
|
+
contexts: { route: {
|
|
103
|
+
to: pathOf(to),
|
|
104
|
+
from: pathOf(from)
|
|
105
|
+
} }
|
|
106
|
+
});
|
|
107
|
+
} catch (_unused2) {}
|
|
108
|
+
});
|
|
109
|
+
} catch (_unused3) {}
|
|
110
|
+
try {
|
|
111
|
+
var _router$afterEach;
|
|
112
|
+
(_router$afterEach = router.afterEach) === null || _router$afterEach === void 0 || _router$afterEach.call(router, (to, from, failure) => {
|
|
113
|
+
try {
|
|
114
|
+
const toPath = pathOf(to) || "/";
|
|
115
|
+
const fromPath = pathOf(from) || "/";
|
|
116
|
+
const toParams = plainValues(to === null || to === void 0 ? void 0 : to.params);
|
|
117
|
+
const toQuery = plainValues(to === null || to === void 0 ? void 0 : to.query);
|
|
118
|
+
const fromParams = plainValues(from === null || from === void 0 ? void 0 : from.params);
|
|
119
|
+
const fromQuery = plainValues(from === null || from === void 0 ? void 0 : from.query);
|
|
120
|
+
if (fromPath !== toPath) {
|
|
121
|
+
var _client$startPageSess;
|
|
122
|
+
(_client$startPageSess = client.startPageSession) === null || _client$startPageSess === void 0 || _client$startPageSess.call(client, toPath, _objectSpread2(_objectSpread2({
|
|
123
|
+
navigation_type: "router",
|
|
124
|
+
from: fromPath
|
|
125
|
+
}, typeof (to === null || to === void 0 ? void 0 : to.name) === "string" ? { route_name: to.name } : {}), toQuery ? { query: toQuery } : {}));
|
|
126
|
+
client.addBreadcrumb({
|
|
127
|
+
type: "navigation",
|
|
128
|
+
category: "router",
|
|
129
|
+
level: "info",
|
|
130
|
+
timestamp: Date.now(),
|
|
131
|
+
message: `${fromPath} -> ${toPath}`,
|
|
132
|
+
data: _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
|
|
133
|
+
to: toPath,
|
|
134
|
+
from: fromPath
|
|
135
|
+
}, toParams ? { to_params: toParams } : {}), toQuery ? { to_query: toQuery } : {}), fromParams ? { from_params: fromParams } : {}), fromQuery ? { from_query: fromQuery } : {})
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
if (failure instanceof Error) client.captureException(failure, { tags: { source: "router" } });
|
|
139
|
+
} catch (_unused4) {}
|
|
140
|
+
});
|
|
141
|
+
} catch (_unused5) {}
|
|
142
|
+
}
|
|
143
|
+
// src/vue3.ts
|
|
144
|
+
/** 记录 firefly 安装过的 errorHandler 对应的原始 handler,避免重复包装。 */
|
|
145
|
+
const previousHandlers$1 = /* @__PURE__ */ new WeakMap();
|
|
146
|
+
/**
|
|
147
|
+
* Vue 3 插件。
|
|
148
|
+
*
|
|
149
|
+
* 用法:
|
|
150
|
+
* ```ts
|
|
151
|
+
* app.use(fireflyVue, { client: firefly.client, router })
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
const fireflyVue = { install(app, options) {
|
|
155
|
+
if (!(options === null || options === void 0 ? void 0 : options.client)) throw new Error("Firefly: fireflyVue 需要传入 client");
|
|
156
|
+
const { client, router } = options;
|
|
157
|
+
const current = app.config.errorHandler;
|
|
158
|
+
const previous = current && previousHandlers$1.has(current) ? previousHandlers$1.get(current) : current;
|
|
159
|
+
const errorHandler = (err, instance, info) => {
|
|
160
|
+
try {
|
|
161
|
+
client.captureException(err, { tags: {
|
|
162
|
+
source: "vue",
|
|
163
|
+
info
|
|
164
|
+
} });
|
|
165
|
+
} catch (_unused) {}
|
|
166
|
+
previous === null || previous === void 0 || previous(err, instance, info);
|
|
167
|
+
};
|
|
168
|
+
previousHandlers$1.set(errorHandler, previous);
|
|
169
|
+
app.config.errorHandler = errorHandler;
|
|
170
|
+
app.config.globalProperties.$firefly = client;
|
|
171
|
+
if (router) installRouterHooks(router, client);
|
|
172
|
+
} };
|
|
173
|
+
// src/vue2.ts
|
|
174
|
+
/**
|
|
175
|
+
* 记录 firefly 安装过的 errorHandler 对应的原始 handler。
|
|
176
|
+
*
|
|
177
|
+
* 重复安装时替换旧 handler,而不是把旧 firefly handler 再包一层,
|
|
178
|
+
* 否则同一个组件错误会被上报多次。
|
|
179
|
+
*/
|
|
180
|
+
const previousHandlers = /* @__PURE__ */ new WeakMap();
|
|
181
|
+
/**
|
|
182
|
+
* Vue 2 插件。
|
|
183
|
+
*
|
|
184
|
+
* 用法:
|
|
185
|
+
* ```ts
|
|
186
|
+
* Vue.use(fireflyVue2, { client: firefly.client, router })
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
const fireflyVue2 = { install(Vue, options) {
|
|
190
|
+
if (!(options === null || options === void 0 ? void 0 : options.client)) throw new Error("Firefly: fireflyVue2 需要传入 client");
|
|
191
|
+
const { client, router } = options;
|
|
192
|
+
const current = Vue.config.errorHandler;
|
|
193
|
+
const previous = current && previousHandlers.has(current) ? previousHandlers.get(current) : current;
|
|
194
|
+
const errorHandler = (err, vm, info) => {
|
|
195
|
+
try {
|
|
196
|
+
client.captureException(err, { tags: {
|
|
197
|
+
source: "vue",
|
|
198
|
+
info
|
|
199
|
+
} });
|
|
200
|
+
} catch (_unused) {}
|
|
201
|
+
previous === null || previous === void 0 || previous(err, vm, info);
|
|
202
|
+
};
|
|
203
|
+
previousHandlers.set(errorHandler, previous);
|
|
204
|
+
Vue.config.errorHandler = errorHandler;
|
|
205
|
+
Vue.prototype.$firefly = client;
|
|
206
|
+
if (router) installRouterHooks(router, client);
|
|
207
|
+
} };
|
|
208
|
+
// src/index.ts
|
|
209
|
+
/**
|
|
210
|
+
* 把 Firefly 挂到 Vue 2 / Vue 3 上。
|
|
211
|
+
*
|
|
212
|
+
* 传 `app` 走 Vue 3,传 `Vue` 走 Vue 2;两个都不传时不做任何事,
|
|
213
|
+
* 方便 uni-app 等只需要平台钩子的场景复用。
|
|
214
|
+
*
|
|
215
|
+
* @returns 是否安装了 Vue 插件。
|
|
216
|
+
*/
|
|
217
|
+
function installVue(options) {
|
|
218
|
+
if (options.app && options.Vue) throw new Error("Firefly: app 和 Vue 只能传一个");
|
|
219
|
+
if (options.app) {
|
|
220
|
+
fireflyVue.install(options.app, {
|
|
221
|
+
client: options.client,
|
|
222
|
+
router: options.router
|
|
223
|
+
});
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
if (options.Vue) {
|
|
227
|
+
fireflyVue2.install(options.Vue, {
|
|
228
|
+
client: options.client,
|
|
229
|
+
router: options.router
|
|
230
|
+
});
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
exports.fireflyVue = fireflyVue;
|
|
236
|
+
exports.fireflyVue2 = fireflyVue2;
|
|
237
|
+
exports.installVue = installVue;
|
|
238
|
+
|
|
239
|
+
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { FireflyClient, FireflyClient as FireflyClient$1 } from "@fz-firefly/core";
|
|
2
|
+
/**
|
|
3
|
+
* 路由对象的最小结构。
|
|
4
|
+
*
|
|
5
|
+
* 只描述插件真正用到的字段,兼容 vue-router 2 / 3 / 4,
|
|
6
|
+
* 也避免 SDK 直接依赖某个 vue-router 版本。
|
|
7
|
+
*/
|
|
8
|
+
interface RouteLike {
|
|
9
|
+
/** 完整路径,例如 /orders/42?tab=detail。 */
|
|
10
|
+
fullPath?: string;
|
|
11
|
+
/** 基础路径。 */
|
|
12
|
+
path?: string;
|
|
13
|
+
/** 路由名称。 */
|
|
14
|
+
name?: unknown;
|
|
15
|
+
/** 路由参数,例如 { id: '42' }。 */
|
|
16
|
+
params?: Record<string, unknown>;
|
|
17
|
+
/** 查询参数,例如 { tab: 'detail' }。 */
|
|
18
|
+
query?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** 路由钩子所需的最小接口。 */
|
|
21
|
+
interface RouterLike {
|
|
22
|
+
/** 注册路由错误钩子。vue-router 2/3/4 都有这个方法。 */
|
|
23
|
+
onError?(handler: (error: unknown, to?: RouteLike, from?: RouteLike) => void): void;
|
|
24
|
+
/** 注册导航完成钩子,用于记录面包屑。 */
|
|
25
|
+
afterEach?(hook: (to: RouteLike, from: RouteLike, failure?: unknown) => void): void;
|
|
26
|
+
}
|
|
27
|
+
/** Vue 插件公共参数。 */
|
|
28
|
+
interface FireflyVueOptions {
|
|
29
|
+
/** 已初始化的客户端,由业务传入。 */
|
|
30
|
+
client: FireflyClient$1;
|
|
31
|
+
/** 可选路由实例,用于捕获路由错误与记录导航面包屑。 */
|
|
32
|
+
router?: RouterLike;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Vue 3 app 的最小结构,避免插件直接依赖 Vue 类型。
|
|
36
|
+
*
|
|
37
|
+
* errorHandler 用 unknown 接收,兼容 Vue 3 的 ComponentPublicInstance
|
|
38
|
+
* 类型参数;插件内部会按统一签名收窄。
|
|
39
|
+
*/
|
|
40
|
+
interface Vue3AppLike {
|
|
41
|
+
config: {
|
|
42
|
+
errorHandler?: unknown;
|
|
43
|
+
globalProperties: Record<string, unknown>;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Vue 2 构造函数的最小结构,避免插件直接依赖 Vue 类型。 */
|
|
47
|
+
interface Vue2ConstructorLike {
|
|
48
|
+
config: {
|
|
49
|
+
errorHandler?: unknown;
|
|
50
|
+
};
|
|
51
|
+
prototype: {
|
|
52
|
+
$firefly?: FireflyClient$1;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Vue 3 插件。
|
|
57
|
+
*
|
|
58
|
+
* 用法:
|
|
59
|
+
* ```ts
|
|
60
|
+
* app.use(fireflyVue, { client: firefly.client, router })
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare const fireflyVue: {
|
|
64
|
+
install(app: Vue3AppLike, options?: FireflyVueOptions): void;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Vue 2 插件。
|
|
68
|
+
*
|
|
69
|
+
* 用法:
|
|
70
|
+
* ```ts
|
|
71
|
+
* Vue.use(fireflyVue2, { client: firefly.client, router })
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare const fireflyVue2: {
|
|
75
|
+
install(Vue: Vue2ConstructorLike, options?: FireflyVueOptions): void;
|
|
76
|
+
};
|
|
77
|
+
/** 安装 Vue 插件所需的参数。 */
|
|
78
|
+
export interface InstallVueOptions {
|
|
79
|
+
/** 已经初始化的 Firefly 客户端。 */
|
|
80
|
+
client: FireflyClient$1;
|
|
81
|
+
/** 可选:vue-router 实例,用于捕获路由错误和记录导航面包屑。 */
|
|
82
|
+
router?: RouterLike;
|
|
83
|
+
/** Vue 3 app;与 Vue 二选一。 */
|
|
84
|
+
app?: Vue3AppLike;
|
|
85
|
+
/** Vue 2 构造函数;与 app 二选一。 */
|
|
86
|
+
Vue?: Vue2ConstructorLike;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 把 Firefly 挂到 Vue 2 / Vue 3 上。
|
|
90
|
+
*
|
|
91
|
+
* 传 `app` 走 Vue 3,传 `Vue` 走 Vue 2;两个都不传时不做任何事,
|
|
92
|
+
* 方便 uni-app 等只需要平台钩子的场景复用。
|
|
93
|
+
*
|
|
94
|
+
* @returns 是否安装了 Vue 插件。
|
|
95
|
+
*/
|
|
96
|
+
export declare function installVue(options: InstallVueOptions): boolean;
|
|
97
|
+
export type { FireflyClient, FireflyVueOptions, RouteLike, RouterLike, Vue2ConstructorLike, Vue3AppLike };
|
|
98
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { FireflyClient, FireflyClient as FireflyClient$1 } from "@fz-firefly/core";
|
|
2
|
+
/**
|
|
3
|
+
* 路由对象的最小结构。
|
|
4
|
+
*
|
|
5
|
+
* 只描述插件真正用到的字段,兼容 vue-router 2 / 3 / 4,
|
|
6
|
+
* 也避免 SDK 直接依赖某个 vue-router 版本。
|
|
7
|
+
*/
|
|
8
|
+
interface RouteLike {
|
|
9
|
+
/** 完整路径,例如 /orders/42?tab=detail。 */
|
|
10
|
+
fullPath?: string;
|
|
11
|
+
/** 基础路径。 */
|
|
12
|
+
path?: string;
|
|
13
|
+
/** 路由名称。 */
|
|
14
|
+
name?: unknown;
|
|
15
|
+
/** 路由参数,例如 { id: '42' }。 */
|
|
16
|
+
params?: Record<string, unknown>;
|
|
17
|
+
/** 查询参数,例如 { tab: 'detail' }。 */
|
|
18
|
+
query?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** 路由钩子所需的最小接口。 */
|
|
21
|
+
interface RouterLike {
|
|
22
|
+
/** 注册路由错误钩子。vue-router 2/3/4 都有这个方法。 */
|
|
23
|
+
onError?(handler: (error: unknown, to?: RouteLike, from?: RouteLike) => void): void;
|
|
24
|
+
/** 注册导航完成钩子,用于记录面包屑。 */
|
|
25
|
+
afterEach?(hook: (to: RouteLike, from: RouteLike, failure?: unknown) => void): void;
|
|
26
|
+
}
|
|
27
|
+
/** Vue 插件公共参数。 */
|
|
28
|
+
interface FireflyVueOptions {
|
|
29
|
+
/** 已初始化的客户端,由业务传入。 */
|
|
30
|
+
client: FireflyClient$1;
|
|
31
|
+
/** 可选路由实例,用于捕获路由错误与记录导航面包屑。 */
|
|
32
|
+
router?: RouterLike;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Vue 3 app 的最小结构,避免插件直接依赖 Vue 类型。
|
|
36
|
+
*
|
|
37
|
+
* errorHandler 用 unknown 接收,兼容 Vue 3 的 ComponentPublicInstance
|
|
38
|
+
* 类型参数;插件内部会按统一签名收窄。
|
|
39
|
+
*/
|
|
40
|
+
interface Vue3AppLike {
|
|
41
|
+
config: {
|
|
42
|
+
errorHandler?: unknown;
|
|
43
|
+
globalProperties: Record<string, unknown>;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Vue 2 构造函数的最小结构,避免插件直接依赖 Vue 类型。 */
|
|
47
|
+
interface Vue2ConstructorLike {
|
|
48
|
+
config: {
|
|
49
|
+
errorHandler?: unknown;
|
|
50
|
+
};
|
|
51
|
+
prototype: {
|
|
52
|
+
$firefly?: FireflyClient$1;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Vue 3 插件。
|
|
57
|
+
*
|
|
58
|
+
* 用法:
|
|
59
|
+
* ```ts
|
|
60
|
+
* app.use(fireflyVue, { client: firefly.client, router })
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare const fireflyVue: {
|
|
64
|
+
install(app: Vue3AppLike, options?: FireflyVueOptions): void;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Vue 2 插件。
|
|
68
|
+
*
|
|
69
|
+
* 用法:
|
|
70
|
+
* ```ts
|
|
71
|
+
* Vue.use(fireflyVue2, { client: firefly.client, router })
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare const fireflyVue2: {
|
|
75
|
+
install(Vue: Vue2ConstructorLike, options?: FireflyVueOptions): void;
|
|
76
|
+
};
|
|
77
|
+
/** 安装 Vue 插件所需的参数。 */
|
|
78
|
+
export interface InstallVueOptions {
|
|
79
|
+
/** 已经初始化的 Firefly 客户端。 */
|
|
80
|
+
client: FireflyClient$1;
|
|
81
|
+
/** 可选:vue-router 实例,用于捕获路由错误和记录导航面包屑。 */
|
|
82
|
+
router?: RouterLike;
|
|
83
|
+
/** Vue 3 app;与 Vue 二选一。 */
|
|
84
|
+
app?: Vue3AppLike;
|
|
85
|
+
/** Vue 2 构造函数;与 app 二选一。 */
|
|
86
|
+
Vue?: Vue2ConstructorLike;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 把 Firefly 挂到 Vue 2 / Vue 3 上。
|
|
90
|
+
*
|
|
91
|
+
* 传 `app` 走 Vue 3,传 `Vue` 走 Vue 2;两个都不传时不做任何事,
|
|
92
|
+
* 方便 uni-app 等只需要平台钩子的场景复用。
|
|
93
|
+
*
|
|
94
|
+
* @returns 是否安装了 Vue 插件。
|
|
95
|
+
*/
|
|
96
|
+
export declare function installVue(options: InstallVueOptions): boolean;
|
|
97
|
+
export type { FireflyClient, FireflyVueOptions, RouteLike, RouterLike, Vue2ConstructorLike, Vue3AppLike };
|
|
98
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/typeof.js
|
|
2
|
+
function _typeof(o) {
|
|
3
|
+
"@babel/helpers - typeof";
|
|
4
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
5
|
+
return typeof o;
|
|
6
|
+
} : function(o) {
|
|
7
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
8
|
+
}, _typeof(o);
|
|
9
|
+
}
|
|
10
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPrimitive.js
|
|
11
|
+
function toPrimitive(t, r) {
|
|
12
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
13
|
+
var e = t[Symbol.toPrimitive];
|
|
14
|
+
if (void 0 !== e) {
|
|
15
|
+
var i = e.call(t, r || "default");
|
|
16
|
+
if ("object" != _typeof(i)) return i;
|
|
17
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
18
|
+
}
|
|
19
|
+
return ("string" === r ? String : Number)(t);
|
|
20
|
+
}
|
|
21
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPropertyKey.js
|
|
22
|
+
function toPropertyKey(t) {
|
|
23
|
+
var i = toPrimitive(t, "string");
|
|
24
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
25
|
+
}
|
|
26
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/defineProperty.js
|
|
27
|
+
function _defineProperty(e, r, t) {
|
|
28
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
29
|
+
value: t,
|
|
30
|
+
enumerable: !0,
|
|
31
|
+
configurable: !0,
|
|
32
|
+
writable: !0
|
|
33
|
+
}) : e[r] = t, e;
|
|
34
|
+
}
|
|
35
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/objectSpread2.js
|
|
36
|
+
function ownKeys(e, r) {
|
|
37
|
+
var t = Object.keys(e);
|
|
38
|
+
if (Object.getOwnPropertySymbols) {
|
|
39
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
40
|
+
r && (o = o.filter(function(r) {
|
|
41
|
+
return Object.getOwnPropertyDescriptor(e, r).enumerable;
|
|
42
|
+
})), t.push.apply(t, o);
|
|
43
|
+
}
|
|
44
|
+
return t;
|
|
45
|
+
}
|
|
46
|
+
function _objectSpread2(e) {
|
|
47
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
48
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
49
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
|
|
50
|
+
_defineProperty(e, r, t[r]);
|
|
51
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
|
|
52
|
+
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return e;
|
|
56
|
+
}
|
|
57
|
+
// src/router.ts
|
|
58
|
+
/** 安全还原 URL 百分号编码;格式不合法时保留原值,避免 SDK 抛错。 */
|
|
59
|
+
function decodePath(value) {
|
|
60
|
+
try {
|
|
61
|
+
return decodeURIComponent(value);
|
|
62
|
+
} catch (_unused) {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** 路由对象转成可读路径,拿不到时回退为空串。 */
|
|
67
|
+
function pathOf(route) {
|
|
68
|
+
var _ref, _route$fullPath;
|
|
69
|
+
return decodePath((_ref = (_route$fullPath = route === null || route === void 0 ? void 0 : route.fullPath) !== null && _route$fullPath !== void 0 ? _route$fullPath : route === null || route === void 0 ? void 0 : route.path) !== null && _ref !== void 0 ? _ref : "");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 路由参数只保留可序列化的原始值。
|
|
73
|
+
*
|
|
74
|
+
* query 里可能出现 null、函数或循环引用,直接上报会让信封序列化失败。
|
|
75
|
+
*/
|
|
76
|
+
function plainValues(value) {
|
|
77
|
+
if (!value || typeof value !== "object") return void 0;
|
|
78
|
+
const result = {};
|
|
79
|
+
for (const [key, item] of Object.entries(value).slice(0, 20)) if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") result[key] = item;
|
|
80
|
+
else if (Array.isArray(item)) {
|
|
81
|
+
const list = item.filter((entry) => typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean");
|
|
82
|
+
if (list.length > 0) result[key] = list;
|
|
83
|
+
}
|
|
84
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 挂载路由相关钩子。
|
|
88
|
+
*
|
|
89
|
+
* - onError:路由懒加载、导航守卫里的异常;
|
|
90
|
+
* - afterEach:导航面包屑,并在导航失败对象是 Error 时补报一条。
|
|
91
|
+
*
|
|
92
|
+
* 所有注册都做容错,SDK 不能因为路由实现差异阻断业务启动。
|
|
93
|
+
*/
|
|
94
|
+
function installRouterHooks(router, client) {
|
|
95
|
+
try {
|
|
96
|
+
var _router$onError;
|
|
97
|
+
(_router$onError = router.onError) === null || _router$onError === void 0 || _router$onError.call(router, (error, to, from) => {
|
|
98
|
+
try {
|
|
99
|
+
client.captureException(error, {
|
|
100
|
+
tags: { source: "router" },
|
|
101
|
+
contexts: { route: {
|
|
102
|
+
to: pathOf(to),
|
|
103
|
+
from: pathOf(from)
|
|
104
|
+
} }
|
|
105
|
+
});
|
|
106
|
+
} catch (_unused2) {}
|
|
107
|
+
});
|
|
108
|
+
} catch (_unused3) {}
|
|
109
|
+
try {
|
|
110
|
+
var _router$afterEach;
|
|
111
|
+
(_router$afterEach = router.afterEach) === null || _router$afterEach === void 0 || _router$afterEach.call(router, (to, from, failure) => {
|
|
112
|
+
try {
|
|
113
|
+
const toPath = pathOf(to) || "/";
|
|
114
|
+
const fromPath = pathOf(from) || "/";
|
|
115
|
+
const toParams = plainValues(to === null || to === void 0 ? void 0 : to.params);
|
|
116
|
+
const toQuery = plainValues(to === null || to === void 0 ? void 0 : to.query);
|
|
117
|
+
const fromParams = plainValues(from === null || from === void 0 ? void 0 : from.params);
|
|
118
|
+
const fromQuery = plainValues(from === null || from === void 0 ? void 0 : from.query);
|
|
119
|
+
if (fromPath !== toPath) {
|
|
120
|
+
var _client$startPageSess;
|
|
121
|
+
(_client$startPageSess = client.startPageSession) === null || _client$startPageSess === void 0 || _client$startPageSess.call(client, toPath, _objectSpread2(_objectSpread2({
|
|
122
|
+
navigation_type: "router",
|
|
123
|
+
from: fromPath
|
|
124
|
+
}, typeof (to === null || to === void 0 ? void 0 : to.name) === "string" ? { route_name: to.name } : {}), toQuery ? { query: toQuery } : {}));
|
|
125
|
+
client.addBreadcrumb({
|
|
126
|
+
type: "navigation",
|
|
127
|
+
category: "router",
|
|
128
|
+
level: "info",
|
|
129
|
+
timestamp: Date.now(),
|
|
130
|
+
message: `${fromPath} -> ${toPath}`,
|
|
131
|
+
data: _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
|
|
132
|
+
to: toPath,
|
|
133
|
+
from: fromPath
|
|
134
|
+
}, toParams ? { to_params: toParams } : {}), toQuery ? { to_query: toQuery } : {}), fromParams ? { from_params: fromParams } : {}), fromQuery ? { from_query: fromQuery } : {})
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (failure instanceof Error) client.captureException(failure, { tags: { source: "router" } });
|
|
138
|
+
} catch (_unused4) {}
|
|
139
|
+
});
|
|
140
|
+
} catch (_unused5) {}
|
|
141
|
+
}
|
|
142
|
+
// src/vue3.ts
|
|
143
|
+
/** 记录 firefly 安装过的 errorHandler 对应的原始 handler,避免重复包装。 */
|
|
144
|
+
const previousHandlers$1 = /* @__PURE__ */ new WeakMap();
|
|
145
|
+
/**
|
|
146
|
+
* Vue 3 插件。
|
|
147
|
+
*
|
|
148
|
+
* 用法:
|
|
149
|
+
* ```ts
|
|
150
|
+
* app.use(fireflyVue, { client: firefly.client, router })
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
const fireflyVue = { install(app, options) {
|
|
154
|
+
if (!(options === null || options === void 0 ? void 0 : options.client)) throw new Error("Firefly: fireflyVue 需要传入 client");
|
|
155
|
+
const { client, router } = options;
|
|
156
|
+
const current = app.config.errorHandler;
|
|
157
|
+
const previous = current && previousHandlers$1.has(current) ? previousHandlers$1.get(current) : current;
|
|
158
|
+
const errorHandler = (err, instance, info) => {
|
|
159
|
+
try {
|
|
160
|
+
client.captureException(err, { tags: {
|
|
161
|
+
source: "vue",
|
|
162
|
+
info
|
|
163
|
+
} });
|
|
164
|
+
} catch (_unused) {}
|
|
165
|
+
previous === null || previous === void 0 || previous(err, instance, info);
|
|
166
|
+
};
|
|
167
|
+
previousHandlers$1.set(errorHandler, previous);
|
|
168
|
+
app.config.errorHandler = errorHandler;
|
|
169
|
+
app.config.globalProperties.$firefly = client;
|
|
170
|
+
if (router) installRouterHooks(router, client);
|
|
171
|
+
} };
|
|
172
|
+
// src/vue2.ts
|
|
173
|
+
/**
|
|
174
|
+
* 记录 firefly 安装过的 errorHandler 对应的原始 handler。
|
|
175
|
+
*
|
|
176
|
+
* 重复安装时替换旧 handler,而不是把旧 firefly handler 再包一层,
|
|
177
|
+
* 否则同一个组件错误会被上报多次。
|
|
178
|
+
*/
|
|
179
|
+
const previousHandlers = /* @__PURE__ */ new WeakMap();
|
|
180
|
+
/**
|
|
181
|
+
* Vue 2 插件。
|
|
182
|
+
*
|
|
183
|
+
* 用法:
|
|
184
|
+
* ```ts
|
|
185
|
+
* Vue.use(fireflyVue2, { client: firefly.client, router })
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
const fireflyVue2 = { install(Vue, options) {
|
|
189
|
+
if (!(options === null || options === void 0 ? void 0 : options.client)) throw new Error("Firefly: fireflyVue2 需要传入 client");
|
|
190
|
+
const { client, router } = options;
|
|
191
|
+
const current = Vue.config.errorHandler;
|
|
192
|
+
const previous = current && previousHandlers.has(current) ? previousHandlers.get(current) : current;
|
|
193
|
+
const errorHandler = (err, vm, info) => {
|
|
194
|
+
try {
|
|
195
|
+
client.captureException(err, { tags: {
|
|
196
|
+
source: "vue",
|
|
197
|
+
info
|
|
198
|
+
} });
|
|
199
|
+
} catch (_unused) {}
|
|
200
|
+
previous === null || previous === void 0 || previous(err, vm, info);
|
|
201
|
+
};
|
|
202
|
+
previousHandlers.set(errorHandler, previous);
|
|
203
|
+
Vue.config.errorHandler = errorHandler;
|
|
204
|
+
Vue.prototype.$firefly = client;
|
|
205
|
+
if (router) installRouterHooks(router, client);
|
|
206
|
+
} };
|
|
207
|
+
// src/index.ts
|
|
208
|
+
/**
|
|
209
|
+
* 把 Firefly 挂到 Vue 2 / Vue 3 上。
|
|
210
|
+
*
|
|
211
|
+
* 传 `app` 走 Vue 3,传 `Vue` 走 Vue 2;两个都不传时不做任何事,
|
|
212
|
+
* 方便 uni-app 等只需要平台钩子的场景复用。
|
|
213
|
+
*
|
|
214
|
+
* @returns 是否安装了 Vue 插件。
|
|
215
|
+
*/
|
|
216
|
+
function installVue(options) {
|
|
217
|
+
if (options.app && options.Vue) throw new Error("Firefly: app 和 Vue 只能传一个");
|
|
218
|
+
if (options.app) {
|
|
219
|
+
fireflyVue.install(options.app, {
|
|
220
|
+
client: options.client,
|
|
221
|
+
router: options.router
|
|
222
|
+
});
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
if (options.Vue) {
|
|
226
|
+
fireflyVue2.install(options.Vue, {
|
|
227
|
+
client: options.client,
|
|
228
|
+
router: options.router
|
|
229
|
+
});
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
export { fireflyVue, fireflyVue2, installVue };
|
|
235
|
+
|
|
236
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fz-firefly/vue-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "萤火虫 SDK 的 Vue 2 / Vue 3 错误处理与路由面包屑插件(内部包)。",
|
|
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
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"!dist/**/*.map"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@fz-firefly/core": "0.2.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"tsdown": "0.23.2",
|
|
29
|
+
"typescript": "^5.6.0",
|
|
30
|
+
"vitest": "^5.0.5",
|
|
31
|
+
"vue": "^3.5.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"vue": "^2.0.0 || ^3.0.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"vue": {
|
|
38
|
+
"optional": true
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsdown",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
}
|
|
6
46
|
}
|