UNPKG

@sparse-bug/react

Version:

Official React SDK for Sparse Bug, a lightweight, self-hosted error tracker.

140 lines (132 loc) 4.01 kB
// src/hooks.ts import { useContext } from "react"; // src/context.ts import { createContext } from "react"; var SparseBugContext = createContext({ client: null }); // src/hooks.ts var useSparseBug = () => { const context = useContext(SparseBugContext); if (!context?.client) { throw new Error("useSparseBug must be used within a SparseBugProvider"); } return context.client; }; useSparseBug.Context = SparseBugContext; // src/provider.tsx import { useEffect, useMemo } from "react"; // src/client.ts var SparseBugClient = class { config; userContext = null; constructor(config) { if (!config.apiKey || !config.endpoint) { console.error( "[SparseBug] apiKey and endpoint are required for initialization." ); } this.config = { enabled: true, ...config }; } /** * Sets user information to be sent with subsequent error reports. * @param user - An object containing user details, or null to clear. */ setUser(user) { this.userContext = user; } /** * Reports an error to the Sparse Bug API. * @param error - The Error object to report. * @param componentStack - Optional React component stack trace. */ report(error, componentStack) { if (!this.config.enabled) { console.log("[SparseBug] Reporting disabled. Error not sent.", { error }); return; } const payload = { errorMessage: error.message, stackTrace: componentStack ? `${error.stack} Component Stack: ${componentStack}` : error.stack || "No stack trace available", context: { url: window.location.href, userAgent: navigator.userAgent, user: this.userContext } }; try { const data = JSON.stringify(payload); const reportUrl = `${this.config.endpoint}/report`; fetch(reportUrl, { method: "POST", headers: { "Content-Type": "application/json", "X-API-Key": this.config.apiKey }, body: data, keepalive: true // Crucial for requests during page unload }).catch(() => { }); } catch (e) { console.error("[SparseBug] Failed to report error.", e); } } }; // src/ErrorBoundary.tsx import React from "react"; var ErrorBoundary = class extends React.Component { // eslint-disable-next-line @typescript-eslint/no-explicit-any static contextType = useSparseBug.Context; constructor(props) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { const client = this.context?.client; client?.report(error, errorInfo.componentStack); } render() { if (this.state.hasError && this.props.fallback !== void 0 && this.state.error) { if (typeof this.props.fallback === "function") { return this.props.fallback(this.state.error); } return this.props.fallback; } return this.props.children; } }; // src/provider.tsx import { jsx } from "react/jsx-runtime"; function SparseBugProvider({ children, config, fallback }) { const client = useMemo(() => new SparseBugClient(config), [config]); useEffect(() => { const handleError = (event) => { client.report(event.error); }; const handleRejection = (event) => { const reason = event.reason instanceof Error ? event.reason : new Error(String(event.reason || "Unhandled promise rejection")); client.report(reason); }; window.addEventListener("error", handleError); window.addEventListener("unhandledrejection", handleRejection); return () => { window.removeEventListener("error", handleError); window.removeEventListener("unhandledrejection", handleRejection); }; }, [client]); return /* @__PURE__ */ jsx(SparseBugContext.Provider, { value: { client }, children: /* @__PURE__ */ jsx(ErrorBoundary, { fallback, children }) }); } export { SparseBugProvider, useSparseBug };