@fz-firefly/react-plugin 0.0.0-stage → 0.2.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 +11 -2
- package/dist/index.cjs +188 -0
- package/dist/index.d.cts +100 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.js +183 -0
- package/package.json +44 -3
package/README.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fz-firefly/react-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
萤火虫 SDK 的 React 错误边界与根级错误处理器,供 `@fz-firefly/react` 和 `@fz-firefly/taro/react` 复用。
|
|
4
|
+
|
|
5
|
+
这个包不包含任何平台 API,也不创建客户端。业务通常不需要直接安装它。
|
|
6
|
+
|
|
7
|
+
## 导出
|
|
8
|
+
|
|
9
|
+
- `FireflyErrorBoundary`:类组件错误边界,捕获渲染期异常并调用 `client.captureException()`。
|
|
10
|
+
- `FireflyProvider` / `useFirefly`:可选的 Context 注入,方便组件里手动上报。
|
|
11
|
+
- `createFireflyErrorHandlers`:生成 React 19 `createRoot` 的 `onUncaughtError` / `onCaughtError` / `onRecoverableError`。
|
|
12
|
+
- `toError`:把任意抛出值转成 `Error`。
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let react = require("react");
|
|
3
|
+
// src/reported.ts
|
|
4
|
+
/**
|
|
5
|
+
* 已经由 FireflyErrorBoundary 上报过的错误。
|
|
6
|
+
*
|
|
7
|
+
* React 19 的 onCaughtError 会在错误边界处理后再次回调,
|
|
8
|
+
* 这里用 WeakSet 标记,避免同一条错误被上报两次。
|
|
9
|
+
* WeakSet 不持有强引用,不会阻止错误对象被回收。
|
|
10
|
+
*/
|
|
11
|
+
const reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
12
|
+
/** 把错误标记为已上报。 */
|
|
13
|
+
function markReported(error) {
|
|
14
|
+
if (typeof error === "object" && error !== null) reportedErrors.add(error);
|
|
15
|
+
}
|
|
16
|
+
/** 判断错误是否已经由 FireflyErrorBoundary 上报。 */
|
|
17
|
+
function isReported(error) {
|
|
18
|
+
return typeof error === "object" && error !== null && reportedErrors.has(error);
|
|
19
|
+
}
|
|
20
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/typeof.js
|
|
21
|
+
function _typeof(o) {
|
|
22
|
+
"@babel/helpers - typeof";
|
|
23
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
24
|
+
return typeof o;
|
|
25
|
+
} : function(o) {
|
|
26
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
27
|
+
}, _typeof(o);
|
|
28
|
+
}
|
|
29
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPrimitive.js
|
|
30
|
+
function toPrimitive(t, r) {
|
|
31
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
32
|
+
var e = t[Symbol.toPrimitive];
|
|
33
|
+
if (void 0 !== e) {
|
|
34
|
+
var i = e.call(t, r || "default");
|
|
35
|
+
if ("object" != _typeof(i)) return i;
|
|
36
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
37
|
+
}
|
|
38
|
+
return ("string" === r ? String : Number)(t);
|
|
39
|
+
}
|
|
40
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPropertyKey.js
|
|
41
|
+
function toPropertyKey(t) {
|
|
42
|
+
var i = toPrimitive(t, "string");
|
|
43
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
44
|
+
}
|
|
45
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/defineProperty.js
|
|
46
|
+
function _defineProperty(e, r, t) {
|
|
47
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
48
|
+
value: t,
|
|
49
|
+
enumerable: !0,
|
|
50
|
+
configurable: !0,
|
|
51
|
+
writable: !0
|
|
52
|
+
}) : e[r] = t, e;
|
|
53
|
+
}
|
|
54
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/objectSpread2.js
|
|
55
|
+
function ownKeys(e, r) {
|
|
56
|
+
var t = Object.keys(e);
|
|
57
|
+
if (Object.getOwnPropertySymbols) {
|
|
58
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
59
|
+
r && (o = o.filter(function(r) {
|
|
60
|
+
return Object.getOwnPropertyDescriptor(e, r).enumerable;
|
|
61
|
+
})), t.push.apply(t, o);
|
|
62
|
+
}
|
|
63
|
+
return t;
|
|
64
|
+
}
|
|
65
|
+
function _objectSpread2(e) {
|
|
66
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
67
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
68
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
|
|
69
|
+
_defineProperty(e, r, t[r]);
|
|
70
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
|
|
71
|
+
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return e;
|
|
75
|
+
}
|
|
76
|
+
// src/error-boundary.ts
|
|
77
|
+
/** 把任意抛出值统一成 Error,保证 fallback 与上报都能拿到 message。 */
|
|
78
|
+
function toError(value) {
|
|
79
|
+
if (value instanceof Error) return value;
|
|
80
|
+
if (typeof value === "string") return new Error(value);
|
|
81
|
+
try {
|
|
82
|
+
return new Error(typeof value === "object" && value !== null ? JSON.stringify(value) : String(value));
|
|
83
|
+
} catch (_unused) {
|
|
84
|
+
return /* @__PURE__ */ new Error("Unknown error");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* React 错误边界。
|
|
89
|
+
*
|
|
90
|
+
* React 没有 Vue 那样的全局 errorHandler,渲染期异常只能靠错误边界捕获。
|
|
91
|
+
* 组件会把错误上报到传入的 client,然后渲染 fallback。
|
|
92
|
+
*
|
|
93
|
+
* ```tsx
|
|
94
|
+
* <FireflyErrorBoundary client={firefly.client}>
|
|
95
|
+
* <App />
|
|
96
|
+
* </FireflyErrorBoundary>
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
var FireflyErrorBoundary = class extends react.Component {
|
|
100
|
+
constructor(..._args) {
|
|
101
|
+
super(..._args);
|
|
102
|
+
_defineProperty(this, "state", { error: null });
|
|
103
|
+
_defineProperty(this, "reset", () => {
|
|
104
|
+
this.setState({ error: null });
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
static getDerivedStateFromError(error) {
|
|
108
|
+
return { error: toError(error) };
|
|
109
|
+
}
|
|
110
|
+
componentDidCatch(error, info) {
|
|
111
|
+
markReported(error);
|
|
112
|
+
try {
|
|
113
|
+
this.props.client.captureException(error, _objectSpread2({ tags: { source: "react" } }, info.componentStack ? { contexts: { react: { component_stack: info.componentStack } } } : {}));
|
|
114
|
+
} catch (_unused2) {}
|
|
115
|
+
}
|
|
116
|
+
render() {
|
|
117
|
+
var _this$props$children;
|
|
118
|
+
const { error } = this.state;
|
|
119
|
+
if (!error) return (_this$props$children = this.props.children) !== null && _this$props$children !== void 0 ? _this$props$children : null;
|
|
120
|
+
const { fallback } = this.props;
|
|
121
|
+
if (typeof fallback === "function") return fallback(error, this.reset);
|
|
122
|
+
return fallback !== null && fallback !== void 0 ? fallback : null;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
// src/context.ts
|
|
126
|
+
/** 当前子树可用的 Firefly 客户端。 */
|
|
127
|
+
const FireflyContext = (0, react.createContext)(null);
|
|
128
|
+
/**
|
|
129
|
+
* 把 client 放进 React Context,方便组件里用 useFirefly() 手动上报。
|
|
130
|
+
*
|
|
131
|
+
* 错误边界本身要求显式传 client,不依赖这个 Provider。
|
|
132
|
+
*/
|
|
133
|
+
function FireflyProvider(props) {
|
|
134
|
+
return (0, react.createElement)(FireflyContext.Provider, { value: props.client }, props.children);
|
|
135
|
+
}
|
|
136
|
+
/** 读取最近的 FireflyProvider 注入的客户端。 */
|
|
137
|
+
function useFirefly() {
|
|
138
|
+
const client = (0, react.useContext)(FireflyContext);
|
|
139
|
+
if (!client) throw new Error("Firefly: useFirefly 必须在 FireflyProvider 内使用");
|
|
140
|
+
return client;
|
|
141
|
+
}
|
|
142
|
+
// src/root-handlers.ts
|
|
143
|
+
/**
|
|
144
|
+
* 创建 React 19 createRoot / hydrateRoot 的错误回调。
|
|
145
|
+
*
|
|
146
|
+
* ```ts
|
|
147
|
+
* createRoot(root, {
|
|
148
|
+
* ...createFireflyErrorHandlers(firefly.client),
|
|
149
|
+
* })
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* - onUncaughtError:没有被错误边界接住的异常;
|
|
153
|
+
* - onCaughtError:被错误边界接住的异常,已由 FireflyErrorBoundary 上报的会跳过;
|
|
154
|
+
* - onRecoverableError:hydration 等可恢复错误,按 warning 上报。
|
|
155
|
+
*/
|
|
156
|
+
function createFireflyErrorHandlers(client, options = {}) {
|
|
157
|
+
function report(error, info, level, phase) {
|
|
158
|
+
try {
|
|
159
|
+
client.captureException(error, _objectSpread2({
|
|
160
|
+
level,
|
|
161
|
+
tags: {
|
|
162
|
+
source: "react",
|
|
163
|
+
phase
|
|
164
|
+
}
|
|
165
|
+
}, info.componentStack ? { contexts: { react: { component_stack: info.componentStack } } } : {}));
|
|
166
|
+
} catch (_unused) {}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
onUncaughtError(error, info) {
|
|
170
|
+
report(error, info, "error", "uncaught");
|
|
171
|
+
},
|
|
172
|
+
onCaughtError(error, info) {
|
|
173
|
+
if (options.reportCaught === false) return;
|
|
174
|
+
if (isReported(error)) return;
|
|
175
|
+
report(error, info, "error", "caught");
|
|
176
|
+
},
|
|
177
|
+
onRecoverableError(error, info) {
|
|
178
|
+
report(error, info, "warning", "recoverable");
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
exports.FireflyErrorBoundary = FireflyErrorBoundary;
|
|
183
|
+
exports.FireflyProvider = FireflyProvider;
|
|
184
|
+
exports.createFireflyErrorHandlers = createFireflyErrorHandlers;
|
|
185
|
+
exports.toError = toError;
|
|
186
|
+
exports.useFirefly = useFirefly;
|
|
187
|
+
|
|
188
|
+
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Component, ErrorInfo, ReactNode } from "react";
|
|
2
|
+
import { FireflyClient, FireflyClient as FireflyClient$1 } from "@fz-firefly/core";
|
|
3
|
+
/** React 错误边界收集到的组件栈。 */
|
|
4
|
+
interface FireflyErrorInfo {
|
|
5
|
+
/** React 生成的组件调用栈,部分版本可能为空。 */
|
|
6
|
+
componentStack?: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** FireflyErrorBoundary 的参数。 */
|
|
9
|
+
interface FireflyErrorBoundaryProps {
|
|
10
|
+
/** 已初始化的客户端,由业务传入。 */
|
|
11
|
+
client: FireflyClient$1;
|
|
12
|
+
/** 被保护的子树。 */
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* 捕获错误后的兜底内容。
|
|
16
|
+
*
|
|
17
|
+
* 传函数时可以拿到错误与 reset,适合渲染「重试」按钮。
|
|
18
|
+
*/
|
|
19
|
+
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
20
|
+
}
|
|
21
|
+
/** FireflyErrorBoundary 的内部状态。 */
|
|
22
|
+
interface FireflyErrorBoundaryState {
|
|
23
|
+
/** 当前捕获到的错误;null 表示正常渲染。 */
|
|
24
|
+
error: Error | null;
|
|
25
|
+
}
|
|
26
|
+
/** FireflyProvider 的参数。 */
|
|
27
|
+
interface FireflyProviderProps {
|
|
28
|
+
/** 已初始化的客户端。 */
|
|
29
|
+
client: FireflyClient$1;
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* React 19 createRoot / hydrateRoot 的错误回调。
|
|
34
|
+
*
|
|
35
|
+
* 结构与 react-dom 的 RootOptions 对齐,避免 SDK 直接依赖 react-dom。
|
|
36
|
+
*/
|
|
37
|
+
interface FireflyRootErrorHandlers {
|
|
38
|
+
/** 没有被任何错误边界捕获的异常。 */
|
|
39
|
+
onUncaughtError(error: unknown, info: FireflyErrorInfo): void;
|
|
40
|
+
/** 被错误边界捕获的异常。 */
|
|
41
|
+
onCaughtError(error: unknown, info: FireflyErrorInfo): void;
|
|
42
|
+
/** 可恢复错误,例如 hydration 不匹配。 */
|
|
43
|
+
onRecoverableError(error: unknown, info: FireflyErrorInfo): void;
|
|
44
|
+
}
|
|
45
|
+
/** createFireflyErrorHandlers 的可选项。 */
|
|
46
|
+
interface FireflyErrorHandlerOptions {
|
|
47
|
+
/**
|
|
48
|
+
* 是否上报被错误边界捕获的异常,默认 true。
|
|
49
|
+
*
|
|
50
|
+
* FireflyErrorBoundary 已经上报过的错误会自动跳过;
|
|
51
|
+
* 如果业务自己的错误边界也会手动上报,可以设为 false 避免重复。
|
|
52
|
+
*/
|
|
53
|
+
reportCaught?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** 把任意抛出值统一成 Error,保证 fallback 与上报都能拿到 message。 */
|
|
56
|
+
export declare function toError(value: unknown): Error;
|
|
57
|
+
/**
|
|
58
|
+
* React 错误边界。
|
|
59
|
+
*
|
|
60
|
+
* React 没有 Vue 那样的全局 errorHandler,渲染期异常只能靠错误边界捕获。
|
|
61
|
+
* 组件会把错误上报到传入的 client,然后渲染 fallback。
|
|
62
|
+
*
|
|
63
|
+
* ```tsx
|
|
64
|
+
* <FireflyErrorBoundary client={firefly.client}>
|
|
65
|
+
* <App />
|
|
66
|
+
* </FireflyErrorBoundary>
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare class FireflyErrorBoundary extends Component<FireflyErrorBoundaryProps, FireflyErrorBoundaryState> {
|
|
70
|
+
state: FireflyErrorBoundaryState;
|
|
71
|
+
static getDerivedStateFromError(error: unknown): FireflyErrorBoundaryState;
|
|
72
|
+
componentDidCatch(error: Error, info: ErrorInfo): void;
|
|
73
|
+
/** 清除错误状态,让子树重新渲染。 */
|
|
74
|
+
private reset;
|
|
75
|
+
render(): ReactNode;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 把 client 放进 React Context,方便组件里用 useFirefly() 手动上报。
|
|
79
|
+
*
|
|
80
|
+
* 错误边界本身要求显式传 client,不依赖这个 Provider。
|
|
81
|
+
*/
|
|
82
|
+
export declare function FireflyProvider(props: FireflyProviderProps): import("react").FunctionComponentElement<import("react").ProviderProps<FireflyClient$1 | null>>;
|
|
83
|
+
/** 读取最近的 FireflyProvider 注入的客户端。 */
|
|
84
|
+
export declare function useFirefly(): FireflyClient$1;
|
|
85
|
+
/**
|
|
86
|
+
* 创建 React 19 createRoot / hydrateRoot 的错误回调。
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* createRoot(root, {
|
|
90
|
+
* ...createFireflyErrorHandlers(firefly.client),
|
|
91
|
+
* })
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* - onUncaughtError:没有被错误边界接住的异常;
|
|
95
|
+
* - onCaughtError:被错误边界接住的异常,已由 FireflyErrorBoundary 上报的会跳过;
|
|
96
|
+
* - onRecoverableError:hydration 等可恢复错误,按 warning 上报。
|
|
97
|
+
*/
|
|
98
|
+
export declare function createFireflyErrorHandlers(client: FireflyClient$1, options?: FireflyErrorHandlerOptions): FireflyRootErrorHandlers;
|
|
99
|
+
export type { FireflyClient, FireflyErrorBoundaryProps, FireflyErrorBoundaryState, FireflyErrorHandlerOptions, FireflyErrorInfo, FireflyProviderProps, FireflyRootErrorHandlers };
|
|
100
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Component, ErrorInfo, ReactNode } from "react";
|
|
2
|
+
import { FireflyClient, FireflyClient as FireflyClient$1 } from "@fz-firefly/core";
|
|
3
|
+
/** React 错误边界收集到的组件栈。 */
|
|
4
|
+
interface FireflyErrorInfo {
|
|
5
|
+
/** React 生成的组件调用栈,部分版本可能为空。 */
|
|
6
|
+
componentStack?: string | null;
|
|
7
|
+
}
|
|
8
|
+
/** FireflyErrorBoundary 的参数。 */
|
|
9
|
+
interface FireflyErrorBoundaryProps {
|
|
10
|
+
/** 已初始化的客户端,由业务传入。 */
|
|
11
|
+
client: FireflyClient$1;
|
|
12
|
+
/** 被保护的子树。 */
|
|
13
|
+
children?: ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* 捕获错误后的兜底内容。
|
|
16
|
+
*
|
|
17
|
+
* 传函数时可以拿到错误与 reset,适合渲染「重试」按钮。
|
|
18
|
+
*/
|
|
19
|
+
fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);
|
|
20
|
+
}
|
|
21
|
+
/** FireflyErrorBoundary 的内部状态。 */
|
|
22
|
+
interface FireflyErrorBoundaryState {
|
|
23
|
+
/** 当前捕获到的错误;null 表示正常渲染。 */
|
|
24
|
+
error: Error | null;
|
|
25
|
+
}
|
|
26
|
+
/** FireflyProvider 的参数。 */
|
|
27
|
+
interface FireflyProviderProps {
|
|
28
|
+
/** 已初始化的客户端。 */
|
|
29
|
+
client: FireflyClient$1;
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* React 19 createRoot / hydrateRoot 的错误回调。
|
|
34
|
+
*
|
|
35
|
+
* 结构与 react-dom 的 RootOptions 对齐,避免 SDK 直接依赖 react-dom。
|
|
36
|
+
*/
|
|
37
|
+
interface FireflyRootErrorHandlers {
|
|
38
|
+
/** 没有被任何错误边界捕获的异常。 */
|
|
39
|
+
onUncaughtError(error: unknown, info: FireflyErrorInfo): void;
|
|
40
|
+
/** 被错误边界捕获的异常。 */
|
|
41
|
+
onCaughtError(error: unknown, info: FireflyErrorInfo): void;
|
|
42
|
+
/** 可恢复错误,例如 hydration 不匹配。 */
|
|
43
|
+
onRecoverableError(error: unknown, info: FireflyErrorInfo): void;
|
|
44
|
+
}
|
|
45
|
+
/** createFireflyErrorHandlers 的可选项。 */
|
|
46
|
+
interface FireflyErrorHandlerOptions {
|
|
47
|
+
/**
|
|
48
|
+
* 是否上报被错误边界捕获的异常,默认 true。
|
|
49
|
+
*
|
|
50
|
+
* FireflyErrorBoundary 已经上报过的错误会自动跳过;
|
|
51
|
+
* 如果业务自己的错误边界也会手动上报,可以设为 false 避免重复。
|
|
52
|
+
*/
|
|
53
|
+
reportCaught?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** 把任意抛出值统一成 Error,保证 fallback 与上报都能拿到 message。 */
|
|
56
|
+
export declare function toError(value: unknown): Error;
|
|
57
|
+
/**
|
|
58
|
+
* React 错误边界。
|
|
59
|
+
*
|
|
60
|
+
* React 没有 Vue 那样的全局 errorHandler,渲染期异常只能靠错误边界捕获。
|
|
61
|
+
* 组件会把错误上报到传入的 client,然后渲染 fallback。
|
|
62
|
+
*
|
|
63
|
+
* ```tsx
|
|
64
|
+
* <FireflyErrorBoundary client={firefly.client}>
|
|
65
|
+
* <App />
|
|
66
|
+
* </FireflyErrorBoundary>
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare class FireflyErrorBoundary extends Component<FireflyErrorBoundaryProps, FireflyErrorBoundaryState> {
|
|
70
|
+
state: FireflyErrorBoundaryState;
|
|
71
|
+
static getDerivedStateFromError(error: unknown): FireflyErrorBoundaryState;
|
|
72
|
+
componentDidCatch(error: Error, info: ErrorInfo): void;
|
|
73
|
+
/** 清除错误状态,让子树重新渲染。 */
|
|
74
|
+
private reset;
|
|
75
|
+
render(): ReactNode;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 把 client 放进 React Context,方便组件里用 useFirefly() 手动上报。
|
|
79
|
+
*
|
|
80
|
+
* 错误边界本身要求显式传 client,不依赖这个 Provider。
|
|
81
|
+
*/
|
|
82
|
+
export declare function FireflyProvider(props: FireflyProviderProps): import("react").FunctionComponentElement<import("react").ProviderProps<FireflyClient$1 | null>>;
|
|
83
|
+
/** 读取最近的 FireflyProvider 注入的客户端。 */
|
|
84
|
+
export declare function useFirefly(): FireflyClient$1;
|
|
85
|
+
/**
|
|
86
|
+
* 创建 React 19 createRoot / hydrateRoot 的错误回调。
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* createRoot(root, {
|
|
90
|
+
* ...createFireflyErrorHandlers(firefly.client),
|
|
91
|
+
* })
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* - onUncaughtError:没有被错误边界接住的异常;
|
|
95
|
+
* - onCaughtError:被错误边界接住的异常,已由 FireflyErrorBoundary 上报的会跳过;
|
|
96
|
+
* - onRecoverableError:hydration 等可恢复错误,按 warning 上报。
|
|
97
|
+
*/
|
|
98
|
+
export declare function createFireflyErrorHandlers(client: FireflyClient$1, options?: FireflyErrorHandlerOptions): FireflyRootErrorHandlers;
|
|
99
|
+
export type { FireflyClient, FireflyErrorBoundaryProps, FireflyErrorBoundaryState, FireflyErrorHandlerOptions, FireflyErrorInfo, FireflyProviderProps, FireflyRootErrorHandlers };
|
|
100
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { Component, createContext, createElement, useContext } from "react";
|
|
2
|
+
// src/reported.ts
|
|
3
|
+
/**
|
|
4
|
+
* 已经由 FireflyErrorBoundary 上报过的错误。
|
|
5
|
+
*
|
|
6
|
+
* React 19 的 onCaughtError 会在错误边界处理后再次回调,
|
|
7
|
+
* 这里用 WeakSet 标记,避免同一条错误被上报两次。
|
|
8
|
+
* WeakSet 不持有强引用,不会阻止错误对象被回收。
|
|
9
|
+
*/
|
|
10
|
+
const reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
11
|
+
/** 把错误标记为已上报。 */
|
|
12
|
+
function markReported(error) {
|
|
13
|
+
if (typeof error === "object" && error !== null) reportedErrors.add(error);
|
|
14
|
+
}
|
|
15
|
+
/** 判断错误是否已经由 FireflyErrorBoundary 上报。 */
|
|
16
|
+
function isReported(error) {
|
|
17
|
+
return typeof error === "object" && error !== null && reportedErrors.has(error);
|
|
18
|
+
}
|
|
19
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/typeof.js
|
|
20
|
+
function _typeof(o) {
|
|
21
|
+
"@babel/helpers - typeof";
|
|
22
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
23
|
+
return typeof o;
|
|
24
|
+
} : function(o) {
|
|
25
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
26
|
+
}, _typeof(o);
|
|
27
|
+
}
|
|
28
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPrimitive.js
|
|
29
|
+
function toPrimitive(t, r) {
|
|
30
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
31
|
+
var e = t[Symbol.toPrimitive];
|
|
32
|
+
if (void 0 !== e) {
|
|
33
|
+
var i = e.call(t, r || "default");
|
|
34
|
+
if ("object" != _typeof(i)) return i;
|
|
35
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
36
|
+
}
|
|
37
|
+
return ("string" === r ? String : Number)(t);
|
|
38
|
+
}
|
|
39
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/toPropertyKey.js
|
|
40
|
+
function toPropertyKey(t) {
|
|
41
|
+
var i = toPrimitive(t, "string");
|
|
42
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
43
|
+
}
|
|
44
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/defineProperty.js
|
|
45
|
+
function _defineProperty(e, r, t) {
|
|
46
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
47
|
+
value: t,
|
|
48
|
+
enumerable: !0,
|
|
49
|
+
configurable: !0,
|
|
50
|
+
writable: !0
|
|
51
|
+
}) : e[r] = t, e;
|
|
52
|
+
}
|
|
53
|
+
// \0@oxc-project+runtime@0.155.0/helpers/esm/objectSpread2.js
|
|
54
|
+
function ownKeys(e, r) {
|
|
55
|
+
var t = Object.keys(e);
|
|
56
|
+
if (Object.getOwnPropertySymbols) {
|
|
57
|
+
var o = Object.getOwnPropertySymbols(e);
|
|
58
|
+
r && (o = o.filter(function(r) {
|
|
59
|
+
return Object.getOwnPropertyDescriptor(e, r).enumerable;
|
|
60
|
+
})), t.push.apply(t, o);
|
|
61
|
+
}
|
|
62
|
+
return t;
|
|
63
|
+
}
|
|
64
|
+
function _objectSpread2(e) {
|
|
65
|
+
for (var r = 1; r < arguments.length; r++) {
|
|
66
|
+
var t = null != arguments[r] ? arguments[r] : {};
|
|
67
|
+
r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
|
|
68
|
+
_defineProperty(e, r, t[r]);
|
|
69
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
|
|
70
|
+
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return e;
|
|
74
|
+
}
|
|
75
|
+
// src/error-boundary.ts
|
|
76
|
+
/** 把任意抛出值统一成 Error,保证 fallback 与上报都能拿到 message。 */
|
|
77
|
+
function toError(value) {
|
|
78
|
+
if (value instanceof Error) return value;
|
|
79
|
+
if (typeof value === "string") return new Error(value);
|
|
80
|
+
try {
|
|
81
|
+
return new Error(typeof value === "object" && value !== null ? JSON.stringify(value) : String(value));
|
|
82
|
+
} catch (_unused) {
|
|
83
|
+
return /* @__PURE__ */ new Error("Unknown error");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* React 错误边界。
|
|
88
|
+
*
|
|
89
|
+
* React 没有 Vue 那样的全局 errorHandler,渲染期异常只能靠错误边界捕获。
|
|
90
|
+
* 组件会把错误上报到传入的 client,然后渲染 fallback。
|
|
91
|
+
*
|
|
92
|
+
* ```tsx
|
|
93
|
+
* <FireflyErrorBoundary client={firefly.client}>
|
|
94
|
+
* <App />
|
|
95
|
+
* </FireflyErrorBoundary>
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
var FireflyErrorBoundary = class extends Component {
|
|
99
|
+
constructor(..._args) {
|
|
100
|
+
super(..._args);
|
|
101
|
+
_defineProperty(this, "state", { error: null });
|
|
102
|
+
_defineProperty(this, "reset", () => {
|
|
103
|
+
this.setState({ error: null });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
static getDerivedStateFromError(error) {
|
|
107
|
+
return { error: toError(error) };
|
|
108
|
+
}
|
|
109
|
+
componentDidCatch(error, info) {
|
|
110
|
+
markReported(error);
|
|
111
|
+
try {
|
|
112
|
+
this.props.client.captureException(error, _objectSpread2({ tags: { source: "react" } }, info.componentStack ? { contexts: { react: { component_stack: info.componentStack } } } : {}));
|
|
113
|
+
} catch (_unused2) {}
|
|
114
|
+
}
|
|
115
|
+
render() {
|
|
116
|
+
var _this$props$children;
|
|
117
|
+
const { error } = this.state;
|
|
118
|
+
if (!error) return (_this$props$children = this.props.children) !== null && _this$props$children !== void 0 ? _this$props$children : null;
|
|
119
|
+
const { fallback } = this.props;
|
|
120
|
+
if (typeof fallback === "function") return fallback(error, this.reset);
|
|
121
|
+
return fallback !== null && fallback !== void 0 ? fallback : null;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
// src/context.ts
|
|
125
|
+
/** 当前子树可用的 Firefly 客户端。 */
|
|
126
|
+
const FireflyContext = createContext(null);
|
|
127
|
+
/**
|
|
128
|
+
* 把 client 放进 React Context,方便组件里用 useFirefly() 手动上报。
|
|
129
|
+
*
|
|
130
|
+
* 错误边界本身要求显式传 client,不依赖这个 Provider。
|
|
131
|
+
*/
|
|
132
|
+
function FireflyProvider(props) {
|
|
133
|
+
return createElement(FireflyContext.Provider, { value: props.client }, props.children);
|
|
134
|
+
}
|
|
135
|
+
/** 读取最近的 FireflyProvider 注入的客户端。 */
|
|
136
|
+
function useFirefly() {
|
|
137
|
+
const client = useContext(FireflyContext);
|
|
138
|
+
if (!client) throw new Error("Firefly: useFirefly 必须在 FireflyProvider 内使用");
|
|
139
|
+
return client;
|
|
140
|
+
}
|
|
141
|
+
// src/root-handlers.ts
|
|
142
|
+
/**
|
|
143
|
+
* 创建 React 19 createRoot / hydrateRoot 的错误回调。
|
|
144
|
+
*
|
|
145
|
+
* ```ts
|
|
146
|
+
* createRoot(root, {
|
|
147
|
+
* ...createFireflyErrorHandlers(firefly.client),
|
|
148
|
+
* })
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* - onUncaughtError:没有被错误边界接住的异常;
|
|
152
|
+
* - onCaughtError:被错误边界接住的异常,已由 FireflyErrorBoundary 上报的会跳过;
|
|
153
|
+
* - onRecoverableError:hydration 等可恢复错误,按 warning 上报。
|
|
154
|
+
*/
|
|
155
|
+
function createFireflyErrorHandlers(client, options = {}) {
|
|
156
|
+
function report(error, info, level, phase) {
|
|
157
|
+
try {
|
|
158
|
+
client.captureException(error, _objectSpread2({
|
|
159
|
+
level,
|
|
160
|
+
tags: {
|
|
161
|
+
source: "react",
|
|
162
|
+
phase
|
|
163
|
+
}
|
|
164
|
+
}, info.componentStack ? { contexts: { react: { component_stack: info.componentStack } } } : {}));
|
|
165
|
+
} catch (_unused) {}
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
onUncaughtError(error, info) {
|
|
169
|
+
report(error, info, "error", "uncaught");
|
|
170
|
+
},
|
|
171
|
+
onCaughtError(error, info) {
|
|
172
|
+
if (options.reportCaught === false) return;
|
|
173
|
+
if (isReported(error)) return;
|
|
174
|
+
report(error, info, "error", "caught");
|
|
175
|
+
},
|
|
176
|
+
onRecoverableError(error, info) {
|
|
177
|
+
report(error, info, "warning", "recoverable");
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
export { FireflyErrorBoundary, FireflyProvider, createFireflyErrorHandlers, toError, useFirefly };
|
|
182
|
+
|
|
183
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fz-firefly/react-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "萤火虫 SDK 的 React 错误边界与根级错误处理器(内部包)。",
|
|
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.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^18.3.0",
|
|
29
|
+
"react": "^18.3.0",
|
|
30
|
+
"tsdown": "0.23.2",
|
|
31
|
+
"typescript": "^5.6.0",
|
|
32
|
+
"vitest": "^5.0.5"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": ">=16.8.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependenciesMeta": {
|
|
38
|
+
"react": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsdown",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"typecheck": "tsc --noEmit"
|
|
46
|
+
}
|
|
6
47
|
}
|